summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore6
-rw-r--r--AUTHORS236
-rw-r--r--BUGS5
-rw-r--r--COPYING944
-rw-r--r--ChangeLog1175
-rw-r--r--Contributing135
-rw-r--r--INSTALL89
-rw-r--r--INSTALL.macos44
-rw-r--r--INSTALL.pkgs39
-rw-r--r--INSTALL.symbian94
-rw-r--r--INSTALL.win3242
-rw-r--r--Makefile.build90
-rw-r--r--Makeinfo1
-rw-r--r--Makeproject46
-rw-r--r--README23
-rw-r--r--README-SDL13
-rw-r--r--WhatsNew512
-rwxr-xr-xbuild.sh34
-rwxr-xr-xbuild.trimui.sh8
-rw-r--r--build.vars.in65
-rw-r--r--build/msvc6/AbxDecoder.dsp105
-rw-r--r--build/msvc6/UrQuanMasters.dsp3786
-rw-r--r--build/msvc6/UrQuanMasters.dsw29
-rw-r--r--build/unix/README5
-rw-r--r--build/unix/README.crossbuild49
-rw-r--r--build/unix/README.packages31
-rw-r--r--build/unix/ansi21
-rw-r--r--build/unix/build.config780
-rw-r--r--build/unix/build.docs205
-rw-r--r--build/unix/build.sh143
-rw-r--r--build/unix/build_clean29
-rw-r--r--build/unix/build_collect26
-rw-r--r--build/unix/build_functions311
-rw-r--r--build/unix/config_functions1139
-rw-r--r--build/unix/config_proginfo_build336
-rw-r--r--build/unix/config_proginfo_host356
-rw-r--r--build/unix/make/buildtools-armv517
-rw-r--r--build/unix/make/buildtools-gcce8
-rw-r--r--build/unix/make/buildtools-generic36
-rw-r--r--build/unix/make/buildtools-winscw19
-rw-r--r--build/unix/menu_functions662
-rwxr-xr-xbuild/unix/recurse88
-rw-r--r--build/unix/todo15
-rwxr-xr-xbuild/unix/uqm-wrapper.in4
-rw-r--r--build/unix_installer/README3
-rw-r--r--build/unix_installer/USAGE14
-rwxr-xr-xbuild/unix_installer/buildinstaller.sh107
-rwxr-xr-xbuild/unix_installer/copy_mac_frameworks.pl128
-rw-r--r--build/unix_installer/install.sh.in277
-rw-r--r--build/unix_installer/installer.config139
-rw-r--r--build/unix_installer/template40
-rw-r--r--build/win32_install/build-win32-installer.sh70
-rw-r--r--build/win32_install/orzshofixti.bmpbin0 -> 154542 bytes
-rw-r--r--build/win32_install/packages.nsh23
-rwxr-xr-xbuild/win32_install/procpkgs.sh61
-rw-r--r--build/win32_install/ultron.bmpbin0 -> 25818 bytes
-rw-r--r--build/win32_install/uqm-3do.cfg11
-rw-r--r--build/win32_install/uqm-installer.nsi556
-rw-r--r--build/win32_install/uqm-pc.cfg11
-rw-r--r--content/version3
-rw-r--r--doc/devel/aniformat37
-rw-r--r--doc/devel/battleinput13
-rw-r--r--doc/devel/blue-gas-giant-pal.pngbin0 -> 1980 bytes
-rw-r--r--doc/devel/checklist52
-rw-r--r--doc/devel/contentfiles28
-rw-r--r--doc/devel/debug74
-rw-r--r--doc/devel/dialogs74
-rw-r--r--doc/devel/files115
-rw-r--r--doc/devel/fontres75
-rw-r--r--doc/devel/generate155
-rw-r--r--doc/devel/gfxlib328
-rw-r--r--doc/devel/gfxres64
-rw-r--r--doc/devel/gfxversions60
-rw-r--r--doc/devel/glossary6
-rw-r--r--doc/devel/historical39
-rw-r--r--doc/devel/input126
-rw-r--r--doc/devel/livecd80
-rw-r--r--doc/devel/meleeteams56
-rw-r--r--doc/devel/musicres18
-rw-r--r--doc/devel/netplay/notes21
-rw-r--r--doc/devel/netplay/protocol336
-rw-r--r--doc/devel/netplay/states178
-rw-r--r--doc/devel/netplay/todo120
-rw-r--r--doc/devel/orggenerate133
-rw-r--r--doc/devel/pkgformat174
-rw-r--r--doc/devel/planetrender54
-rw-r--r--doc/devel/planetrotate31
-rw-r--r--doc/devel/planettopo92
-rw-r--r--doc/devel/plugins129
-rw-r--r--doc/devel/queues62
-rw-r--r--doc/devel/racestrings45
-rw-r--r--doc/devel/resources196
-rw-r--r--doc/devel/savefile149
-rw-r--r--doc/devel/sc1186
-rw-r--r--doc/devel/script117
-rw-r--r--doc/devel/sfx202
-rw-r--r--doc/devel/statefiles123
-rw-r--r--doc/devel/strtab76
-rw-r--r--doc/devel/threads165
-rw-r--r--doc/devel/timing66
-rw-r--r--doc/devel/versions42
-rw-r--r--doc/devel/voiceeffects20
-rw-r--r--doc/users/manual.txt799
-rw-r--r--doc/users/unixinstall45
-rw-r--r--doc/users/uqm.6985
-rw-r--r--src/Makeinfo21
-rw-r--r--src/abxadec/Makefile9
-rw-r--r--src/abxadec/abxaud.c638
-rw-r--r--src/abxadec/abxaud.def16
-rw-r--r--src/abxadec/abxaud.h34
-rw-r--r--src/config.h21
-rw-r--r--src/config_unix.h.in63
-rw-r--r--src/config_vc6.h61
-rw-r--r--src/config_win.h.in65
-rw-r--r--src/darwin/Makeinfo2
-rw-r--r--src/darwin/SDLMain.h16
-rw-r--r--src/darwin/SDLMain.m404
-rw-r--r--src/endian_uqm.h136
-rw-r--r--src/getopt/Makeinfo2
-rw-r--r--src/getopt/getopt.c1061
-rw-r--r--src/getopt/getopt.h180
-rw-r--r--src/getopt/getopt1.c189
-rw-r--r--src/libs/Makeinfo19
-rw-r--r--src/libs/alarm.h9
-rw-r--r--src/libs/async.h10
-rw-r--r--src/libs/callback.h10
-rw-r--r--src/libs/callback/Makeinfo2
-rw-r--r--src/libs/callback/alarm.c177
-rw-r--r--src/libs/callback/alarm.h56
-rw-r--r--src/libs/callback/async.c56
-rw-r--r--src/libs/callback/async.h28
-rw-r--r--src/libs/callback/callback.c193
-rw-r--r--src/libs/callback/callback.h43
-rw-r--r--src/libs/cdp/Makeinfo3
-rw-r--r--src/libs/cdp/cdp.c437
-rw-r--r--src/libs/cdp/cdp.h47
-rw-r--r--src/libs/cdp/cdp_alli.h31
-rw-r--r--src/libs/cdp/cdp_iio.h50
-rw-r--r--src/libs/cdp/cdp_imem.h42
-rw-r--r--src/libs/cdp/cdp_isnd.h43
-rw-r--r--src/libs/cdp/cdp_ivid.h43
-rw-r--r--src/libs/cdp/cdpapi.c864
-rw-r--r--src/libs/cdp/cdpapi.h154
-rw-r--r--src/libs/cdp/cdpint.h48
-rw-r--r--src/libs/cdp/cdpmod.h92
-rw-r--r--src/libs/cdp/windl.c76
-rw-r--r--src/libs/cdp/windl.h37
-rw-r--r--src/libs/cdplib.h32
-rw-r--r--src/libs/compiler.h96
-rw-r--r--src/libs/declib.h57
-rw-r--r--src/libs/decomp/Makeinfo2
-rw-r--r--src/libs/decomp/lzdecode.c415
-rw-r--r--src/libs/decomp/lzencode.c468
-rw-r--r--src/libs/decomp/lzh.h91
-rw-r--r--src/libs/decomp/update.c115
-rw-r--r--src/libs/file.h95
-rw-r--r--src/libs/file/Makeinfo2
-rw-r--r--src/libs/file/dirs.c830
-rw-r--r--src/libs/file/files.c165
-rw-r--r--src/libs/file/filintrn.h24
-rw-r--r--src/libs/file/temp.c199
-rw-r--r--src/libs/gfxlib.h474
-rw-r--r--src/libs/graphics/Makeinfo12
-rw-r--r--src/libs/graphics/bbox.c133
-rw-r--r--src/libs/graphics/bbox.h46
-rw-r--r--src/libs/graphics/boxint.c183
-rw-r--r--src/libs/graphics/clipline.c241
-rw-r--r--src/libs/graphics/cmap.c663
-rw-r--r--src/libs/graphics/cmap.h77
-rw-r--r--src/libs/graphics/context.c404
-rw-r--r--src/libs/graphics/context.h147
-rw-r--r--src/libs/graphics/dcqueue.c670
-rw-r--r--src/libs/graphics/dcqueue.h55
-rw-r--r--src/libs/graphics/drawable.c501
-rw-r--r--src/libs/graphics/drawable.h88
-rw-r--r--src/libs/graphics/drawcmd.h202
-rw-r--r--src/libs/graphics/filegfx.c72
-rw-r--r--src/libs/graphics/font.c334
-rw-r--r--src/libs/graphics/font.h71
-rw-r--r--src/libs/graphics/frame.c266
-rw-r--r--src/libs/graphics/gfx_common.c196
-rw-r--r--src/libs/graphics/gfx_common.h112
-rw-r--r--src/libs/graphics/gfxintrn.h32
-rw-r--r--src/libs/graphics/gfxload.c597
-rw-r--r--src/libs/graphics/intersec.c415
-rw-r--r--src/libs/graphics/loaddisp.c65
-rw-r--r--src/libs/graphics/pixmap.c170
-rw-r--r--src/libs/graphics/prim.h80
-rw-r--r--src/libs/graphics/resgfx.c54
-rw-r--r--src/libs/graphics/sdl/2xscalers.c260
-rw-r--r--src/libs/graphics/sdl/2xscalers.h30
-rw-r--r--src/libs/graphics/sdl/2xscalers_3dnow.c102
-rw-r--r--src/libs/graphics/sdl/2xscalers_mmx.c136
-rw-r--r--src/libs/graphics/sdl/2xscalers_mmx.h56
-rw-r--r--src/libs/graphics/sdl/2xscalers_sse.c100
-rw-r--r--src/libs/graphics/sdl/Makeinfo9
-rw-r--r--src/libs/graphics/sdl/biadv2x.c532
-rw-r--r--src/libs/graphics/sdl/bilinear2x.c112
-rw-r--r--src/libs/graphics/sdl/canvas.c2176
-rw-r--r--src/libs/graphics/sdl/hq2x.c2888
-rw-r--r--src/libs/graphics/sdl/nearest2x.c207
-rw-r--r--src/libs/graphics/sdl/opengl.c575
-rw-r--r--src/libs/graphics/sdl/opengl.h89
-rw-r--r--src/libs/graphics/sdl/palette.c47
-rw-r--r--src/libs/graphics/sdl/palette.h57
-rw-r--r--src/libs/graphics/sdl/png2sdl.c300
-rw-r--r--src/libs/graphics/sdl/png2sdl.h24
-rw-r--r--src/libs/graphics/sdl/primitives.c633
-rw-r--r--src/libs/graphics/sdl/primitives.h62
-rw-r--r--src/libs/graphics/sdl/pure.c474
-rw-r--r--src/libs/graphics/sdl/pure.h29
-rw-r--r--src/libs/graphics/sdl/rotozoom.c1038
-rw-r--r--src/libs/graphics/sdl/rotozoom.h96
-rw-r--r--src/libs/graphics/sdl/scaleint.h433
-rw-r--r--src/libs/graphics/sdl/scalemmx.h793
-rw-r--r--src/libs/graphics/sdl/scalers.c289
-rw-r--r--src/libs/graphics/sdl/scalers.h27
-rw-r--r--src/libs/graphics/sdl/sdl1_common.c247
-rw-r--r--src/libs/graphics/sdl/sdl2_common.c222
-rw-r--r--src/libs/graphics/sdl/sdl2_pure.c465
-rw-r--r--src/libs/graphics/sdl/sdl_common.c308
-rw-r--r--src/libs/graphics/sdl/sdl_common.h63
-rw-r--r--src/libs/graphics/sdl/sdluio.c153
-rw-r--r--src/libs/graphics/sdl/sdluio.h39
-rw-r--r--src/libs/graphics/sdl/triscan2x.c155
-rw-r--r--src/libs/graphics/tfb_draw.c493
-rw-r--r--src/libs/graphics/tfb_draw.h199
-rw-r--r--src/libs/graphics/tfb_prim.c237
-rw-r--r--src/libs/graphics/tfb_prim.h30
-rw-r--r--src/libs/graphics/widgets.c941
-rw-r--r--src/libs/graphics/widgets.h222
-rw-r--r--src/libs/heap.h9
-rw-r--r--src/libs/heap/Makeinfo2
-rw-r--r--src/libs/heap/heap.c197
-rw-r--r--src/libs/heap/heap.h69
-rw-r--r--src/libs/inplib.h70
-rw-r--r--src/libs/input/Makeinfo6
-rw-r--r--src/libs/input/inpintrn.h25
-rw-r--r--src/libs/input/input_common.c20
-rw-r--r--src/libs/input/input_common.h39
-rw-r--r--src/libs/input/sdl/Makeinfo2
-rw-r--r--src/libs/input/sdl/input.c625
-rw-r--r--src/libs/input/sdl/input.h27
-rw-r--r--src/libs/input/sdl/keynames.c229
-rw-r--r--src/libs/input/sdl/keynames.h22
-rw-r--r--src/libs/input/sdl/vcontrol.c1300
-rw-r--r--src/libs/input/sdl/vcontrol.h108
-rw-r--r--src/libs/list.h29
-rw-r--r--src/libs/list/Makeinfo2
-rw-r--r--src/libs/list/list.c132
-rw-r--r--src/libs/list/list.h70
-rw-r--r--src/libs/log.h25
-rw-r--r--src/libs/log/Makeinfo15
-rw-r--r--src/libs/log/loginternal.h24
-rw-r--r--src/libs/log/msgbox.h23
-rw-r--r--src/libs/log/msgbox_macosx.m42
-rw-r--r--src/libs/log/msgbox_stub.c34
-rw-r--r--src/libs/log/msgbox_win.c67
-rw-r--r--src/libs/log/uqmlog.c331
-rw-r--r--src/libs/log/uqmlog.h59
-rw-r--r--src/libs/math/Makeinfo2
-rw-r--r--src/libs/math/mthintrn.h25
-rw-r--r--src/libs/math/random.c101
-rw-r--r--src/libs/math/random.h56
-rw-r--r--src/libs/math/random2.c89
-rw-r--r--src/libs/math/sqrt.c97
-rw-r--r--src/libs/mathlib.h36
-rw-r--r--src/libs/md5.h32
-rw-r--r--src/libs/md5/Makeinfo2
-rw-r--r--src/libs/md5/README6
-rw-r--r--src/libs/md5/md5.c452
-rw-r--r--src/libs/md5/md5.h130
-rw-r--r--src/libs/memlib.h43
-rw-r--r--src/libs/memory/Makeinfo1
-rw-r--r--src/libs/memory/w_memlib.c84
-rw-r--r--src/libs/mikmod/AUTHORS124
-rw-r--r--src/libs/mikmod/Makeinfo5
-rw-r--r--src/libs/mikmod/README5
-rw-r--r--src/libs/mikmod/drv_nos.c107
-rw-r--r--src/libs/mikmod/load_it.c1008
-rw-r--r--src/libs/mikmod/load_mod.c512
-rw-r--r--src/libs/mikmod/load_s3m.c470
-rw-r--r--src/libs/mikmod/load_stm.c376
-rw-r--r--src/libs/mikmod/load_xm.c817
-rw-r--r--src/libs/mikmod/mdreg.c47
-rw-r--r--src/libs/mikmod/mdriver.c935
-rw-r--r--src/libs/mikmod/mikmod.h730
-rw-r--r--src/libs/mikmod/mikmod_build.h9
-rw-r--r--src/libs/mikmod/mikmod_internals.h679
-rw-r--r--src/libs/mikmod/mloader.c607
-rw-r--r--src/libs/mikmod/mlreg.c50
-rw-r--r--src/libs/mikmod/mlutil.c337
-rw-r--r--src/libs/mikmod/mmalloc.c73
-rw-r--r--src/libs/mikmod/mmerror.c197
-rw-r--r--src/libs/mikmod/mmio.c490
-rw-r--r--src/libs/mikmod/mplayer.c3561
-rw-r--r--src/libs/mikmod/munitrk.c303
-rw-r--r--src/libs/mikmod/mwav.c210
-rw-r--r--src/libs/mikmod/npertab.c48
-rw-r--r--src/libs/mikmod/sloader.c519
-rw-r--r--src/libs/mikmod/virtch.c935
-rw-r--r--src/libs/mikmod/virtch2.c887
-rw-r--r--src/libs/mikmod/virtch_common.c459
-rw-r--r--src/libs/misc.h66
-rw-r--r--src/libs/net.h36
-rw-r--r--src/libs/network/FILES26
-rw-r--r--src/libs/network/Makeinfo14
-rw-r--r--src/libs/network/bytesex.h96
-rw-r--r--src/libs/network/connect/Makeinfo2
-rw-r--r--src/libs/network/connect/connect.c490
-rw-r--r--src/libs/network/connect/connect.h111
-rw-r--r--src/libs/network/connect/listen.c456
-rw-r--r--src/libs/network/connect/listen.h106
-rw-r--r--src/libs/network/connect/resolve.c211
-rw-r--r--src/libs/network/connect/resolve.h109
-rw-r--r--src/libs/network/netmanager/Makeinfo11
-rw-r--r--src/libs/network/netmanager/ndesc.c211
-rw-r--r--src/libs/network/netmanager/ndesc.h82
-rw-r--r--src/libs/network/netmanager/ndindex.ci103
-rw-r--r--src/libs/network/netmanager/netmanager.h48
-rw-r--r--src/libs/network/netmanager/netmanager_bsd.c223
-rw-r--r--src/libs/network/netmanager/netmanager_bsd.h26
-rw-r--r--src/libs/network/netmanager/netmanager_common.ci58
-rw-r--r--src/libs/network/netmanager/netmanager_win.c464
-rw-r--r--src/libs/network/netmanager/netmanager_win.h35
-rw-r--r--src/libs/network/netport.c91
-rw-r--r--src/libs/network/netport.h43
-rw-r--r--src/libs/network/network.h27
-rw-r--r--src/libs/network/network_bsd.c30
-rw-r--r--src/libs/network/network_win.c75
-rw-r--r--src/libs/network/socket/Makeinfo11
-rw-r--r--src/libs/network/socket/socket.c61
-rw-r--r--src/libs/network/socket/socket.h99
-rw-r--r--src/libs/network/socket/socket_bsd.c283
-rw-r--r--src/libs/network/socket/socket_bsd.h33
-rw-r--r--src/libs/network/socket/socket_win.c314
-rw-r--r--src/libs/network/socket/socket_win.h34
-rw-r--r--src/libs/network/wspiapiwrap.c34
-rw-r--r--src/libs/network/wspiapiwrap.h33
-rw-r--r--src/libs/platform.h57
-rw-r--r--src/libs/reslib.h140
-rw-r--r--src/libs/resource/Makeinfo3
-rw-r--r--src/libs/resource/direct.c101
-rw-r--r--src/libs/resource/filecntl.c146
-rw-r--r--src/libs/resource/getres.c257
-rw-r--r--src/libs/resource/index.h54
-rw-r--r--src/libs/resource/loadres.c54
-rw-r--r--src/libs/resource/propfile.c129
-rw-r--r--src/libs/resource/propfile.h30
-rw-r--r--src/libs/resource/resinit.c651
-rw-r--r--src/libs/resource/resintrn.h34
-rw-r--r--src/libs/resource/stringbank.c181
-rw-r--r--src/libs/resource/stringbank.h57
-rw-r--r--src/libs/sndlib.h107
-rw-r--r--src/libs/sound/Makeinfo9
-rw-r--r--src/libs/sound/audiocore.c272
-rw-r--r--src/libs/sound/audiocore.h169
-rw-r--r--src/libs/sound/decoders/Makeinfo8
-rw-r--r--src/libs/sound/decoders/aiffaud.c650
-rw-r--r--src/libs/sound/decoders/aiffaud.h36
-rw-r--r--src/libs/sound/decoders/decoder.c936
-rw-r--r--src/libs/sound/decoders/decoder.h129
-rw-r--r--src/libs/sound/decoders/dukaud.c546
-rw-r--r--src/libs/sound/decoders/dukaud.h36
-rw-r--r--src/libs/sound/decoders/modaud.c430
-rw-r--r--src/libs/sound/decoders/modaud.h26
-rw-r--r--src/libs/sound/decoders/oggaud.c278
-rw-r--r--src/libs/sound/decoders/oggaud.h26
-rw-r--r--src/libs/sound/decoders/wav.c385
-rw-r--r--src/libs/sound/decoders/wav.h26
-rw-r--r--src/libs/sound/fileinst.c87
-rw-r--r--src/libs/sound/mixer/Makeinfo3
-rw-r--r--src/libs/sound/mixer/mixer.c1760
-rw-r--r--src/libs/sound/mixer/mixer.h274
-rw-r--r--src/libs/sound/mixer/mixerint.h110
-rw-r--r--src/libs/sound/mixer/nosound/Makeinfo2
-rw-r--r--src/libs/sound/mixer/nosound/audiodrv_nosound.c410
-rw-r--r--src/libs/sound/mixer/nosound/audiodrv_nosound.h69
-rw-r--r--src/libs/sound/mixer/sdl/Makeinfo2
-rw-r--r--src/libs/sound/mixer/sdl/audiodrv_sdl.c486
-rw-r--r--src/libs/sound/mixer/sdl/audiodrv_sdl.h66
-rw-r--r--src/libs/sound/music.c233
-rw-r--r--src/libs/sound/openal/Makeinfo2
-rw-r--r--src/libs/sound/openal/audiodrv_openal.c420
-rw-r--r--src/libs/sound/openal/audiodrv_openal.h86
-rw-r--r--src/libs/sound/resinst.c65
-rw-r--r--src/libs/sound/sfx.c306
-rw-r--r--src/libs/sound/sndintrn.h76
-rw-r--r--src/libs/sound/sound.c178
-rw-r--r--src/libs/sound/sound.h81
-rw-r--r--src/libs/sound/stream.c814
-rw-r--r--src/libs/sound/stream.h37
-rw-r--r--src/libs/sound/trackint.h41
-rw-r--r--src/libs/sound/trackplayer.c874
-rw-r--r--src/libs/sound/trackplayer.h52
-rw-r--r--src/libs/strings/Makeinfo2
-rw-r--r--src/libs/strings/getstr.c643
-rw-r--r--src/libs/strings/sfileins.c50
-rw-r--r--src/libs/strings/sresins.c55
-rw-r--r--src/libs/strings/stringhashtable.c67
-rw-r--r--src/libs/strings/stringhashtable.h43
-rw-r--r--src/libs/strings/strings.c347
-rw-r--r--src/libs/strings/strintrn.h56
-rw-r--r--src/libs/strings/unicode.c541
-rw-r--r--src/libs/strlib.h80
-rw-r--r--src/libs/task/Makeinfo1
-rw-r--r--src/libs/task/tasklib.c139
-rw-r--r--src/libs/tasklib.h62
-rw-r--r--src/libs/threadlib.h186
-rw-r--r--src/libs/threads/Makeinfo11
-rw-r--r--src/libs/threads/pthread/Makeinfo2
-rw-r--r--src/libs/threads/pthread/posixthreads.c672
-rw-r--r--src/libs/threads/pthread/posixthreads.h103
-rw-r--r--src/libs/threads/sdl/Makeinfo2
-rw-r--r--src/libs/threads/sdl/sdlthreads.c706
-rw-r--r--src/libs/threads/sdl/sdlthreads.h106
-rw-r--r--src/libs/threads/thrcommon.c451
-rw-r--r--src/libs/threads/thrcommon.h28
-rw-r--r--src/libs/time/Makeinfo3
-rw-r--r--src/libs/time/sdl/Makeinfo2
-rw-r--r--src/libs/time/sdl/sdltime.c30
-rw-r--r--src/libs/time/sdl/sdltime.h35
-rw-r--r--src/libs/time/timecommon.c41
-rw-r--r--src/libs/time/timecommon.h30
-rw-r--r--src/libs/timelib.h49
-rw-r--r--src/libs/uio.h34
-rw-r--r--src/libs/uio/COPYING350
-rw-r--r--src/libs/uio/Makeinfo22
-rw-r--r--src/libs/uio/charhashtable.c77
-rw-r--r--src/libs/uio/charhashtable.h39
-rw-r--r--src/libs/uio/debug.c914
-rw-r--r--src/libs/uio/debug.h29
-rw-r--r--src/libs/uio/defaultfs.c41
-rw-r--r--src/libs/uio/defaultfs.h41
-rw-r--r--src/libs/uio/doc/basics178
-rw-r--r--src/libs/uio/doc/conventions30
-rw-r--r--src/libs/uio/doc/todo144
-rw-r--r--src/libs/uio/fileblock.c332
-rw-r--r--src/libs/uio/fileblock.h88
-rw-r--r--src/libs/uio/fstypes.c272
-rw-r--r--src/libs/uio/fstypes.h113
-rw-r--r--src/libs/uio/getint.h141
-rw-r--r--src/libs/uio/gphys.c620
-rw-r--r--src/libs/uio/gphys.h313
-rw-r--r--src/libs/uio/hashtable.c374
-rw-r--r--src/libs/uio/hashtable.h157
-rw-r--r--src/libs/uio/io.c1859
-rw-r--r--src/libs/uio/io.h159
-rw-r--r--src/libs/uio/ioaux.c930
-rw-r--r--src/libs/uio/ioaux.h53
-rw-r--r--src/libs/uio/iointrn.h197
-rw-r--r--src/libs/uio/match.c569
-rw-r--r--src/libs/uio/match.h182
-rw-r--r--src/libs/uio/mem.h61
-rw-r--r--src/libs/uio/memdebug.c293
-rw-r--r--src/libs/uio/memdebug.h97
-rw-r--r--src/libs/uio/mount.c168
-rw-r--r--src/libs/uio/mount.h64
-rw-r--r--src/libs/uio/mounttree.c814
-rw-r--r--src/libs/uio/mounttree.h204
-rw-r--r--src/libs/uio/paths.c602
-rw-r--r--src/libs/uio/paths.h96
-rw-r--r--src/libs/uio/physical.c174
-rw-r--r--src/libs/uio/physical.h92
-rw-r--r--src/libs/uio/stdio/Makeinfo2
-rw-r--r--src/libs/uio/stdio/stdio.c854
-rw-r--r--src/libs/uio/stdio/stdio.h111
-rw-r--r--src/libs/uio/types.h64
-rw-r--r--src/libs/uio/uioport.h173
-rw-r--r--src/libs/uio/uiostream.c603
-rw-r--r--src/libs/uio/uiostream.h97
-rw-r--r--src/libs/uio/uioutils.c228
-rw-r--r--src/libs/uio/uioutils.h92
-rw-r--r--src/libs/uio/utils.c497
-rw-r--r--src/libs/uio/utils.h43
-rw-r--r--src/libs/uio/zip/Makeinfo2
-rw-r--r--src/libs/uio/zip/zip.c1680
-rw-r--r--src/libs/uio/zip/zip.h106
-rw-r--r--src/libs/uioutils.h34
-rw-r--r--src/libs/unicode.h72
-rw-r--r--src/libs/video/Makeinfo3
-rw-r--r--src/libs/video/dukvid.c748
-rw-r--r--src/libs/video/dukvid.h36
-rw-r--r--src/libs/video/legacyplayer.c81
-rw-r--r--src/libs/video/vfileins.c28
-rw-r--r--src/libs/video/video.c190
-rw-r--r--src/libs/video/video.h56
-rw-r--r--src/libs/video/videodec.c363
-rw-r--r--src/libs/video/videodec.h124
-rw-r--r--src/libs/video/vidintrn.h41
-rw-r--r--src/libs/video/vidplayer.c481
-rw-r--r--src/libs/video/vidplayer.h31
-rw-r--r--src/libs/video/vresins.c186
-rw-r--r--src/libs/vidlib.h68
-rw-r--r--src/options.c647
-rw-r--r--src/options.h94
-rw-r--r--src/port.c145
-rw-r--r--src/port.h554
-rw-r--r--src/regex/Makeinfo2
-rw-r--r--src/regex/regcomp.ci3931
-rw-r--r--src/regex/regex.c99
-rw-r--r--src/regex/regex.h593
-rw-r--r--src/regex/regex_internal.ci1673
-rw-r--r--src/regex/regex_internal.h801
-rw-r--r--src/regex/regexec.ci4325
-rw-r--r--src/res/Makeinfo6
-rw-r--r--src/res/UrQuanMasters.rc76
-rw-r--r--src/res/darwin/Info.plist22
-rw-r--r--src/res/darwin/PkgInfo1
-rw-r--r--src/res/darwin/The Ur-Quan Masters.icnsbin0 -> 66834 bytes
-rw-r--r--src/res/darwin/uqm.r3
-rw-r--r--src/res/kohr-ah1.icobin0 -> 2238 bytes
-rw-r--r--src/res/sis1.icobin0 -> 2238 bytes
-rw-r--r--src/res/starcon2.icobin0 -> 766 bytes
-rw-r--r--src/res/ur-quan-icon-24-hover-alpha.icobin0 -> 4718 bytes
-rw-r--r--src/res/ur-quan-icon-24-hover.icobin0 -> 2262 bytes
-rw-r--r--src/res/ur-quan-icon-alpha.icobin0 -> 25214 bytes
-rw-r--r--src/res/ur-quan-icon-std.icobin0 -> 10134 bytes
-rw-r--r--src/res/ur-quan1.icobin0 -> 2238 bytes
-rw-r--r--src/res/ur-quan2.icobin0 -> 2238 bytes
-rw-r--r--src/symbian/bld.inf9
-rw-r--r--src/symbian/config.h57
-rw-r--r--src/symbian/icons_scalable_dc.mk37
-rw-r--r--src/symbian/uqm-armv5.pkg26
-rw-r--r--src/symbian/uqm-gcce.pkg26
-rw-r--r--src/symbian/uqm.cfg26
-rw-r--r--src/symbian/uqm.mmp45
-rw-r--r--src/symbian/uqm.rss26
-rw-r--r--src/symbian/uqm.svg70
-rw-r--r--src/symbian/uqm_reg.rss12
-rw-r--r--src/symbian/uqmapp.cpp308
-rw-r--r--src/types.h188
-rw-r--r--src/uqm.c1285
-rw-r--r--src/uqm/Makeinfo24
-rw-r--r--src/uqm/battle.c512
-rw-r--r--src/uqm/battle.h66
-rw-r--r--src/uqm/battlecontrols.c100
-rw-r--r--src/uqm/battlecontrols.h99
-rw-r--r--src/uqm/border.c200
-rw-r--r--src/uqm/build.c547
-rw-r--r--src/uqm/build.h69
-rw-r--r--src/uqm/cleanup.c99
-rw-r--r--src/uqm/clock.c314
-rw-r--r--src/uqm/clock.h111
-rw-r--r--src/uqm/cnctdlg.c630
-rw-r--r--src/uqm/cnctdlg.h38
-rw-r--r--src/uqm/coderes.h43
-rw-r--r--src/uqm/collide.c183
-rw-r--r--src/uqm/collide.h70
-rw-r--r--src/uqm/colors.h440
-rw-r--r--src/uqm/comm.c1649
-rw-r--r--src/uqm/comm.h142
-rw-r--r--src/uqm/comm/Makeinfo4
-rw-r--r--src/uqm/comm/arilou/Makeinfo2
-rw-r--r--src/uqm/comm/arilou/arilouc.c855
-rw-r--r--src/uqm/comm/arilou/resinst.h9
-rw-r--r--src/uqm/comm/arilou/strings.h123
-rw-r--r--src/uqm/comm/blackur/Makeinfo2
-rw-r--r--src/uqm/comm/blackur/blackurc.c567
-rw-r--r--src/uqm/comm/blackur/resinst.h9
-rw-r--r--src/uqm/comm/blackur/strings.h103
-rw-r--r--src/uqm/comm/chmmr/Makeinfo2
-rw-r--r--src/uqm/comm/chmmr/chmmrc.c641
-rw-r--r--src/uqm/comm/chmmr/resinst.h9
-rw-r--r--src/uqm/comm/chmmr/strings.h105
-rw-r--r--src/uqm/comm/comandr/Makeinfo2
-rw-r--r--src/uqm/comm/comandr/comandr.c694
-rw-r--r--src/uqm/comm/comandr/resinst.h12
-rw-r--r--src/uqm/comm/comandr/strings.h127
-rw-r--r--src/uqm/comm/commall.h26
-rw-r--r--src/uqm/comm/druuge/Makeinfo2
-rw-r--r--src/uqm/comm/druuge/druugec.c926
-rw-r--r--src/uqm/comm/druuge/resinst.h9
-rw-r--r--src/uqm/comm/druuge/strings.h132
-rw-r--r--src/uqm/comm/ilwrath/Makeinfo2
-rw-r--r--src/uqm/comm/ilwrath/ilwrathc.c649
-rw-r--r--src/uqm/comm/ilwrath/resinst.h9
-rw-r--r--src/uqm/comm/ilwrath/strings.h135
-rw-r--r--src/uqm/comm/melnorm/Makeinfo2
-rw-r--r--src/uqm/comm/melnorm/melnorm.c1855
-rw-r--r--src/uqm/comm/melnorm/resinst.h9
-rw-r--r--src/uqm/comm/melnorm/strings.h309
-rw-r--r--src/uqm/comm/mycon/Makeinfo2
-rw-r--r--src/uqm/comm/mycon/myconc.c643
-rw-r--r--src/uqm/comm/mycon/resinst.h9
-rw-r--r--src/uqm/comm/mycon/strings.h136
-rw-r--r--src/uqm/comm/orz/Makeinfo2
-rw-r--r--src/uqm/comm/orz/orzc.c898
-rw-r--r--src/uqm/comm/orz/resinst.h9
-rw-r--r--src/uqm/comm/orz/strings.h143
-rw-r--r--src/uqm/comm/pkunk/Makeinfo2
-rw-r--r--src/uqm/comm/pkunk/pkunkc.c1148
-rw-r--r--src/uqm/comm/pkunk/resinst.h9
-rw-r--r--src/uqm/comm/pkunk/strings.h214
-rw-r--r--src/uqm/comm/rebel/Makeinfo2
-rw-r--r--src/uqm/comm/rebel/rebel.c449
-rw-r--r--src/uqm/comm/rebel/strings.h61
-rw-r--r--src/uqm/comm/shofixt/Makeinfo2
-rw-r--r--src/uqm/comm/shofixt/resinst.h9
-rw-r--r--src/uqm/comm/shofixt/shofixt.c652
-rw-r--r--src/uqm/comm/shofixt/strings.h122
-rw-r--r--src/uqm/comm/slyhome/Makeinfo2
-rw-r--r--src/uqm/comm/slyhome/resinst.h9
-rw-r--r--src/uqm/comm/slyhome/slyhome.c921
-rw-r--r--src/uqm/comm/slyhome/strings.h143
-rw-r--r--src/uqm/comm/slyland/Makeinfo2
-rw-r--r--src/uqm/comm/slyland/resinst.h9
-rw-r--r--src/uqm/comm/slyland/slyland.c518
-rw-r--r--src/uqm/comm/slyland/strings.h113
-rw-r--r--src/uqm/comm/spahome/Makeinfo2
-rw-r--r--src/uqm/comm/spahome/spahome.c1018
-rw-r--r--src/uqm/comm/spahome/strings.h174
-rw-r--r--src/uqm/comm/spathi/Makeinfo2
-rw-r--r--src/uqm/comm/spathi/resinst.h14
-rw-r--r--src/uqm/comm/spathi/spathic.c834
-rw-r--r--src/uqm/comm/spathi/strings.h160
-rw-r--r--src/uqm/comm/starbas/Makeinfo2
-rw-r--r--src/uqm/comm/starbas/starbas.c1961
-rw-r--r--src/uqm/comm/starbas/strings.h327
-rw-r--r--src/uqm/comm/supox/Makeinfo2
-rw-r--r--src/uqm/comm/supox/resinst.h9
-rw-r--r--src/uqm/comm/supox/strings.h124
-rw-r--r--src/uqm/comm/supox/supoxc.c708
-rw-r--r--src/uqm/comm/syreen/Makeinfo2
-rw-r--r--src/uqm/comm/syreen/resinst.h9
-rw-r--r--src/uqm/comm/syreen/strings.h158
-rw-r--r--src/uqm/comm/syreen/syreenc.c878
-rw-r--r--src/uqm/comm/talkpet/Makeinfo2
-rw-r--r--src/uqm/comm/talkpet/resinst.h9
-rw-r--r--src/uqm/comm/talkpet/strings.h140
-rw-r--r--src/uqm/comm/talkpet/talkpet.c841
-rw-r--r--src/uqm/comm/thradd/Makeinfo2
-rw-r--r--src/uqm/comm/thradd/resinst.h9
-rw-r--r--src/uqm/comm/thradd/strings.h181
-rw-r--r--src/uqm/comm/thradd/thraddc.c954
-rw-r--r--src/uqm/comm/umgah/Makeinfo2
-rw-r--r--src/uqm/comm/umgah/resinst.h9
-rw-r--r--src/uqm/comm/umgah/strings.h114
-rw-r--r--src/uqm/comm/umgah/umgahc.c729
-rw-r--r--src/uqm/comm/urquan/Makeinfo2
-rw-r--r--src/uqm/comm/urquan/resinst.h9
-rw-r--r--src/uqm/comm/urquan/strings.h101
-rw-r--r--src/uqm/comm/urquan/urquanc.c555
-rw-r--r--src/uqm/comm/utwig/Makeinfo2
-rw-r--r--src/uqm/comm/utwig/resinst.h10
-rw-r--r--src/uqm/comm/utwig/strings.h144
-rw-r--r--src/uqm/comm/utwig/utwigc.c996
-rw-r--r--src/uqm/comm/vux/Makeinfo2
-rw-r--r--src/uqm/comm/vux/resinst.h9
-rw-r--r--src/uqm/comm/vux/strings.h129
-rw-r--r--src/uqm/comm/vux/vuxc.c796
-rw-r--r--src/uqm/comm/yehat/Makeinfo2
-rw-r--r--src/uqm/comm/yehat/resinst.h11
-rw-r--r--src/uqm/comm/yehat/strings.h102
-rw-r--r--src/uqm/comm/yehat/yehatc.c685
-rw-r--r--src/uqm/comm/zoqfot/Makeinfo2
-rw-r--r--src/uqm/comm/zoqfot/resinst.h9
-rw-r--r--src/uqm/comm/zoqfot/strings.h365
-rw-r--r--src/uqm/comm/zoqfot/zoqfotc.c975
-rw-r--r--src/uqm/commanim.c623
-rw-r--r--src/uqm/commanim.h141
-rw-r--r--src/uqm/commglue.c421
-rw-r--r--src/uqm/commglue.h183
-rw-r--r--src/uqm/confirm.c250
-rw-r--r--src/uqm/cons_res.c112
-rw-r--r--src/uqm/cons_res.h38
-rw-r--r--src/uqm/controls.h172
-rw-r--r--src/uqm/corecode.h49
-rw-r--r--src/uqm/credits.c839
-rw-r--r--src/uqm/credits.h32
-rw-r--r--src/uqm/cyborg.c1339
-rw-r--r--src/uqm/demo.c141
-rw-r--r--src/uqm/demo.h55
-rw-r--r--src/uqm/displist.c274
-rw-r--r--src/uqm/displist.h131
-rw-r--r--src/uqm/dummy.c207
-rw-r--r--src/uqm/dummy.h52
-rw-r--r--src/uqm/element.h242
-rw-r--r--src/uqm/encount.c844
-rw-r--r--src/uqm/encount.h119
-rw-r--r--src/uqm/flash.c805
-rw-r--r--src/uqm/flash.h223
-rw-r--r--src/uqm/fmv.c134
-rw-r--r--src/uqm/fmv.h41
-rw-r--r--src/uqm/galaxy.c464
-rw-r--r--src/uqm/gameev.c729
-rw-r--r--src/uqm/gameev.h68
-rw-r--r--src/uqm/gameinp.c496
-rw-r--r--src/uqm/gameopt.c1347
-rw-r--r--src/uqm/gameopt.h36
-rw-r--r--src/uqm/gamestr.h93
-rw-r--r--src/uqm/gendef.c137
-rw-r--r--src/uqm/gendef.h71
-rw-r--r--src/uqm/getchar.c442
-rw-r--r--src/uqm/globdata.c511
-rw-r--r--src/uqm/globdata.h1059
-rw-r--r--src/uqm/gravity.c200
-rw-r--r--src/uqm/grpinfo.c865
-rw-r--r--src/uqm/grpinfo.h93
-rw-r--r--src/uqm/grpintrn.h56
-rw-r--r--src/uqm/hyper.c1747
-rw-r--r--src/uqm/hyper.h90
-rw-r--r--src/uqm/ifontres.h12
-rw-r--r--src/uqm/igfxres.h274
-rw-r--r--src/uqm/ikey_con.h2
-rw-r--r--src/uqm/imusicre.h20
-rw-r--r--src/uqm/init.c351
-rw-r--r--src/uqm/init.h46
-rw-r--r--src/uqm/intel.c76
-rw-r--r--src/uqm/intel.h85
-rw-r--r--src/uqm/intro.c875
-rw-r--r--src/uqm/ipdisp.c777
-rw-r--r--src/uqm/ipdisp.h37
-rw-r--r--src/uqm/isndres.h7
-rw-r--r--src/uqm/istrtab.h154
-rw-r--r--src/uqm/load.c774
-rw-r--r--src/uqm/load_legacy.c821
-rw-r--r--src/uqm/loadship.c200
-rw-r--r--src/uqm/master.c217
-rw-r--r--src/uqm/master.h69
-rw-r--r--src/uqm/menu.c603
-rw-r--r--src/uqm/menustat.h131
-rw-r--r--src/uqm/misc.c407
-rw-r--r--src/uqm/nameref.h33
-rw-r--r--src/uqm/oscill.c191
-rw-r--r--src/uqm/oscill.h43
-rw-r--r--src/uqm/outfit.c795
-rw-r--r--src/uqm/pickship.c501
-rw-r--r--src/uqm/pickship.h35
-rw-r--r--src/uqm/plandata.c1850
-rw-r--r--src/uqm/planets/Makeinfo7
-rw-r--r--src/uqm/planets/calc.c530
-rw-r--r--src/uqm/planets/cargo.c356
-rw-r--r--src/uqm/planets/devices.c690
-rw-r--r--src/uqm/planets/elemdata.h215
-rw-r--r--src/uqm/planets/generate.h110
-rw-r--r--src/uqm/planets/generate/Makeinfo6
-rw-r--r--src/uqm/planets/generate/genall.h27
-rw-r--r--src/uqm/planets/generate/genand.c164
-rw-r--r--src/uqm/planets/generate/genburv.c192
-rw-r--r--src/uqm/planets/generate/genchmmr.c154
-rw-r--r--src/uqm/planets/generate/gencol.c126
-rw-r--r--src/uqm/planets/generate/gendefault.c373
-rw-r--r--src/uqm/planets/generate/gendefault.h66
-rw-r--r--src/uqm/planets/generate/gendru.c169
-rw-r--r--src/uqm/planets/generate/genilw.c150
-rw-r--r--src/uqm/planets/generate/genmel.c114
-rw-r--r--src/uqm/planets/generate/genmyc.c286
-rw-r--r--src/uqm/planets/generate/genorz.c222
-rw-r--r--src/uqm/planets/generate/genpet.c257
-rw-r--r--src/uqm/planets/generate/genpku.c159
-rw-r--r--src/uqm/planets/generate/genrain.c102
-rw-r--r--src/uqm/planets/generate/gensam.c324
-rw-r--r--src/uqm/planets/generate/genshof.c178
-rw-r--r--src/uqm/planets/generate/gensly.c70
-rw-r--r--src/uqm/planets/generate/gensol.c671
-rw-r--r--src/uqm/planets/generate/genspa.c283
-rw-r--r--src/uqm/planets/generate/gensup.c159
-rw-r--r--src/uqm/planets/generate/gensyr.c102
-rw-r--r--src/uqm/planets/generate/genthrad.c217
-rw-r--r--src/uqm/planets/generate/gentrap.c80
-rw-r--r--src/uqm/planets/generate/genutw.c269
-rw-r--r--src/uqm/planets/generate/genvault.c130
-rw-r--r--src/uqm/planets/generate/genvux.c329
-rw-r--r--src/uqm/planets/generate/genwreck.c111
-rw-r--r--src/uqm/planets/generate/genyeh.c140
-rw-r--r--src/uqm/planets/generate/genzfpscout.c96
-rw-r--r--src/uqm/planets/generate/genzoq.c170
-rw-r--r--src/uqm/planets/gentopo.c206
-rw-r--r--src/uqm/planets/lander.c2101
-rw-r--r--src/uqm/planets/lander.h88
-rw-r--r--src/uqm/planets/lifeform.h75
-rw-r--r--src/uqm/planets/orbits.c629
-rw-r--r--src/uqm/planets/oval.c329
-rw-r--r--src/uqm/planets/pl_stuff.c318
-rw-r--r--src/uqm/planets/plandata.h318
-rw-r--r--src/uqm/planets/planets.c483
-rw-r--r--src/uqm/planets/planets.h322
-rw-r--r--src/uqm/planets/plangen.c1954
-rw-r--r--src/uqm/planets/pstarmap.c1631
-rw-r--r--src/uqm/planets/report.c271
-rw-r--r--src/uqm/planets/roster.c428
-rw-r--r--src/uqm/planets/scan.c1385
-rw-r--r--src/uqm/planets/scan.h72
-rw-r--r--src/uqm/planets/solarsys.c2021
-rw-r--r--src/uqm/planets/solarsys.h34
-rw-r--r--src/uqm/planets/sundata.h73
-rw-r--r--src/uqm/planets/surface.c251
-rw-r--r--src/uqm/process.c1108
-rw-r--r--src/uqm/process.h37
-rw-r--r--src/uqm/races.h675
-rw-r--r--src/uqm/resinst.h24
-rw-r--r--src/uqm/restart.c413
-rw-r--r--src/uqm/restart.h33
-rw-r--r--src/uqm/save.c813
-rw-r--r--src/uqm/save.h78
-rw-r--r--src/uqm/settings.c97
-rw-r--r--src/uqm/settings.h41
-rw-r--r--src/uqm/setup.c332
-rw-r--r--src/uqm/setup.h89
-rw-r--r--src/uqm/setupmenu.c1613
-rw-r--r--src/uqm/setupmenu.h100
-rw-r--r--src/uqm/ship.c574
-rw-r--r--src/uqm/ship.h43
-rw-r--r--src/uqm/shipcont.h44
-rw-r--r--src/uqm/ships/Makeinfo5
-rw-r--r--src/uqm/ships/androsyn/Makeinfo2
-rw-r--r--src/uqm/ships/androsyn/androsyn.c528
-rw-r--r--src/uqm/ships/androsyn/androsyn.h31
-rw-r--r--src/uqm/ships/androsyn/icode.h5
-rw-r--r--src/uqm/ships/androsyn/resinst.h19
-rw-r--r--src/uqm/ships/arilou/Makeinfo2
-rw-r--r--src/uqm/ships/arilou/arilou.c303
-rw-r--r--src/uqm/ships/arilou/arilou.h31
-rw-r--r--src/uqm/ships/arilou/icode.h5
-rw-r--r--src/uqm/ships/arilou/resinst.h16
-rw-r--r--src/uqm/ships/blackurq/Makeinfo2
-rw-r--r--src/uqm/ships/blackurq/blackurq.c567
-rw-r--r--src/uqm/ships/blackurq/blackurq.h31
-rw-r--r--src/uqm/ships/blackurq/icode.h5
-rw-r--r--src/uqm/ships/blackurq/resinst.h19
-rw-r--r--src/uqm/ships/chenjesu/Makeinfo2
-rw-r--r--src/uqm/ships/chenjesu/chenjesu.c588
-rw-r--r--src/uqm/ships/chenjesu/chenjesu.h31
-rw-r--r--src/uqm/ships/chenjesu/icode.h5
-rw-r--r--src/uqm/ships/chenjesu/resinst.h19
-rw-r--r--src/uqm/ships/chmmr/Makeinfo2
-rw-r--r--src/uqm/ships/chmmr/chmmr.c790
-rw-r--r--src/uqm/ships/chmmr/chmmr.h31
-rw-r--r--src/uqm/ships/chmmr/icode.h5
-rw-r--r--src/uqm/ships/chmmr/resinst.h19
-rw-r--r--src/uqm/ships/druuge/Makeinfo2
-rw-r--r--src/uqm/ships/druuge/druuge.c324
-rw-r--r--src/uqm/ships/druuge/druuge.h31
-rw-r--r--src/uqm/ships/druuge/icode.h5
-rw-r--r--src/uqm/ships/druuge/resinst.h16
-rw-r--r--src/uqm/ships/human/Makeinfo2
-rw-r--r--src/uqm/ships/human/human.c360
-rw-r--r--src/uqm/ships/human/human.h31
-rw-r--r--src/uqm/ships/human/icode.h5
-rw-r--r--src/uqm/ships/human/resinst.h16
-rw-r--r--src/uqm/ships/ilwrath/Makeinfo2
-rw-r--r--src/uqm/ships/ilwrath/icode.h5
-rw-r--r--src/uqm/ships/ilwrath/ilwrath.c409
-rw-r--r--src/uqm/ships/ilwrath/ilwrath.h31
-rw-r--r--src/uqm/ships/ilwrath/resinst.h16
-rw-r--r--src/uqm/ships/lastbat/Makeinfo2
-rw-r--r--src/uqm/ships/lastbat/icode.h5
-rw-r--r--src/uqm/ships/lastbat/lastbat.c926
-rw-r--r--src/uqm/ships/lastbat/lastbat.h31
-rw-r--r--src/uqm/ships/lastbat/resinst.h16
-rw-r--r--src/uqm/ships/melnorme/Makeinfo2
-rw-r--r--src/uqm/ships/melnorme/icode.h5
-rw-r--r--src/uqm/ships/melnorme/melnorme.c658
-rw-r--r--src/uqm/ships/melnorme/melnorme.h31
-rw-r--r--src/uqm/ships/melnorme/resinst.h19
-rw-r--r--src/uqm/ships/mmrnmhrm/Makeinfo2
-rw-r--r--src/uqm/ships/mmrnmhrm/icode.h5
-rw-r--r--src/uqm/ships/mmrnmhrm/mmrnmhrm.c527
-rw-r--r--src/uqm/ships/mmrnmhrm/mmrnmhrm.h31
-rw-r--r--src/uqm/ships/mmrnmhrm/resinst.h19
-rw-r--r--src/uqm/ships/mycon/Makeinfo2
-rw-r--r--src/uqm/ships/mycon/icode.h5
-rw-r--r--src/uqm/ships/mycon/mycon.c376
-rw-r--r--src/uqm/ships/mycon/mycon.h31
-rw-r--r--src/uqm/ships/mycon/resinst.h16
-rw-r--r--src/uqm/ships/orz/Makeinfo2
-rw-r--r--src/uqm/ships/orz/icode.h5
-rw-r--r--src/uqm/ships/orz/orz.c1083
-rw-r--r--src/uqm/ships/orz/orz.h35
-rw-r--r--src/uqm/ships/orz/resinst.h19
-rw-r--r--src/uqm/ships/pkunk/Makeinfo2
-rw-r--r--src/uqm/ships/pkunk/icode.h5
-rw-r--r--src/uqm/ships/pkunk/pkunk.c640
-rw-r--r--src/uqm/ships/pkunk/pkunk.h31
-rw-r--r--src/uqm/ships/pkunk/resinst.h16
-rw-r--r--src/uqm/ships/probe/Makeinfo2
-rw-r--r--src/uqm/ships/probe/icode.h5
-rw-r--r--src/uqm/ships/probe/probe.c118
-rw-r--r--src/uqm/ships/probe/probe.h31
-rw-r--r--src/uqm/ships/probe/resinst.h5
-rw-r--r--src/uqm/ships/ship.h37
-rw-r--r--src/uqm/ships/shofixti/Makeinfo2
-rw-r--r--src/uqm/ships/shofixti/icode.h5
-rw-r--r--src/uqm/ships/shofixti/resinst.h23
-rw-r--r--src/uqm/ships/shofixti/shofixti.c521
-rw-r--r--src/uqm/ships/shofixti/shofixti.h31
-rw-r--r--src/uqm/ships/sis_ship/Makeinfo2
-rw-r--r--src/uqm/ships/sis_ship/icode.h5
-rw-r--r--src/uqm/ships/sis_ship/resinst.h15
-rw-r--r--src/uqm/ships/sis_ship/sis_ship.c1002
-rw-r--r--src/uqm/ships/sis_ship/sis_ship.h31
-rw-r--r--src/uqm/ships/slylandr/Makeinfo2
-rw-r--r--src/uqm/ships/slylandr/icode.h5
-rw-r--r--src/uqm/ships/slylandr/resinst.h13
-rw-r--r--src/uqm/ships/slylandr/slylandr.c438
-rw-r--r--src/uqm/ships/slylandr/slylandr.h31
-rw-r--r--src/uqm/ships/spathi/Makeinfo2
-rw-r--r--src/uqm/ships/spathi/icode.h5
-rw-r--r--src/uqm/ships/spathi/resinst.h19
-rw-r--r--src/uqm/ships/spathi/spathi.c301
-rw-r--r--src/uqm/ships/spathi/spathi.h31
-rw-r--r--src/uqm/ships/supox/Makeinfo2
-rw-r--r--src/uqm/ships/supox/icode.h5
-rw-r--r--src/uqm/ships/supox/resinst.h16
-rw-r--r--src/uqm/ships/supox/supox.c288
-rw-r--r--src/uqm/ships/supox/supox.h31
-rw-r--r--src/uqm/ships/syreen/Makeinfo2
-rw-r--r--src/uqm/ships/syreen/icode.h5
-rw-r--r--src/uqm/ships/syreen/resinst.h16
-rw-r--r--src/uqm/ships/syreen/syreen.c284
-rw-r--r--src/uqm/ships/syreen/syreen.h31
-rw-r--r--src/uqm/ships/thradd/Makeinfo2
-rw-r--r--src/uqm/ships/thradd/icode.h5
-rw-r--r--src/uqm/ships/thradd/resinst.h19
-rw-r--r--src/uqm/ships/thradd/thradd.c400
-rw-r--r--src/uqm/ships/thradd/thradd.h31
-rw-r--r--src/uqm/ships/umgah/Makeinfo2
-rw-r--r--src/uqm/ships/umgah/icode.h5
-rw-r--r--src/uqm/ships/umgah/resinst.h17
-rw-r--r--src/uqm/ships/umgah/umgah.c434
-rw-r--r--src/uqm/ships/umgah/umgah.h31
-rw-r--r--src/uqm/ships/urquan/Makeinfo2
-rw-r--r--src/uqm/ships/urquan/icode.h5
-rw-r--r--src/uqm/ships/urquan/resinst.h19
-rw-r--r--src/uqm/ships/urquan/urquan.c554
-rw-r--r--src/uqm/ships/urquan/urquan.h31
-rw-r--r--src/uqm/ships/utwig/Makeinfo2
-rw-r--r--src/uqm/ships/utwig/icode.h5
-rw-r--r--src/uqm/ships/utwig/resinst.h16
-rw-r--r--src/uqm/ships/utwig/utwig.c380
-rw-r--r--src/uqm/ships/utwig/utwig.h31
-rw-r--r--src/uqm/ships/vux/Makeinfo2
-rw-r--r--src/uqm/ships/vux/icode.h5
-rw-r--r--src/uqm/ships/vux/resinst.h17
-rw-r--r--src/uqm/ships/vux/vux.c398
-rw-r--r--src/uqm/ships/vux/vux.h31
-rw-r--r--src/uqm/ships/yehat/Makeinfo2
-rw-r--r--src/uqm/ships/yehat/icode.h5
-rw-r--r--src/uqm/ships/yehat/resinst.h19
-rw-r--r--src/uqm/ships/yehat/yehat.c369
-rw-r--r--src/uqm/ships/yehat/yehat.h31
-rw-r--r--src/uqm/ships/zoqfot/Makeinfo2
-rw-r--r--src/uqm/ships/zoqfot/icode.h5
-rw-r--r--src/uqm/ships/zoqfot/resinst.h19
-rw-r--r--src/uqm/ships/zoqfot/zoqfot.c377
-rw-r--r--src/uqm/ships/zoqfot/zoqfot.h31
-rw-r--r--src/uqm/shipstat.c437
-rw-r--r--src/uqm/shipyard.c1495
-rw-r--r--src/uqm/sis.c1741
-rw-r--r--src/uqm/sis.h241
-rw-r--r--src/uqm/sounds.c199
-rw-r--r--src/uqm/sounds.h85
-rw-r--r--src/uqm/starbase.c602
-rw-r--r--src/uqm/starbase.h55
-rw-r--r--src/uqm/starcon.c323
-rw-r--r--src/uqm/starcon.h35
-rw-r--r--src/uqm/starmap.c125
-rw-r--r--src/uqm/starmap.h42
-rw-r--r--src/uqm/state.c354
-rw-r--r--src/uqm/state.h166
-rw-r--r--src/uqm/status.c582
-rw-r--r--src/uqm/status.h75
-rw-r--r--src/uqm/supermelee/Makeinfo5
-rw-r--r--src/uqm/supermelee/buildpick.c221
-rw-r--r--src/uqm/supermelee/buildpick.h25
-rw-r--r--src/uqm/supermelee/loadmele.c826
-rw-r--r--src/uqm/supermelee/loadmele.h67
-rw-r--r--src/uqm/supermelee/melee.c2640
-rw-r--r--src/uqm/supermelee/melee.h144
-rw-r--r--src/uqm/supermelee/meleesetup.c440
-rw-r--r--src/uqm/supermelee/meleesetup.h143
-rw-r--r--src/uqm/supermelee/meleeship.h55
-rw-r--r--src/uqm/supermelee/netplay/FILES50
-rw-r--r--src/uqm/supermelee/netplay/Makeinfo4
-rw-r--r--src/uqm/supermelee/netplay/checkbuf.c145
-rw-r--r--src/uqm/supermelee/netplay/checkbuf.h77
-rw-r--r--src/uqm/supermelee/netplay/checksum.c302
-rw-r--r--src/uqm/supermelee/netplay/checksum.h99
-rw-r--r--src/uqm/supermelee/netplay/crc.c142
-rw-r--r--src/uqm/supermelee/netplay/crc.h60
-rw-r--r--src/uqm/supermelee/netplay/nc_connect.ci300
-rw-r--r--src/uqm/supermelee/netplay/netconnection.c378
-rw-r--r--src/uqm/supermelee/netplay/netconnection.h260
-rw-r--r--src/uqm/supermelee/netplay/netinput.c157
-rw-r--r--src/uqm/supermelee/netplay/netinput.h57
-rw-r--r--src/uqm/supermelee/netplay/netmelee.c740
-rw-r--r--src/uqm/supermelee/netplay/netmelee.h90
-rw-r--r--src/uqm/supermelee/netplay/netmisc.c134
-rw-r--r--src/uqm/supermelee/netplay/netmisc.h77
-rw-r--r--src/uqm/supermelee/netplay/netoptions.c39
-rw-r--r--src/uqm/supermelee/netplay/netoptions.h56
-rw-r--r--src/uqm/supermelee/netplay/netplay.h77
-rw-r--r--src/uqm/supermelee/netplay/netrcv.c193
-rw-r--r--src/uqm/supermelee/netplay/netrcv.h34
-rw-r--r--src/uqm/supermelee/netplay/netsend.c95
-rw-r--r--src/uqm/supermelee/netplay/netsend.h35
-rw-r--r--src/uqm/supermelee/netplay/netstate.c48
-rw-r--r--src/uqm/supermelee/netplay/netstate.h83
-rw-r--r--src/uqm/supermelee/netplay/notify.c118
-rw-r--r--src/uqm/supermelee/netplay/notify.h62
-rw-r--r--src/uqm/supermelee/netplay/notifyall.c146
-rw-r--r--src/uqm/supermelee/netplay/notifyall.h49
-rw-r--r--src/uqm/supermelee/netplay/packet.c263
-rw-r--r--src/uqm/supermelee/netplay/packet.h299
-rw-r--r--src/uqm/supermelee/netplay/packethandlers.c649
-rw-r--r--src/uqm/supermelee/netplay/packethandlers.h56
-rw-r--r--src/uqm/supermelee/netplay/packetq.c149
-rw-r--r--src/uqm/supermelee/netplay/packetq.h59
-rw-r--r--src/uqm/supermelee/netplay/packetsenders.c197
-rw-r--r--src/uqm/supermelee/netplay/packetsenders.h67
-rw-r--r--src/uqm/supermelee/netplay/proto/Makeinfo2
-rw-r--r--src/uqm/supermelee/netplay/proto/npconfirm.c81
-rw-r--r--src/uqm/supermelee/netplay/proto/npconfirm.h36
-rw-r--r--src/uqm/supermelee/netplay/proto/ready.c106
-rw-r--r--src/uqm/supermelee/netplay/proto/ready.h38
-rw-r--r--src/uqm/supermelee/netplay/proto/reset.c166
-rw-r--r--src/uqm/supermelee/netplay/proto/reset.h41
-rw-r--r--src/uqm/supermelee/pickmele.c948
-rw-r--r--src/uqm/supermelee/pickmele.h102
-rw-r--r--src/uqm/tactrans.c1032
-rw-r--r--src/uqm/tactrans.h59
-rw-r--r--src/uqm/trans.c154
-rw-r--r--src/uqm/units.h227
-rw-r--r--src/uqm/uqmdebug.c1926
-rw-r--r--src/uqm/uqmdebug.h200
-rw-r--r--src/uqm/util.c312
-rw-r--r--src/uqm/util.h39
-rw-r--r--src/uqm/velocity.c153
-rw-r--r--src/uqm/velocity.h76
-rw-r--r--src/uqm/weapon.c414
-rw-r--r--src/uqm/weapon.h68
-rw-r--r--src/uqmversion.h36
-rwxr-xr-xsubst51
-rwxr-xr-xuqm-indent2
-rw-r--r--uqm.lsm24
1037 files changed, 246768 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..79ab785
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+content/version export-subst
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b0a5961
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+build.vars
+/obj
+config.state
+config_unix.h
+uqm
+uqm-wrapper
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..acdea2a
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,236 @@
+We worship these two higher beings for making the original, legendary game:
+ Fred Ford
+ Paul Reiche III
+
+
+The Ur-Quan Masters port:
+-------------------------
+
+Core team (in alphabetical order):
+ Serge van den Boom <serge@vdboom.org>
+ Mika Kolehmainen <mk@kapsi.fi>
+ Michael Chapman Martin <mcmartin@gmail.com>
+ Chris Nelson <chris@toysforbob.com>
+ Alex Volkov <codepro@usa.net>
+
+Additional programming (in alphabetical order):
+ Karl Bartel
+ Scott A. Colcord
+ Loius Delacroix
+ jdorje
+ Geoffrey Hausheer <uqm-devel@phracturedblue.com>
+ Sze Howe Koh
+ Ala-lala
+ Mudrony Laszlo
+ Peter MacMillan
+ Sanjay Madhav
+ Robert McNamara
+ Vlad-Ceru Opran
+ Peter Piwowarski
+ Brian Rogers
+ Horatiu Romosan
+ Nicolas Simonds <uqm@submedia.net>
+ Peter van Valderen
+ Alexander Waseleski
+
+Music remixers (in alphabetical order):
+ Jouni Airaksinen <markvera@spacesynth.net>
+ András Barják <forevian@freemail.hu>
+ Matt Bentley
+ Travis Chase
+ Tore Aune Fjellstad <void@medievalfuture.com>
+ Espen Gätzschmann <tilt@medievalfuture.com>
+ Aaron J. Grier <agrier@poofygoof.com>
+ A. Keren
+ Casey Monroe
+ Dan Nicholson <dan@kosmic.org>
+ George Nowik <norgio@attbi.com>
+ Riku Nuottajärvi <riku.nuottajarvi@pp.inet.fi>
+ Erol Otus <erol@toysforbob.com>
+ Greg Szymanski
+
+Additional artwork (in alphabetical order):
+ Jouni Airaksinen <markvera@spacesynth.net>
+ Erol Otus <erol@toysforbob.com>
+ Zarla Sheenaza <astronia@aol.com>
+ Joffrey Smith
+ Yukki
+
+Other contributions (in alphabetical order):
+ Karl Bartel <karlb@gmx.net>
+ Travis Chase <cftc@shaw.ca> (BeOS port)
+ Felix Lazarev <felix@freedo.org> (3DO internals)
+ Mike Melanson (ADPCM basis from FFmpeg used for DUK audio)
+ Brian Rogers <burpmaster@truffula.net>
+ Horatiu Romosan <hory@post.ro> (v0.1 Win32 installer)
+
+Third party libraries:
+ Simple Directmedia Layer (SDL) by Sam Lantinga et al.
+ https://www.libsdl.org/
+
+ libpng by Guy Schalnat, Andreas Dilger, et al.
+ https://libpng.org/pub/png/libpng.html
+
+ Ogg Vorbis by the Xiph.Org Foundation
+ https://xiph.org/
+
+ Mikmod Sound System by Jean-Paul Mikkers et al.
+ http://mikmod.sourceforge.net
+
+ zlib by Jean-loup Gailly and Mark Adler
+ https://www.zlib.net
+
+Original game:
+--------------
+
+Programming & technology:
+ Fred Ford
+
+Game design and fiction:
+ Paul Reiche III
+
+3DO programming:
+ Ken Ford
+ Fred Ford
+ Brad Van Tighem
+
+Producer (3DO version):
+ Mark Wallace
+
+3DO production:
+ Paul Reiche III
+ Richard Antaki
+
+Starring the voices of:
+ Richard Antaki ....... Thraddash
+ Alex Bennett ......... Starbase Commander
+ Rich Betz ............ Ariloulaleelay
+ ............ Druuge
+ Roy Blumenfeld ....... Zoq-Fot-Pik
+ David Bryce .......... Kohr-Ah
+ .......... Ilwrath
+ .......... Shofixti
+ .......... Spathi
+ Lauren Forcella ...... Supox
+ Greg Johnson ......... Orz
+ ......... Pkunk
+ ......... Utwig
+ Bruce Leyland ........ Yehat
+ Erol Otus ............ Chmmr
+ Paul Reiche III ...... Mycon
+ ...... Talking Pet
+ Brad Van Tighem ...... Slylandro Speaker
+ Madeleine Wild ....... Zoq-Fot-Pik
+ ....... Syreen
+ ....... VUX
+ Larry Zee ............ Umgah
+ ............ Melnorme
+ ............ Ur-Quan
+ 840-AV ............... Slylandro Probe
+ Paul II, Paul III .... Victory Sequence
+ Arianna & Devin Reiche
+
+Voice effects:
+ Jeff Forehan
+ Burke Treischmann
+ Mark Miller
+
+Voice editing:
+ Richard Antaki
+ Paul Reiche III
+ Burke Treischmann
+ Steve Henefin
+ Jeremy Bredow
+ Erik Griss
+ Brad Van Tighem
+
+Art and animation:
+ George Barr
+ Paul Reiche III
+ Erol Otus
+ Greg Johnson
+ Kyle Balda
+ Jeff Rianda
+ Taunya Shiffer
+ Leonard Robel
+ Greg Hammond
+ Armand Cabrera
+ Silicon Knights
+
+Additional writing:
+ Greg Johnson
+ Mat Genser
+ Robert Leyland
+ Iain McCaig
+ Tomi Quintana
+ Erol Otus
+ Leonard Robel
+ John Estes
+
+Music:
+ Burke Treischmann
+ Dan Nicholson
+ Riku Nuottajärvi
+ Eric Berge
+ Erol Otus
+ Marc Brown
+ Aaron Grier
+ Kevin Palivec
+ Tommy Dunbar
+
+3D cinemagraphics:
+ Gene Bodio
+ Phil Le Marbre
+ TrueMotion(R) "S" Video Compression by The Duck Corporation
+
+Product marketing manager (3DO version):
+ Jim Curry
+
+3DO testers:
+ Susan Michele
+ Jeremy Bredow
+ Wes Gittens
+ Ty Johnson
+ Tate Schieferle
+ Carolina Esmurdoc
+ Rob Johnson
+ Kevin Kwan
+ Joe Ganis
+ Chang Fadel
+ Erik Griss
+ Eugene Law
+ Mark Ybarra
+ Steve Groll
+ Tim Jordan
+ Matt Young
+
+PC/DOS testers:
+ Pam Levins
+ Tomi Quintana
+ Joel Dinolt
+ Robert Daly
+ Greg Hammond
+ B.J. Shea
+ Robert Leyland
+ Sean Vikoren
+ Mike Ebert
+ Tony Hsieh
+ ROL
+ Ed Gwynn
+ Akila Redmer
+ Russell Bornschlegel
+ Steve Graziano
+ Mark Voorsanger
+
+Special thanks to:
+ Greg Johnson
+ John Ratcliffe
+
+Paul's Foundation:
+ Laurie
+ Devin
+ and Arianna
+
+Got us 86'ed out of a restaurant in Las Vegas:
+ Madeline Canepa (we love her anyway)
+
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..f7bac6a
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,5 @@
+All known bugs and missing features are listed in our online bug database,
+which can be found at
+ http://bugs.uqm.stack.nl/
+New bugs that you may find can be reported at the same location.
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..bda2c02
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,944 @@
+
+ The Ur-Quan Masters
+ Copyright (C) 1992, 2002 Toys for Bob, 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 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be entertaining,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details. A copy of the
+ General Public License is included at the end of this document.
+
+ The content -- voiceovers, dialogue, graphics, sounds, and music --
+ are copyright (C) 1992, 1993, 2002 Toys for Bob, Inc. or their
+ respective creators. The content may be used freely under the
+ terms of the Creative Commons Attribution-NonCommercial-ShareAlike
+ 2.5 license (included below, and also available at
+ http://creativecommons.org/licenses/by-nc-sa/2.5/). The content
+ may also be copied freely as part of a distribution of The Ur-Quan
+ Masters.
+
+ The documentation -- excluding documentation that is part of the
+ code or otherwise clearly governed by the preceding licenses --
+ may be used freely under the terms of the Creative Commons
+ Attribution 2.0 license (included below, and also available at
+ http://creativecommons.org/licenses/by/2.0/).
+
+----------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+----------------------------------------------------------------------------
+
+CREATIVE COMMONS LICENSE
+Attribution-NonCommercial-ShareAlike 2.5
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL
+SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT
+RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS"
+BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION
+PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE
+RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
+
+1. Definitions
+
+ a. "Collective Work" means a work, such as a periodical issue,
+ anthology or encyclopedia, in which the Work in its entirety in
+ unmodified form, along with a number of other contributions,
+ constituting separate and independent works in themselves, are
+ assembled into a collective whole. A work that constitutes a
+ Collective Work will not be considered a Derivative Work (as
+ defined below) for the purposes of this License.
+
+ b. "Derivative Work" means a work based upon the Work or upon the
+ Work and other pre-existing works, such as a translation,
+ musical arrangement, dramatization, fictionalization, motion
+ picture version, sound recording, art reproduction, abridgment,
+ condensation, or any other form in which the Work may be recast,
+ transformed, or adapted, except that a work that constitutes a
+ Collective Work will not be considered a Derivative Work for the
+ purpose of this License. For the avoidance of doubt, where the
+ Work is a musical composition or sound recording, the
+ synchronization of the Work in timed-relation with a moving
+ image ("synching") will be considered a Derivative Work for the
+ purpose of this License.
+
+ c. "Licensor" means the individual or entity that offers the Work
+ under the terms of this License.
+
+ d. "Original Author" means the individual or entity who created the
+ Work.
+
+ e. "Work" means the copyrightable work of authorship offered under
+ the terms of this License.
+
+ f. "You" means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this
+ License with respect to the Work, or who has received express
+ permission from the Licensor to exercise rights under this
+ License despite a previous violation.
+
+ g. "License Elements" means the following high-level license
+ attributes as selected by Licensor and indicated in the title of
+ this License: Attribution, Noncommercial, ShareAlike.
+
+2. Fair Use Rights. Nothing in this license is intended to reduce,
+ limit, or restrict any rights arising from fair use, first sale or
+ other limitations on the exclusive rights of the copyright owner
+ under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License,
+ Licensor hereby grants You a worldwide, royalty-free,
+ non-exclusive, perpetual (for the duration of the applicable
+ copyright) license to exercise the rights in the Work as stated
+ below:
+
+ a. to reproduce the Work, to incorporate the Work into one or more
+ Collective Works, and to reproduce the Work as incorporated in
+ the Collective Works;
+
+ b. to create and reproduce Derivative Works;
+
+ c. to distribute copies or phonorecords of, display publicly,
+ perform publicly, and perform publicly by means of a digital
+ audio transmission the Work including as incorporated in
+ Collective Works;
+
+ d. to distribute copies or phonorecords of, display publicly,
+ perform publicly, and perform publicly by means of a digital
+ audio transmission Derivative Works;
+
+ The above rights may be exercised in all media and formats whether
+ now known or hereafter devised. The above rights include the right
+ to make such modifications as are technically necessary to exercise
+ the rights in other media and formats. All rights not expressly
+ granted by Licensor are hereby reserved, including but not limited
+ to the rights set forth in Sections 4(e) and 4(f).
+
+4. Restrictions.
+
+ The license granted in Section 3 above is expressly made subject to and
+ limited by the following restrictions:
+
+ a. You may distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work only under the terms of this
+ License, and You must include a copy of, or the Uniform Resource
+ Identifier for, this License with every copy or phonorecord of
+ the Work You distribute, publicly display, publicly perform, or
+ publicly digitally perform. You may not offer or impose any
+ terms on the Work that alter or restrict the terms of this
+ License or the recipients' exercise of the rights granted
+ hereunder. You may not sublicense the Work. You must keep intact
+ all notices that refer to this License and to the disclaimer of
+ warranties. You may not distribute, publicly display, publicly
+ perform, or publicly digitally perform the Work with any
+ technological measures that control access or use of the Work in
+ a manner inconsistent with the terms of this License
+ Agreement. The above applies to the Work as incorporated in a
+ Collective Work, but this does not require the Collective Work
+ apart from the Work itself to be made subject to the terms of
+ this License. If You create a Collective Work, upon notice from
+ any Licensor You must, to the extent practicable, remove from
+ the Collective Work any credit as required by clause 4(d), as
+ requested. If You create a Derivative Work, upon notice from any
+ Licensor You must, to the extent practicable, remove from the
+ Derivative Work any credit as required by clause 4(d), as
+ requested.
+
+ b. You may distribute, publicly display, publicly perform, or
+ publicly digitally perform a Derivative Work only under the
+ terms of this License, a later version of this License with the
+ same License Elements as this License, or a Creative Commons
+ iCommons license that contains the same License Elements as this
+ License (e.g. Attribution-NonCommercial-ShareAlike 2.5
+ Japan). You must include a copy of, or the Uniform Resource
+ Identifier for, this License or other license specified in the
+ previous sentence with every copy or phonorecord of each
+ Derivative Work You distribute, publicly display, publicly
+ perform, or publicly digitally perform. You may not offer or
+ impose any terms on the Derivative Works that alter or restrict
+ the terms of this License or the recipients' exercise of the
+ rights granted hereunder, and You must keep intact all notices
+ that refer to this License and to the disclaimer of
+ warranties. You may not distribute, publicly display, publicly
+ perform, or publicly digitally perform the Derivative Work with
+ any technological measures that control access or use of the
+ Work in a manner inconsistent with the terms of this License
+ Agreement. The above applies to the Derivative Work as
+ incorporated in a Collective Work, but this does not require the
+ Collective Work apart from the Derivative Work itself to be made
+ subject to the terms of this License.
+
+ c. You may not exercise any of the rights granted to You in Section
+ 3 above in any manner that is primarily intended for or directed
+ toward commercial advantage or private monetary
+ compensation. The exchange of the Work for other copyrighted
+ works by means of digital file-sharing or otherwise shall not be
+ considered to be intended for or directed toward commercial
+ advantage or private monetary compensation, provided there is no
+ payment of any monetary compensation in connection with the
+ exchange of copyrighted works.
+
+ d. If you distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work or any Derivative Works or
+ Collective Works, You must keep intact all copyright notices for
+ the Work and provide, reasonable to the medium or means You are
+ utilizing: (i) the name of the Original Author (or pseudonym, if
+ applicable) if supplied, and/or (ii) if the Original Author
+ and/or Licensor designate another party or parties (e.g. a
+ sponsor institute, publishing entity, journal) for attribution
+ in Licensor's copyright notice, terms of service or by other
+ reasonable means, the name of such party or parties; the title
+ of the Work if supplied; to the extent reasonably practicable,
+ the Uniform Resource Identifier, if any, that Licensor specifies
+ to be associated with the Work, unless such URI does not refer
+ to the copyright notice or licensing information for the Work;
+ and in the case of a Derivative Work, a credit identifying the
+ use of the Work in the Derivative Work (e.g., "French
+ translation of the Work by Original Author," or "Screenplay
+ based on original Work by Original Author"). Such credit may be
+ implemented in any reasonable manner; provided, however, that in
+ the case of a Derivative Work or Collective Work, at a minimum
+ such credit will appear where any other comparable authorship
+ credit appears and in a manner at least as prominent as such
+ other comparable authorship credit.
+
+ e. For the avoidance of doubt, where the Work is a musical composition:
+
+ i. Performance Royalties Under Blanket Licenses. Licensor
+ reserves the exclusive right to collect, whether
+ individually or via a performance rights society
+ (e.g. ASCAP, BMI, SESAC), royalties for the public
+ performance or public digital performance (e.g. webcast)
+ of the Work if that performance is primarily intended for
+ or directed toward commercial advantage or private
+ monetary compensation.
+
+ ii. Mechanical Rights and Statutory Royalties. Licensor
+ reserves the exclusive right to collect, whether
+ individually or via a music rights agency or designated
+ agent (e.g. Harry Fox Agency), royalties for any
+ phonorecord You create from the Work ("cover version")
+ and distribute, subject to the compulsory license created
+ by 17 USC Section 115 of the US Copyright Act (or the
+ equivalent in other jurisdictions), if Your distribution
+ of such cover version is primarily intended for or
+ directed toward commercial advantage or private monetary
+ compensation.
+
+ f. Webcasting Rights and Statutory Royalties. For the avoidance of
+ doubt, where the Work is a sound recording, Licensor reserves
+ the exclusive right to collect, whether individually or via a
+ performance-rights society (e.g. SoundExchange), royalties for
+ the public digital performance (e.g. webcast) of the Work,
+ subject to the compulsory license created by 17 USC Section 114
+ of the US Copyright Act (or the equivalent in other
+ jurisdictions), if Your public digital performance is primarily
+ intended for or directed toward commercial advantage or private
+ monetary compensation.
+
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING,
+LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR
+WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED,
+STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF
+TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
+NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY,
+OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
+DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
+WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+6. Limitation on Liability.
+
+EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL
+LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL,
+INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT
+OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+ a. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this
+ License. Individuals or entities who have received Derivative
+ Works or Collective Works from You under this License, however,
+ will not have their licenses terminated provided such
+ individuals or entities remain in full compliance with those
+ licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any
+ termination of this License.
+
+ b. Subject to the above terms and conditions, the license granted
+ here is perpetual (for the duration of the applicable copyright
+ in the Work). Notwithstanding the above, Licensor reserves the
+ right to release the Work under different license terms or to
+ stop distributing the Work at any time; provided, however that
+ any such election will not serve to withdraw this License (or
+ any other license that has been, or is required to be, granted
+ under the terms of this License), and this License will continue
+ in full force and effect unless terminated as stated above.
+
+8. Miscellaneous
+
+ a. Each time You distribute or publicly digitally perform the Work
+ or a Collective Work, the Licensor offers to the recipient a
+ license to the Work on the same terms and conditions as the
+ license granted to You under this License.
+
+ b. Each time You distribute or publicly digitally perform a
+ Derivative Work, Licensor offers to the recipient a license to
+ the original Work on the same terms and conditions as the
+ license granted to You under this License.
+
+ c. If any provision of this License is invalid or unenforceable
+ under applicable law, it shall not affect the validity or
+ enforceability of the remainder of the terms of this License,
+ and without further action by the parties to this agreement,
+ such provision shall be reformed to the minimum extent necessary
+ to make such provision valid and enforceable.
+
+ d. No term or provision of this License shall be deemed waived and
+ no breach consented to unless such waiver or consent shall be in
+ writing and signed by the party to be charged with such waiver
+ or consent.
+
+ e. This License constitutes the entire agreement between the
+ parties with respect to the Work licensed here. There are no
+ understandings, agreements or representations with respect to
+ the Work not specified here. Licensor shall not be bound by any
+ additional provisions that may appear in any communication from
+ You. This License may not be modified without the mutual written
+ agreement of the Licensor and You.
+
+Creative Commons is not a party to this License, and makes no warranty
+whatsoever in connection with the Work. Creative Commons will not be
+liable to You or any party on any legal theory for any damages
+whatsoever, including without limitation any general, special,
+incidental or consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences, if Creative
+Commons has expressly identified itself as the Licensor hereunder, it
+shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the
+Work is licensed under the CCPL, neither party will use the trademark
+"Creative Commons" or any related trademark or logo of Creative
+Commons without the prior written consent of Creative Commons. Any
+permitted use will be in compliance with Creative Commons'
+then-current trademark usage guidelines, as may be published on its
+website or otherwise made available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/.
+
+----------------------------------------------------------------------
+
+CREATIVE COMMONS LICENSE ATTRIBUTION-2.0
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+DAMAGES RESULTING FROM ITS USE.
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
+CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
+PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
+WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
+PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
+AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS
+YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF
+SUCH TERMS AND CONDITIONS.
+
+1. Definitions
+
+ a. "Collective Work" means a work, such as a periodical issue,
+ anthology or encyclopedia, in which the Work in its entirety in
+ unmodified form, along with a number of other contributions,
+ constituting separate and independent works in themselves, are
+ assembled into a collective whole. A work that constitutes a
+ Collective Work will not be considered a Derivative Work (as
+ defined below) for the purposes of this License.
+
+ b. "Derivative Work" means a work based upon the Work or upon the
+ Work and other pre-existing works, such as a translation,
+ musical arrangement, dramatization, fictionalization, motion
+ picture version, sound recording, art reproduction, abridgment,
+ condensation, or any other form in which the Work may be recast,
+ transformed, or adapted, except that a work that constitutes a
+ Collective Work will not be considered a Derivative Work for the
+ purpose of this License. For the avoidance of doubt, where the
+ Work is a musical composition or sound recording, the
+ synchronization of the Work in timed-relation with a moving
+ image ("synching") will be considered a Derivative Work for the
+ purpose of this License.
+
+ c. "Licensor" means the individual or entity that offers the Work
+ under the terms of this License.
+
+ d. "Original Author" means the individual or entity who created the
+ Work.
+
+ e. "Work" means the copyrightable work of authorship offered under
+ the terms of this License.
+
+ f. "You" means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this
+ License with respect to the Work, or who has received express
+ permission from the Licensor to exercise rights under this
+ License despite a previous violation.
+
+2. Fair Use Rights. Nothing in this license is intended to reduce,
+ limit, or restrict any rights arising from fair use, first sale or
+ other limitations on the exclusive rights of the copyright owner
+ under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License,
+ Licensor hereby grants You a worldwide, royalty-free,
+ non-exclusive, perpetual (for the duration of the applicable
+ copyright) license to exercise the rights in the Work as stated
+ below:
+
+ a. to reproduce the Work, to incorporate the Work into one or more
+ Collective Works, and to reproduce the Work as incorporated in
+ the Collective Works;
+
+ b. to create and reproduce Derivative Works;
+
+ c. to distribute copies or phonorecords of, display publicly,
+ perform publicly, and perform publicly by means of a digital
+ audio transmission the Work including as incorporated in
+ Collective Works;
+
+ d. to distribute copies or phonorecords of, display publicly,
+ perform publicly, and perform publicly by means of a digital
+ audio transmission Derivative Works.
+
+ e. For the avoidance of doubt, where the work is a musical
+ composition:
+
+ i. Performance Royalties Under Blanket Licenses. Licensor
+ waives the exclusive right to collect, whether
+ individually or via a performance rights society
+ (e.g. ASCAP, BMI, SESAC), royalties for the public
+ performance or public digital performance (e.g. webcast)
+ of the Work.
+
+ ii. Mechanical Rights and Statutory Royalties. Licensor
+ waives the exclusive right to collect, whether
+ individually or via a music rights agency or designated
+ agent (e.g. Harry Fox Agency), royalties for any
+ phonorecord You create from the Work ("cover version")
+ and distribute, subject to the compulsory license created
+ by 17 USC Section 115 of the US Copyright Act (or the
+ equivalent in other jurisdictions).
+
+ f. Webcasting Rights and Statutory Royalties. For the avoidance of
+ doubt, where the Work is a sound recording, Licensor waives the
+ exclusive right to collect, whether individually or via a
+ performance-rights society (e.g. SoundExchange), royalties for
+ the public digital performance (e.g. webcast) of the Work,
+ subject to the compulsory license created by 17 USC Section 114
+ of the US Copyright Act (or the equivalent in other
+ jurisdictions).
+
+ The above rights may be exercised in all media and formats whether
+ now known or hereafter devised. The above rights include the right
+ to make such modifications as are technically necessary to exercise
+ the rights in other media and formats. All rights not expressly
+ granted by Licensor are hereby reserved.
+
+4. Restrictions. The license granted in Section 3 above is expressly
+ made subject to and limited by the following restrictions:
+
+ a. You may distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work only under the terms of this
+ License, and You must include a copy of, or the Uniform Resource
+ Identifier for, this License with every copy or phonorecord of
+ the Work You distribute, publicly display, publicly perform, or
+ publicly digitally perform. You may not offer or impose any
+ terms on the Work that alter or restrict the terms of this
+ License or the recipients' exercise of the rights granted
+ hereunder. You may not sublicense the Work. You must keep intact
+ all notices that refer to this License and to the disclaimer of
+ warranties. You may not distribute, publicly display, publicly
+ perform, or publicly digitally perform the Work with any
+ technological measures that control access or use of the Work in
+ a manner inconsistent with the terms of this License
+ Agreement. The above applies to the Work as incorporated in a
+ Collective Work, but this does not require the Collective Work
+ apart from the Work itself to be made subject to the terms of
+ this License. If You create a Collective Work, upon notice from
+ any Licensor You must, to the extent practicable, remove from
+ the Collective Work any reference to such Licensor or the
+ Original Author, as requested. If You create a Derivative Work,
+ upon notice from any Licensor You must, to the extent
+ practicable, remove from the Derivative Work any reference to
+ such Licensor or the Original Author, as requested.
+
+ b. If you distribute, publicly display, publicly perform, or
+ publicly digitally perform the Work or any Derivative Works or
+ Collective Works, You must keep intact all copyright notices for
+ the Work and give the Original Author credit reasonable to the
+ medium or means You are utilizing by conveying the name (or
+ pseudonym if applicable) of the Original Author if supplied; the
+ title of the Work if supplied; to the extent reasonably
+ practicable, the Uniform Resource Identifier, if any, that
+ Licensor specifies to be associated with the Work, unless such
+ URI does not refer to the copyright notice or licensing
+ information for the Work; and in the case of a Derivative Work,
+ a credit identifying the use of the Work in the Derivative Work
+ (e.g., "French translation of the Work by Original Author," or
+ "Screenplay based on original Work by Original Author"). Such
+ credit may be implemented in any reasonable manner; provided,
+ however, that in the case of a Derivative Work or Collective
+ Work, at a minimum such credit will appear where any other
+ comparable authorship credit appears and in a manner at least as
+ prominent as such other comparable authorship credit.
+
+5. Representations, Warranties and Disclaimer. UNLESS OTHERWISE
+MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK
+AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND
+CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF
+ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW
+THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY
+TO YOU.
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY
+ APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
+ LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
+ OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE
+ WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGES.
+
+7. Termination
+
+ a. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this
+ License. Individuals or entities who have received Derivative
+ Works or Collective Works from You under this License, however,
+ will not have their licenses terminated provided such
+ individuals or entities remain in full compliance with those
+ licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any
+ termination of this License.
+
+ b. Subject to the above terms and conditions, the license granted
+ here is perpetual (for the duration of the applicable copyright
+ in the Work). Notwithstanding the above, Licensor reserves the
+ right to release the Work under different license terms or to
+ stop distributing the Work at any time; provided, however that
+ any such election will not serve to withdraw this License (or
+ any other license that has been, or is required to be, granted
+ under the terms of this License), and this License will continue
+ in full force and effect unless terminated as stated above.
+
+8. Miscellaneous
+
+ a. Each time You distribute or publicly digitally perform the Work
+ or a Collective Work, the Licensor offers to the recipient a
+ license to the Work on the same terms and conditions as the
+ license granted to You under this License.
+
+ b. Each time You distribute or publicly digitally perform a
+ Derivative Work, Licensor offers to the recipient a license to
+ the original Work on the same terms and conditions as the
+ license granted to You under this License.
+
+ c. If any provision of this License is invalid or unenforceable
+ under applicable law, it shall not affect the validity or
+ enforceability of the remainder of the terms of this License,
+ and without further action by the parties to this agreement,
+ such provision shall be reformed to the minimum extent necessary
+ to make such provision valid and enforceable.
+
+ d. No term or provision of this License shall be deemed waived and
+ no breach consented to unless such waiver or consent shall be in
+ writing and signed by the party to be charged with such waiver
+ or consent.
+
+ e. This License constitutes the entire agreement between the
+ parties with respect to the Work licensed here. There are no
+ understandings, agreements or representations with respect to
+ the Work not specified here. Licensor shall not be bound by any
+ additional provisions that may appear in any communication from
+ You. This License may not be modified without the mutual written
+ agreement of the Licensor and You.
+
+Creative Commons is not a party to this License, and makes no warranty
+whatsoever in connection with the Work. Creative Commons will not be
+liable to You or any party on any legal theory for any damages
+whatsoever, including without limitation any general, special,
+incidental or consequential damages arising in connection to this
+license. Notwithstanding the foregoing two (2) sentences, if Creative
+Commons has expressly identified itself as the Licensor hereunder, it
+shall have all rights and obligations of Licensor.
+
+Except for the limited purpose of indicating to the public that the
+Work is licensed under the CCPL, neither party will use the trademark
+"Creative Commons" or any related trademark or logo of Creative
+Commons without the prior written consent of Creative Commons. Any
+permitted use will be in compliance with Creative Commons'
+then-current trademark usage guidelines, as may be published on its
+website or otherwise made available upon request from time to time.
+
+Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..36052bb
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,1175 @@
+0.8:
+- Grouping ship properties together at the top of the ship .c files, from
+ oldlaptop.
+- Some added documentation for UIO.
+- Added a StringHashTable type.
+- A few superficial fixes to charhashtable.
+- Removed GraphicsLock and made comm callbacks queued - Alex
+- Split debug key function into sync and async parts, paving the way
+ for GraphicsLock removal - Alex
+- PageUp/PageDown now add/remove 10 fuel in the shipyard, from
+ Scott A. Colcord, Nic
+- Annihigate flash thread - SvdB
+- Switch SetFlashRect() to the new flash code - SvdB
+- Add C++ support to the build system, from Scott A. Colcord
+- Cleaning up DoModifyShips() - SvdB
+- Added Valgrind suppression file, from Louis Delacroix
+- Fix several memory leaks, from Loius Delacroix
+- Some cleanups / warnings fixes, from Louis Delacroix
+- Added a free callback function for the values of the key-value pairs
+ in hash tables - SvdB
+- Annigilate ActivateStarShip() - SvdB
+- Removed obsolete RESPONSE_TO_REF() - SvdB
+- Don't require the 'shadow' dir in addon packs, from Alex
+- Make use of GAME_STATE_FILE consistently, from Scott A. Colcord
+- Fixed unconst(), from Scott A. Colcord
+- Fixes to a few small bugs in UIO which shouldn't have had an impact on
+ UQM, but would in the future, from Alex
+- Allow F6 as a default alternate search key, from related projects.
+- "Player 1" and "Player 2" are now "Bottom" and "Top" player (#954) - McM
+- Fix Utwig dialog inconsistency (#756) - McM
+- Use a deterministic seed for Melnorme modules-for-fuel deals (#567) - McM
+- Various warnings cleanup (bug #50), from Scott A. Colcord
+- Fixed various Pkunk reincarnation and Shofixti Glory device interactions;
+ Pkunk ditty plays in a simultaneous destruction (bug #666) - Alex
+- Preparing for linking with C++ code, from Scott A. Colcord
+- Fixed player's phrase leading to Tanaka's response about a solitary
+ vigil (bug #859) - Alex
+- Added the Ur-Quan to the list of starfaring races on which Commander
+ Hayes provides info (bug #865) - Alex
+- Commander Hayes now advises the player correctly on the number of
+ landers one needs (bug #1098) - Alex
+- All Sound Options are now preserved between visits to the setup menu.
+ Some options still require a restart (bug #1132) - Alex
+- Removed the remnants of the --music option (bug #1133) - Alex
+- Fixed flagship re-entering the inner system after an encounter in the
+ outer system, thus trapping the player (bug #1135) - Alex
+- Fixed starmap fuel range circle shrinking (bug #1130) - Alex
+- Added gamma correction to the setup menu (bug #977) - Alex, Nic
+- Refactor Melnorme comm code to make modding easier; step 1 (bug #1128),
+ from Scott A. Colcord
+- Added aspect ratio option to the setup menu - Alex
+- Positional audio setup menu option now has an immediate effect - Alex
+- Added speech option which controls loading of 3dovoice addon (no need
+ to set speech volume to 0 anymore) - Alex
+- Added the MetaChron story to the no-voice Melnorme script (bug #43) - Alex
+- Spelling and punctuation pass (bug #1200), from Anthony J. Bentley
+- New Man Page edition of the user manual (Bug #1204), from Bentley
+- STAR CONTROL trademark information updated to reflect current events (#1201)
+- Reorganize and update credits generally - Michael
+- Port Win32 Installer generator to run in MSYS2 bash - Michael
+- Update Win32 Installer to bring net-install logic back up to date with
+ current Sourceforge installs; generic support now available for both HTTPS
+ and automatic redirection to mirrors - Michael
+- Fixed MSYS resource script compilation/linking, from Ala-lala
+- Fixed stale main-screen header text displays, from Kruzen
+
+0.7.1:
+- New SDL2 backend for modern operating systems - Michael
+- Fixed netplay when compiling against modern versions of Visual
+ Studio, from Ala-lala
+
+0.7.0-1: (Maintenance release):
+- Altered header include guard constants to stop conflicts with modern
+ versions of gcc - Michael
+
+0.7.0:
+- Build with debug info in strict-debug mode (bug #1127), from Scott A. Colcord
+- Added spoken Slylandro probe coordinates (bug #732) - Nic, Alex
+- Added the missing bits to the no-voice versions of Mycon, Syreen and
+ Utwig dialogue (bug #327) - Alex
+- Syreen dialog tree no longer resets after visiting the Syreen Vault
+ for the very first time (bug #891) - Alex
+- Fixed lander report at the Syreen Vault inconsistency (bug #1121) - Alex
+- Added lander reports for Supox ruins and Ultron. The reports text
+ is new content from TFB. (bug #1120) - Alex
+- Fixed 'caster comm with Ilwrath after they die off (bug #850) - Alex
+- Fixed Syreen Vault lander report infinite loop (bug #1118) - Alex
+- Same captain names are used on both net sides (bug #989) - Alex
+- Fixed sync loss in netplay games having duplicate ships (bug #1081) - Alex
+- Fixed loading of melee.cfg (bug #1099) - Alex
+- Fixed the 64KB size limitation on key-value files (bug #1112) - Alex
+- Update download paths for the new SourceForge File Release system - Michael
+- Fix UAC issues with installer for cleaner Vista/Win7 installs - Michael
+- Fixed compilation with Netplay disabled (bug #1091), from Sze Howe Koh
+- Fixed ship picking order after a simultaneous destruction, e.g.
+ Shofixti picks last after Glory device (bugs #1087, #1088) - Alex
+- Game no longer locks up after quickly escaping melee (bug #1003) - Alex
+- Reset input delay upon leaving Supermelee (bug #1022) - Alex
+- Properly account for simultaneous destruction of last ships
+ in each fleet in Supermelee (bug #437) - Alex
+- Do not match singular stars when given a prefix in star search
+ (bug #1071) - Alex
+- Preserve character case when editing with joystick (bug #1080) - Alex
+- Fixed misaligned cargo count (bug #1092) - Coredev
+- Fixed the network SuperMelee team configuration protocol - SvdB
+- Fixed fuel reserve bounds checks - SvdB
+- Fixed a crash when filling fuel tanks over 10 (bug #1082) - Alex
+- Got rid of many warnings - SvdB
+- Clean up and some refactoring of the SuperMelee code - SvdB
+- Fixed concurrent screen fades regression (bug #1079) - Alex
+- Removed some legacy source code files related to resources - SvdB
+- Put SuperMelee source files in separate subdirectory - SvdB
+- Added additive and alpha drawing modes to graphics lib - Alex
+- Fixed black pixel gaps between the planet and shield when entering the
+ orbit of a shielded planet (bug #32) - Alex
+- Split off SDL-specific colormap bits into SDL domain - Alex
+- Fixed planet blinking when exiting scan (bug #799) - Alex
+- Restore menu sounds after editing a control set name (bug #1066) - Alex
+- Use an own 'UniChar' rather than 'wchar_t', which may not be large
+ enough, depending on the platform - SvdB
+- Added 'COLOR' resource type - SvdB
+- All graphics operations use 24-bits colors at the game level too now,
+ instead of 16 bits colors. - SvdB
+- Cross-platform safemode (ignores uqm.cfg, bug #946) - Michael
+- Correct Chmmr response when asking about Sa-Matra (bug #1073) - Alex
+- Refactored universe generation - SvdB
+- Comm animation processing rewrite, bugs fixed - Alex
+- Added graphics context debugging function - SvdB
+- Thread down-throttling and game sleep when inactive (currently disabled),
+ (bug #1070), from Flandry
+- Internal changes: GOOD_GUY/BAD_GUY ship flags retired - Alex
+- Fixed Melee menu timeout when both sides are Cyborgs (bug #1067) - Alex
+- Fixed AI ship not moving on warp in (bug #648) - Alex
+- Revert gfx settings entirely when a mode switch fails (bug #1056) - Alex
+- Fixed the Syreen lights-out scene timing (bug #1011) - Alex
+- Added a native error box for MacOSX (like we have for Windows) - Alex
+- Fix for weird colors problem on MacOSX w/ SDL 1.2.14; also improves
+ overall compatibility on all platforms - Alex
+- Unix build system cleanups, fix detection of SDL, libmikmod, pthread - SvdB
+- Make DoPopupWindow() work over faded out screens. - SvdB
+- Trackplayer rewrite; fixed many bugs - Alex
+- Source tree reorg: libs/ moved out of sc2code/, msvc++/ moved to
+ build/msvc6/, src/sc2code/ renamed to src/uqm/ - Coredev
+- Druuge no longer turn hostile after attempting a salvage (bug #1013) - Alex
+- Process subtitles correctly with no timestamp file (bug #1060) - Alex
+- Lander will no longer hang when killed on planets with a lot of
+ natural disasters (bug #584) - Alex
+- Canceling load from the main menu returns to main menu (bug #679) - Alex
+- Fixed inputting numbers with the numpad, except directx (bug #934) - Alex
+- Better location description in savegame summaries (bug #844) - Alex
+- Fixed crash when saving a game into the last slot while having
+ too many devices on board - Alex
+- Allow any sound data format to be graphed by comm oscilloscope; also
+ auto-adjust the scope for different gain levels (bug #1064) - Alex
+- Game settings Quit menu now delegates to F10 quit (bug #462) - Alex
+- Do not pause the game in places where not relevant (bug #984) - Alex
+- Fixed crashes and potential weirdness when loading savegames from
+ a Homeworld encounter screen (bug #997) - Alex
+- Cleanup of GLOBAL(ShipStamp.frame) abuse; fixes bug #1054 - Alex
+- Game attempts to exit cleanly under normal circustances (bug #52) - Alex
+- Fixed Mmrnmhrm's X-Form transformation without energy use (bug #1004) - Alex
+- Added missing sleeps in DoInput() functions (bug #893) - Alex
+- Starmap unit conversion corrections; fixes bug #970 - Alex
+- Rounding-error correction in log(x|y)ToUniverse (bug #1046), from Nic
+- Change hardcoded Starbase and Sa-Matra values to pretty enum values
+ (bug #1047), from Nic
+- Load override.cfg from user's dir to add or override menu controls - Alex
+- Allow addons to override any content by placing zips into their
+ 'shadow-content' dir - Alex
+- Content reorg: font chars now use hexadecimal numbering - Alex
+- Content reorg: some race comm and ships renamed, ship files renamed,
+ many ani files renamed, new naming scheme for ani frames and voice - Alex
+- .cfg files are now kept as subtrees of the resource map - Michael
+- Flight control data no longer unnecessarily copied to config directory - Michael
+- Index loading/saving now can operate on subtrees - Michael
+- Videos vs. slide now controlled by a '3dovideo' addon - Michael
+- New video resource type for 3DO videos - Michael
+- Allow reaching 999.9:999.9 in HyperSpace (bug #628), from Nic
+- Use system getopt_long() when available - SvdB
+- Added --addondir commandline option - Mika
+- Case insensitive matching when looking for .zip/.uqm/.rmp files - SvdB
+- Added read-ahead buffering when reading zip index files. - SvdB
+- Added support for packed ani and font files - Mika
+- DrawTracedText abstraction (bug #1029), from Nic
+- Experimental support for Symbian S60 3rd edition - Mika & SvdB
+- Pthread support - Mika
+- Content Dirs completely reorganized; 3DO and PC segregation - Coredev
+- Voiceovers controlled by a synthetic '3dovoice' addon - Michael
+- CONVERSATION explicitly names text/voice/timestamps - Michael
+- Replaced stricmp() by the POSIX compatible strcasecmp() - SvdB
+- Split STRTAB further into STRTAB and CONVERSATION - Michael
+- INT32, BOOLEAN, and STRING resource types - Michael
+- UNKNOWNRES is now safe to load, and "loads" as its resvalue - Michael
+- CODE is now SHIP, and uses an integer descriptor instead of a one-byte
+ .cod file - Michael
+- ResourceLoadFun is now descriptor-based, not stream-based - Michael
+- Removed RES_TYPE enum, folded into ResourceDesc - Michael
+- Split STRTAB into STRTAB (strings) and BINTAB (color/xlat tables) - Michael
+- Removed internal references to defunct resource types - Michael
+- Revamped resource system to only use .rmp files - Michael
+- Isolated all constructed resources into cons_res.c - Michael
+- Fixed a crash when conversing with music disabled - Michael
+- Moved all resources into starcon.ls2 - Michael
+- Fixed compile errors when compiling without joystick support - Michael
+- Added endian-aware integer read functions to uio - SvdB
+- Introduce the concept of an "InputContext" - SvdB
+- Don't use alloca() in uio. - SvdB
+- Replace PlayerOne/PlayerTwo by PlayerControls[0]/PlayerControls[1] - SvdB
+- Moved comm resources into starcon.ls2 - Michael
+- Repackaged static comm/ship data to it all uniquely named - Michael
+- On MacOS X, search for the content in the application bundle, from Nic
+- Planetside resource names are now consistent and generatable - Michael
+- Androsynth ruins freeing condition now uses cycles (Bug #1028) - Michael
+- .rmp files now carry the types of the targets - Michael
+- Joystick threshold defaults to 10,000, not 0 (Bug #1046) - Michael
+- Remove MEM_HANDLEs from everywhere outside of memlib - Michael
+- Split out RESOURCEs from the loaded data in RACE_DESC and LOCDATA
+ structures - Michael
+- Prevent overflow for planet weight when scanning a planet (bug #1025)
+ - from Benjamin Alan Weaver
+- Don't set _POSIX_THREAD_SAFE_FUNCTIONS - SvdB
+- Support for Windows CE. - SvdB with Pavel Chernikov
+- Added support to the unix build system for explicitely specifying the name
+ of the define to set to show when a symbol is found.
+ Also a fix for when 'strcasecmp' is #define'd by the system. - SvdB
+- Rewrote mapres.c to use uio's hashtables instead of its own
+ association lists - Michael
+- Added a Remix option to the setup menu - Michael
+- Addon zips can live in content/addons directly - Michael
+- 3DO music separated into an addon pack - Michael
+- Major change in resource index scheme - Michael
+ - .lst replaced (mapping to IDs instead of to files)
+ - .rmp files give the mapping from IDs to files.
+ - Addons provide additional files instead of overriding UIO
+ - Addons must provide .rmp files to do the necessary overrides.
+- Removed unnecessary _ALIGNED_ON macro usage - SvdB
+- The current directory is now among the locations searched for the content
+ when no explicit location has been specified. (bug fix) - from Nic
+- Non-3DO Shipspin anims now use Presentations - Michael
+- Added presentation commands TEXT, TE (text effect), MOVIE - Alex
+- Increased the size of display queue (elements were sometimes missing
+ in e.g. Nemesis vs. Nemesis battles with many marines out) - Alex
+- ShowPresentation() no longer clears the screen by force; presentations
+ now do this by request - Michael
+- Added match_matchPatternOnce() - SvdB
+- Fixed a problem with blue ships after Avatar's tractor beam,
+ along with some other fill-stamp situations; bug #929 - Alex
+- Added TFB_Canvas_Lock(), TFB_Canvas_Unlock() and TFB_Canvas_GetStride()
+ - SvdB
+- Scaling images with respect to their hotspots: stabilizes compound
+ Melee objects; re-added bilinear Melee scaler; zooming planet uses
+ bilinear; fixes bug #685 - Alex
+- Added --keepaspectratio to keep correct aspect ratio when using
+ custom resolutions in OpenGL mode - Mika
+- Add /var/tmp as possible location for temporary files. Don't try
+ /tmp and /var/tmp at all on MS Windows (Cygwin excepted) - SvdB
+- Added fullscreen/windowed toggle key F11 (bug #578) - Mika
+- Allow building without ogg vorbis support (bug #852) - SvdB
+- Reworked SuperMelee fleet loading (fixes bug #823) - SvdB
+- Fixed enemy ships getting recrewed between ecnounters in HyperSpace
+ (bug #996) - Alex
+- Removed mouse_err.c since DoPopupWindow() is used now - SvdB
+- (debugging) Fixed instant-move towards the current location - SvdB
+- Fixed wrong Sa-Matra guards icons after Kohr-Ah win (bug #1001) - Alex
+- Internal ship structures and queues refactoring and cleanup - Alex
+- Fix quitting out of IP before the IP is fully set (bug #987) - Michael
+- Fixed speech looping with long tracks at high sampling rates;
+ scope supports higher rates for speech now; bug 999 - Alex
+- Basic support for .ani-based shipspin animations - Michael
+- Fixed some Melnorme history info timestamps - Alex
+- Fixed Ur-Quan story timestamps, from Vlad-Ceru Opran
+- Removed the 256-frame limit on .ani files - Michael
+- Renamed PlaySpeech/StopSpeech to work around name collisions
+ on OSX - Alex
+- Ending the battle with a simultaneous death no longer triggers an
+ assertion - SvdB
+- Concurrent supermelee ship selection - SvdB
+- New generic, unthreaded flashing code - SvdB
+- Cleanup of 3DO ship spin support; spin speech works now - Alex
+- No longer creating and mounting a temporary directory. It is no longer
+ used, but it might be again at some point, for loadable modules. - SvdB
+- Added RNG functions that work on a supplied state - SvdB
+- Fixed a crash on startup if uqm.cfg did not exist, flagged and fixed
+ by jdorje - Michael
+- Support for 3do "ship spin" videos (Bug #733, patch by Jan Lönnberg)
+ -Michael
+- Major refactoring of input configuration to use the resource system
+ instead of custom files (bugs #961 and #949) - Michael
+- Added ability to remove entries from ALists - Michael
+- Cleaned up FRAME, CONTEXT, and FONT abstraction layers - Michael
+- Added Input Frames to pause code to stop infinite loops - Michael
+- Added more netplay debug code - SvdB
+- Added uio_fprintf() and uio_vfprintf() - SvdB
+- Any input will register for at least one frame (Bug #864) - Michael
+- Many VControl cleanups - Michael
+- Fix compilation without Netplay support - SvdB
+- Added limited AIFF sound file decoder for playing 3DO originals;
+ SDX2 decoder by SvdB - Alex
+- Typo fix in Starbase speech (bug #959) - Michael
+- (MacOS) Don't package up .svn dirs with 'build.sh uqm install' (bug #958),
+ from Nic.
+- No more extra newlines to log_add() calls for libs/network/ code - SvdB
+- DUCK videos now play correctly after a video mode change; bug #734 - Alex
+- Cancel key will now quit out of the Manifest Menu (Bug #838) - Michael
+- Added -w and -x commandline options, to counter -f and -o; used these
+ to implement "Safe Mode" links in the Win32 installer (Bug #946)
+ - Michael
+- OpenGL texture loading uses surface pitch instead of screen width.
+ This should head off future bugs similar to Bug #740 (this issue was
+ reported as Bug #956) - Michael
+
+0.6.2 (maintenance release):
+- A bit more debug info for failed connects. - SvdB
+- Exit with failure if basic content cannot be found, ensuring proper
+ error reporting under Windows - Michael
+- Popup windows for "Really Exit?" and Game Pause are immune to fades
+ and crossfades (Bug #455) - Michael
+- No longer depend on SDLK_LAST statically; key input should now be safe
+ if compiled with a different version of SDL than the one running the
+ program (Bug #936, possibly also #834 and #883) - Michael
+- Unix build scripts now work under LC_CTYPE=tr_TR - SvdB
+- Flush write buffer when doing a uio_fclose() after doing only
+ uio_fwrite() operations. - SvdB
+- Major rewrite of the SwapBuffers commands -- screen compositing logic
+ has all been abstracted out into sdl_common.c instead of being
+ nearly-duplicated in opengl.c and pure.c - Michael
+
+0.6.1 (maintenance release):
+- The Unicode Private Use Area is no longer considered printable. This
+ is a stopgap to handle unusual behavior with text entry under OS X.
+ From Nic; see bug #942 for more details - Michael
+- Do not rely on GL_UNPACK_SKIP_* arguments, which some OpenGL drivers
+ mishandle (Bug #914) - Michael
+- Do not overwrite GLOBAL_SIS (CrewEnlisted) when leaving Hyperspace
+ (Bug #938) - Michael
+- Fixed a text entry width problem that was deleting control template
+ names (Bug #947) - Michael
+- uio cleanups, documentation - SvdB
+- uio path parsing fixes/improvements - SvdB
+ - Windows UNC path support (#907)
+ - Windows drive-relative paths ("D:path" without a path seperator)
+ - treat multiple consecutive path seperators as one (like POSIX)
+- config dir no longer needs trailing path seperator (bug #738)- SvdB
+- Simplification of uio Stream functions. No more internal seeks. - SvdB
+
+0.6.0:
+- Fixed a bug where an input delay was used for non-network games - SvdB
+- Fixed a bug where the victory ditty would end prematurely when UQM
+ is compiled without Netplay support - SvdB
+- Take $CFLAGS and $LDFLAGS into account for dependency detection
+ (they were already used for the building itself) - SvdB
+- Validate UQM version of either side of a Netplay game - SvdB
+- Better abort and disconnect handling for Netplay - SvdB
+- Menu sounds in Setup track rest of game (#922), from Nic - Michael
+- Shifted the Mouse Error to a Popup Window, moved the message to
+ starcon.txt for translators - Michael
+- Generic DoPopupWindow() command for status messages - Michael
+- Update the 'current selection' icon after deleting or inserting ships
+ in a fleet in SuperMelee - SvdB
+- Correct some background pixels in melebkgd.{25,26}.png - SvdB
+- Unix build scripts improvements. - SvdB
+- Build fixes for MacOS X (with thanks to Nic) - SvdB
+- Ships in battle can resume normal speed in all circumstances after
+ the enemy Avatar's tractor beam disengages (bug #860; this is a
+ netplay desynchronizing change) - Alex
+- Conversation summary breaks lines based on actual chars/words
+ that fit (bug #916) - Alex
+- Netplay configuration dialog - Michael
+- Better error message for 'Kernel failed to load' (#917) - Michael
+- Pushbutton for connecting to netplay, in anticipation of a full
+ configuration dialog later - Michael
+- Cleanup of comm.c - SvdB
+- Netplay - SvdB
+- Added step-by-step instructions for compilation on MSVC++ - SvdB
+- All output done in binary mode, due to uio/Win32 conflicts (#912) - Michael
+- Online key configuration menu actually functional now - Michael
+- Tweak to Lander UI - Special Weapon is now explicitly Lander Escape,
+ just as Warp Escape is - Michael
+- Default key configuration changed slightly; online display of current
+ key bindings - Michael
+- Input templates can now be renamed, both online and off - Michael
+- Fixed an integer-size error that was causing crashes on AMD64, from
+ Solomon Peachy (#895) - Michael
+- Added some generic lib code to be used by future code. - SvdB
+- Joystick support is now optional; from SvdB, Alex
+- Restructed and normalized savegame and game-state reading/writing code;
+ savegames are now compatible between same-endian 32- and 64-bit systems;
+ savegames from prior 64-bit builds cannot be used - Alex
+- (Unix build scripts) Failed mkdep doesn't result in empty .d files
+ anymore. Now using the gcc 3 "-MF" and "-MT" options. - SvdB
+- removed internal libmikmod, adding a dependancy on an external one - SvdB
+- Added a quit button for the Super Melee main menu - Michael
+- Fixed sporadic uqm.cfg loading errors in release builds - Alex
+- Quit (F10) now works correctly during intro (bug #862) - Alex
+- Playing failure sounds when entering text consistently (bug #884) - Alex
+- 64-bit fixes in construct_response () - Michael
+- Removed rotating 3d planet frame caching (cuts mem usage by 5M) - Alex
+- One pixel fix in melee menu pictures. - SvdB
+- Debugging function to add energy during battle. - SvdB
+- Changed slaveshield throb rate to match the 3DO - SvdB
+- Fixed recently introduced bug with crew count on exit to HyperSpace.
+ (bug #875) - SvdB
+- Fixed static vars not reinited in alien comm code (caused various
+ side-effects; bug #870) - Alex
+- Fixed a spinlock in Melee's final score screen (Bug 879) - Michael
+- Phase 2 of online keyconfig - Setup Menu can configure assignment of
+ templates to players - Michael
+- Control scheme shifted to Menus + Control Templates in preparation for
+ the online keyconfig - Michael
+- Fixes to logic in the Utwig conversations (Bugs 327, 647), from Nic
+ and Michael
+- Removed unused and invalid lander font chars - Alex
+- Setup menu reads strings out of lbm/setupmenu.txt - Michael
+- More fixes towards working 64-bits binaries. - SvdB
+- Flashing outfit modules to build with PC menus too; bug #871 - Alex
+- Corrected caption Orbit: to Tilt: in planet scan; bug #847 - Alex
+- Added missing failure sounds in Outfit, Shipyard, Cargo and Roster;
+ played when over/under capacity, not enough RUs, etc; bug #842 -Alex
+- Crew retrieved from space after the end of an encounter will no
+ longer set the crew larger than the ship's maximum. - SvdB
+- Flagship crew numbers are no longer continuously synchronised during
+ melee, but only at the beginning and end of the battle. - SvdB
+- Various small cleanups. - SvdB
+- Debugging function to add crew during battle. - SvdB
+- More documentation - SvdB.
+- RMPLIFIED PRECURSOR BOMB is now AMPLIFIED, from Vorn (bug #812)
+ -Michael
+
+0.5:
+- Innocent original c&p bug fixed, from bpoint.
+- Handle relative dirs in -C correctly, from Jan Lönnberg (part of bug #738).
+- Internationalization fixes: moved many hardcoded English strings
+ into string resource file starcon.txt (bug #778), from Andrew Zabolotny
+- Melee Scale and Slides/Movies now take effect immediately - Michael
+- The intro now plays only when a new game is started - Alex
+- fixed uio_rename() and some other cases where a new file is created
+ under specific circumstances. - SvdB
+- Fix bug in GetStringContents which used the number of chars where it
+ should have used the memory size. - SvdB
+- Control scheme upgrades checked (VControl Version upgrade) - Michael
+- Better instant-move (for debugging) - SvdB
+- Fixed version checking in unix build scripts. SDL 1.2.10 is
+ now recognised as newer than 1.2.9. - SvdB
+- Some small improvements to the portability of the build system,
+ from Jim Paris
+- Fixed two-week bomb installation at the Starbase bypassing the
+ defeat condition (bug #757), from Nic, Alex
+- Added star search in Starmap (define Menu-Search and Menu-Next keys
+ in your keys.cfg) from kworces, Alex, SvdB
+- Fixed Orz greeting at Taalo homeworld inconsistency (bug #819) - Alex
+- Fixed Venus' atmo density to 90 times that of Earth (bug #821) - Alex
+- Internationalization fixes: better or, in some cases, fixed support of
+ non-Latin UTF-8 strings (more to come; bug #778), from Andrew Zabolotny
+- Remaped special chars used in the game (degree, infinity and earth
+ signs; middle dot) to their UCS equivalent codes (bug #818) - Alex
+- Color depth is now determined entirely automatically - Michael
+- Text input is now available in languages other than English
+ (UCS/Unicode; SDL does not support Unicode input on Windows yet;
+ you must have proper font chars installed -- see translations) - Alex
+- Re-added joystick text input (with Up/Down/PageUp/PageDown keys);
+ joystick alphabet in content/lbm/joyalpha.txt (bug #495) - Alex
+- Text input refactoring: enabled key repeat, added support for
+ Home, End and BackSpace keys; (bugs #671, #815) - Alex
+- Selecting the scan methods for shielded and gas giant planets
+ is now allowed when using PC menus (bug #800) - Alex
+- Added a unified credit roll combined with outtakes (bug #46) - Alex
+- Setup menu selection doesn't reset when you quit a submenu now - Michael
+- Split config.alwaysgl and config.usegl so that "Use Pure Mode If Possible"
+ persists across runs even when using a GL mode - Michael
+- Fixed Yehat Rebel left hand doubling up problem (bug #807) - Alex
+- Fixed the problem with Melnorme stripping Chmmr bomb/crystal
+ modules from the ship in a fuel deal (bug #803) - Alex
+- Corrected baseline of 'j' in Micro font (bug #797) - Alex
+- Fixed small VUX animation glitch (out of order frames; bug #808) - Alex
+- CANCEL now behaves as expected in setup menu - Michael
+- Only SELECT and CANCEL trigger the fade-to-black at the end of a
+ Super Melee, solving the issue in bug #547 - Michael
+- Admiral ZEX is no longer referred to as "Commander" or "Zex" (bug
+ #811) - Michael
+- Keypress status is not reset when entering battle mode (solves
+ bug #596) - Michael
+- Added the rest of devel/ and users/ documentation into MSVC .dsp
+ files (bug #589) - Michael
+- Fixed PNG transparency info (tRNS chunk) in all images according
+ to info specified by .ani - Alex
+- Comm animation fixes (bugs 557, 705, 806); from chmmravatar, Alex
+- Imported DOS versions of alien comm graphics which have richer
+ palettes and look better in most cases (bug #314) - Alex
+- Added slider controls for volume to setup menu - Michael
+- Added --shield and --scaler hq to setup menu - Michael
+- Fixed lockup when skipping past VUX beast analysis data in Starbase
+ (bug #790; should take care of all spliced comm edge cases) - Alex
+- Added 3DO-style throbbing slave shield (--shield; bug #32);
+ special thanks to Nic for inspiration and some code - Alex
+- Rotating 3D planet changes: nicer looking slave shield; planet now
+ finally looks like a rotating sphere (and not cylinder); added some
+ lighting variance to give it a 3D feel (not a smooth ball) - Alex
+- Refactored colormaps storage, management and transforms; paletted
+ images rendering should be much faster now - Alex
+- Corrected intro slide 5 (crosshair removed); bug #794, from AusME
+- Split off alien comm colormaps from global scclrtab into
+ corresponding race dirs (avoids potential mod collisions) - Alex
+- Refactored font engine: fonts are loaded and treated as
+ alpha-channel-only images (allowing for antialiased fonts now);
+ solid color and gradient/alternate effects processing is unified - Alex
+- Fixed problems with pausing the game during ending sequences
+ (game-clock bug; undrawing incorrectly due to cliprect) - Alex
+- Removed hotspot abuse from lander report drawing to fix bad
+ positioning desync brought on by new hotspot handling - Alex
+- Trilinear melee scaler overhaul (melee smooth mode should look
+ nicer now; melee can now fully use alpha gfx) - Alex
+- Miscellaneous Sa-Matra gfx fixes (Generator collision masks and
+ explosion frame 0 were off; Shield position was off in med and sml;
+ Main sml gfx was 2 pixels too narrow) - Alex
+- Replaced the binary resource indexes by textual ones
+ (also resolves bug #687) - SvdB
+- Some subtitle timing fixes (bug #s 771, 780) - Alex
+- Entering Planetary Orbit wait screen is back (from DOS) - Alex
+- Gfx engine changes: now always 32bpp internally (facilitates
+ alpha channel usage); accelerated platform-specific scaler code;
+ only using alpha channel where needed. Processor pack is necessary
+ for compilation on VC6. - Alex
+- New 2x scaler 'hq' (by Maxim Stepin; www.hiend3d.com/hq2x.html) - Alex
+- Removed MikMod i/o hacks (using MREADER i/o now; bug #787) - Alex
+- Fixed fast escape weirdness (bug #619) - from Jan Lönnberg
+- New topographical 4x planet surface scaler (bug #786) - Alex
+- Added support for Tremor for Ogg Vorbis decoding (avoids floating point
+ math) - SvdB
+- Fixed T-Pet compulsion graphics (partially); bug #772 - Alex
+- Doing game-state file I/O in memory instead of temp files;
+ should avoid problems as in bug #752 - Alex
+- Separate config_win.h file for build.sh builds on Windows - SvdB
+- Fixed a typo ("we" -> "he") in Thraddash dialog (bug #783) - Alex
+- Corrected grammar in Umgah 'Caster lander report (bug #781);
+ from James Ho
+- Options selected in the Setup Menu now persist across runs -Michael
+- Added a simple implementation of key-value pair "resource" files
+ for organizing simple data such as configuration options -Michael
+- Added a 'fullscreen' setup menu option - Alex
+- Fixed a bug that prevented Slylandro Probes from ever showing up
+ in interplanetary exploration (found by SvdB, bug #768)
+- Lowered the Comm ambient animation rate from 120 to 40fps, thus
+ limiting the CPU usage - Alex
+- Fixed Recursive Mutexes to still work even if the ThreadID is 0
+ (bug 779) - Michael
+- Permit independent selection of graphics driver and resolution in
+ setup menu - Michael
+- Changed comm subtitle caching to use own context instead of
+ screen grabs; should resolve Blue Comm Screen universally - Alex
+- Added missing break statement in DrawBatch:RECT_PRIM - Alex
+- Setup menu split into four submenus - Michael
+- Fixed fallback in getHomeDir() for when $HOME is not defined on *nix
+ - SvdB
+- %APPDATA% fallback no longer to "../userdata", but to "./userdata",
+ as we don't chdir() to the content dir anymore. - SvdB
+- Commander Hayes flickering lights animation now properly reenabled
+ when applicable (bug #777) - Alex
+- Refactored setupmenu code to use generic widgets - Michael
+- Support UTF-8 chars in mineral names (bug #770) - SvdB
+- Modified scalers to use surface pitch instead of width - reported to
+ fix bug #740 - Michael
+- Cleaner build output. Set '$MAKE_VERBOSE' to 1 for old output. - SvdB
+- Improved dependency tracking for unix build system. - SvdB
+ "./build.sh uqm depend" is only needed for checking for new source files.
+- handle "." and ".." in paths - SvdB
+
+0.4:
+- Nicer title image during intro, from Nic
+- Installation routine for MacOS X, from Nic.
+- Lots and lots of dialog fixes, from Nic.
+- Some speech is dependant on whether spoken voices are on, from Nic.
+- OpenAL header cleanup. Updating OpenAL may be necessary.
+ Windows users should put the OpenAL headers in an AL/ directory
+ now too (should be the default). - SvdB
+- Improvements to the unix build system for cross-compilation - SvdB
+- Do not define ssize_t for MinGW. - SvdB
+- Fallback readdir_r() - SvdB
+- Attempting to click the screen pops up an error message, from
+ Nic, heavily modified (dodge on #533) -Michael
+- Fixed potential crash with a truecolor oscilloscope image (thanks
+ jdorje) and made it generally more flexible (bug #729) -Alex
+- Added missing Tanaka battle portrait images (new artwork; oldcap) and
+ minor fixes to other shofixti images (bug #183), from Nic
+- Mycon captain portrait finally fixed (bug #183), from Nic
+- Saving the game while on autopilot inside an interplanetary system
+ will no longer reset the autopilot (bug #725) -Alex
+- Main menu and Super Melee menu can now play music tracks; drop in
+ 'lbm/mainmenu.ogg' and 'melee/melemenu.ogg', respectively -Alex
+- Can now switch mod->ogg in intro/outro player (thanks Nic), and
+ mod<->ogg everywhere in general -Alex
+- Fixed crash after T.Pet conversation upon reentering D.Crateris
+ once Ur-Quan have been confused (bug #531) -Alex
+- Fixed last subtitle flashing after fast-forwarding to the end,
+ pressing F10 and selecting NO (bug #498) -Alex
+- Fixed first contact with Arilou at homeworld; Talking Pet confusion
+ (bug #638), from Paxtez
+- Fixed warp-escape in Cyborg mode removed by previous game input
+ patches (bug #563) -Alex
+- Fixed spurious story-line changes (usually the manner of the encountered
+ race) caused by loading a game from an Encounter screen (bug #519) -Alex
+- Option change: --meleescale to --meleezoom (bug #694) -Alex
+- The alpha icon is now default (for XP/2003); bug #474 -Alex
+- Fixed incomplete memset()s in comm alien animation code (comm should
+ be saner now), from jdorje
+- Guard access to the Clock so that accessing it is a no-op when the game
+ isn't actually in progress (bug #678), from Nic
+- Accept files with .uqm extension for packages (bug #558)
+ Added regex fallback files. - SvdB
+- Music volume normalized throughout the game (bug #718) -Alex
+- VControl parse errors suppressed unless critical - attempting to set
+ the threshold of a nonexistent joystick no longer makes the game refuse
+ to run (Bug #660, again) - Michael
+- Properly handling Thraddash-Ilwrath mission overlap (bug #530) -Alex
+- Separate 'ask for fuel' player responses for Mercury and Luna missions
+ (bug #716), from Nic
+- Stereo SFX model changed slightly (fixes #472) -Alex
+- Pkunk Spindle news items are now disabled until you befriend them
+ (bug #315), from Paxtez
+- Melee captain names re-extracted and fixed (bug #188) -Alex
+- It is now possible to complete the game without ever allying with the
+ Starbase at Earth; known as 'Beating the Game Differently' mode;
+ (bug #592) -Alex
+- The direction flagship is facing is now preserved through an
+ encounter in Hyperspace -Alex
+- Added support for PC-style 3-step melee zooming; -b=pc|step option;
+ (bug #694) -Alex
+- Melee team building pick-ship box is now generated with actual ship
+ icons (bug #692) -Alex
+- Setup Menu no longer crashes or misbehaves on custom resolutions (bug
+ #693) - Michael
+- Quit confirmation window (on F10) undraws correctly during intro slides
+ (bug #673) -Alex
+- Attempting to set to a mode that Doesn't Work in Setup no longer ends
+ the program (bug #695) - Michael
+- Fixed segfault when shutting down because video could not be initialized
+ (bug #683) - Michael
+- Fixed crash when restarting Sa-Matra battle after abort (bug #700) -Alex
+- Some cleanups, enabling successful build with GCC 4 (bug #710),
+ mostly from Ville Skyttà -SvdB
+- Sa-Matra portrait now fills the portrait space (bug #514), from Nic
+- Updated melee images with original DOS content; should resolve most
+ captain portrait issues (bug #183) -Alex
+- Bug #702 fixes (dead code + compiler warnings) -Alex
+- Flagship will not fly sideways on auto-pilot in Hyperspace (bug #642) -Alex
+- Fixed menu sounds in full-game battle ship selection box (bug #566) -Alex
+- Added '--version' - SvdB
+- Fixed a bad memory access which could occur when the code relies
+ on CharCount for determining the end of a string (bug #701). - SvdB
+- Battle planet images now have corrected transparency info, so there
+ is no more black square overlaping ships (bug #128); and coincidently
+ 3DO credits now have a starfield background (bug #470) -Alex
+- Unicode support for fonts and strings - SvdB
+- Fixed image clipping with --meleescale=nearest (bug #126) -Alex
+- Setup menu now uses a background contributed by Joffrey Smith
+- If keys.cfg refers to a nonexistent joystick, the game will stil run
+ (bug #660) -McMartin
+- Fixed support for languages other than English; loading font chars
+ with codes above 136 is now possible (bug #690), from Matthias Hager
+- Enable the other insults against the Mycon (bug #559), from Nic.
+- Colormap format (.ct files) changed to allow for richer graphics.
+ First step towards importing some of the original DOS gfx.
+ See bug #314 for more info. -Alex
+- Better dependency checking in unix build scripts - SvdB
+- Many options menu changes: left-justified categories, and the ability
+ to change driver/resolution/bpp/scaler in real time, and updates are
+ less frequent so as to spare the DCQ -McMartin
+- PC ending animation now draws the flagship with modules according to
+ the actual player's load; also the script structure changed
+ allowing for an FPS improvement -Alex
+- Version number in the main menu does not blink anymore when the
+ menu selection is changed (bug #672), from Nic
+- Massive amount of file restructuring and cleanups. Need many more. - SvdB
+- Added Intro and Ending slide shows (bug #46); use "-i pc" -Alex
+- Cross-fades in OpenGL mode with complex scalers fixed (bug #674) -Alex
+- Added doc/devel/dialogs - SvdB
+- Don't allow the reply "Symbionts, how interesting!" in the Supox
+ conversation until they mentioned they were Symbionts. (bug #528),
+ from Nic
+- Not initialising the various systems when -h/-? is supplied (bug #656),
+ from Nic
+- F10 works when playing the intro (bug #665), from fOSSiL
+- Game clock counter overflow fix (bug #668) - [collective effort]
+- Cocoa hooks for MacOS X, from Nic
+- Corrected number for combat energy when outfitting starship. - SvdB
+- Added dumping planet info to uqmdebug.c - SvdB
+- Check language.txt for locale, from Zap
+- Make it possible to specify the config dir on the command line.
+ (bug #645) - SvdB
+- Speech .txt and .ts corrections, from Nic. - SvdB
+- Made the unix build scripts more portable. Removed some small
+ bugs. - SvdB
+- No longer chdir() to the content dir (bug #564)
+ Environment variables and ~ are interpreted in the supplied content
+ path now.
+ Don't look for content in the default directories if an explicitely
+ supplied path failed. It would only confuse users. - SvdB
+- The unix build script is now able to detect SDL on Darwin (bug #358) - SvdB
+- Resource units given more obviously when ordering a probe to
+ self-destruct (bug #586), from Nic.
+- Shipyard "Combat Energy" changed to reflect the recharge rate (bug #522).
+ Also, some cleanups. Thanks and apologies to Nic. - SvdB
+- Added lots of debugging functions, SvdB
+- Cleaned up use of the DEBUG define, SvdB
+- Talking Pet .txt file corrected to match the .ogg files,
+ and talkpet.ts corrections, from Nic
+- Abstracted window-drawing code from confirm.c -Michael
+- Fixed blue comms screen problem (bug #363), from Joel Holveck & Nic
+- Automatically adding an icon for Darwin builds, from Nic
+- Fixed Roster-F10-Quit bug (#591), - Michael
+- Restructured starcon2.c, with better checks for argument parsing,
+ and consistent error messages. - SvdB
+- Restructuring of the unix build scripts.
+ Also, interrupted dependency builds are now detected. - SvdB
+- Unix build: Make it possible to use another directory than the current
+ one for putting the build data in (such as build.vars, config.state,
+ the obj/ dir, and the final binary). - SvdB
+- Fixed various odd behaviors when loading from HyperSpace (bug #587),
+ from Nic and Michael
+- Added new 'triscan' scaler; derived from scale2x[.sf.net] -Alex
+- Space marines die in a self-destructing Scout (Bug #445), from Nic
+- Added the -l option to produce logfiles (bug #560), from Nic
+- Zoq-Fot-Pik speech properly vertically centered (bug #579), from Nic
+- Input code refactoring, phase 2: All player input is brokered by
+ DoInput -Michael
+- Updated .cvsignore commands, from Nic
+- Fixed a keyrepeatbug from when the player cancels out of the
+ Starmap in IP -Michael
+- IP_taskfunc now uses PulsedInputState instead of handling its
+ own debounce delays -Michael
+- Input code refactoring, phase 1: Replaced messy structs with an
+ array indexed by an enum. -Michael
+- Thread code refactoring: only the main thread will actually spawn
+ threads, and thread IDs are properly recycled with SDL_WaitThead ()
+ once they're done. (With luck, this will fix bug #561) -Michael
+- Sound code refactoring: core api is now virtualized,
+ MixSDL is divided to generic mixer and driver entities - Mika
+- Optimized MixSDL mixing and resampling routines
+ (hopefully fixes bug #435) - Mika
+- MOD music should now play properly on big endian machines if using
+ high quality mode (workaround for bug #166) - Mika
+- Better-looking slave shield (bug #32), from Nic
+- Bay door animations don't stall before aborting (bug #500), from
+ chmmravatar
+- Reports SDL version on startup (bug #520) - Mika,Nic
+- Fuel usage on planet landing is now reported correctly on all situations
+ (bug #556), from Nic
+- Fine-grained control of menu sounds, "MenuSounds" global now
+ guaranteed to always be non-null -Michael
+- Added support for stdio file access through temporary files to uio.
+ added uio_copyFile to uio - SvdB
+- Added uio_getFileLocation() and uio_getMountFileSystemType() to uio.
+ Also some small improvements. - SvdB
+- Fixed fuel usage estimate to selected destination on the starmap.
+ (original bug, not reported) - SvdB
+- Sound decoders refactoring: decoders are now virtualized,
+ the high-level decoding code is unified and any format is
+ theoretically streamable -Alex
+- Patches to enforce the invariant that the GraphicsLock is held when
+ SetFlashRect is called (bug #504) -Michael
+- Major refactoring of threadlib; see doc/devel/threads -Michael
+- Downgraded the GraphicsLock to an ordinary Mutex -Michael
+- Added movie player; only movies defined are intro and ending;
+ only .duk decoder present (.duk audio decoder mostly derived
+ from decoder by SvdB) -Alex
+- Extra fallback for the unlikely situation that $HOME isn't set on a
+ unix system. (#493) - SvdB
+- Accept spaces in --contentdir argument (#492) - SvdB
+- Separated and abstracted sound buffer-tagging and trackplayer
+ clip/subtitle chaining -Alex
+- Abstracted the recursive mutexes in MixSDL and DCQ code -Michael
+- Introduced a new synchronization construct (CrossThreadMutex) and
+ migrated the GraphicsSem and clock_sem over to it (#359) -Michael
+- Replaced thread-local Semaphores with Mutexes (#359) -Michael
+- Load/save icons don't flash anymore when in savegame menu
+ (part of bug #291), from Paxtez
+- Savegame slot now defaults to the last one used during one execution
+ of uqm (bug #477), from chmmravatar
+- Fixed one-pixel glitch in shipyard when scrapping (bug #461),
+ from Paxtez
+- Adjusted subtitle timings when there are no oggs (part of bug #362),
+ from chmmravatar
+- Fixed crash in conversation summary and ffw/frew problem when
+ selling data to Melnorme (bug #476), from chmmravatar
+- Flush input after selecting "Navigate" (bug #475) - Michael
+- Fixed ships in solar system getting displaced after having
+ visited a planet (bug #365) -Alex
+- Fixed collisions/encounters with "invisible" fleeing ships
+ (bug #319) -Alex
+- Quit confirmation fixes: lander reports, conversation summaries,
+ outtakes, end credits; font effect properly set/saved/restored;
+ (bugs #454, #465, #466); from chmmravatar
+- Obsolete "register" keywords removed -Michael
+
+0.3:
+- Dirty hack to avoid a warning about the "ignored.key" hack - SvdB
+- Typo 'natually' fixed in Arilou speech - SvdB
+- uio: Fixed a bug which could cause spurious warnings when using .zip files
+ generated from DOS/Windows - SvdB
+- uio: Fixed a potentially memory-corrupting bug when in a .zip file a subdir
+ is described before a dir higher in the directory structure.
+- uio: Fixed an incomplete message that would only show when handling a
+ specific error - SvdB
+- Added doc/checklist file - SvdB
+- RUs properly cleared in savegame display, from Paxtez, chmmravatar
+- Support for up to 50 savegames, from Paxtez
+- Action names are properly null-terminated, stops a crash for some
+ bad keys.cfg types -Michael
+- added '--addon <addon>' - SvdB
+- keys.cfg from incompatible control scheme version is now renamed
+ automatically to keys.old -Mika
+- Added warranty message in the console on startup, SvdB
+- Left and Right directions disabled in fuel screen (bug #452), from
+ Paxtez
+- Syreen don't rejoice until victory truly is theirs (bug #451), from
+ Nic
+- Guardian in Blazer mode being drained by DOGI will no longer
+ result in a non-blazer Guardian with Blazer effects. Original sc2 bug.
+ (bug #283); from Paxtez
+- 'CREW' and 'BATT' instead of icons in combat screen when using
+ PC-style menus (bug #308); from Paxtez
+- New shipyard SCRAP system with scrapping confirmation
+ (bug #413); from Paxtez
+- Syreen ships become available when Talana says they are
+ (bug #264); from Nic, Alex
+- The infinity symbol is correctly printed for gas giant atmosphere
+ in coarse scan screen (bug #239) -Alex
+- Prevent Syreen crew above 12 getting thrown out the airlock in
+ Roster screen (bug #184); from chmmravatar, Alex
+- Lander report messages now use the entire panel (bug #36), from chmmravatar
+- Fix colour cycling in the roster screen (bug #279), from Nic
+- Fixed last seen battle-group teleporting to Sol after invoking
+ Talking Pet in Sol (bug #109) -Alex
+- Fixed a dialog glitch on Slylandro homeworld (#442) -Michael
+- Freeze planetary simulation at beginning of landing sequence to stop
+ event bursts (bug #80) -Michael
+- Extra 2 ship slots in supermelee, as in the PC SC2, and all the PC
+ default teams, from chmmravatar (bug #248)
+ NOTE: this breaks old team saves!
+- Made functions for GET_GAME_STATE and SET_GAME_STATE - SvdB
+- Fixed bug with entering closing portals (bug #108) - SvdB+chmmravatar
+- Mouse cursor is now hidden in fullscreen mode (bug #173) -Mika
+- Version checks inside the input code to flag incompatible changes --Michael
+- Install location for content on unix systems is now ${prefix}/share/uqm
+ instead of ${prefix}/lib/uqm - SvdB
+- New main menu graphics from MarkVera, Paxtez, Nic (bug #393)
+- Removed a place where you could ask for repairs when you shouldn't
+ (bug #432), from Nic
+- Can use F10 to quit during splash screen as well as main menu -Michael
+- Fixed starship location when being teleported from Procyon to
+ the Earth Starbase after the Precursor bomb is installed. - SvdB
+- Crew death on planet is now counted properly in all cases (bug #70) -Mika
+- Fixed lockup in 'save failed' alert box (bug #397), from ghakko
+- Fixed glitch in Pkunk animation (bug #354), from Paxtez
+- Fixed Kohr-Ah final defeat message repeating (bug #426) -Mika
+- Fix for picking up talking pet after Umgah genocide - from Paxtez
+- MinGW compilation fixes - SvdB+Mika
+- Bilinear scaler is now faster, has 24bpp mode and uses regions -Alex
+- Accept CRLF line endings in .txt and .ts files - SvdB
+- Fixed overflow problem with 32bpp bilinear,biadapt,biadv scalers -Mika
+- MixSDL now handles resampling correctly (less cracklings);
+ added cubic interpolation for high quality mode -Mika
+- Removed the redundant "GameExiting" variable -Michael
+- Added quit options to ingame menu (bug #409), from Paxtez
+- Fix position of blinking save/load in melee (bug #406), from Paxtez
+- New packaging/io system. - SvdB
+- Exit confirmation dialog is prettier and safer, from Paxtez
+- Exiting from the main menu with F10 now exits the game -Michael
+- Changed lander speed to 35 FPS, which matches reported 3DO speed
+ (Bug #22) -Michael
+- Added a Menu-Delete key for the Super-Melee menu (#123) -Michael
+- Added support for positional (stereo) sound effects, currently
+ works only with OpenAL -Mika
+- Fixed screen transitions from homeworld conversations (bug #348) -Michael
+- Fixed Fwiffo join_us_refusals initialization (bug #405) -Mika
+- Fix Druuge transactions to not elevate crew costs baselessly (bug #235),
+ from ghakko
+- Fixed glitches and NULL Stamp draw attempt in menu code (bug #26) -Mika
+- Absence of voice files is now automatically detected (bug #309) -Mika
+- Fixed Commander Hayes mouth movement before radioactives are given
+ (bug #343) -Mika
+- Fix glitches in slylandro probe animation (bugs #398, #399), from Paxtez
+- Confirmation dialog box for exiting the game is now menu-based
+- Commander Hayes explains his predicament before you get the option to
+ rescue, closes #366, from Nic
+- Gestalt mode accelerates continuously as long as some key is held;
+ should resolve to PC-style input wrt bug #381 - Michael
+- Fixed some more false key cancels, addressing 378 again - Michael
+- Added "gestalt mode" for acceleration cancels; fixes #381 - Michael
+- Fixed some "false key cancels" in the input system, addressing bugs
+ #378 and #379 -Michael
+- Completely reworked the input system
+- Added PC version outtakes, from chmmravatar
+- Fix crash in PlayStream when whole file is prebuffered and its not
+ speech (bug #259) -Mika
+- Fix endian problem in colormap transform code (bug #137) -Mika
+- Fix minor glitch in planet coarse scan (bug #238), from chmmravatar
+- Wav loader is now endian safe (bug #165) -Mika
+- Dialogue patch for Melnorme, fixing bug #335
+- Two dialogue spots where visit count could run away patched; fixes bug
+ #333, from Stas Sergeev
+- EventHandler checks CurStarDescPtr before dereferencing it (bug #347)
+- Oscilloscope/mini-map now has borders (bug #307 part 4) -Mika
+- Flashing rects are no longer constrained to even-numbered pixels on the
+ y axis; fixes bug #255, from Nic
+- Fixed minor glitches in main window borders (bug #307 parts 1,2,3) -Mika
+- There's now space before and after : in coordinates (bug #307 part 6),
+ from Paxtez
+- Star coordinates no longer shimmy (bug #331), from chmmravatar
+- The DRAWABLE_DESC datatype now uses separately allocated arrays for
+ animation frames instead of doing pointer arithmetic between it and
+ FRAME_DESCs --McMartin
+- Date on the green bar now has floating period between day and
+ the year like in PC version (bug #307 part 5) -Mika
+- AWARE_OF_SAMATRA flag is now written as well as read (closes #113),
+ from Nic
+- Subtitle drawing is now cached (closes #313) -Mika
+- Fix glitch and lockup in dialogs when rewinding (bugs #311 and #272),
+ from chmmravatar
+- Fastforwarding when in last subtitle now works as expected (bug #318),
+ from chmmravatar
+- Fixed MixSDL buffer underrun handling (bug #211) -Mika
+- Fix issue with caps lock and num lock preventing planet scan
+ and lander message skipping (bug #299), from chmmravatar
+- Subtitles are now hidden after alien has finished talking (bug #312),
+ from chmmravatar
+- Fix for mycon portrait (bug #183), from Nic
+- Music levels no longer drop in conversations when using -T 0
+ (partial fix for bug #309), from chmmravatar
+- Added PC-style conversation summaries (bug #310), from chmmravatar
+- Pressing pause key now stops dialog correctly (bug #167), from chmmravatar
+- Fix some glitches in load/save screen (bug #163), from chmmravatar
+- Utwig shield now pulsates properly (bug #269), from Nic
+- Fix for memory leak in TFB_DrawCanvas_ExtractPalette (bug #277),
+ from Richard Braakman
+- Patched Umgah dialogue, fixing bug #8
+- Crossfade code now explicitly caches the screen to transition from, and
+ thus no longer glitches. The code needed a slight rewrite, but this
+ does fix bug #33 -McMartin
+- Melee scaling is now trilinear by default, but it's still possible to
+ choose nearest neighbour with --meleescale; fixes #34 -Mika
+- Minimum scaling extent is now 1,1; fixes mostly small-objects-disappearing
+ problem in melee, from chmmravatar
+- Fixed a long standing memory leak relating to planet surface -PhracturedBlue
+- Scaled images no longer allocate/free memory all the time -McMartin
+- Planet spin on lander launch/return is now enabled -PhracturedBlue
+- Fix skipping after planet scan, landing (closes bug 31) -PhracturedBlue
+- Decelerate when entering orbit to give a smoother effect -PhracturedBlue
+- fix subtitle text overlap issues (bug 232)
+- 'Esc' now leaves planet surface (bug233) -PhracturedBlue
+- Fix race on exiting starbase (bug 230) -PracturedBlue
+- Cleanup shipyard door animation (bug 215) -PhracturedBlue
+- Misc .ani fixes; Fixes asteroid destruction crash (bugs 150, 155, 158)
+ and somewhat fixes Mycon potrait (bug #183) -fOSSiL
+- Moved image scaling to DCQ thread; fixes scale-out-of-sync problem
+ in melee -Mika
+- Fix lockup in cyborg melee (fixes 204 and 218) from chmmravatar
+- Added -g option to control gamma correction, from chmmravatar
+- Restored the CondBank to actually use condition variables properly
+ (resolves a race condition under OpenBSD)
+- Removed aspects of the legacy graphics code that are never used or that
+ are redundant. More 'C-like' use of the PRIMITIVE datatype.
+- Fix various graphics glitches during dialog. Especially Spathi Eye,and ZFP
+ Closes #23, #156 - PhracturedBlue
+- Fix Syreen, KohrAh and Slylandro ship effects to not be screen
+ size dependant (corolary to bug 93) - PhracturedBlue
+- Line clipping is now handled correctly; fixes #28 (one pixel corruptions)
+ and #198 (beam weapons changing direction) -Mika
+- Vux warps in close (fixes bug 93) -from Nic
+- Fixed lockup on lander-report (Bug #144 annd 187?) - PhracturedBlue
+- Version # is now printed in the main menu, from Nic
+- Added PC Shipyard + Hangar power lines animation;
+ (closes #176) -fOSSiL
+
+0.2:
+- Shipyard/Outfit screens now use larger PC graphics;
+ Outfit blueprint is properly aligned; from Nic
+- Key config is now saved in user dir too.
+ melee.cfg too again. - SvdB
+- Added copyFile() - SvdB
+- Pure mode partial screen updates are now more efficient;
+ fixed 'crossfades not finished' problem -Mika
+- Graduated colours for crew in shipyard, from Nic
+- Alien dialog fixes: Talking Pet, Utwig, Mycon, Syreen -fOSSiL
+- Updated all game, menu, melee and weapon sounds to original 3DO
+ ones (extracted using various tools); some sounds were simply
+ wrong: menu - "device success", weapons - arilou, slylandro, thraddash;
+ others had bad sampling rates; (fixes #19) -fOSSiL
+- Fixed Melnrome repeating "Please do not mention this subject again"
+ text (bug #145) -fOSSiL
+- Fixed load/save screen leaving characters when having more
+ than 1000 units of some type of resources (bug #75) -fOSSiL
+- Lowered Drawable memory footprint, made Frame safer - Martin
+- Support for running without voice .ogg files present -PBlue
+- Slider should now work correctly everywhere -PhracturedBlue
+- Added 'nosound' driver and --sound=openal|mixsdl|none
+ option; -a option has been removed -fOSSiL
+- Fix ZFP stuttering and some other random sound issues -PBlue
+- Correctly deal with multiply-mapped keys. This may fix some keys
+ not being detected correctly on the Mac as well - PBlue
+- OpenGL mode now fully supports partial screen updates -Mika
+- Fixed melee ship selection-box bugs -fOSSiL
+- Added a 4th button to starcon.key 'Esc' now emergency-escapes -PBlue
+- Biadapt and biadv scalers now work in OpenGL mode too -Mika
+- Fixed lockup when fastforwarding through orz comm -PBlue
+- A new windows installer is now in builds/win32_install -PBlue
+- Added '-a' switch to go between OpenAL and MixSDL at runtime -PBlue
+- Melnorme will pronounce numbers now -fOSSiL
+- Added icons to win32 builds (MSVC and mingw) -fOSSiL
+- Removed the GraphicStrength code, since all that is handled with
+ an arithmetic blit routine outside of the drawing thread
+- Added an improved version of biadapt scaling filter,
+ working name "biadv" (use --scale=biadv) -fOSSiL
+- Removed SDL_mixer sound module
+- TFB_FlushGraphics keeps track of smallest bounding box that requires
+ updating, allowing for faster scaling
+- Added a TFB_Canvas data buffer to let TFB_Image be more accessible
+- Added new graphics primitives
+- Added 'smooth' scolling for ff, frev (similar to the 3DO) - PhracturedBlue
+- New streaming code for openal/mixsdl. supports ff/frev in subtitles -PBlue
+- New sound module "mixsdl" (experimental) -fOSSiL
+- Fixed overlapping subtitle text while switch tracks -PhracturedBlue
+- New flash-thread cacheing scheme - PhracturedBlue
+- Voice-over / subtitle synch is done - PhracturedBlue
+- Optimized DCQ to be much smaller and faster
+- Added stat data in outfit screen (use --font=pc) - from Nic
+- Added fixed introx.mod, from fOSSiL
+- Fixed Orz .mod file, from fOSSiL
+- Added vertical alignment for subtitles (text runs off screen fix)-from Nic
+- Updated infinity text/symbol for RUs - from fOSSiL, Nic
+- Split away DCQ-specific code into its own header file
+- Added correct lander font - from fOSSiL
+- Added support for multiple menu hierarchies, and a few PC hiers. -PBlue
+- Fixed a potential semaphore race when suspending clock -PhracturedBlue
+- Function name conflict fixes for Mac OSX -by peterb
+- Minor fixes for pc-fonts (Outfit screen, gradient color swap) -by Nic
+- DCQ is now accessed uniformly by routines in gfx_common.c
+- Fixed OpenGL colors on MacOS X -Mika
+- Added a sane cmd-line naming scheme: --opt=(pc|3do) see --help for more -PBlue
+- Added new font effect (PC-lander messages), and set colors for text -PBlue
+- Use correct font for 'CAPTAIN', 'FUEL', and 'CREW' is status screen -PBlue
+- Added gradiated font support (for ship name), and PC-font option -PBlue
+- Minor fixes to PCMenu by fOSSiL, Nic, PhracturedBlue
+- Updated mingw support with better directions, and easier build -PBlue
+- PCMenus now suports 'settings', and menu font is correct -PhracturedBlue
+- Added '-b' option to get PC Menus -PhracturedBlue
+- Check for a deadlock in savedgames and try to continue -PhracturedBlue
+- Added an option ('-a') to display 'PC-style' coarse-scan -PhracturedBlue
+- Fixed deadlock races in new FlushGraphics method -PhracturedBlue
+- FlushGraphics now waits and notifies on a per-thread level - McMartin
+- No longer using SHGetFolderPath on Windows - SvdB
+- Key repeat is now enabled when typing text, from slayne
+- Capital letter bug in new input code fixed, from slayne
+- Oscilloscope now reacts to music when speech is disabled (OpenAL) -Mika
+- Rewritten input code (better and adds joystick/pad support), from slayne
+- Biadapt scaling for pure mode, from fOSSiL
+- Saving user data in "%APPDATA%/Application Data" on windows - SvdB
+- Melnorme comm fix, from TD.
+- Planet scan font character fixes (micro.fon), from fOSSiL
+- Temporary files are deleted on exit. - SvdB
+- Font png's no longer need to have alpha channel, black background with
+ white pixels is enough -Mika
+- Added PC version intro/ending font, from VileRancour
+- Added files for intro and ending sequence, from fOSSiL; extracted using
+ Mudrony's scripts plus his own work.
+- Added some corrected mods, from fOSSiL; extracted using Mudrony's scripts.
+- Replaced add_sub_frame with arith_frame_blit -PhracturedBlue
+- Crosshair in orbit leaving light trace to image in OpenGL mode fixed -Mika
+- Planet scan should now take ~2secs on all computers -PhracturedBlue
+- Updated earth image to look nicer (no vertical lines) -PhracturedBlue
+- Clock semaphore is now created with a value of 0 -PhracturedBlue
+- Rewrote Semaphore debugging code to be more useful - PhracturedBlue
+- Better fix for clearing load/save screen - fOSSiL
+- Implemented bilinear scaling in pure SDL mode - Mika
+- Planet surface is now smoothed, from PhracturedBlue
+- Implemented scanlines in pure SDL mode - Mika
+- Star sizes and colors are now correct in solar system, from fOSSiL
+- Captain portraits in melee are now updated properly after battle and
+ bottom portrait always stays in correct place, from fOSSiL
+- Removed 2xSaI and SuperSAI scaling due to GPL incompatiblities.
+ They should be reimplemented later.
+- Bucks print from wrong team in melee ship selection box fixed, from fOSSiL
+- Autopilot works in Quasispace when without fuel, from fOSSiL.
+- Negative shift warnings removed, from Fizban
+- Displaying correct lander images, from fOSSiL.
+- 3D planet now uses phong lighting, from PhracturedBlue
+- Put save data and temporary files in a seperate dir - SvdB
+- Fix OpenGL colors on big-endian CPUs, from Bryce McKinlay
+- MikMod now loops modules correctly (OpenAL), from Nic
+- player.fon/42.png corrected, from Parker
+- GraphicsSem usage reverted back in RotatePlanet, from PhracturedBlue
+- Pause/exit game dialog problem with planets fixed, from PhracturedBlue
+- Fixed lockup if sbuf_size was zero (OpenAL)
+- Planet surface changing color when using device fixed, from PhracturedBlue
+- Some race conditions eliminated, from PhracturedBlue
+- Earth / other slave shielded planet color issue fixed, from PhracturedBlue
+- Orbit/starmap related lockup fixed, from PhracturedBlue
+- 3D planet showing on starmap when in orbit fixed, from PhracturedBlue
+- Scan (single, interrupting) fixed, from PhracturedBlue
+- Defining DCQ_OF_DOOM lowers the DrawCommandQueue size to 512, to aid in
+ simulating severe overload stresses on the machine
+- Removed TFB_FlushGraphics' dependency on GraphicsSem, which the new
+ condition variable code both breaks and makes unnecessary
+- Scan tint is now cleared right after the scan, from PhracturedBlue
+- Recoded the DCQ to not sit on the heap, added debugging info
+- Fixed Outfit Starship and Shipyard graphics, from TDuck
+- 3D planet is now zoomed randomly from any corner, from PhracturedBlue
+- Flagship thrusters and modules one-pixel place fix, from TDuck
+- Earth topo map is now tinted as should, from PhracturedBlue
+- Fixed planet stuff when leaving surface with lander, from PhracturedBlue
+- RotatePlanet now holds GraphicsSem a shorter time, from PhracturedBlue
+- Added proper mutex usage to 3do_getbody.c, from PhracturedBlue
+- Scan uses now additive blit instead of transparency, from PhracturedBlue
+- Loading game saved while in orbit fixed (bg correct), from PhracturedBlue
+- Changed SDL_Delay to SleepThread in TFB_FlushGraphics
+- Rendering thread now broadcasts to a condition variable, stopping most
+ of the problems we were having where a fast thread spams the DCQ with
+ too many requests to handle in a timely manner
+- Fixed an unsafe memory freeing from sfx.c
+- Thread library now includes condition variables
+- Shofixti dialogue fixed to subtitles, by BlckKnght
+- Crash during loading from orbit fixed, from PhracturedBlue
+- Starmap issue when orbiting earth fixed, from PhracturedBlue
+- Oscilloscope is now implemented (OpenAL)
+- Moved initialisation of _MemorySem to memInit - SvdB
+- Planet scan is now cleared correctly, from l0ci
+- Flagship modules are now drawn correctly instead of one pix left, from l0ci
+- Planet code fixes, from PhracturedBlue
+- Build fixes for FreeBSD, from Max Horn.
+- Fixed typo in Thraddash text, from Dan Plimak.
+- Make HMalloc abort when no memory available, from Abaddon.
+- Replace malloc() calls by HMalloc, from Abaddon.
+- #include <SDL.h> in main program, for OSX, from Max Horn.
+- Use sources for getopt() for all systems that don't support it, not only
+ for Windows, from Max Horn.
+- Planet code cleanups, from PhracturedBlue
+- Changed some types to SDLKey in input.c to prevent overflows, from tamlin
+- Slider now moves in communications (OpenAL)
+- Fixed a DCQ bug where it wasn't freeing batches of graphics commands
+- > vs. >= bugfix on plangen.c, from PhracturedBlue
+- More appropriate names to ROSTER + 1 and ROSTER + 2, from slayne
+- Lines and colouring of planet surface when scanning, from PhracturedBlue
+- Melnorme bridge turns purple at the right spot in his conversation
+- Repaired a DrawCommandQueue invariant, from tamlin
+- Color transforms in communication merged into core animation thread
+- 3D planet is now antialiased, from PhracturedBlue
+- Hyperspace saving crash introduced by prev. patches fixed, from slayne
+- Precursor ship crew count is now placed correctly in melee, from wjp
+- Dialog choices doesn't go off rectangle in right anymore, from wjp
+- When saving in planet scan screen, screen is now redrawn, from slayne
+- Commas causing pixels to appear in planetary reports fixed, from slayne
+- Team names switching when selecting next ship to fight fixed, from slayne
+- More memory leak fixes in 3do_getbody.c, from PhracturedBlue
+- Fixed memory leak in _ReleaseCelData, from PhracturedBlue.
+- Fixed mutex deadlock in 3do_blt.c, from tamlin
+- 'additional credits' amount for Melnorme correct now, by Windplume.
+- Spheres of influence now move correctly in starmap, from l0ci@hotmail.com
+- Linux OpenAL fixes (music plays now as stereo)
+- Fuel giveaway bug fixed, from steve@blckknght.org and Windplume.
+- Starmap fuel range calculator and actual consumption matches now
+- Collision detection is now pixel-perfect (fixes Sa-Matra, BUTT missile, etc)
+- Fixed lander position sign bug which was introduced by previous fixes
+- Initial display of planet surface on landing is at correct position
+- Planet scan is now properly erased when cancelling/landing
+- 3D planet view when entering orbit is now implemented
+- TFB_DrawCommandQueue->FullSize was uninitialised.
+- Typo 'cultrue' for Thraddash fixed.
+- Autopilot indicator no longer blinking on starmap and combat
+- Entering a star system lockup/messed graphics fixed
+- Position of planet info icons/texts is now correct and centered
+- Yehat had no subtitles.
+- Incorrect position of 'empty slot' and 'team name' in supermelee fixed
+
+0.1:
+- Initial release
+
diff --git a/Contributing b/Contributing
new file mode 100644
index 0000000..985a6af
--- /dev/null
+++ b/Contributing
@@ -0,0 +1,135 @@
+These are some guidelines for people who want to contribute to the code.
+Don't be surprised if your contributions get tossed in the bit-bucket if you
+do not follow them. We don't want to be unfriendly, but our time is limited.
+These guidelines are there so that you won't waste both our and your time.
+
+Before making changes:
+- Read this entire document
+- See if the Bugzilla bug database at
+ http://bugs.uqm.stack.nl/ contains any comments on what you're planning
+ to do.
+- Make sure you're using the most recent Subversion version
+- Discuss in advance what you're planning to do, with the core team.
+ The best place to do this is on #sc2 on irc.freenode.net.
+ This prevents you from wasting your time when
+ - someone else is already working on your issue
+ - we've got a very clear idea of how we want it to be
+ - the code you're planning to change will be completely rewritten
+ in the near future.
+- Don't bother on adding "great ideas" you have for the game;
+ Our current goal is a straight port. The code is GPL, so feel free
+ to start your own modified version, but don't bother sending them
+ in for the official version.
+
+Making changes:
+- Follow the coding style of the existing source. You don't have to like it,
+ we don't even always do, but we've accepted this as our standard. The main
+ reason is that this is very close to the original style.
+ Trying to start a discussion about the standard is pointless and is
+ definitely NOT appreciated.
+ - Use 1 tab per indentation level
+ - Use no more than 76 chars on a line, when using a tab size of 4.
+ - Use 2 extra indentation levels for the continuation of a broken line,
+ like this:
+ if (blablablabla || foobar ||
+ zut || linefiller ||
+ morezut)
+ printf ("Yeah!\n");
+ - Don't use tabs for anything but indenting. If you would, and someone
+ has a different tab size, or something in the line changes, other stuff
+ on the line may or may not move, depending on where on the line it is.
+ If you for instance want to align a list of declarations, use spaces,
+ like this:
+ {
+ <TAB>long l,
+ <TAB> m;
+ <TAB>int i;
+ }
+ (Though in this particular case, I personally would repeat the 'long',
+ or place l and m on the same line)
+ - Put { on a separate line, both for the start of a function and
+ for the start of a block.
+ - one space around binary operators, and after commas.
+ - one space between the function name and following '(', both in
+ declaration and call (unusual as it is).
+ - one space after 'if', 'while', 'do', 'for' and 'switch'.
+ - even for short selections or repetitions, don't have the statement
+ to execute on the same line as the guard. So:
+ if (a)
+ a--;
+ - Use unix-style line-endings, that is '\n' only. If the editor you're
+ using doesn't support this, please pass your code through a conversion
+ program before submitting.
+- Don't hurry into changing code. All code is there for a reason. Be sure
+ you understand that reason before changing it. Don't just go recode a part
+ because you think that would be easier than trying to understand the
+ original. If you don't have the skills or patience to do so, this is not
+ the place for you.
+- Only use portable functions. The code is intended to work on Windows
+ (MSVC 6), Linux (GCC 4), FreeBSD (GCC 4) and MacOS X (GCC 4).
+ Try to avoid unnecessary system-dependant code, but use #ifdefs if you
+ really have to.
+- No shortcuts. Don't assume anything about user input (like the length),
+ and check the return values of functions that may fail.
+- Your code shouldn't cause any compile-time or runtime-warnings. We know
+ the current source is far from warning-free, but those should be removed
+ eventually and we don't want to make it worse.
+- Don't add comment lines saying things like "This line added by <me>".
+ These comments only foul the code and don't add anything for people
+ reading it. You'll still be credited in the Changelog, and for large
+ contributions (or many small ones) in the authors list. We have
+ Subversion for when we need to find out when what changes were made.
+
+Making the patches:
+- One issue per patch.
+ We need to keep track of what's being changed, and multiple changes
+ in one patch will make that more difficult.
+ Also, we might want to accept one patch, and reject the other.
+- Use unified diffs.
+ That way, there's a bigger chance the patch can be automatically applied
+ successfully against modified files.
+- Make the patches against the current Subversion tree.
+
+Test the patches:
+- If possible, test your changes both on Windows and a *nix platform, or
+ send them to someone to test them for you.
+
+Getting the patches committed:
+- Either attach the patches to the appropriate bug report in the Bugzilla
+ bug database or send them to one of the committers, in plain-text format.
+ This can be done by email, DCC from within the #sc2 channel, or by
+ mentioning an URL where we can get the patch.
+ The committers are listed below (in alphabetical order), with their
+ particular field of expertise with the source. Though all of us
+ should have enough experience to deal with most issues not explicitly
+ mentioned.
+ Serge van den Boom (svdb at stack.nl), SvdB at #sc2
+ - Resource system
+ - 3DO historical code
+ - *nix build system
+ - Netplay
+ - General issues (particularly on *nix)
+ Mika Kolehmainen (mk at kapsi.fi), Gwl at #sc2
+ - Graphics
+ - Sound
+ - General issues
+ Michael Martin (mcmartin at mail.com), McMartin at #sc2
+ - Threading
+ - Graphics
+ - Input system
+ - In-game configuration
+ - General issues
+ Alex Volkov (codepro at usa.net), fossil at #sc2
+ - Graphics
+ - Sound (particularly MixSDL)
+ - Alien communications code
+ - General issues
+- Only submit code that can be used under the GPL. By submitting code you
+ hold the copyright to, you agree that it can be used under the term of
+ the GPL. If you use code by someone else, make sure that it can be used
+ under the GPL and let us know, so that adequate credit can be given.
+
+
+Initial version of this file by Serge van den Boom, 2002-12-05.
+
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..e91cf2d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,89 @@
+ THE UR-QUAN MASTERS: BUILD INSTRUCTIONS
+ ---------------------------------------
+
+INSTALLING PREREQUISITES
+------------------------
+
+To build The Ur-Quan Masters, you must first install its
+prerequisites: SDL2, PNG, Ogg Vorbis, and Zlib. How to do this will
+depend on what operating system you are running:
+
+On Debian or similar systems like Ubuntu, install the following packages:
+
+ sudo apt-get install build-essential libogg-dev libpng-dev libsdl2-dev \
+ libvorbis-dev libz-dev
+
+On Fedora or similar systems like CentOS, install these packages:
+ sudo dnf install libogg-devel libpng-devel libvorbis-devel make SDL2-devel \
+ zlib-devel
+
+On macOS, install Xcode from the App Store, and then install
+"Additional components" when you run it for the first time. You can
+then install brew from https://brew.sh and then use it to install your
+requirements from the Terminal:
+
+ brew install libogg libpng libvorbis sdl2
+
+On Windows, you will need to use the MSYS2 system from
+https://www.msys2.org -- after you install the base system, open an
+"MSYS2 MSYS" window and update the system with the command
+
+ pacman -Syuu
+
+until there is nothing left to do. Aftr that you can install the
+packages you will need to build the 32-bit version of UQM:
+
+ pacman -S make pkg-config mingw-w64-i686-gcc mingw-w64-i686-libogg \
+ mingw-w64-i686-libpng mingw-w64-i686-libsystre \
+ mingw-w64-i686-libvorbis mingw-w64-i686-SDL2 mingw-w64-i686-zlib
+
+Actually building UQM will need to be done from a "MSYS2 MinGW 32-bit"
+window, not "MSYS2 MSYS".
+
+BUILDING THE PROGRAM
+--------------------
+
+Building and configuration is managed by the "build.sh" script in the
+same directory as this file. Ordinarily, you will only need the command
+
+ ./build.sh uqm
+
+To configure and build the system. Pass an argument like "-j5" for a
+parallel build using 5 processes. To delete the current bulid and
+reconfigure, issue the command
+
+ ./build.sh uqm clean
+
+And it will clear out all configuration choices.
+
+The configuration process is interactive; for unattended or scripted
+installs, consult the "config.state" file generated by the
+configuration process and synthesize an equivalent as needed; builds
+will then skip the configure step after that.
+
+After the build completes, a binary named "uqm" or "uqm-debug" will be
+created, and should be runnable out of this directory. To produce a
+distributable or installable package that runs on any system, more
+work is needed.
+
+BUILDING AN INSTALLABLE PACKAGE
+-------------------------------
+
+LINUX: The UQM project does not officially maintain any installation
+packages for any Linux distro, but other volunteers have often done
+this already. In general, all that will be needed is to arrange
+matters so that the uqm binary and the content directory are installed
+into globally accessible locations, and that it is invoked with
+arguments that properly identify those directories.
+
+WINDOWS: An installable Windows build takes the UQM.EXE file created
+by the build process and then correlates it with the prepackaged
+content packs to produce an installer executable that will download
+all other data from sourceforge. See INSTALL.win32 for the extra steps
+required for this.
+
+MAC: A redistributable package on macOS is an app bundle that contains
+everything needed to run the program. Because of the way brew handles
+system dependencies, the program must be built differently to be
+redistributable. See INSTALL.macos for details on how to do this.
+
diff --git a/INSTALL.macos b/INSTALL.macos
new file mode 100644
index 0000000..bbf4b83
--- /dev/null
+++ b/INSTALL.macos
@@ -0,0 +1,44 @@
+ BUILDING THE UR-QUAN MASTERS ON MAC OS X
+ ----------------------------------------
+
+Starting with 0.7.1, it is possible to build and run The Ur-Quan
+Masters with the brew system (https://brew.sh). This is much easier
+than building a redistributable package, and personal builds should
+probably rely on this. See the main INSTALL file for instructions
+there.
+
+To create a .app file that will work on most Macs, you will need to
+create an app bundle with custom copies of its libraries. To do
+*that*, you will to build most of them yourself:
+
+SDL2: https://www.libsdl.org
+ The development libraries for macOS available here already work
+ fine and do not need any extra work.
+
+Ogg Vorbis: http://www.xiph.org
+ You will need to build frameworks out of libogg and libvorbis,
+ which will be named Ogg.framework and Vorbis.framework.
+
+libpng: http://www.libpng.org/pub/png/libpng.html
+ You will ultimately need to create libpng.framework.
+
+These frameworks should be built as "archives" in Xcode with a macOS
+deployment target of 10.6. Copy the framework directories out of the
+archives into /Library/Frameworks.
+
+With these in place, copy the content packages that you intend to
+undle with the app (at minimum content, but potentially also voice and
+3DO music or even remixes) into a subdirectory under this one named
+"dist-packages".
+
+You are now ready to actually build and package the application:
+
+ DEPS_PATH=/Library/Frameworks ./build.sh uqm
+ build/unix_installer/copy_mac_frameworks.pl
+
+This should produce a working app bundle named "The Ur-Quan
+Masters.app". For proper redistribution this app should be put in a
+disk image with the Disk Utility. If you are running on a version of
+macOS later than 10.12, don't forget to make sure you use HFS+ instead
+of APFS, or your disk image won't be mountable on any version of macOS
+10.12 or earlier!
diff --git a/INSTALL.pkgs b/INSTALL.pkgs
new file mode 100644
index 0000000..8ce46d1
--- /dev/null
+++ b/INSTALL.pkgs
@@ -0,0 +1,39 @@
+ CREATING NEW CONTENT PACKAGES
+ -----------------------------
+
+When a new version of UQM is released, new versions of the content
+packages will also need to be created and uploaded. These packages are
+renamed zip files created by the uqmzip utility under tools/uqmzip, and
+ultimately by the "zip" commandline utility itself.
+
+Because of the way Windows and Git interact with line endings and the like,
+and because of macOS's occasionaly pollution of zip files with macOS-specific
+metadata, official releases always create these packages on a Linux system,
+
+The behavior of uqmzip is controlled by variables set at the beginning of it;
+customize these variables for the version and for your system, but remember
+to back out any changes that refer to your system's unique configuration!
+
+Once the packages are created, upload them to SourceForge in an appropriate
+directory matching the current pattern for previous versions. This will
+make them available for new users and for the Windows network installer.
+
+The network installer will also need to be modified to know how to recognize
+the new packages. Make a dist-packages/ directory in the repository root (so,
+next to sc2 and tools) and put all of them in there. There should be seven;
+content, 3domusic, voice, and the four remix packs. Change the directory to
+sc2/build/win32_install and run:
+
+ ./procpkgs.sh
+
+This will update packages.nsh with fresh versions of the MD5 sums and
+package filenames for later inclusion in the installer logic.
+
+If this was a release more significant than a patch release, then the
+SourceForge packages were uploaded to a new directory; edit any necessary
+instances of the $DOWNLOADPATH variable in uqm-installer.nsi to point
+to the new URLs.
+
+Changes to packages.nsh and uqm-installer.nsi SHOULD be committed to the
+repository in their corresponding version branch. Changes to the uqmzip
+script SHOULD NOT be committed.
diff --git a/INSTALL.symbian b/INSTALL.symbian
new file mode 100644
index 0000000..cd7ff0a
--- /dev/null
+++ b/INSTALL.symbian
@@ -0,0 +1,94 @@
+UQM for Symbian S60 3rd edition
+===============================
+
+First some general notes. These instructions might be somewhat incomplete or
+unclear. If you encounter any problems or have suggestions how to improve this
+document, please report them to our Bugzilla at
+ http://bugs.uqm.stack.nl/
+
+UQM's Symbian support is currently at experimental stage. Development is
+being done on Nokia N73, which has too little memory for longer playing
+sessions. Reports of successes and failures on other devices are very welcome.
+
+Known problems:
+- No netmelee.
+
+
+Prerequisites:
+--------------
+
+- Platform SDK for S60 3rd ed.
+ * Download from http://www.forum.nokia.com/
+
+- Open C plugins (if SDK is other than FP2; in FP2 those are included already)
+ * Download from http://www.forum.nokia.com/main/resources/tools_and_sdks/openc_cpp/
+
+- Carbide.c++ (if building for emulator)
+ * Download from http://www.forum.nokia.com/
+ * Command line compiling must be enabled
+
+- CSL ARM Toolchain or RVCT v2.2 (if building for hardware)
+ * GCCE included with SDK
+
+- MinGW and MSYS (for build scripts)
+ * Download from http://www.mingw.org/
+
+- Info-Zip (for building content package)
+ * Download from http://www.info-zip.org/Zip.html
+
+- SDL for S60 3rd ed.
+ * Download from http://koti.mbnet.fi/mertama/sdl.html
+ * You also need the sources from http://www.libsdl.org/
+
+- Tremor
+ * Check out from SVN repository at http://svn.xiph.org/trunk/Tremor/
+
+Building:
+---------
+
+1) Set up platform SDK, compilers, Open C plugins and MinGW + MSYS.
+ * Be sure zip and other tools are listed in path so they can be accessed
+ directly from command line.
+
+2) Subst your Symbian SDK to a new drive along the following example:
+ subst z: C:\Symbian\9.1\S60_3rd_MR_2
+
+3) Put UQM sources and Tremor inside the substed drive, if not already there.
+
+4) Install and build SDL, instructions are inside the package.
+
+5) Build a partial port of libpng and SDL_image, which are included
+ in UQM SVN repository at trunk/symbian/png:
+ cd png\group
+ bldmake bldfiles
+ abld build
+
+6) Build Tremor. First apply a patch found from UQM SVN repository
+ at trunk/symbian/tremor:
+ cd Tremor
+ patch -p0 </path/to/tremor_symbian.patch
+ bldmake bldfiles
+ abld build
+
+7) Open MSYS terminal. All following steps are done inside it.
+
+8) Two environment variables control the building, BUILD_EPOCROOT and BUILD_HOST.
+
+ Set BUILD_EPOCROOT to point to the substed drive:
+ export BUILD_EPOCROOT=/z
+
+ Set BUILD_HOST to WINSCW (emulator) or GCCE / ARMV5 (hardware), depending
+ for which target you want to build:
+ export BUILD_HOST=GCCE
+
+9) Compile (you don't need to change any options from the interactive menu):
+ ./build.sh uqm
+
+10) Install. It builds the content zip and puts everything to proper
+ place (WINSCW) or creates a self-signed SIS package (GCCE / ARMV5):
+ ./build.sh uqm install
+
+11) If you built for GCCE or ARMV5, you should now have uqm.sisx in the same
+ directory from where you did the build. Remember to also install SDL SIS
+ package and if device does not support FP2, Open C plugins before
+ attempting to run UQM on actual device.
diff --git a/INSTALL.win32 b/INSTALL.win32
new file mode 100644
index 0000000..b43c72c
--- /dev/null
+++ b/INSTALL.win32
@@ -0,0 +1,42 @@
+BUILDING THE WIN32 INSTALLER
+----------------------------
+
+You will need additional packages installed beyond what is necessary
+for a build:
+
+ pacman -S dos2unix mingw-w64-i686-nsis mingw-w64-i686-ntldd
+ pacman -S mingw-w64-i686-SDL2_gfx unzip zip
+
+You will also need the INetC and MD5dll plugins for NSIS:
+
+ https://nsis.sourceforge.io/mediawiki/images/c/c9/Inetc.zip
+ https://nsis.sourceforge.io/mediawiki/images/d/d7/Md5dll.zip
+
+Unzip these somewhere and then copy the 32-bit ANSI versions of the library
+into place:
+
+ cp Plugins/x86-ansi/INetC.dll /mingw32/share/nsis/Plugins/ansi/
+ cp md5dll/ANSI/md5dll.dll /mingw32/share/nsis/Plugins/ansi/
+
+If this is a new release of UQM, and content packs have not already been
+regenerated, consult INSTALL.pkgs for the steps to update it. These steps
+include updates to the "packages.nsh" file and the "uqm-installer.nsi" files,
+so you should inspect these files to make sure that it's announcing the
+correct version and downloading the correct versions of the content packs.
+
+With these things in place, create the release build:
+
+ cd sc2
+ ./build.sh uqm clean
+ ./build.sh uqm
+
+Make sure that you select a release build so that uqm.exe will be built
+instead of uqm-debug.exe. Assuming the build works according to plan,
+you will then run the installer creator out of its directory:
+
+ cd build/win32_install
+ ./build-win32-installer.sh
+
+If all goes well, this will build KeyJam application, import, rename, and
+convert the necessary ancillary files, and then build an NSIS-based
+installer that will install them.
diff --git a/Makefile.build b/Makefile.build
new file mode 100644
index 0000000..b8fae45
--- /dev/null
+++ b/Makefile.build
@@ -0,0 +1,90 @@
+# Make file for the uqm build system. Invoked from build/unix/build_functions
+# By Serge van den Boom
+
+.DELETE_ON_ERROR:
+
+SOURCES := $(shell cat "$(DEPEND_FILE)")
+-include $(SOURCES:=.d)
+
+ifeq ($(MAKE_VERBOSE),1)
+define act
+ $(call $(1))
+endef
+else
+define act
+ @echo " $(2) $(3)"
+ @$(call $(1))
+endef
+endif
+
+ifeq ($(HOST_SYSTEM),ARMV5)
+ include build/unix/make/buildtools-armv5
+else
+ifeq ($(HOST_SYSTEM),WINSCW)
+ include build/unix/make/buildtools-winscw
+else
+ifeq ($(HOST_SYSTEM),GCCE)
+ include build/unix/make/buildtools-gcce
+else
+ include build/unix/make/buildtools-generic
+endif
+endif
+endif
+
+
+default:
+ ./build.sh uqm
+
+$(OBJDIR)%.c.d: $(BUILD_ROOT)%.c
+ @DIR=$(dir $@); \
+ if [ ! -d $$DIR ]; then \
+ mkdir -p "$$DIR"; \
+ fi
+ $(call act,act_mkdep_c,MKDEP ,$@)
+
+$(OBJDIR)%.cpp.d: $(BUILD_ROOT)%.cpp
+ @DIR=$(dir $@); \
+ if [ ! -d $$DIR ]; then \
+ mkdir -p "$$DIR"; \
+ fi
+ $(call act,act_mkdep_cxx,MKDEP ,$@)
+
+$(OBJDIR)%.m.d: $(BUILD_ROOT)%.m
+ @DIR=$(dir $@); \
+ if [ ! -d $$DIR ]; then \
+ mkdir -p "$$DIR"; \
+ fi
+ $(call act,act_mkdep_m,MKDEP ,$@)
+
+$(OBJDIR)%.rc.o: $(BUILD_ROOT)%.rc
+ @DIR=$(dir $@); \
+ if [ ! -d $$DIR ]; then \
+ mkdir -p "$$DIR"; \
+ fi
+ $(call act,act_windres,WINDRES,$@)
+
+%.c.o:
+ @DIR=$(dir $@); \
+ if [ ! -d $$DIR ]; then \
+ mkdir -p "$$DIR"; \
+ fi
+ $(call act,act_cc,CC ,$@)
+
+%.cpp.o:
+ @DIR=$(dir $@); \
+ if [ ! -d $$DIR ]; then \
+ mkdir -p "$$DIR"; \
+ fi
+ $(call act,act_cxx,CXX ,$@)
+
+%.m.o:
+ @DIR=$(dir $@); \
+ if [ ! -d $$DIR ]; then \
+ mkdir -p "$$DIR"; \
+ fi
+ $(call act,act_objcc,OBJCC ,$@)
+
+$(TARGET_FILE): $(SOURCES:=.o)
+ $(call act,act_link,LINK ,$@)
+
+
diff --git a/Makeinfo b/Makeinfo
new file mode 100644
index 0000000..103277c
--- /dev/null
+++ b/Makeinfo
@@ -0,0 +1 @@
+uqm_SUBDIRS=src
diff --git a/Makeproject b/Makeproject
new file mode 100644
index 0000000..23d5ec0
--- /dev/null
+++ b/Makeproject
@@ -0,0 +1,46 @@
+TARGETS="uqm"
+
+# For the 'uqm' target:
+if [ "$DEBUG" = "1" ]; then
+ uqm_OBJS=obj/debug/ # Directory for object files
+ uqm_NAME=uqm-debug # File name of executable
+else
+ uqm_OBJS=obj/release/ # Directory for object files
+ uqm_NAME=uqm # File name of executable
+fi
+case "$HOST_SYSTEM" in
+ ARMV5|WINSCW|GCCE)
+ uqm_NAME="uqm.lib"
+ ;;
+ MINGW32*|CYGWIN*)
+ uqm_NAME="$uqm_NAME.exe"
+ ;;
+esac
+uqm_CFLAGS="$uqm_CFLAGS -Isrc"
+uqm_CXXFLAGS="$uqm_CXXFLAGS -Isrc"
+if [ "$uqm_HAVE_REGEX" = 0 ]; then
+ uqm_CFLAGS="$uqm_CFLAGS -Isrc/regex"
+ uqm_CXXFLAGS="$uqm_CXXFLAGS -Isrc/regex"
+fi
+
+# Stuff to install under the directory for libraries, as specified during
+# config.
+uqm_INSTALL_LIBS="executable"
+uqm_INSTALL_LIB_executable_SRC="$BUILD_WORK/$uqm_NAME"
+uqm_INSTALL_LIB_executable_DEST=uqm/uqm
+uqm_INSTALL_LIB_executable_MODE="0755"
+
+# Stuff to install under the directory for system-independant data, as
+# specified during config.
+uqm_INSTALL_SHARED="content"
+uqm_INSTALL_SHARED_content_SRC=content
+uqm_INSTALL_SHARED_content_DEST=uqm/
+uqm_INSTALL_SHARED_content_MODE="go+rX"
+
+# Stuff to install under the directory for binaries, as specified during
+# config.
+uqm_INSTALL_BINS=wrapper
+uqm_INSTALL_BIN_wrapper_SRC="$BUILD_WORK/uqm-wrapper"
+uqm_INSTALL_BIN_wrapper_DEST="uqm"
+uqm_INSTALL_BIN_wrapper_MODE="0755"
+
diff --git a/README b/README
new file mode 100644
index 0000000..6572e49
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+You're looking at the readme for 'The Ur-Quan Masters', a volunteer
+project that intends to bring the classic game 'Star Control II' to
+modern systems.
+
+The program code that comprises 'The Ur-Quan Masters' was derived from code
+written by Toys for Bob, Inc. for the 3DO version of 'Star Control II', with
+their permission and encouragement.
+
+If you've got this file from the source tree, you can find everything
+you need to get started in INSTALL. Additionally and depending on your
+platform, please see:
+ INSTALL.mingw for MinGW build instructions
+ INSTALL.msvc for MSVC++ build instructions
+ INSTALL.macosx for MacOS X build instructions
+ INSTALL.symbian for Symbian build instructions
+ doc/users/unixinstall
+
+The home page of the project is located at http://sc2.sourceforge.net/
+You can find links to downloads, our bug database, and our forum there.
+
+Have fun!
+
+All trademarks and tradenames belong to their respective owners.
diff --git a/README-SDL b/README-SDL
new file mode 100644
index 0000000..4d36ca9
--- /dev/null
+++ b/README-SDL
@@ -0,0 +1,13 @@
+
+Please distribute this file with the SDL runtime environment:
+
+The Simple DirectMedia Layer (SDL for short) is a cross-platfrom library
+designed to make it easy to write multi-media software, such as games and
+emulators.
+
+The Simple DirectMedia Layer library source code is available from:
+http://www.libsdl.org/
+
+This library is distributed under the terms of the GNU LGPL license:
+http://www.gnu.org/copyleft/lesser.html
+
diff --git a/WhatsNew b/WhatsNew
new file mode 100644
index 0000000..89ec6c2
--- /dev/null
+++ b/WhatsNew
@@ -0,0 +1,512 @@
+This file tries to organize and describe the changes between releases.
+Changes are broadly classified into the following categories:
+
+- New Features: These are either elements of the original games that
+ were first implemented in this release, or actually new capabilities
+ that are visible to the user.
+
+- Bugfixes: The removal of visible Bad Things from the code.
+
+- Internal Changes: The removal of invisible Bad Things from the code,
+ or changes in the way the program itself is structured. These are
+ unlikely to be of interest to you unless you plan on coding
+ extensions.
+
+ Version 0.8
+ -------------
+
+NEW FEATURES
+
+- Savegames may now be given descriptive names. Savegames from 0.7 or before
+ will load fine, but games saved in 0.8 will not work in earlier versions.
+- You can now buy fuel at the Starbase 10 at a time with PageUp and PageDn.
+- F6 may be used by default to search the starmap along with slash.
+- Remixes and voices can be turned on or off directly from the Setup menu,
+ instead of needing to drive the voice volume to zero by hand.
+- A new OS/hardware compatibility layer based on SDL2 is now available. This
+ provides much better support for fullscreen and more efficient use of 2D
+ GPU acceleration. The new layer supports D3D11 on Windows, Metal on macOS
+ and iOS, and can even produce a proper fullscreen display on the Raspberry
+ Pi without X11. The original pixel-based SDL1 backend is still supported
+ for older or more obscure systems.
+
+BUGFIXES
+- The Windows net installer works again and can automatically download
+ packages you ask for that are missing.
+- Earlier versions of the code used some symbols in the include guards that
+ were theoretically reserved to gcc. In the intervening nine years, that
+ became less theoretical and the code stopped compiling. These have all now
+ been renamed.
+- Netplay code no longer breaks when compiled on modern versions of MSVC.
+- You can no longer savescum Melnorme rescues to get better deals.
+- Glitches related to the Shofixti Glory Device and the Pkunk Ressurection
+ have been resolved.
+- Various dialogue lines typo-fixed or brought into line with original
+- Sound options that require a restart aren't lost if you revisit the setup
+ menu without restarting.
+- Fixed a softlock when encountering ships in interplanetary space at
+ exactly the wrong place.
+- Fixed incorrectly sized fuel range circle on Hyperspace map.
+- Credits and acknowledgements are no longer nine years out of date and are
+ now at the level of detail expected of 2020-era indie games.
+
+INTERNAL CHANGES
+
+- The build system has been unified around the Linux build system. Windows
+ builds are built under MSYS2, and macOS supports both traditional framework
+ libraries or building under brew.
+- SDL_image is no longer a prerequisite; libpng is now used directly.
+- The save file format has changed, in part to allow namable savegames, but
+ also to make it easier to edit with simple tools or for independent mods
+ to compatibly and simultaneously extend.
+- Improved documentation and code organization throughout the code.
+- Removed many warnings and memory leaks.
+- Dramatic streamlining of the multithreading code.
+- The C-based code and build system has been modified and annotated to allow
+ extensions to be written in C++ if desired.
+
+ Version 0.7
+ -------------
+
+NEW FEATURES
+
+- Added the missing bits to the no-voice versions of Mycon, Syreen and
+ Utwig dialogue
+- New lander reports for Supox ruins and Ultron. The reports text
+ is new content from TFB.
+- Added spoken Slylandro probe coordinates
+- Remixes are now selectable from the setup menu
+- Added a "safe mode", which will ignore settings. Useful if you've somehow
+ wrecked your configuration files.
+- Added --addondir commandline option
+- Added --keepaspectratio to keep correct aspect ratio when using
+ custom resolutions in OpenGL mode
+- New addon machanism allows greater flexibility for selecting which
+ resource goes with which game element.
+- Joystick text input improved
+- Unicode strings now could theoretically support UCS-4 on all platforms
+- Experimental support for Symbian S60 3rd edition
+- Support for Windows CE
+- Added fullscreen/windowed toggle key F11
+- Support for 3DO "ship spin" videos
+- Better location description in savegame summaries
+- Cancel key will now quit out of the Manifest Menu
+- Error boxes on MacOSX are now actually native to the OS - no need to check
+ Console.app anymore.
+
+BUGFIXES
+
+- Properly account for simultaneous destruction in SuperMelee.
+- Ending a battle with a simultaneous death no longer triggers an
+ assertion
+- Druuge no longer turn hostile after attempting a salvage
+- Fixed Mmrnmhrm's X-Form transformation without energy use
+- Fuel and distance estimates are now more accurate
+- Analog joysticks less sensitive to jitter
+- Cannot talk to Ilwrath with a 'caster anymore after they die off
+- Fixed enemy ships getting recrewed between encounters in HyperSpace
+- Old Windows installer was horribly obsolete; fixed now.
+- Fixed rendering error when entering the orbit of a shielded planet
+- Fixed planet blinking when exiting scan
+- Menu sounds restored after editing a control set name.
+- Fixed a freeze when quickly escaping melee.
+- Fixed misaligned cargo count
+- Fixed a crash when filling fuel tanks over 10
+- Comm code rewrite, fixing a lot of minor annoyances
+- Team configuration in netplay no longer desyncs
+- Fixed Melee menu timeout when both sides are Cyborgs
+- Fixed AI ship not moving on warp in
+- Revert gfx settings entirely when a mode switch fails
+- Fix for weird colors problem on MacOSX with SDL 1.2.14
+- Lander will no longer hang when killed on planets with a lot of
+ natural disasters
+- Fixed crash when saving a game into the last slot while having
+ too many devices on board
+- Fixed crashes and potential weirdness when loading savegames from
+ a Homeworld encounter screen
+- Fixed a crash when conversing with music disabled
+- Extremely busy battle sequences (Orz vs. Orz with many space marines
+ out, for instance) no longer introduces graphical glitches
+- Fixed rendering bug that would occasionally make a ship just be solid
+ blue all the time
+- Objects in melee made of multiple "parts" no longer jitter during zoom
+- Fixed a crash on startup if uqm.cfg did not exist
+- Extremely rapid key presses will no longer be lost at low framerates
+- 3DO videos now work after a resolution change
+- OpenGL texture loading uses surface pitch instead of screen width,
+ improving compatibility across older graphics cards.
+- Miscellaneous conversational typo and timing fixes
+- 999.9:999.9 is now reachable in HyperSpace
+- Do not match singular stars when given a prefix in star search
+- Fixed wrong Sa-Matra guards icons after Kohr-Ah win
+- Canceling load from the main menu returns to main menu
+- Gas giants can no longer have negative masses
+
+INTERNAL CHANGES
+
+- Code cleanup; many warnings removed
+- Massive changes to the way the resource system works. This is
+ mostly interesting in that 3DO voices, 3DO videos, and 3DO music
+ are now addon packs like any other.
+- Code reorganization; old stuff removed, new stuff better set-up
+- 32-bit graphics everywhere internally.
+- Refactored universe generation
+- Pthread support
+- Removed the 256-frame limit on .ani files
+
+ Version 0.6
+ -------------
+
+NEW FEATURES
+
+- Netplay! You can now engage in Super Melee over the Internet.
+- Key configuration is now entirely in-game.
+- UQM now compiles and runs on 64-bit systems.
+
+BUGFIXES
+
+- The Quit button (F10) now works properly when used during the
+ introduction sequence.
+- Various small fixes to text and conversation logic.
+
+INTERNAL CHANGES
+
+- The setup menu now reads all its text from the content, easing
+ translation.
+- The MOD player for PC-style music can now be linked against an
+ external version of libmikmod.
+- Various code cleanups and memory optimizations.
+
+ Version 0.5
+ -------------
+
+NEW FEATURES
+
+- The Starmap is searchable! Type / then the beginning of the
+ constellation name, and you can tab through all possible
+ completions.
+- New 'hq' scaler, based on Maxim Stepin's "HQ2X" scaler. See
+ www.hiend3d.com/hq2x.html for more details.
+- Scalers can use MMX/SSE/3DNow! instructions for significant speed
+ improvements. The "Processor pack" is necessary for compilation of
+ same on VC6.
+- Imported DOS versions of many graphics; these have richer palettes
+ and so generally look better
+- There's a proper credit roll at the end of the game now.
+- Superior Planet handling: topographical maps scaled far more
+ precisely; optional 3DO-style throbbing slave shield; planets are
+ finally rotating spheres instead of spotlit cylinders or rectangles.
+ The old PC-style "Entering Planetary Orbit..." screen is back.
+- Setup Menu far more complete and easy to use, and selections made in
+ the Setup Menu will actually persist when you restart the program
+- Added support for the Tremor Ogg Vorbis decoder (avoids floating point
+ math)
+- Home, End and BackSpace keys work as you'd expect in text input.
+- Most of the game works with Unicode properly now, and so, with proper
+ font characters installed, will work with non-Latin alphabets. (On
+ Windows, Unicode input requires a very recent version of SDL.)
+- Text may be input with the joystick again, as on the 3DO. The
+ available characters are stored in content/lbm/joyalpha.txt.
+- The intro now plays only when a new game is started.
+- Color depth is now determined entirely automatically.
+
+BUGFIXES
+
+- Only SELECT and CANCEL trigger the fade-to-black at the end of a
+ Super Melee, solving the issue of invisible "Really Quit?" menus for
+ Super Melee. The "quit during fades" problem in general is still
+ extant.
+- Many dialog, and comm animation, and general graphical fixes
+- Keypress status is not reset when entering battle mode, so (for
+ instance) Melnorme ships can continue to charge a shot across
+ battles.
+- The Melnorme would occasionally strip off Plot Points as part of a
+ fuel deal. No longer.
+- Bugs in the original code prevented certain ships from properly
+ spawning in "uncontrolled" space. The "wilds" are now a bit wilder,
+ as apparently originally intended.
+- Venus's atmospheric density was incorrectly corrected in the 3DO
+ version. It's been correctly corrected now.
+- Scheduled plot events work properly, even when the game suddenly
+ skips ahead in time.
+- Fixed version checking in unix build scripts. SDL 1.2.10 is now
+ recognised as newer than 1.2.9.
+- Spliced communications can be safely skipped past now.
+- Fixed fast escape weirdness (bug #619)
+- Threading system no longer assumes that there is no thread 0; this
+ permits compilation on AmigaOS.
+- Relative paths and fallback paths work properly now.
+- Scalers now use surface pitch instead of image width - this is
+ reported to solve many strange display problems in non-OpenGL mode
+ on Macs
+- Ending sequences may now be safely paused.
+
+INTERNAL CHANGES
+
+- Added the rest of devel/ and users/ documentation into MSVC .dsp
+ files.
+- Control scheme upgraded. Old versions of keys.cfg will no longer
+ work.
+- PNG transparency info (tRNS chunk) is now set properly, based on
+ info in the .ani files.
+- Paletted images should render much faster now, and collisions
+ between mods are less likely.
+- New font engine: fonts are loaded and treated as alpha-channel-only
+ images, with font effects handled as backing images.
+- Melee works properly with alpha-channel graphics new, even when
+ mipmapped.
+- Lander report drawing handled more sensibly now
+- Binary resource indexes have been replaced with textual ones.
+- All spritework is done internally in 32BPP.
+- Removed MikMod i/o hacks.
+- The temp files for representing star and group data are now kept in
+ memory instead.
+- Separate config_win.h file for build.sh builds on Windows
+- Lowered some animation rates to reasonable levels, lowering CPU
+ usage.
+- Changed comm subtitle caching scheme. Should kill the "blue comm
+ screen" problem forever.
+- Refactored setup menu code to use generic widgets.
+- Cleaner build output. Set '$MAKE_VERBOSE' to 1 for old output.
+- Improved dependency tracking for unix build system. "./build.sh uqm
+ depend" is now only needed for checking for new source files.
+
+ Version 0.4
+ -------------
+
+NEW FEATURES
+
+- Savegame slot defaults to the last one used during this play
+- PC intro and ending sequence are now present
+- 3DO intro and ending movies are supported for those who have these
+ movies from the 3DO CD of Star Control II
+- Improved slave shield graphics
+- Added a new -l option to produce logfiles
+- Added a new 'triscan' scaler derived from scale2x
+- Made the fact that self-destructing ships grant resources more obvious
+- Added a "config dir" option for holding saves and melee information
+- Cocoa hooks
+- Setup menu now permits configuration of some options. They do not
+ yet persist past program quit, though.
+- Added a "--version" option
+- Melee images are now based on the (richer) DOS content
+- 3-step melee zooming as per the PC version is now implemented
+- It is now possible to complete the game without ever allying with the
+ Starbase (if one is insane; this is known on the forum as "Beating The
+ Game Differently" - but bugs and plot elements preventing this have been
+ fixed or evaded under this circumstance).
+
+BUGFIXES
+
+- Fixes to the Quit Confirmation dialog
+- Collisions/encounters with "invisible" fleeing ships gone
+- Other ships insystem remain in proper locations after planet landing
+- Fixed some keyboard "focus" problems where flight controls were
+ being cleared at inappropriate times, or interfering with menus
+- Graphics fixes
+- Subtitle timings work in the absence of oggs
+- Conversation summaries with Melnorme no longer crash
+- --contentdir argument may now have spaces
+- $HOME isn't required for Unix systems anymore
+- Fuel estimates fixed (original bug)
+- Fuel usage on planet landing now correcly reported on all versions
+- MOD music will play on big-endian machines under high-quality now
+- Space marines die in a self-destructing Scout
+- ZFP speech is properly vertically aligned now
+- Various odd behaviors when loading in HyperSpace now fixed
+- Quitting from the Roster screen no longer crashes
+- Many dialog fixes
+- The "Blue Comm Screen" problem no longer occurs
+- Combat Energy computation in Outfit Flagship screen corrected
+- A game crash that manifested when pausing after 85 minutes has been
+ corrected
+- Main menu version number drawn more consistently
+- Transparency bugfixes - 3DO ending credits, planets in battle
+- Content packs may also be named ".uqm" - these are really still
+ zip files, but you can't expand them by accident.
+- One may now configure a joystick without crashing the game if the
+ joystick is not present
+- Music volume normalized throughout the game
+
+INTERNAL CHANGES
+
+- Thread system completely reworked to provide more detailed thread
+ information, and to prevent resource leaks
+- Sound code is now virtualized and separate from game logic
+- Fine-grained control of menu sounds
+- Input system fully unified - all parts of the game use the same
+ basic structure now
+- Build scripts made more robust
+- Window-drawing code was lifted out of confirm.c and made more general
+- Lots of debugging functions
+- Unicode support for game dialogs and fonts
+- Lots of code cleanups
+- Documentation of more internals
+
+
+ Version 0.3
+ -------------
+
+NEW FEATURES
+
+- PC-style shipyard graphics, complete with animated power lines
+- Selling ships in the shipyards has slightly different controls
+ that make it harder to sell ships accidentally
+- Main menu displays version number
+- Added a '-g' option to control gamma correction
+- Planet spin has been improved
+- Trilinear (mipmap-based) scaling in melee
+- PC-style conversation summaries have been implemented
+- Ship location display in status bar matches original more closely
+- Game flags (such as AWARE_OF_SAMATRA) that were ignored by half the
+ game (even in the original) now are more universally available
+- Oscilloscope and Mini-Map have borders now
+- Commander Hayes won't let you rescue him until he's explained his
+ predicament
+- Confirmation dialogs are now menu-based
+- Positional sound effects are available for OpenAL
+- Delete key works on Super-Melee team editing
+- Exit key from the Main Menu will quit the game
+- Quit option available in GAME menu
+- New Main Menu graphics: Setup (not yet implemented) and Quit are
+ options at the Main Menu now
+- Cubic resampling in high quality audio mode
+- PC-style alien outtakes when game is completed
+- 2 more ship slots in melee, as the PC Star Control 2 had.
+- All the standard melee teams from the Star Control 2 PC version.
+- Support for 50 savegames, instead of just 10
+- 'CREW'/'BATT' instead of icons in melee when using PC menus
+
+BUGFIXES
+
+- Text entry doesn't freeze up lander reports anymore
+- VUX warps in at proper range
+- Distance-based ship effects (Syreen, Slylandro, Kohr-Ah) were screen
+ size dependent
+- Line clipping fixed
+- Various communication animation glitches solved
+- Various melee crashes solved
+- Various race conditions eliminated
+- Scaling of graphics is handled by the rendering thread now, solving
+ issues where the background and object scales would drift out of
+ sync
+- Subtitle text is clipped more carefully
+- Objects can no longer be scaled to total invisibility
+- Various dialogue fixes
+- Crossfade glitches eliminated
+- Pause and Exit keys function properly everywhere
+- Visit Count overflow with Yehat and Chmmr patched
+- Wav loader and color transforms are now endian safe
+- Menu glitches when leaving the "GAME" menu are gone
+- Druuge transactions no longer baselessly increase crew cost
+- Lander speed has been retimed to match 3DO
+- Audio resampling works now correctly (less cracklings)
+- MixSDL buffer underrun handling fixed
+- Some memory leaks have been eliminated
+- Fixes and speed improvements on bilinear, biadapt, biadv scalers
+- Various minor graphics glitch fixes
+- Crew death on planet is now counted properly in all cases
+- Mouse cursor is now hidden in fullscreen mode
+- Guardian in Blazer mode being drained by DOGI will no longer
+ result in a non-blazer Guardian with Blazer effects.
+
+INTERNAL CHANGES
+
+- Legacy graphics code stripped down tremendously, a lot of "poor
+ man's object orientation" has fallen away too. Many uses of
+ function pointers with only one possible value replaced with simple
+ direct calls.
+- Crossfades require you to explicitly cache the screen before drawing
+ the transition target
+- User Input code has been completely rewritten
+- File I/O code has been completely rewritten
+- Subtitle drawing is now cached as its own sprite; communication
+ screens are much less graphics intensive now
+- DRAWABLE_DESC uses separately allocated arrays for its frames
+
+
+ Version 0.2
+ -------------
+
+NEW FEATURES
+
+- Planet info "coarse scan" information available and properly rendered,
+ either in 3DO way (symbols) or PC (text)
+- 3D spinning planet in orbit window is present, with antialiasing and
+ phong lighting and an animated approach
+- Pixel-perfect collision detection; Spathi/Mycon are thus playable, and
+ the game is now beatable
+- Build system enhanced and generalized to handle OpenBSD and FreeBSD cleanly.
+- Oscilloscope display works in communication now
+- Color-shading in planet scans
+- Earth with its slave shield now functions properly
+- Outfit Starship and Shipyard graphics re-extracted
+- Save data and temp files are put in a separate directory now
+- Star sizes and colors are properly differentiated.
+- Planet surface is smoothed, blurred, and randomized slightly when it's
+ magnified
+- SIS Status window fully implemented (including gradiated fonts from PC
+ version)
+- Dialogue "slider" shows how far along the speech is; rewind/forward works
+- 'ESC' alone lets you emergency-warp-escape
+- Optional PC version menus
+- Preliminary support for MacOS X (needs more work)
+
+BUGFIXES
+
+- Melee screens cleaned up, SuperMelee menus work.
+- Subtitles now render for all alien races
+- Major editing of subtitles to match more closely the speeches
+- Speech and subtitles are now synchronized properly
+- Autopilot indicator doesn't take over the top of the screen anymore.
+- Entering star systems on autopilot doesn't crash the system.
+- Planet scan is properly erased when cancelling/landing
+- Initial display of planet surface on landing is at correct position
+- Spheres of influence now move correctly on the starmap
+- Melnorme correctly compute the required additional credits
+- Team names switching when selecting the next ship to fight in melee fixed
+- Commas left dots behind on planetary reports; this is fixed
+- Screen resets properly after loading/saving
+- Dialog choices wrap properly
+- Precursor ship crew count now placed correctly in melee
+- 'Cued' color transforms in conversations occur at the right time now
+- SIS correctly predicts its own fuel usage now
+- Melnorme no longer give away fuel
+- Flagship modules are properly aligned
+- The PC soundtrack now loops correctly
+- Lander upgrades were drawn incorrectly
+- Autopilot now works in QuasiSpace without fuel
+- Captain portraits in melee are updated properly now
+- If you have over 1000 units of a resource, those numbers are properly
+ cleared
+
+INTERNAL CHANGES
+
+- SDL_mixer has been abandoned in favor of either OpenAL or the new
+ "MixSDL" library built on the basic SDL_audio routines
+- Color transforms in communications are merged into the core animation
+ thread now, improving speed and stability
+- Much more flexible handling of commandline options
+- All allocated memory is allocated through 'safe' routines that abort if
+ the allocation fails.
+- Thread library now includes condition variables
+- Threads can tell the rendering thread to sleep until everything they've
+ requested is done
+- The rendering thread has been recoded to use no heap space and to have a
+ far smaller memory footprint.
+- Added a special #define, DCQ_OF_DOOM, which simulates severe overload
+ stresses.
+- SAI and related scalers removed due to GPL incompatibilities; similar
+ algorithms reimplemented under GPL.
+- Files for the intro and ending sequence have been successfully extracted
+- Rendering thread routines are somewhat more modular than before
+- Created a new set of graphics primitives that's much easier to use
+- Only parts of the screen that actually changed are updated
+- Input code rewritten entirely
+- Various functions have their names changed to avoid conflicts with
+ core library routines on OS X
+
+
+ Version 0.1
+ -------------
+Initial release.
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..51d2f1c
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Build helper
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# You can start this program as 'SH="/bin/sh -x" ./build.sh' to
+# enable command tracing.
+
+if [ -z "$SH" ]; then
+ if [ `uname -s` = SunOS ]; then
+ # /bin/sh of Solaris is incompatible. Fortunately, Sun ships
+ # a decent sh in /usr/xpg4/bin/ nowadays.
+ SH=/usr/xpg4/bin/sh
+ else
+ SH=/bin/sh
+ fi
+ export SH
+fi
+
+$SH build/unix/build.sh "$@"
+
diff --git a/build.trimui.sh b/build.trimui.sh
new file mode 100755
index 0000000..ced5060
--- /dev/null
+++ b/build.trimui.sh
@@ -0,0 +1,8 @@
+export CC=${CROSS_COMPILE}gcc
+export SYSROOT=$(${CROSS_COMPILE}gcc --print-sysroot)
+export CROSS_ROOT=$SYSROOT
+export PATH=$SYSROOT/usr/bin/:$PATH
+export CFLAGS="$($SYSROOT/usr/bin/sdl-config --cflags) -I$SYSROOT/usr/include"
+export LDFLAGS="-L$SYSROOT/usr/lib"
+export PKG_CONFIG_PATH=$SYSROOT/usr/lib/pkgconfig/
+./build.sh $1 $2
diff --git a/build.vars.in b/build.vars.in
new file mode 100644
index 0000000..552faa2
--- /dev/null
+++ b/build.vars.in
@@ -0,0 +1,65 @@
+# Variables for the build procedure
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+BUILD_SYSTEM='@BUILD_SYSTEM@'
+HOST_SYSTEM='@HOST_SYSTEM@'
+PREPROC_C='@PREPROC_C@'
+MKDEP_C='@MKDEP_C@'
+COMPILE_C='@COMPILE_C@'
+PREPROC_CXX='@PREPROC_CXX@'
+MKDEP_CXX='@MKDEP_CXX@'
+COMPILE_CXX='@COMPILE_CXX@'
+PREPROC_OBJC='@PREPROC_OBJC@'
+MKDEP_OBJC='@MKDEP_OBJC@'
+COMPILE_OBJC='@COMPILE_OBJC@'
+LINK='@LINK@'
+MAKE='@MAKE@'
+ECHON='@ECHON@'
+SED='@SED@'
+REZ='@REZ@'
+WINDRES='@WINDRES@'
+uqm_CFLAGS='@CFLAGS@'
+uqm_CXXFLAGS='@CXXFLAGS@'
+uqm_LDFLAGS='@LDFLAGS@'
+uqm_INSTALL_BINDIR='@INSTALL_BINDIR@'
+uqm_INSTALL_LIBDIR='@INSTALL_LIBDIR@'
+uqm_INSTALL_SHAREDIR='@INSTALL_SHAREDIR@'
+
+# Exported variables are available from all the Makeinfo files
+# Non-exported files only where build.vars is explicitely included.
+uqm_SOUNDMODULE='@SOUNDMODULE@'
+uqm_OGGVORBIS='@OGGVORBIS@'
+uqm_USE_INTERNAL_MIKMOD='@USE_INTERNAL_MIKMOD@'
+uqm_HAVE_GETOPT_LONG='@HAVE_GETOPT_LONG@'
+uqm_HAVE_REGEX='@HAVE_REGEX_H_FLAG@'
+uqm_GFXMODULE='@GFXMODULE@'
+uqm_HAVE_OPENGL='@HAVE_OPENGL@'
+uqm_USE_ZIP_IO='@USE_ZIP_IO@'
+uqm_USE_PLATFORM_ACCEL='@USE_PLATFORM_ACCEL@'
+uqm_THREADLIB='@THREADLIB@'
+uqm_NETPLAY='@NETPLAY@'
+uqm_USE_WINSOCK='@USE_WINSOCK@'
+DEBUG='@DEBUG@'
+MACRO_WIN32='@MACRO_WIN32@'
+MACRO___MINGW32__='@MACRO___MINGW32__@'
+export BUILD_SYSTEM HOST_SYSTEM DEBUG
+export MACRO_WIN32 MACRO___MINGW32__
+export uqm_SOUNDMODULE uqm_OGGVORBIS uqm_USE_INTERNAL_MIKMOD
+export uqm_HAVE_GETOPT_LONG uqm_HAVE_REGEX uqm_USE_WINSOCK uqm_GFXMODULE
+export uqm_HAVE_OPENGL
+export uqm_USE_ZIP_IO uqm_USE_PLATFORM_ACCEL uqm_THREADLIB uqm_NETPLAY
+
diff --git a/build/msvc6/AbxDecoder.dsp b/build/msvc6/AbxDecoder.dsp
new file mode 100644
index 0000000..0818bc6
--- /dev/null
+++ b/build/msvc6/AbxDecoder.dsp
@@ -0,0 +1,105 @@
+# Microsoft Developer Studio Project File - Name="AbxDecoder" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=AbxDecoder - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "AbxDecoder.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "AbxDecoder.mak" CFG="AbxDecoder - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "AbxDecoder - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "AbxDecoder - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=xicl6.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "AbxDecoder - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "AbxDecoder___Win32_Release"
+# PROP BASE Intermediate_Dir "AbxDecoder___Win32_Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "AbxDecoder_Release"
+# PROP Intermediate_Dir "AbxDecoder_Release"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=xilink6.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib advapi32.lib shell32.lib /nologo /base:"0x1d000000" /subsystem:windows /dll /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../content/cdps/abxadec.dll"
+
+!ELSEIF "$(CFG)" == "AbxDecoder - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "AbxDecoder___Win32_Debug"
+# PROP BASE Intermediate_Dir "AbxDecoder___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "AbxDecoder_Debug"
+# PROP Intermediate_Dir "AbxDecoder_Debug"
+# PROP Ignore_Export_Lib 1
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=xilink6.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib advapi32.lib shell32.lib /nologo /base:"0x1d000000" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../content/cdps/abxadec.dll" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "AbxDecoder - Win32 Release"
+# Name "AbxDecoder - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=../abxadec/abxaud.c
+# End Source File
+# Begin Source File
+
+SOURCE=../abxadec/abxaud.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/build/msvc6/UrQuanMasters.dsp b/build/msvc6/UrQuanMasters.dsp
new file mode 100644
index 0000000..8bf05f8
--- /dev/null
+++ b/build/msvc6/UrQuanMasters.dsp
@@ -0,0 +1,3786 @@
+# Microsoft Developer Studio Project File - Name="UrQuanMasters" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=UrQuanMasters - Win32 Debug NoAccel
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "UrQuanMasters.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "UrQuanMasters.mak" CFG="UrQuanMasters - Win32 Debug NoAccel"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "UrQuanMasters - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "UrQuanMasters - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "UrQuanMasters - Win32 Debug NoAccel" (based on "Win32 (x86) Console Application")
+!MESSAGE "UrQuanMasters - Win32 Release NoAccel" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "UrQuanMasters - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W2 /GX /Zi /O2 /I "..\..\src" /I "..\..\src\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "THREADLIB_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=Stripping debug info...
+PostBuild_Cmds=rebase -b 0x400000 -x . "../../uqm.exe"
+# End Special Build Tool
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\src" /I "..\..\src\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "DEBUG_DCQ_THREADS" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "THREADLIB_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo /o"UrQuanMasters.bsc"
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug_NoAccel"
+# PROP BASE Intermediate_Dir "Debug_NoAccel"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug_NoAccel"
+# PROP Intermediate_Dir "Debug_NoAccel"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "..\..\src" /I "..\..\src\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "DEBUG_DCQ_THREADS" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "THREADLIB_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /GZ /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo /o"UrQuanMasters.bsc"
+# ADD BSC32 /nologo /o"UrQuanMasters.bsc"
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
+# SUBTRACT LINK32 /nodefaultlib
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release_NoAccel"
+# PROP BASE Intermediate_Dir "Release_NoAccel"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release_NoAccel"
+# PROP Intermediate_Dir "Release_NoAccel"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W2 /GX /Zi /O2 /I "..\..\src" /I "..\..\src\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "THREADLIB_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FD /c
+# SUBTRACT CPP /YX
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=Stripping debug info...
+PostBuild_Cmds=rebase -b 0x400000 -x . "../../uqm.exe"
+# End Special Build Tool
+
+!ENDIF
+
+# Begin Target
+
+# Name "UrQuanMasters - Win32 Release"
+# Name "UrQuanMasters - Win32 Debug"
+# Name "UrQuanMasters - Win32 Debug NoAccel"
+# Name "UrQuanMasters - Win32 Release NoAccel"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Group "getopt"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\getopt\getopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\getopt\getopt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\getopt\getopt1.c
+# End Source File
+# End Group
+# Begin Group "regex"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\regex\regcomp.ci
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\regex\regex.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\regex\regex.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\regex\regex_internal.ci
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\regex\regex_internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\regex\regexec.ci
+# End Source File
+# End Group
+# Begin Group "libs"
+
+# PROP Default_Filter ""
+# Begin Group "callback"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\callback\alarm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\callback\alarm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\callback\async.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\callback\async.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\callback\callback.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\callback\callback.h
+# End Source File
+# End Group
+# Begin Group "decomp"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\decomp\lzdecode.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\decomp\lzencode.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\decomp\lzh.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\decomp\update.c
+# End Source File
+# End Group
+# Begin Group "file"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\file\dirs.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\file\files.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\file\filintrn.h
+# End Source File
+# End Group
+# Begin Group "graphics"
+
+# PROP Default_Filter ""
+# Begin Group "sdl"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\2xscalers.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\2xscalers.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\2xscalers_3dnow.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\2xscalers_mmx.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\2xscalers_mmx.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\2xscalers_sse.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\biadv2x.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\bilinear2x.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\canvas.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\hq2x.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\nearest2x.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\opengl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\opengl.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\palette.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\palette.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\png2sdl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\png2sdl.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\primitives.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\primitives.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\pure.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\pure.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\rotozoom.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\rotozoom.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\scaleint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\scalemmx.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\scalers.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\scalers.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\sdl1_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\sdl2_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\sdl2_pure.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\sdl_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\sdl_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\sdluio.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\sdluio.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\sdl\triscan2x.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\bbox.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\bbox.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\boxint.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\clipline.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\cmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\cmap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\context.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\context.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\dcqueue.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\dcqueue.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\drawable.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\drawable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\drawcmd.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\filegfx.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\font.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\font.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\frame.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\gfx_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\gfx_common.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\gfxintrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\gfxload.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\intersec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\loaddisp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\pixmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\prim.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\resgfx.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\tfb_draw.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\tfb_draw.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\tfb_prim.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\tfb_prim.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\widgets.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\graphics\widgets.h
+# End Source File
+# End Group
+# Begin Group "heap"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\heap\heap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\heap\heap.h
+# End Source File
+# End Group
+# Begin Group "input"
+
+# PROP Default_Filter ""
+# Begin Group "sdl No. 1"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\sdl\input.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\sdl\input.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\sdl\keynames.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\sdl\keynames.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\sdl\vcontrol.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\sdl\vcontrol.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\sdl\vcontrol_malloc.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\inpintrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\input_common.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\input\input_common.h
+# End Source File
+# End Group
+# Begin Group "list"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\list\list.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\list\list.h
+# End Source File
+# End Group
+# Begin Group "log"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\log\loginternal.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\log\msgbox.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\log\msgbox_win.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\log\uqmlog.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\log\uqmlog.h
+# End Source File
+# End Group
+# Begin Group "math"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\math\mthintrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\math\random.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\math\random.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\math\random2.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\math\sqrt.c
+# End Source File
+# End Group
+# Begin Group "md5"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\md5\md5.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\md5\md5.h
+# End Source File
+# End Group
+# Begin Group "memory"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\memory\w_memlib.c
+# End Source File
+# End Group
+# Begin Group "resource"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\direct.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\filecntl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\getres.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\index.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\loadres.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\propfile.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\propfile.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\resinit.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\resintrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\stringbank.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\resource\stringbank.h
+# End Source File
+# End Group
+# Begin Group "sound"
+
+# PROP Default_Filter ""
+# Begin Group "openal"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\openal\audiodrv_openal.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\openal\audiodrv_openal.h
+# End Source File
+# End Group
+# Begin Group "decoders"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\aiffaud.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\aiffaud.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\decoder.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\decoder.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\dukaud.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\dukaud.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\modaud.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\modaud.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\oggaud.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\oggaud.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\wav.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\decoders\wav.h
+# End Source File
+# End Group
+# Begin Group "mixer"
+
+# PROP Default_Filter ""
+# Begin Group "mixsdl"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\mixer\sdl\audiodrv_sdl.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\mixer\sdl\audiodrv_sdl.h
+# End Source File
+# End Group
+# Begin Group "nosound"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\mixer\nosound\audiodrv_nosound.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\mixer\nosound\audiodrv_nosound.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\mixer\mixer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\mixer\mixer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\mixer\mixerint.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\audiocore.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\audiocore.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\fileinst.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\music.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\resinst.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\sfx.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\sndintrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\sound.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\sound.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\stream.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\stream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\trackint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\trackplayer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sound\trackplayer.h
+# End Source File
+# End Group
+# Begin Group "strings"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\strings\getstr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\strings\sfileins.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\strings\sresins.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\strings\strings.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\strings\strintrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\strings\unicode.c
+# End Source File
+# End Group
+# Begin Group "video"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\dukvid.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\dukvid.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\legacyplayer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\vfileins.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\video.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\video.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\videodec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\videodec.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\vidintrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\vidplayer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\vidplayer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\video\vresins.c
+# End Source File
+# End Group
+# Begin Group "threads"
+
+# PROP Default_Filter ""
+# Begin Group "sdl No. 3"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\threads\sdl\sdlthreads.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\threads\sdl\sdlthreads.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\threads\thrcommon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\threads\thrcommon.h
+# End Source File
+# End Group
+# Begin Group "time"
+
+# PROP Default_Filter ""
+# Begin Group "sdl No. 4"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\time\sdl\sdltime.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\time\sdl\sdltime.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\time\timecommon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\time\timecommon.h
+# End Source File
+# End Group
+# Begin Group "task"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\task\tasklib.c
+# End Source File
+# End Group
+# Begin Group "uio"
+
+# PROP Default_Filter ""
+# Begin Group "stdio"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\stdio\stdio.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\stdio\stdio.h
+# End Source File
+# End Group
+# Begin Group "zip"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\zip\zip.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\zip\zip.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\charhashtable.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\charhashtable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\debug.c
+
+!IF "$(CFG)" == "UrQuanMasters - Win32 Release"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel"
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel"
+
+# PROP BASE Exclude_From_Build 1
+# PROP Exclude_From_Build 1
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\debug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\defaultfs.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\defaultfs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\fileblock.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\fileblock.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\fstypes.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\fstypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\getint.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\gphys.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\gphys.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\hashtable.c
+
+!IF "$(CFG)" == "UrQuanMasters - Win32 Release"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel"
+
+# PROP BASE Exclude_From_Build 1
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel"
+
+# PROP BASE Exclude_From_Build 1
+# PROP Exclude_From_Build 1
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\hashtable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\io.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\io.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\ioaux.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\ioaux.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\iointrn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\match.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\match.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\mem.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\memdebug.c
+
+!IF "$(CFG)" == "UrQuanMasters - Win32 Release"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel"
+
+# PROP BASE Exclude_From_Build 1
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel"
+
+# PROP BASE Exclude_From_Build 1
+# PROP Exclude_From_Build 1
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\memdebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\mount.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\mount.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\mounttree.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\mounttree.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\paths.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\paths.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\physical.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\physical.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\uioport.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\uiostream.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\uiostream.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\uioutils.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\uioutils.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\utils.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio\utils.h
+# End Source File
+# End Group
+# Begin Group "mikmod"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\drv_nos.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\load_it.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\load_mod.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\load_s3m.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\load_stm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\load_xm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mdreg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mdriver.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mikmod.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mikmod_build.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mikmod_internals.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mloader.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mlreg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mlutil.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mmalloc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mmerror.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mmio.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mplayer.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\munitrk.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\mwav.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\npertab.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\sloader.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\virtch.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\virtch2.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mikmod\virtch_common.c
+# End Source File
+# End Group
+# Begin Group "network"
+
+# PROP Default_Filter ""
+# Begin Group "connect"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\connect\connect.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\connect\connect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\connect\listen.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\connect\listen.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\connect\resolve.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\connect\resolve.h
+# End Source File
+# End Group
+# Begin Group "netmanager"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netmanager\ndesc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netmanager\ndesc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netmanager\ndindex.ci
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netmanager\netmanager.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netmanager\netmanager_common.ci
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netmanager\netmanager_win.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netmanager\netmanager_win.h
+# End Source File
+# End Group
+# Begin Group "socket"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\socket\socket.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\socket\socket.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\socket\socket_win.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\socket\socket_win.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\bytesex.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netport.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\netport.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\network.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\network_win.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\network\wspiapiwrap.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\libs\alarm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\async.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\callback.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\compiler.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\declib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\file.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\gfxlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\heap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\inplib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\list.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\log.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\mathlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\memlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\misc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\net.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\platform.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\reslib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\sndlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\strlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\tasklib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\threadlib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\timelib.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\uio.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\unicode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\libs\vidlib.h
+# End Source File
+# End Group
+# Begin Group "uqm"
+
+# PROP Default_Filter ""
+# Begin Group "comm"
+
+# PROP Default_Filter ""
+# Begin Group "arilou.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\arilou\arilouc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\arilou\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\arilou\strings.h
+# End Source File
+# End Group
+# Begin Group "blackur.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\blackur\blackurc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\blackur\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\blackur\strings.h
+# End Source File
+# End Group
+# Begin Group "chmmr.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\chmmr\chmmrc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\chmmr\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\chmmr\strings.h
+# End Source File
+# End Group
+# Begin Group "comandr.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\comandr\comandr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\comandr\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\comandr\strings.h
+# End Source File
+# End Group
+# Begin Group "druuge.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\druuge\druugec.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\druuge\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\druuge\strings.h
+# End Source File
+# End Group
+# Begin Group "ilwrath.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\ilwrath\ilwrathc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\ilwrath\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\ilwrath\strings.h
+# End Source File
+# End Group
+# Begin Group "melnorm.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\melnorm\melnorm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\melnorm\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\melnorm\strings.h
+# End Source File
+# End Group
+# Begin Group "mycon.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\mycon\myconc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\mycon\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\mycon\strings.h
+# End Source File
+# End Group
+# Begin Group "orz.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\orz\orzc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\orz\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\orz\strings.h
+# End Source File
+# End Group
+# Begin Group "pkunk.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\pkunk\pkunkc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\pkunk\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\pkunk\strings.h
+# End Source File
+# End Group
+# Begin Group "rebel.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\rebel\rebel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\rebel\strings.h
+# End Source File
+# End Group
+# Begin Group "shofixt.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\shofixt\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\shofixt\shofixt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\shofixt\strings.h
+# End Source File
+# End Group
+# Begin Group "slyhome.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\slyhome\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\slyhome\slyhome.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\slyhome\strings.h
+# End Source File
+# End Group
+# Begin Group "slyland.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\slyland\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\slyland\slyland.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\slyland\strings.h
+# End Source File
+# End Group
+# Begin Group "spahome.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\spahome\spahome.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\spahome\strings.h
+# End Source File
+# End Group
+# Begin Group "spathi.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\spathi\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\spathi\spathic.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\spathi\strings.h
+# End Source File
+# End Group
+# Begin Group "starbas.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\starbas\starbas.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\starbas\strings.h
+# End Source File
+# End Group
+# Begin Group "supox.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\supox\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\supox\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\supox\supoxc.c
+# End Source File
+# End Group
+# Begin Group "syreen.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\syreen\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\syreen\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\syreen\syreenc.c
+# End Source File
+# End Group
+# Begin Group "talkpet.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\talkpet\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\talkpet\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\talkpet\talkpet.c
+# End Source File
+# End Group
+# Begin Group "thradd.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\thradd\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\thradd\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\thradd\thraddc.c
+# End Source File
+# End Group
+# Begin Group "umgah.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\umgah\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\umgah\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\umgah\umgahc.c
+# End Source File
+# End Group
+# Begin Group "urquan.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\urquan\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\urquan\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\urquan\urquanc.c
+# End Source File
+# End Group
+# Begin Group "utwig.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\utwig\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\utwig\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\utwig\utwigc.c
+# End Source File
+# End Group
+# Begin Group "vux.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\vux\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\vux\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\vux\vuxc.c
+# End Source File
+# End Group
+# Begin Group "yehat.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\yehat\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\yehat\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\yehat\yehatc.c
+# End Source File
+# End Group
+# Begin Group "zoqfot.comm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\zoqfot\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\zoqfot\strings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\zoqfot\zoqfotc.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm\commall.h
+# End Source File
+# End Group
+# Begin Group "planets"
+
+# PROP Default_Filter ""
+# Begin Group "generate"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genall.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genand.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genburv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genchmmr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gencol.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gendefault.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gendefault.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gendru.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genilw.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genmel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genmyc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genorz.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genpet.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genpku.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genrain.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gensam.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genshof.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gensly.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gensol.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genspa.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gensup.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gensyr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genthrad.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\gentrap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genutw.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genvault.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genvux.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genwreck.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genyeh.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genzfpscout.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate\genzoq.c
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\calc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\cargo.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\devices.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\elemdata.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\generate.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\gentopo.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\lander.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\lander.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\lifeform.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\orbits.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\oval.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\pl_stuff.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\plandata.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\planets.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\planets.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\plangen.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\pstarmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\report.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\roster.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\scan.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\scan.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\solarsys.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\solarsys.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\sundata.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\planets\surface.c
+# End Source File
+# End Group
+# Begin Group "ships"
+
+# PROP Default_Filter ""
+# Begin Group "androsyn"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\androsyn\androsyn.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\androsyn\androsyn.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\androsyn\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\androsyn\resinst.h
+# End Source File
+# End Group
+# Begin Group "arilou"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\arilou\arilou.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\arilou\arilou.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\arilou\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\arilou\resinst.h
+# End Source File
+# End Group
+# Begin Group "blackurq"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\blackurq\blackurq.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\blackurq\blackurq.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\blackurq\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\blackurq\resinst.h
+# End Source File
+# End Group
+# Begin Group "chenjesu"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chenjesu\chenjesu.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chenjesu\chenjesu.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chenjesu\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chenjesu\resinst.h
+# End Source File
+# End Group
+# Begin Group "chmmr"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chmmr\chmmr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chmmr\chmmr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chmmr\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\chmmr\resinst.h
+# End Source File
+# End Group
+# Begin Group "druuge"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\druuge\druuge.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\druuge\druuge.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\druuge\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\druuge\resinst.h
+# End Source File
+# End Group
+# Begin Group "human"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\human\human.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\human\human.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\human\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\human\resinst.h
+# End Source File
+# End Group
+# Begin Group "ilwrath"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\ilwrath\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\ilwrath\ilwrath.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\ilwrath\ilwrath.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\ilwrath\resinst.h
+# End Source File
+# End Group
+# Begin Group "lastbat"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\lastbat\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\lastbat\lastbat.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\lastbat\lastbat.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\lastbat\resinst.h
+# End Source File
+# End Group
+# Begin Group "melnorme"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\melnorme\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\melnorme\melnorme.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\melnorme\melnorme.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\melnorme\resinst.h
+# End Source File
+# End Group
+# Begin Group "mmrnmhrm"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mmrnmhrm\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mmrnmhrm\mmrnmhrm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mmrnmhrm\mmrnmhrm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mmrnmhrm\resinst.h
+# End Source File
+# End Group
+# Begin Group "mycon"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mycon\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mycon\mycon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mycon\mycon.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\mycon\resinst.h
+# End Source File
+# End Group
+# Begin Group "orz"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\orz\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\orz\orz.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\orz\orz.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\orz\resinst.h
+# End Source File
+# End Group
+# Begin Group "pkunk"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\pkunk\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\pkunk\pkunk.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\pkunk\pkunk.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\pkunk\resinst.h
+# End Source File
+# End Group
+# Begin Group "probe"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\probe\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\probe\probe.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\probe\probe.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\probe\resinst.h
+# End Source File
+# End Group
+# Begin Group "shofixti"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\shofixti\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\shofixti\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\shofixti\shofixti.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\shofixti\shofixti.h
+# End Source File
+# End Group
+# Begin Group "sis_ship"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\sis_ship\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\sis_ship\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\sis_ship\sis_ship.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\sis_ship\sis_ship.h
+# End Source File
+# End Group
+# Begin Group "slylandr"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\slylandr\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\slylandr\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\slylandr\slylandr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\slylandr\slylandr.h
+# End Source File
+# End Group
+# Begin Group "spathi"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\spathi\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\spathi\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\spathi\spathi.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\spathi\spathi.h
+# End Source File
+# End Group
+# Begin Group "supox"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\supox\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\supox\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\supox\supox.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\supox\supox.h
+# End Source File
+# End Group
+# Begin Group "syreen"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\syreen\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\syreen\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\syreen\syreen.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\syreen\syreen.h
+# End Source File
+# End Group
+# Begin Group "thradd"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\thradd\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\thradd\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\thradd\thradd.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\thradd\thradd.h
+# End Source File
+# End Group
+# Begin Group "umgah"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\umgah\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\umgah\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\umgah\umgah.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\umgah\umgah.h
+# End Source File
+# End Group
+# Begin Group "urquan"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\urquan\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\urquan\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\urquan\urquan.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\urquan\urquan.h
+# End Source File
+# End Group
+# Begin Group "utwig"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\utwig\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\utwig\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\utwig\utwig.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\utwig\utwig.h
+# End Source File
+# End Group
+# Begin Group "vux"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\vux\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\vux\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\vux\vux.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\vux\vux.h
+# End Source File
+# End Group
+# Begin Group "yehat"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\yehat\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\yehat\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\yehat\yehat.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\yehat\yehat.h
+# End Source File
+# End Group
+# Begin Group "zoqfot"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\zoqfot\icode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\zoqfot\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\zoqfot\zoqfot.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\zoqfot\zoqfot.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ships\ship.h
+# End Source File
+# End Group
+# Begin Group "supermelee"
+
+# PROP Default_Filter ""
+# Begin Group "netplay"
+
+# PROP Default_Filter ""
+# Begin Group "proto"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\proto\npconfirm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\proto\npconfirm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\proto\ready.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\proto\ready.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\proto\reset.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\proto\reset.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\checkbuf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\checkbuf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\checksum.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\checksum.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\crc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\crc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\nc_connect.ci
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netconnection.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netconnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netinput.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netinput.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netmelee.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netmelee.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netmisc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netmisc.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netoptions.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netoptions.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netplay.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netrcv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netrcv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netsend.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netsend.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netstate.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\netstate.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\notify.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\notify.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\notifyall.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\notifyall.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packet.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packet.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packethandlers.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packethandlers.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packetq.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packetq.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packetsenders.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\netplay\packetsenders.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\buildpick.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\buildpick.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\loadmele.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\loadmele.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\melee.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\melee.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\meleesetup.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\meleesetup.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\pickmele.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\supermelee\pickmele.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\uqm\battle.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\battle.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\battlecontrols.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\battlecontrols.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\border.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\build.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\build.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\cleanup.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\clock.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\clock.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\cnctdlg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\cnctdlg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\coderes.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\collide.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\collide.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\colors.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\comm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\commanim.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\commanim.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\commglue.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\commglue.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\confirm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\cons_res.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\cons_res.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\controls.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\corecode.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\credits.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\credits.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\cyborg.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\demo.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\displist.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\displist.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\dummy.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\dummy.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\element.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\encount.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\encount.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\flash.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\flash.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\fmv.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\fmv.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\galaxy.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gameev.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gameev.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gameinp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gameopt.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gameopt.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gamestr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gendef.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gendef.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\getchar.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\globdata.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\globdata.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\gravity.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\grpinfo.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\grpinfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\hyper.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\hyper.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ifontres.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\igfxres.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ikey_con.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\imusicre.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\init.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\init.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\intel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\intel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\intro.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ipdisp.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ipdisp.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\isndres.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\istrtab.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\load.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\load.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\loadship.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\master.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\master.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\menu.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\menustat.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\misc.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\nameref.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\oscill.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\oscill.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\outfit.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\pickship.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\pickship.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\plandata.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\process.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\process.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\races.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\resinst.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\restart.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\restart.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\restypes.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\save.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\save.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\settings.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\settings.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\setup.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\setup.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\setupmenu.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\setupmenu.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ship.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\ship.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\shipcont.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\shipstat.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\shipyard.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\sis.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\sis.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\sounds.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\sounds.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\starbase.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\starbase.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\starcon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\starcon.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\starmap.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\starmap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\state.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\state.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\status.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\status.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\tactrans.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\tactrans.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\trans.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\units.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\uqmdebug.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\uqmdebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\util.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\util.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\velocity.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\velocity.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\weapon.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm\weapon.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\src\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\config_vc6.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\endian_uqm.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\options.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\options.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\port.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\port.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqm.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\uqmversion.h
+# End Source File
+# End Group
+# Begin Group "Doc"
+
+# PROP Default_Filter ""
+# Begin Group "Devel"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\doc\devel\aniformat
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\checklist
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\debug
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\dialogs
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\files
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\fontres
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\generate
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\gfxlib
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\gfxres
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\gfxversions
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\glossary
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\input
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\musicres
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\pkgformat
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\planetrender
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\planetrotate
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\plugins
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\resources
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\script
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\sfx
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\strtab
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\threads
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\devel\timing
+# End Source File
+# End Group
+# Begin Group "Users"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\doc\users\manual.txt
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\doc\users\unixinstall
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=..\..\BUGS
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\ChangeLog
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\TODO
+# End Source File
+# End Group
+# Begin Group "Resources"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE="..\..\src\res\kohr-ah1.ico"
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\res\sis1.ico
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\res\starcon2.ico
+# End Source File
+# Begin Source File
+
+SOURCE="..\..\src\res\ur-quan-icon-alpha.ico"
+# End Source File
+# Begin Source File
+
+SOURCE="..\..\src\res\ur-quan-icon-std.ico"
+# End Source File
+# Begin Source File
+
+SOURCE="..\..\src\res\ur-quan1.ico"
+# End Source File
+# Begin Source File
+
+SOURCE="..\..\src\res\ur-quan2.ico"
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\src\res\UrQuanMasters.rc
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/build/msvc6/UrQuanMasters.dsw b/build/msvc6/UrQuanMasters.dsw
new file mode 100644
index 0000000..7d42e80
--- /dev/null
+++ b/build/msvc6/UrQuanMasters.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "UrQuanMasters"=".\UrQuanMasters.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/build/unix/README b/build/unix/README
new file mode 100644
index 0000000..0cad7d9
--- /dev/null
+++ b/build/unix/README
@@ -0,0 +1,5 @@
+This directory contains files used for building and installing
+on unix-like environments.
+There's no need to use any of those directly; type './build.sh' from
+the top dir to get started.
+
diff --git a/build/unix/README.crossbuild b/build/unix/README.crossbuild
new file mode 100644
index 0000000..3992087
--- /dev/null
+++ b/build/unix/README.crossbuild
@@ -0,0 +1,49 @@
+-= Instructions for cross-building UQM =-
+
+
+Terminology:
+ Build system
+ The system on which the code is being built.
+ Host system
+ The system on which the code is supposed to run on.
+
+
+Set the environment variable 'CROSS_ROOT' to the root of your cross building
+environment. This is not necessary for the build scripts, but it will
+simplify what you type in the following steps.
+
+Make sure your cross-building tools (gcc, ld, etc) are in the path.
+ export PATH=$CROSS_ROOT/bin:$PATH
+Make sure that it isn't being reset by subshells that are started.
+ unset ENV BASH_ENV
+
+Set the environment variable 'BUILD_HOST' to the host system.
+This is whatever 'uname -s' returns on such a system (like "MINGW32").
+When building for Windows CE, using the cegcc tools, you should use "cegcc".
+
+If your host system is big-endian, set the environment variable
+'BUILD_HOST_ENDIAN' to "big". For a little-endian system, you don't
+have to do anything.
+ export BUILD_HOST_ENDIAN=little
+
+If you're using pkg-config (advised), set the environment variable
+PKG_CONFIG_PATH' to the pkgconfig directory of the cross environment.
+This dir normally resides in the 'lib/' directory.
+ export PKG_CONFIG_PATH=$CROSS_ROOT/lib/pkgconfig
+
+For any libraries that can't be detected - which will be a lot if you
+don't use pkg-config - set the corresponding include dirs in CFLAGS and
+library dirs in LDFLAGS.
+ export CFLAGS="-I$CROSS_ROOT/include"
+ export LDFLAGS="-L$CROSS_ROOT/lib"
+
+Run configure:
+ ./build.sh uqm config
+This will produce the file 'build.vars' and 'config_unix.h' (or config_win.h
+on MinGW or Cygwin). If there are any problems during the build, you can
+usually easilly solve them by editing these files manually.
+
+Start the compilation:
+ ./build.sh uqm
+
+
diff --git a/build/unix/README.packages b/build/unix/README.packages
new file mode 100644
index 0000000..68acfaf
--- /dev/null
+++ b/build/unix/README.packages
@@ -0,0 +1,31 @@
+This file describes the easiest way to use this build system from
+a packaging system such as rpm.
+
+If you want an interactive configuration, you can start the build process
+like you would do manually:
+ ./build.sh uqm
+to build the game, and
+ ./build.sh uqm install
+to install it.
+
+
+If you want the configuration to be non-interactive, you'll want to do
+the following: execute
+ ./build.sh uqm config
+once. This will generate a 'config.vars' file, which you can then
+include in your package. Then when you want to build the game, put this
+file the directory pointed to by $BUILD_WORK (default is the current
+directory), and do
+ ./build.sh uqm reprocess_config
+to generate all the configuration information for the build, and then
+ ./build.sh uqm
+to build the game, and
+ ./build.sh uqm install
+to install it.
+
+You may wish to set the environment variable BUILD_WORK to a directory
+where the files created in the configuration and build process should be
+stored. That way, the source tree will remain clean and no write access
+to it is required.
+
+
diff --git a/build/unix/ansi b/build/unix/ansi
new file mode 100644
index 0000000..ac5b97f
--- /dev/null
+++ b/build/unix/ansi
@@ -0,0 +1,21 @@
+ESC=`printf '\033'`
+ANSI_BOLD="${ESC}[1m"
+ANSI_NORMAL="${ESC}[0m"
+
+ANSI_BLK="${ESC}[30m"
+ANSI_RED="${ESC}[31m"
+ANSI_GRN="${ESC}[32m"
+ANSI_YLW="${ESC}[33m"
+ANSI_BLU="${ESC}[34m"
+ANSI_MGN="${ESC}[35m"
+ANSI_CYN="${ESC}[36m"
+ANSI_WHT="${ESC}[37m"
+ANSI_LBLK="${ESC}[1;30m"
+ANSI_LRED="${ESC}[1;31m"
+ANSI_LGRN="${ESC}[1;32m"
+ANSI_LYLW="${ESC}[1;33m"
+ANSI_LBLU="${ESC}[1;34m"
+ANSI_LMGN="${ESC}[1;35m"
+ANSI_LCYN="${ESC}[1;36m"
+ANSI_LWHT="${ESC}[1;37m"
+
diff --git a/build/unix/build.config b/build/unix/build.config
new file mode 100644
index 0000000..4b4ef3c
--- /dev/null
+++ b/build/unix/build.config
@@ -0,0 +1,780 @@
+# This file is sourced by build.sh
+
+# Include build functions used here
+. build/unix/config_functions
+. build/unix/menu_functions
+. build/unix/ansi
+
+
+uqm_requirements()
+{
+ # Some requirements:
+ have_build_tools_language C || exit 1
+ have_build_tools_language CXX || exit 1
+ have_build_tool LINK || exit 1
+ case "$HOST_SYSTEM" in
+ MINGW32*|cegcc)
+ have_build_tool WINDRES || exit 1
+ ;;
+ Darwin)
+ have_build_tools_language OBJC || exit 1
+ have_build_tool REZ || exit 1
+ ;;
+ esac
+
+
+ # Define WORDS_BIGENDIAN on bigendian machines
+ check_endianness
+
+
+ # Libraries always used
+ have_library SDL 1.2.8
+ have_library SDL2
+ have_library SDL 1.2.8 || have_library SDL2 || exit 1
+ use_library libpng || exit 1
+
+ case "$HOST_SYSTEM" in
+ WINSCW|ARMV5|GCCE)
+ # Symbian does not use dynamically generated config.h
+ return
+ ;;
+ esac
+
+ # Add defines for HAVE_READDIR_R, HAVE_SETENV, HAVE_STRUPR,
+ # HAVE_STRCASECMP, and HAVE_STRICMP
+ define_have_symbol readdir_r
+ define_have_symbol setenv
+ define_have_symbol strupr
+ define_have_symbol strcasecmp
+ define_have_symbol stricmp
+
+ # Check to see whether math functions are available for free.
+ define_have_symbol acos
+
+ # Require either strcasecmp or stricmp.
+ if not have_symbol strcasecmp && not have_symbol stricmp; then
+ echo "Fatal: Your system defines neither strcasecmp() nor stricmp()."
+ exit 1
+ fi
+
+ # If we don't have math for free, add -lm to LDFLAGS.
+ if not have_symbol acos; then
+ LDFLAGS="$LDFLAGS -lm"
+ fi
+
+ # Add defines for HAVE_ISWGRAPH, HAVE_WCHAR_T, and HAVE_WINT_T
+ define_have_symbol iswgraph
+ define_have_type wchar_t
+ define_have_type wint_t
+
+ # Add defines for HAVE_GETOPT_LONG and HAVE_REGEX_H
+ define_have_symbol getopt_long
+ define_have_header regex.h
+
+ # If we have the regex header, see if we need to link it specially
+ case "$HOST_SYSTEM" in
+ MINGW32*)
+ LDFLAGS="$LDFLAGS -lregex"
+ ;;
+ esac
+
+ # Add define for HAVE__BOOL
+ define_have_type _Bool
+
+ # Add an environment variable for MACRO_WIN32 and MACRO___MINGW32__
+ define_have_macro WIN32
+ define_have_macro __MINGW32__
+
+ if [ -n "$MACRO___MINGW32__" ]; then
+ USE_WINSOCK=1
+ fi
+}
+
+uqm_prepare_config()
+{
+ # Describe the menu:
+ MENU_main_ITEMS="debug graphics sound mikmod ovcodec netplay joystick \
+ ioformat accel threadlib"
+ case "$HOST_SYSTEM" in
+ Darwin|WINSCW|ARMV5|GCCE)
+ # Installation directory not modifiable
+ ;;
+ MINGW32*|cegcc)
+ # No install procedure available for MinGW
+ ;;
+ *)
+ MENU_main_ITEMS="$MENU_main_ITEMS install_path"
+ ;;
+ esac
+ MENU_main_TITLE="Main menu"
+ MENU_main_ITEM_debug_TYPE=CHOICE
+ MENU_main_ITEM_graphics_TYPE=CHOICE
+ MENU_main_ITEM_sound_TYPE=CHOICE
+ MENU_main_ITEM_mikmod_TYPE=CHOICE
+ MENU_main_ITEM_ovcodec_TYPE=CHOICE
+ MENU_main_ITEM_netplay_TYPE=CHOICE
+ MENU_main_ITEM_joystick_TYPE=CHOICE
+ MENU_main_ITEM_ioformat_TYPE=CHOICE
+ MENU_main_ITEM_accel_TYPE=CHOICE
+ MENU_main_ITEM_threadlib_TYPE=CHOICE
+ MENU_main_ITEM_install_path_TYPE=MENU
+
+ CHOICE_debug_OPTIONS="nodebug debug strictdebug"
+ CHOICE_debug_TITLE="Type of build"
+ CHOICE_debug_OPTION_nodebug_TITLE="Optimised release build"
+ CHOICE_debug_OPTION_nodebug_ACTION='nodebug_action'
+ nodebug_action() {
+ case "$HOST_SYSTEM" in
+ WINSCW)
+ CCOMMONFLAGS="$CCOMMONFLAGS -O2 -d NDEBUG"
+ ;;
+ ARMV5)
+ CCOMMONFLAGS="$CCOMMONFLAGS -O3 -Otime -DNDEBUG"
+ ;;
+ GCCE)
+ CCOMMONFLAGS="$CCOMMONFLAGS -O3 -DNDEBUG"
+ ;;
+ *)
+ CCOMMONFLAGS="$CCOMMONFLAGS -O3 -DNDEBUG"
+ ;;
+ esac
+ DEBUG=0
+ }
+ CHOICE_debug_OPTION_debug_TITLE="Debugging build"
+ CHOICE_debug_OPTION_debug_ACTION='debug_action'
+ debug_action() {
+ case "$HOST_SYSTEM" in
+ WINSCW)
+ CCOMMONFLAGS="$CCOMMONFLAGS -g -O0 -W all -d DEBUG -d _DEBUG"
+ ;;
+ ARMV5|GCCE)
+ CCOMMONFLAGS="$CCOMMONFLAGS -g -O0 -DDEBUG -D_DEBUG"
+ ;;
+ *)
+ CCOMMONFLAGS="$CCOMMONFLAGS -g -O0 -W -Wall -DDEBUG"
+ LDFLAGS="$LDFLAGS -O0"
+ ;;
+ esac
+ DEBUG=1
+ }
+ CHOICE_debug_OPTION_strictdebug_TITLE="Debug + strict compile checks"
+ CHOICE_debug_OPTION_strictdebug_ACTION='strictdebug_action'
+ strictdebug_action() {
+ case "$HOST_SYSTEM" in
+ WINSCW)
+ CCOMMONFLAGS="$CCOMMONFLAGS -g -O0 -W all -d DEBUG -d _DEBUG"
+ ;;
+ ARMV5|GCCE)
+ CCOMMONFLAGS="$CCOMMONFLAGS -g -O0 -DDEBUG -D_DEBUG"
+ ;;
+ *)
+ CCOMMONFLAGS="$CCOMMONFLAGS -g -O0 -DDEBUG -W -Wall"
+# CCOMMONFLAGS="$CCOMMONFLAGS -O1"
+ # This is needed for -Wunitialized with gcc 3.4
+ CCOMMONFLAGS="$CCOMMONFLAGS -Wcast-qual -Wmissing-declarations \
+ -Wwrite-strings -Wimplicit -Wreturn-type -Wformat \
+ -Wswitch -Wcomment -Wchar-subscripts \
+ -Wparentheses -Wcast-align -Wuninitialized"
+ CFLAGS="$CFLAGS -Wbad-function-cast -Wmissing-prototypes \
+ -Wstrict-prototypes"
+ # CFLAGS is for flags not valid in C++.
+ CFLAGS="$CFLAGS -Wdeclaration-after-statement"
+ # Until we abandon MSVC 6
+# CCOMMONFLAGS="$CCOMMONFLAGS -Waggregate-return"
+ # It's not unreasonable to return structs at times.
+# CCOMMONFLAGS="$CCOMMONFLAGS "-Wpointer-arith"
+ # Some standard header won't even compile with this on
+# CCOMMONFLAGS="$CCOMMONFLAGS -Wshadow"
+ # This gives absurd conflicts with standard files,
+ # like from 'y0 and y1'
+# CCOMMONFLAGS="$CCOMMONFLAGS -Werror"
+ # We shouldn't do this until we actually nail them
+ # all in the original code. Then we can enforce them
+ # on ourselves.
+# CCOMMONFLAGS="$CCOMMONFLAGS -pedantic-errors -ansi -trigraphs" # ANSI
+# CCOMMONFLAGS="$CCOMMONFLAGS -Wnested-externs"
+ # We know they're in the code, and though we'd like to
+ # get rid of them, they're not bugs.
+# CCOMMONFLAGS="$CCOMMONFLAGS -Winline"
+ # This gives too many warnings which we can do nothing
+ # about, obscuring legitimate warnings.
+ CFLAGS=`echo $CFLAGS`
+ CXXFLAGS=`echo $CXXFLAGS`
+ CCOMMONFLAGS=`echo $CCOMMONFLAGS`
+ # Remove all the unnecessary spaces from the flags vars
+ # for more readable messages.
+ LDFLAGS="$LDFLAGS -O0"
+ ;;
+ esac
+ DEBUG=1
+ }
+ case "$HOST_SYSTEM" in
+ ARMV5|WINSCW|GCCE)
+ CHOICE_debug_DEFAULT=nodebug
+ ;;
+ *)
+ CHOICE_debug_DEFAULT=debug
+ ;;
+ esac
+
+
+ CHOICE_graphics_OPTIONS="pure opengl sdl2"
+ CHOICE_graphics_TITLE="Graphics Engine"
+ CHOICE_graphics_OPTION_pure_TITLE="SDL1 without OpenGL graphics support"
+ CHOICE_graphics_OPTION_pure_ACTION='graphics_pure_action'
+ CHOICE_graphics_OPTION_pure_PRECOND='have_library SDL 1.2.8'
+ graphics_pure_action() {
+ CFLAGS="$CFLAGS -DGFXMODULE_SDL -DSDL_DIR=SDL"
+ CCOMMONFLAGS="$CCOMMONFLAGS -DGFXMODULE_SDL"
+ GFXMODULE=sdl
+ HAVE_OPENGL=0
+ use_library SDL 1.2.8
+ }
+ CHOICE_graphics_OPTION_opengl_TITLE="SDL1 with OpenGL graphics support"
+ CHOICE_graphics_OPTION_opengl_ACTION='graphics_opengl_action'
+ CHOICE_graphics_OPTION_opengl_PRECOND="have_library SDL 1.2.8 && have_library opengl"
+ graphics_opengl_action() {
+ CFLAGS="$CFLAGS -DGFXMODULE_SDL -DHAVE_OPENGL -DSDL_DIR=SDL"
+ GFXMODULE=sdl
+ HAVE_OPENGL=1
+ use_library SDL 1.2.8 && use_library opengl
+ }
+ CHOICE_graphics_OPTION_sdl2_TITLE="SDL2 with modern graphics support"
+ CHOICE_graphics_OPTION_sdl2_ACTION='graphics_sdl2_action'
+ CHOICE_graphics_OPTION_sdl2_PRECOND='have_library SDL2'
+ graphics_sdl2_action() {
+ CFLAGS="$CFLAGS -DGFXMODULE_SDL -DSDL_DIR=SDL2"
+ GFXMODULE=sdl
+ HAVE_OPENGL=0
+ use_library SDL2
+ }
+ if have_library SDL2; then
+ CHOICE_graphics_DEFAULT=sdl2
+ elif have_library opengl; then
+ CHOICE_graphics_DEFAULT=opengl
+ else
+ CHOICE_graphics_DEFAULT=pure
+ fi
+
+ CHOICE_sound_OPTIONS="mixsdl openal"
+ CHOICE_sound_TITLE="Sound backend"
+ CHOICE_sound_OPTION_mixsdl_TITLE="Use MixSDL for sound (internal)"
+ CHOICE_sound_OPTION_mixsdl_ACTION=sound_mixsdl_action
+ sound_mixsdl_action() {
+ SOUNDMODULE=mixsdl
+ }
+ CHOICE_sound_OPTION_openal_TITLE="Include OpenAL support (experimental)"
+ CHOICE_sound_OPTION_openal_PRECOND="have_library openal"
+ CHOICE_sound_OPTION_openal_ACTION=sound_openal_action
+ sound_openal_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DHAVE_OPENAL"
+ SOUNDMODULE=openal
+ use_library openal
+ }
+ CHOICE_sound_DEFAULT=mixsdl
+
+ CHOICE_ovcodec_OPTIONS="standard tremor none"
+ CHOICE_ovcodec_TITLE="Ogg Vorbis codec"
+ CHOICE_ovcodec_OPTION_standard_TITLE="Xiph libogg + libvorbis"
+ CHOICE_ovcodec_OPTION_standard_PRECOND="have_library vorbisfile"
+ CHOICE_ovcodec_OPTION_standard_ACTION=ovcodec_standard_action
+ ovcodec_standard_action() {
+ use_library vorbisfile
+ OGGVORBIS=vorbisfile
+ }
+ CHOICE_ovcodec_OPTION_tremor_TITLE="Tremor (avoids floating point math)"
+ CHOICE_ovcodec_OPTION_tremor_PRECOND="have_library tremor"
+ CHOICE_ovcodec_OPTION_tremor_ACTION=ovcodec_tremor_action
+ ovcodec_tremor_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DOVCODEC_TREMOR"
+ OGGVORBIS=tremor
+ use_library tremor
+ }
+ CHOICE_ovcodec_OPTION_none_TITLE="No Ogg Vorbis support"
+ CHOICE_ovcodec_OPTION_none_ACTION=ovcodec_none_action
+ ovcodec_none_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DOVCODEC_NONE"
+ OGGVORBIS=none
+ }
+ CHOICE_ovcodec_DEFAULT=standard
+
+ CHOICE_mikmod_OPTIONS="internal external"
+ CHOICE_mikmod_TITLE="Tracker music support"
+ CHOICE_mikmod_OPTION_internal_TITLE="Included libmikmod"
+ CHOICE_mikmod_OPTION_internal_ACTION=mikmod_internal_action
+ mikmod_internal_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DUSE_INTERNAL_MIKMOD"
+ USE_INTERNAL_MIKMOD=1
+ }
+ CHOICE_mikmod_OPTION_external_TITLE="System libmikmod"
+ CHOICE_mikmod_OPTION_external_PRECOND="have_library libmikmod"
+ CHOICE_mikmod_OPTION_external_ACTION=mikmod_external_action
+ mikmod_external_action() {
+ USE_INTERNAL_MIKMOD=""
+ use_library libmikmod
+ }
+ CHOICE_mikmod_DEFAULT=internal
+
+ CHOICE_joystick_OPTIONS="enabled disabled"
+ CHOICE_joystick_TITLE="Joystick support"
+ CHOICE_joystick_OPTION_enabled_TITLE="enabled"
+ #CHOICE_joystick_OPTION_enabled_PRECOND="have_symbol SDL_Joystick"
+ # TODO: Check whether SDL has joystick support.
+ CHOICE_joystick_OPTION_enabled_ACTION=joystick_enabled_action
+ joystick_enabled_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DHAVE_JOYSTICK"
+ }
+ CHOICE_joystick_OPTION_disabled_TITLE="disabled"
+ case "$HOST_SYSTEM" in
+ ARMV5|WINSCW|GCCE)
+ CHOICE_joystick_DEFAULT=disabled
+ ;;
+ *)
+ CHOICE_joystick_DEFAULT=enabled
+ ;;
+ esac
+
+
+ CHOICE_netplay_OPTIONS="none full ipv4"
+ CHOICE_netplay_TITLE="Network Supermelee support"
+ CHOICE_netplay_OPTION_none_TITLE="disabled"
+ CHOICE_netplay_OPTION_none_ACTION=netplay_none_action
+ netplay_none_action() {
+ NETPLAY=""
+ }
+ CHOICE_netplay_OPTION_full_TITLE="IPv4 and IPv6"
+ CHOICE_netplay_OPTION_full_PRECOND="have_library netlibs"
+ CHOICE_netplay_OPTION_full_ACTION=netplay_full_action
+ netplay_full_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DNETPLAY=NETPLAY_FULL"
+ if [ -n "$MACRO_WIN32" ]; then
+ LDFLAGS="$LDFLAGS -lws2_32"
+ fi
+ NETPLAY="FULL"
+ use_library netlibs
+ }
+ CHOICE_netplay_OPTION_ipv4_TITLE="IPv4; no IPv6"
+ CHOICE_netplay_OPTION_ipv4_PRECOND="have_library netlibs"
+ CHOICE_netplay_OPTION_ipv4_ACTION=netplay_ipv4_action
+ netplay_ipv4_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DNETPLAY=NETPLAY_IPV4"
+ NETPLAY="IPV4"
+ use_library netlibs
+ }
+ CHOICE_netplay_DEFAULT=full
+
+ CHOICE_ioformat_OPTIONS="stdio stdio_zip"
+ CHOICE_ioformat_TITLE="Supported file i/o methods"
+ CHOICE_ioformat_OPTION_stdio_TITLE="Only direct file i/o"
+ CHOICE_ioformat_OPTION_stdio_zip_TITLE="Direct & .zip file i/o"
+ CHOICE_ioformat_OPTION_stdio_zip_PRECOND="have_library zlib"
+ CHOICE_ioformat_OPTION_stdio_zip_ACTION="ioformat_stdio_zip_action"
+ ioformat_stdio_zip_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DHAVE_ZIP=1"
+ USE_ZIP_IO=1
+ use_library zlib
+ }
+ CHOICE_ioformat_DEFAULT=stdio_zip
+
+ CHOICE_accel_OPTIONS="asm plainc"
+ CHOICE_accel_TITLE="Graphics/Sound optimizations"
+ CHOICE_accel_OPTION_asm_TITLE="Platform acceleration (asm, etc.)"
+ CHOICE_accel_OPTION_asm_ACTION="accel_asm_action"
+ accel_asm_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DUSE_PLATFORM_ACCEL"
+ USE_PLATFORM_ACCEL=1
+ }
+ CHOICE_accel_OPTION_plainc_TITLE="Only plain C code"
+ CHOICE_accel_OPTION_plainc_ACTION="accel_plainc_action"
+ accel_plainc_action() {
+ USE_PLATFORM_ACCEL=0
+ }
+ CHOICE_accel_DEFAULT=asm
+
+ CHOICE_threadlib_OPTIONS="sdl pthread"
+ CHOICE_threadlib_TITLE="Thread library"
+ CHOICE_threadlib_OPTION_sdl_TITLE="SDL-controlled thread library"
+ CHOICE_threadlib_OPTION_sdl_ACTION="threadlib_sdl_action"
+ threadlib_sdl_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DTHREADLIB_SDL"
+ THREADLIB="SDL"
+ }
+ CHOICE_threadlib_OPTION_pthread_TITLE="Pthread thread library"
+ CHOICE_threadlib_OPTION_pthread_PRECOND="have_library pthread"
+ CHOICE_threadlib_OPTION_pthread_ACTION="threadlib_pthread_action"
+ threadlib_pthread_action() {
+ CCOMMONFLAGS="$CCOMMONFLAGS -DTHREADLIB_PTHREAD"
+ THREADLIB="PTHREAD"
+ use_library pthread
+ }
+ CHOICE_threadlib_DEFAULT=sdl
+
+ MENU_install_path_ITEMS="install_prefix install_bindir install_libdir \
+ install_sharedir"
+ MENU_install_path_TITLE="Installation paths"
+ MENU_install_path_ITEM_install_prefix_TYPE=INPUT
+ MENU_install_path_ITEM_install_bindir_TYPE=INPUT
+ MENU_install_path_ITEM_install_libdir_TYPE=INPUT
+ MENU_install_path_ITEM_install_sharedir_TYPE=INPUT
+
+ INPUT_install_prefix_DEFAULT="/usr/local/games"
+ INPUT_install_prefix_TITLE="Installation prefix"
+ INPUT_install_prefix_VALIDATOR=validate_path
+ INPUT_install_prefix_ACTION='eval INSTALL_PREFIX=$MENU_install_prefix_VALUE'
+
+ INPUT_install_bindir_DEFAULT='$prefix/bin'
+ INPUT_install_bindir_TITLE="Location for binaries"
+ INPUT_install_bindir_VALIDATOR=validate_path
+
+ INPUT_install_libdir_DEFAULT='$prefix/lib'
+ INPUT_install_libdir_TITLE="Location for non-sharable data"
+ INPUT_install_libdir_VALIDATOR=validate_path
+
+ INPUT_install_sharedir_DEFAULT='$prefix/share'
+ INPUT_install_sharedir_TITLE="Location for sharable data"
+ INPUT_install_sharedir_VALIDATOR=validate_path
+}
+
+uqm_do_config()
+{
+ # Show the menu and let people set things
+ do_menu MENU main "$BUILD_WORK/config.state"
+ echo "Configuration complete."
+}
+
+uqm_process_config() {
+ menu_process MENU main
+
+ # Set INSTALL_LIBDIR, INSTALL_BINDIR, and INSTALL_SHAREDIR to the specified
+ # values, replacing '$prefix' to the prefix set.
+ local prefix
+ prefix="$INPUT_install_prefix_VALUE"
+ eval INSTALL_BINDIR="${INPUT_install_bindir_VALUE%/}/"
+ eval INSTALL_LIBDIR="${INPUT_install_libdir_VALUE%/}/"
+ eval INSTALL_SHAREDIR="${INPUT_install_sharedir_VALUE%/}/"
+
+ # Set the content dir
+ CONTENTDIR="${INSTALL_SHAREDIR}uqm/content"
+
+ CCOMMONFLAGS="$CCOMMONFLAGS -I\"$BUILD_WORK\""
+
+ # Set C++ only flags
+ # These allow use of C++ without the standard library
+ CXXFLAGS="$CXXFLAGS -fno-rtti -fno-exceptions -nostdinc++"
+
+ # At this point, all the compiler flags must be set.
+ CFLAGS="$CFLAGS $CCOMMONFLAGS"
+ CXXFLAGS="$CXXFLAGS $CCOMMONFLAGS"
+ CCOMMONFLAGS=""
+
+ # Export the HAVE_ symbols to config_unix.h, using config_unix.h.in
+ # as template (or config_win.h/config_win.h.in).
+ SUBSTITUTE_VARS="$HAVE_SYMBOLS CONTENTDIR"
+ case "$HOST_SYSTEM" in
+ MINGW32*|CYGWIN*)
+ SUBSTITUTE_FILES="config_win.h"
+ ;;
+ *)
+ SUBSTITUTE_FILES="config_unix.h"
+ ;;
+ esac
+ substitute_vars SUBSTITUTE_VARS SUBSTITUTE_FILES src "$BUILD_WORK"
+
+ # Make build.vars from build.vars.in, substituting variables.
+ SUBSTITUTE_VARS="BUILD_SYSTEM HOST_SYSTEM CFLAGS CXXFLAGS LDFLAGS LINK \
+ PREPROC_C MKDEP_C COMPILE_C \
+ PREPROC_CXX MKDEP_CXX COMPILE_CXX \
+ PREPROC_OBJC MKDEP_OBJC COMPILE_OBJC \
+ MAKE ECHON SED DEBUG JOYSTICK NETPLAY \
+ OGGVORBIS SOUNDMODULE USE_INTERNAL_MIKMOD \
+ GFXMODULE HAVE_OPENGL \
+ HAVE_GETOPT_LONG HAVE_REGEX_H_FLAG \
+ USE_ZIP_IO USE_PLATFORM_ACCEL THREADLIB USE_WINSOCK \
+ INSTALL_LIBDIR INSTALL_BINDIR INSTALL_SHAREDIR \
+ REZ WINDRES $HAVE_SYMBOLS"
+ SUBSTITUTE_FILES="build.vars"
+ substitute_vars SUBSTITUTE_VARS SUBSTITUTE_FILES . "$BUILD_WORK"
+
+ # Make 'uqm' shell script from uqm-wrapper.in, substituting variables.
+ SUBSTITUTE_VARS="INSTALL_LIBDIR INSTALL_BINDIR INSTALL_SHAREDIR uqm_NAME"
+ SUBSTITUTE_FILES="uqm-wrapper"
+ substitute_vars SUBSTITUTE_VARS SUBSTITUTE_FILES build/unix "$BUILD_WORK"
+}
+
+uqm_load_config()
+{
+ do_menu_load MENU main "$BUILD_WORK/config.state"
+}
+
+uqm_save_config()
+{
+ do_menu_save MENU main "$BUILD_WORK/config.state"
+}
+
+uqm_pre_build() {
+ : # Nothing to do
+}
+
+uqm_post_build() {
+ local TARGET_FILE
+ local RFORK
+ eval TARGET_FILE="\$BUILD_WORK/\${${BUILD_PROJECT}_NAME}"
+ RFORK="src/res/darwin/${BUILD_PROJECT}.r"
+
+ test -f "$TARGET_FILE" || return
+
+ # If run from the command-line on OSX, the Window Manager
+ # will refuse to recognize the window unless the program has
+ # a resource fork; so we give it a small one here. When
+ # run from inside an application package, this step is useless
+ # (the cp command in the install step implicitly strips off
+ # the resource fork, in fact)
+ case "$HOST_SYSTEM" in
+ Darwin)
+ $REZ "$RFORK" -o "$TARGET_FILE"
+ ;;
+ ARMV5)
+ cp "$TARGET_FILE" "$BUILD_EPOCROOT/epoc32/release/armv5/urel/"
+ cd src/symbian
+ cmd \\/C bldmake bldfiles
+ cmd \\/C abld build armv5 urel
+ cd ../..
+ ;;
+ WINSCW)
+ cp "$TARGET_FILE" "$BUILD_EPOCROOT/epoc32/release/winscw/udeb/"
+ cd src/symbian
+ cmd \\/C bldmake bldfiles
+ cmd \\/C abld build winscw udeb
+ cd ../..
+ ;;
+ GCCE)
+ cp "$TARGET_FILE" "$BUILD_EPOCROOT/epoc32/release/armv5/urel/"
+ cd src/symbian
+ cmd \\/C bldmake bldfiles
+ cmd \\/C abld build gcce urel
+ cd ../..
+ ;;
+ esac
+}
+
+uqm_pre_install() {
+ : # Nothing to do
+}
+
+uqm_install() {
+ case "$HOST_SYSTEM" in
+ Darwin)
+ uqm_install_osx
+ ;;
+ MINGW32*)
+ echo "No installation procedure available for MinGW."
+ echo "Read the manual for more information."
+ ;;
+ cegcc*)
+ echo "No installation procedure available for Windows CE."
+ echo "Read the manual for more information."
+ ;;
+ WINSCW)
+ uqm_install_winscw
+ ;;
+ ARMV5)
+ uqm_install_armv5
+ ;;
+ GCCE)
+ uqm_install_gcce
+ ;;
+ *)
+ generic_install
+ ;;
+ esac
+}
+
+uqm_post_install() {
+ : # Nothing to do
+}
+
+uqm_install_osx() {
+ local VERSION HEADERS HEADER HEADER_FILE FRAMEWORK
+
+ VERSION=`head -1 content/version`
+
+ INSTROOT="$BUILD_WORK/The Ur-Quan Masters.app/Contents"
+
+ # Make directory structure
+ echo "Creating directory structure..." >&2
+ mkdirhier "$INSTROOT/MacOS" 0755
+ mkdirhier "$INSTROOT/Frameworks" 0755
+ mkdirhier "$INSTROOT/Resources/content/addons" 0755
+ mkdirhier "$INSTROOT/Resources/content/packages" 0755
+
+ # Install misc. resources, icons, etc.
+ echo "Installing miscellaneous resources..." >&2
+ $SED "s/@@VERSION@@/$VERSION/g" src/res/darwin/Info.plist > \
+ "$INSTROOT/Info.plist"
+ cp src/res/darwin/PkgInfo "$INSTROOT"
+ cp "src/res/darwin/The Ur-Quan Masters.icns" "$INSTROOT/Resources"
+
+ # Find Frameworks and copy them into the application.
+ echo "Copying dependancy Frameworks..." >&2
+ HEADERS="Ogg/Ogg.h SDL/SDL.h SDL_image/SDL_image.h Vorbis/vorbisfile.h"
+ if [ "$uqm_SOUNDMODULE" = openal ]; then
+ HEADERS="$HEADERS OpenAL/al.h"
+ fi
+ for HEADER in $HEADERS; do
+ HEADER_FILE=`basename $HEADER`
+ eval FRAMEWORK=`echo '' | \
+ $PREPROC_C -D__MACOSX__ -include $HEADER - | \
+ awk '(/'$HEADER_FILE'/ && $2 == 1) { print $3; exit }' | \
+ $SED 's/.Headers.*$/"/'`
+ cp -a "$FRAMEWORK" "$INSTROOT/Frameworks"
+ done
+
+ # Install game content (it should probably make a zipfile)
+ echo "Creating base content package..." >&2
+ cp content/version "$INSTROOT/Resources/content/"
+ (cd content && \
+ find . -type f -not -path '*/CVS*' -not -path '*/.svn*' -not -path '*/addons*' -print | \
+ $SED 's/^..//' | zip -X -q -n .ogg -8 -@ uqm-${VERSION}-prv-content.uqm)
+ mv content/uqm-$VERSION-prv-content.uqm "$INSTROOT/Resources/content/packages"
+
+ echo "Creating voice content package..." >&2
+ (cd content/addons && \
+ find 3dovoice -type f -not -path '*/CVS*' -not -path '*/.svn*' -print | \
+ zip -X -q -n .ogg -8 -@ ../uqm-${VERSION}-prv-voice.uqm)
+ mv content/uqm-$VERSION-prv-voice.uqm "$INSTROOT/Resources/content/addons"
+
+ echo "Creating 3do music content package..." >&2
+ (cd content/addons && \
+ find 3domusic -type f -not -path '*/CVS*' -not -path '*/.svn*' -print | \
+ zip -X -q -n .ogg -8 -@ ../uqm-${VERSION}-prv-3domusic.uqm)
+ mv content/uqm-$VERSION-prv-3domusic.uqm "$INSTROOT/Resources/content/addons"
+
+ # Install game binary (and rename it)
+ echo "Installing executable..." >&2
+ cp $uqm_NAME "$INSTROOT/MacOS/The Ur-Quan Masters"
+}
+
+uqm_install_winscw() {
+ local PRIVATE_DIR
+
+ PRIVATE_DIR="$BUILD_EPOCROOT/epoc32/winscw/c/private/A000A0C3"
+
+ uqm_create_symbian_content_package
+
+ echo "Creating directory structure to $PRIVATE_DIR ..."
+ mkdir "$PRIVATE_DIR"
+ mkdir "$PRIVATE_DIR/content"
+ mkdir "$PRIVATE_DIR/content/packages"
+ mkdir "$PRIVATE_DIR/userdata"
+
+ echo "Copying data to $PRIVATE_DIR ..."
+ cp content/version "$PRIVATE_DIR/content"
+ cp content.uqm "$PRIVATE_DIR/content/packages"
+ cp src/symbian/uqm.cfg "$PRIVATE_DIR/userdata"
+}
+
+uqm_install_armv5() {
+ uqm_create_symbian_content_package
+
+ cd src/symbian
+ cmd \\/C makekeys -cert -expdays 9999 -password asdfgh -len 2048 -dname "CN=UQM OR=Ur-Quan Masters CO=FI" uqm.key uqm.cer
+ cmd \\/C makesis uqm-armv5.pkg uqm.sis
+ cmd \\/C signsis -v uqm.sis uqm.sisx uqm.cer uqm.key asdfgh
+ mv uqm.sisx ../..
+ cd ../..
+}
+
+uqm_install_gcce() {
+ uqm_create_symbian_content_package
+
+ cd src/symbian
+ cmd \\/C makekeys -cert -expdays 9999 -password asdfgh -len 2048 -dname "CN=UQM OR=Ur-Quan Masters CO=FI" uqm.key uqm.cer
+ cmd \\/C makesis uqm-gcce.pkg uqm.sis
+ cmd \\/C signsis -v uqm.sis uqm.sisx uqm.cer uqm.key asdfgh
+ mv uqm.sisx ../..
+ cd ../..
+}
+
+uqm_create_symbian_content_package() {
+ if [ -e "content.uqm" ]; then
+ echo "Content package already exists, skipping"
+ return
+ fi
+
+ local ANIFILE ANIFILES DNAME ESC_DNAME FONTDIR FONTDIRS FONTFILES
+
+ echo "Building temporary content directory..."
+ cd content
+ find . -type f -not -path '*/CVS*' -not -path '*/.svn*' -not -path './addons*' -not -name "version" -not -name '*.png' -not -name '*.ani' -not -name '*.sml' -not -name '*.med' -not -name '*.mid' -not -name '*.big' >../content.lst
+ tar cf ../tmp1.tar -T ../content.lst
+ rm ../content.lst
+ mkdir ../tmpcontent
+ cd ../tmpcontent
+ tar xf ../tmp1.tar
+ rm ../tmp1.tar
+ cd ../content
+
+ echo "Packing ani files..."
+ ANIFILES=`find . -regex ".*\(sml\|med\|mid\|big\|ani\)$"`
+ for ANIFILE in $ANIFILES; do
+ DNAME=`dirname $ANIFILE`/
+ ESC_DNAME=`echo $DNAME|sed "s/\//\\\\\\\\\//g"`
+ mkdirhier ../tmpcontent/$DNAME 0755
+ zip -q -j -0 ../tmpcontent/$ANIFILE $ANIFILE `cat $ANIFILE|cut -d " " -f 1|sed s/^/$ESC_DNAME/`
+ done
+
+ echo "Packing font files..."
+ FONTDIRS=`find . -name '*.fon'`
+ for FONTDIR in $FONTDIRS; do
+ FONTFILES=`find $FONTDIR -name '*.png'|sort -t. +2.4 -n`
+ zip -q -j -0 ../tmpcontent/$FONTDIR $FONTFILES
+ done
+
+ cd ../tmpcontent
+ echo "Building content package..."
+ zip -q -r ../content.uqm .
+
+ cd ..
+ echo "Removing temporary content directory..."
+ rm -rf tmpcontent
+}
+
+uqm_clean() {
+ case "$HOST_SYSTEM" in
+ MINGW32*|CYGWIN*)
+ rm -f "$BUILD_WORK/config_win.h"
+ ;;
+ ARMV5|GCCE)
+ local TARGET_FILE
+ eval TARGET_FILE="\$BUILD_WORK/\${${BUILD_PROJECT}_NAME}"
+
+ rm -f "$BUILD_WORK/config_unix.h"
+ rm -f "$BUILD_EPOCROOT/epoc32/release/armv5/urel/$TARGET_FILE"
+ rm -f $TARGET_FILE uqm.sisx content.uqm
+
+ cd src/symbian
+ cmd \\/C abld reallyclean
+ cmd \\/C bldmake clean
+ rm -f uqm.sis uqm.key uqm.cer
+ cd ../..
+ ;;
+ WINSCW)
+ local TARGET_FILE
+ eval TARGET_FILE="\$BUILD_WORK/\${${BUILD_PROJECT}_NAME}"
+
+ rm -f "$BUILD_WORK/config_unix.h"
+ rm -f "$BUILD_EPOCROOT/epoc32/release/winscw/udeb/$TARGET_FILE"
+ rm -f $TARGET_FILE content.uqm
+
+ cd src/symbian
+ cmd \\/C abld reallyclean
+ cmd \\/C bldmake clean
+ cd ../..
+ ;;
+ *)
+ rm -f "$BUILD_WORK/config_unix.h"
+ ;;
+ esac
+}
+
+
diff --git a/build/unix/build.docs b/build/unix/build.docs
new file mode 100644
index 0000000..d24620b
--- /dev/null
+++ b/build/unix/build.docs
@@ -0,0 +1,205 @@
+I've made this build system as a replacement for autoconf, autoheader,
+automake, and aclocal as I found them to be too limiting and too slow.
+This build system is not as complete as the auto* tools, but it's very
+easy to extend.
+
+Some of the most important differences between this system and autoconf:
+- it has a menu-driven configuration
+- when compiling, the current directory will never change
+- the object files are stored in a seperate tree from the source files,
+ leaving the latter clean, and easier grep-able.
+- multiple object trees can be used, so you can have for instance a
+ debugging tree and a release tree side by side.
+- it builds faster, mostly because libtool isn't used.
+
+
+The files
+---------
+
+These are the files in the directory with the build script.
+Of these files only build.config should be modified for a specific build.
+For unknown dependencies, additions to config_proginfo_build or
+config_proginfo_host might need to be made. The other files should not
+be modified.
+Only build.sh is to be called directly.
+
+ansi ansi colour definitions.
+build.config configuration options for the program to build.
+ It contains the description of the configuration
+ menu, and specifies the dependencies.
+ The format of the menu description is listed briefly
+ below.
+build.sh the sh script that is to be called for configuration,
+ building, and installation.
+build_functions auxiliary functions to build.sh for building.
+config_functions auxiliary functions to build.sh for configuration.
+config_proginfo_build contains descriptions of dependencies for the
+ system where the code is being built.
+ The information in this file is used in particular
+ for detecting if those libraries and other external
+ programs are present.
+config_proginfo_host contains descriptions of dependencies for the
+ system where the software will eventually be run.
+ The information in this file is used in particular
+ for detecting if those libraries and other external
+ programs are present.
+menu_functions auxiliary functions to build.sh for menus.
+
+
+The menu (from build.config)
+----------------------------
+
+There are three types of menu items.
+
+- Type MENU
+ A menu with menu items.
+ - Variables:
+ - MENU_${NAME}_TITLE
+ The title of the menu.
+ - MENU_${NAME}_TEXT (optional)
+ The text to show with the menu.
+ - MENU_${NAME}_ITEMS
+ The names of the menu items (space-seperated).
+ - MENU_${NAME}_ITEM_${ITEMNAME}_TYPE
+ The type of a menu item.
+- Type CHOICE:
+ A choice between several options.
+ - Variables:
+ - CHOICE_${NAME}_TITLE
+ The title of the choice menu.
+ - CHOICE_${NAME}_TEXT (optional)
+ The text to show with the choice menu.
+ - CHOICE_${NAME}_OPTIONS
+ The names of the options (space-seperated).
+ - CHOICE_${NAME}_OPTION_${OPTIONNAME}_TITLE
+ The title of a menu option.
+ - CHOICE_${NAME}_OPTION_${OPTIONNAME}_ACTION
+ A command to be evaluated if this option is used.
+ - CHOICE_${NAME}_OPTION_${OPTIONNAME}_VALID (set by the config program)
+ 0 if the choice has been verified to be a valid choice
+ 1 if the choice has been verified to be not a valid choice
+ <empty> if the choice hasn't been verified to be valid
+ - CHOICE_${NAME}_DEFAULT (optional)
+ The default choice.
+ - CHOICE_${NAME}_VALUE (set by the config program)
+ The current choice.
+- Type INPUT:
+ A string that is user-definable.
+ - Variables:
+ - INPUT_${NAME}_TITLE
+ The title of the input field.
+ - INPUT_${NAME}_TEXT (optional)
+ The text to show with the input field.
+ - INPUT_${NAME}_VALIDATOR (optional)
+ A function to call after the user supplied a new value which
+ checks if the value is allowed. It should accept the new value as
+ an argument, and return 1 if the value is not allowed, or return 0
+ and output the (possibly modified) value, to accept it.
+ - INPUT_${NAME}_DEFAULT (optional)
+ The default value.
+ - INPUT_${NAME}_VALUE (set by the config program)
+ The current value.
+ - INPUT_${NAME}_OLD_VALUE (set by the config program)
+ The value as it was at the start of the config program.
+- Type CHECK:
+ An option that can either be on or off, like a checkbox.
+ - Variables:
+ - CHECK_${NAME}_TITLE
+ The title of the input field.
+ - CHECK_${NAME}_DEFAULT (optional)
+ The default value, either 'CHECKED' or 'UNCHECKED'.
+ - CHECK_${NAME}_VALUE (set by the config program)
+ The current value, either 'CHECKED' or 'UNCHECKED'.
+ - CHECK_${NAME}_FIXED (optional)
+ A string that evaluates to 0 if this checkbox may not be changed.
+
+
+
+Program info (from config_proginfo_build)
+-----------------------------------------
+
+Information about programs used should be supplied in the following form:
+ - PROG_${PROGRAM}_NAME
+ A string describing the program.
+ - PROG_${PROGRAM}_FILE
+ A string that evaluates to the executable that should be present if
+ this program is used.
+ - PROG_${PROGRAM}_ACTION (optional)
+ A command to be executed if this program is used, after the user is
+ done configuring.
+ - PROG_${PROGRAM}_DETECT (optional)
+ A command to be executed which should return 0 if the program is present
+ on the system, 1 if it is not present, or 2 to fallback to the default
+ detection. If no detect function was set, or it returned 2, 'type' is
+ used to check for the program in the path.
+ This command may include a shell command that modifies any of the
+ variables PROG_${PROGRAM}_FILE, PROG_${PROGRAM}_ACTION, or
+ PROG_${PROGRAM}_VERSION.
+ - PROG_${LIB}_DEPEND_DETECT_BIN (optional)
+ A list of space-separated binaries the detection of this program
+ depends on.
+ - PROG_${LIB}_DEPEND_DETECT_LIB (optional)
+ A list of space-separated libraries the detection of this program
+ depends on.
+ - PROG_${PROGRAM}_VERSION (optional)
+ A string that evaluates to the version of the executable that is
+ present, if it is present.
+ - PROG_${PROGRAM}_PRESENT (set by the configuration program)
+ 0 if the program has been verified to be present
+ 1 if the program has been verified to be not present
+ <empty> if presence of the program hasn't been verified
+
+Information about build tools in general (say "a C compiler"), where you're
+not interested in what program is actually called, should be supplied in
+the following corm:
+ - BUILDTOOL_${TOOL}_NAME
+ A string describing the build tool.
+ - BUILDTOOL_${TOOL}_COMMAND
+ The command, with arguments, that is to be executed to run this tool.
+ - BUILDTOOL_${TOOL}_DEPEND
+ A whitespace separated list of programs (as described above)
+ that this tool depends on.
+ - BUILDTOOL_${TOOL}_PRESENT (set by the configuration program)
+ 0 if the tool has been verified to be present
+ 1 if the tool has been verified to be not present
+ <empty> if presence of the tool hasn't been verified
+Each tool described, once detected, will be available through the environment
+variable with the name as specified in $TOOL.
+
+
+Library info (from config_proginfo_host)
+-----------------------------------------
+
+Information about libraries used should be supplied in the following form:
+ - LIB_${LIB}_NAME
+ A string describing the program.
+ - LIB_${LIB}_CFLAGS
+ A string which evaluates to the string to add to CFLAGS if this library
+ is used.
+ - LIB_${LIB}_LDLAGS
+ A string which evaluates to the string to add to LDLAGS if this library
+ is used.
+ - PROG_${LIB}_DETECT (optional)
+ A command to be executed which should return 0 if the library is present
+ on the system, 1 if it is not present, or 2 to fallback to the default
+ detection. If no detect function was set, or it returned 2,
+ an attempt is made to compile and link an empty C program with
+ the flags as in LIB_${LIB}_CFLAGS and LIB_${LIB}_LDFLAGS.
+ This command may include a shell command that modifies any of the
+ variables PROG_${LIB}_CFLAGS, PROG_${LIB}_LDFLAGS, or
+ PROG_${LIB}_VERSION.
+ - LIB_${LIB}_DEPEND_DETECT_BIN (optional)
+ A list of space-separated binaries the detection of this library depends
+ on.
+ - LIB_${LIB}_DEPEND_DETECT_LIB (optional)
+ A list of space-separated libraries the detection of this library depends
+ on.
+ - LIB_${LIB}_VERSION (optional)
+ A string that evaluates to the version of the library that is
+ present, if it is present.
+ - LIB_${LIB}_PRESENT (set by the configuration program)
+ 0 if the library has been verified to be present
+ 1 if the library has been verified to be not present
+ <empty> if presence of the library hasn't been verified
+
+
diff --git a/build/unix/build.sh b/build/unix/build.sh
new file mode 100644
index 0000000..002ce6a
--- /dev/null
+++ b/build/unix/build.sh
@@ -0,0 +1,143 @@
+#!/bin/sh
+# Build script
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# This file contains functions for the general building procedure.
+# You shouldn't need to change this file if your project changes.
+
+# Read in the functions we need
+. build/unix/build_functions
+
+if [ -z "$BUILD_WORK" ]; then
+ BUILD_WORK=.
+ export BUILD_WORK
+fi
+
+# Read in the config settings that affect the build, if present.
+# Don't reread for every dir when recursing.
+if [ -r "$BUILD_WORK/build.vars" ]; then
+ . "$BUILD_WORK/build.vars"
+fi
+
+# Read in the Makeproject file
+. ./Makeproject
+
+for VAR in TARGETS "$BUILD_PROJECT_NAME" "$BUILD_PROJECT_OBJS"; do
+ eval VALUE="\$$VAR"
+ if [ -z "$VALUE" ]; then
+ echo "$VAR needs to be defined in the top Makeproject file"
+ exit 1
+ fi
+done
+
+##############################################
+### Everything below is parsing user input ###
+
+TOPDIR="$PWD"
+export TOPDIR
+
+if [ $# -lt 1 ]; then
+ usage 1>&2
+ exit 1;
+fi
+
+# Load the configuration functions
+. build/unix/build.config
+
+BUILD_THREADS=""
+for i in "$@"; do
+ shift
+ if [ "`printf "%s" "$i" | cut -c1-2`" = "-j" ]; then
+ num="`printf "%s" "$i" | cut -c3-`"
+ if [ -z "$num" ] || [ "$num" -gt 0 ] 2>/dev/null; then
+ BUILD_THREADS="-j$num"
+ else
+ usage 1>&2
+ exit 1
+ fi
+ else
+ set -- "$@" "$i"
+ fi
+done
+
+case "$1" in
+ cleanall)
+ build_cleanall
+ exit $?
+ ;;
+ distclean)
+ build_distclean
+ exit $?
+ ;;
+esac
+
+unset TARGET
+for TEMP in $TARGETS; do
+ if [ "$1" = "$TEMP" ]; then
+ TARGET="$1"
+ break
+ fi
+done
+if [ -z "$TARGET" ]; then
+ echo "Invalid target; choose one from:"
+ echo " $TARGETS"
+ exit 1
+fi
+BUILD_PROJECT="$TARGET"
+export TARGET BUILD_PROJECT ECHON
+export PREPROC_C MKDEP_C COMPILE_C PREPROC_CXX MKDEP_CXX COMPILE_CXX PREPROC_OBJC MKDEP_OBJC COMPILE_OBJC WINDRES LINK
+export "${BUILD_PROJECT}_CFLAGS" "${BUILD_PROJECT}_CXXFLAGS" "${BUILD_PROJECT}_LDFLAGS"
+
+# Add trailing / from objs dir
+eval ${BUILD_PROJECT}_OBJS=\${${BUILD_PROJECT}_OBJS%/}/
+export "${BUILD_PROJECT}_OBJS"
+
+if [ $# -lt 2 ]; then
+ build_check_config
+ build_check_dependencies
+ build_compile $BUILD_THREADS
+ exit $?
+fi
+
+case "$2" in
+ clean)
+ build_clean
+ ;;
+ config)
+ build_config
+ build_process_config
+ ;;
+ reprocess_config)
+ build_reconfig
+ ;;
+ depend)
+ build_check_config
+ build_depend
+ ;;
+ install)
+ build_check_config
+ build_check_dependencies
+ build_check_compile $BUILD_THREADS
+ build_install
+ ;;
+ *)
+ usage 1>&2
+ exit 1;
+ ;;
+esac
+
+
diff --git a/build/unix/build_clean b/build/unix/build_clean
new file mode 100644
index 0000000..ffc7440
--- /dev/null
+++ b/build/unix/build_clean
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Find all the .c, .cpp, .m, and .rc files which are relevant for the project,
+# and remove their respective object files in the work directory.
+
+# Expected to be passed from the environment:
+# BUILD_PROJECT - the name of the project
+# BUILD_ROOT - The root of the source files.
+# BUILD_WORK - The root of the work directory.
+
+BUILD_ROOT=${BUILD_ROOT%/}/
+BUILD_WORK=${BUILD_WORK%/}/
+eval OBJDIR=\"${BUILD_WORK}\${${BUILD_PROJECT}_OBJS}\"
+
+. "${BUILD_ROOT}Makeproject"
+
+build/unix/recurse "$BUILD_PROJECT" "$BUILD_ROOT" | \
+ while read TYPE FILE; do
+ case "$TYPE" in
+ C|CXX|M|RC)
+ rm -f -- "${OBJDIR}${REC_PREFIX}$FILE.d" \
+ "${OBJDIR}${FILE}.o"
+ ;;
+ DIROUT)
+ rmdir -- "${OBJDIR}${FILE}" 2> /dev/null
+ ;;
+ esac
+done
+
diff --git a/build/unix/build_collect b/build/unix/build_collect
new file mode 100644
index 0000000..7a5dcb9
--- /dev/null
+++ b/build/unix/build_collect
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Find all the .c, .cpp, .m, and .rc files which are relevant for the project,
+# and map them to the work directory.
+
+# Expected to be passed from the environment:
+# BUILD_PROJECT - the name of the project
+# BUILD_ROOT - The root of the source files.
+# BUILD_WORK - The root of the work directory.
+
+BUILD_ROOT=${BUILD_ROOT%/}/
+BUILD_WORK=${BUILD_WORK%/}/
+eval OBJDIR=\"${BUILD_WORK}\${${BUILD_PROJECT}_OBJS}\"
+
+. "${BUILD_ROOT}Makeproject"
+. "${BUILD_WORK}build.vars"
+
+build/unix/recurse "$BUILD_PROJECT" "$BUILD_ROOT" | \
+ while read TYPE FILE; do
+ case "$TYPE" in
+ C|CXX|M|RC)
+ echo "$OBJDIR$FILE"
+ ;;
+ esac
+done
+
diff --git a/build/unix/build_functions b/build/unix/build_functions
new file mode 100644
index 0000000..5f5d20b
--- /dev/null
+++ b/build/unix/build_functions
@@ -0,0 +1,311 @@
+# Auxiliary functions for build.sh
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+DEPEND_NAME=make.depend
+
+# Show usage information
+usage() {
+ echo "Main build script"
+ echo
+ echo "Syntax:"
+ echo " ./build.sh [-j[#JOBS]] <target>"
+ echo " ./build.sh <target> config"
+ echo " ./build.sh <target> depend"
+ echo " ./build.sh <target> clean"
+ echo " ./build.sh [-j[#JOBS]] <target> install"
+ echo " ./build.sh cleanall"
+ echo " ./build.sh distclean"
+ echo
+ echo "Valid targets:"
+ for TARGET in $TARGETS; do
+ echo " $TARGET"
+ done
+ echo
+}
+
+escape_string() {
+ $SED -e s,[\\\'\"\`\ \ \$\&\\\*\\\?\#\!],\\\\\&,g << EOF
+$1
+EOF
+}
+
+# Start the configure program.
+# $1 = target
+build_config() {
+ set_system
+ prepare_build_system
+ config_requirements
+ prepare_host_system
+ if [ "$BUILD_SYSTEM" '!=' "$HOST_SYSTEM" ]; then
+ build_message "Cross-compiling to $HOST_SYSTEM."
+ fi
+ eval "${TARGET}_requirements"
+ eval "${TARGET}_prepare_config"
+ eval "${TARGET}_load_config"
+ eval "${TARGET}_do_config"
+ eval "${TARGET}_save_config"
+}
+
+build_reconfig() {
+ if [ ! -e "$BUILD_WORK/config.state" ]; then
+ echo "*** Warning: file 'config.state' not found - using defaults."
+ fi
+
+ prepare_build_system
+ config_requirements
+ prepare_host_system
+ if [ "$BUILD_SYSTEM" '!=' "$HOST_SYSTEM" ]; then
+ build_message "Cross-compiling to $HOST_SYSTEM."
+ fi
+ eval "${TARGET}_requirements"
+ eval "${TARGET}_prepare_config"
+ eval "${TARGET}_load_config"
+ build_process_config
+
+ echo "Reconfiguring complete..." >&2
+}
+
+# Process the configuration information
+build_process_config() {
+ eval "${TARGET}_process_config"
+}
+
+# Recursively build dependency index
+build_depend() {
+ local DEPEND_FILE EXTRA_OFILES
+
+ echo "Building file dependency index..." >&2
+
+ eval mkdir -p "\$BUILD_WORK/\${${BUILD_PROJECT}_OBJS}"
+ eval DEPEND_FILE="\$BUILD_WORK/\${${BUILD_PROJECT}_OBJS}\$DEPEND_NAME"
+
+ # Remove the old dependency file, if it exists.
+ # The .tmp file is used to detect interrupted dependency builds.
+ rm -f -- "$DEPEND_FILE".tmp "$DEPEND_FILE"
+
+ BUILD_ROOT=./ $SH ./build/unix/build_collect > "$DEPEND_FILE".tmp
+ mv -f -- "$DEPEND_FILE".tmp "$DEPEND_FILE"
+}
+
+# Compile the lot.
+# With the depend info set up, we can leave everything to make.
+# $1 - additional arguments to pass to make (at the moment just
+# an optional -j arg for parallel builds).
+build_compile() {
+ local CFLAGS CXXFLAGS LDFLAGS TARGET_FILE DEPEND_FILE OBJDIR
+
+ eval CFLAGS="\${${BUILD_PROJECT}_CFLAGS}"
+ eval CXXFLAGS="\${${BUILD_PROJECT}_CXXFLAGS}"
+ eval LDFLAGS="\${${BUILD_PROJECT}_LDFLAGS}"
+ eval OBJDIR=\""\$BUILD_WORK/\${${BUILD_PROJECT}_OBJS}"\"
+ eval TARGET_FILE=\""\$BUILD_WORK/\${${BUILD_PROJECT}_NAME}"\"
+ DEPEND_FILE=$OBJDIR$DEPEND_NAME
+
+ eval "${TARGET}_pre_build"
+
+ CFLAGS=$CFLAGS CXXFLAGS=$CXXFLAGS LDFLAGS=$LDFLAGS \
+ OBJDIR=$OBJDIR \
+ BUILD_ROOT= \
+ TARGET_FILE=$TARGET_FILE DEPEND_FILE=$DEPEND_FILE \
+ SED=$SED \
+ $MAKE $1 -f Makefile.build "$TARGET_FILE"
+
+ eval "${TARGET}_post_build"
+}
+
+build_clean() {
+ local DEPEND_FILE
+
+ BUILD_ROOT=./ $SH ./build/unix/build_clean
+
+ eval DEPEND_FILE="\$BUILD_WORK/\${${BUILD_PROJECT}_OBJS}${DEPEND_NAME}"
+ rm -f "$DEPEND_FILE" "$BUILD_WORK/build.vars" \
+ "$BUILD_WORK/uqm-wrapper" \
+ "$BUILD_WORK/config.state"
+ eval "${TARGET}_clean"
+}
+
+build_cleanall() {
+ export BUILD_PROJECT
+ for TARGET in $TARGETS; do
+ BUILD_PROJECT="$TARGET"
+ build_clean
+ done
+ BUILD_PROJECT=""
+}
+
+build_distclean() {
+ build_cleanall
+}
+
+
+# Description: check if the config files are present and load them.
+# If they're not present, remake them.
+build_check_config() {
+ if [ ! -e "$BUILD_WORK/build.vars" ]; then
+ build_config || exit $?
+ build_process_config
+ fi
+ . "$BUILD_WORK/build.vars"
+ . "${BUILD_REC_PATH:=./}Makeproject"
+}
+
+# Description: check if the necessary depend file is present,
+# if not, build it.
+build_check_dependencies() {
+ eval DEPEND_FILE="\$BUILD_WORK/\${${BUILD_PROJECT}_OBJS}${DEPEND_NAME}"
+ [ ! -e "$DEPEND_FILE" -o -n "$BUILD_RUN_DEPEND" ] || return
+
+ build_depend || exit $?
+}
+
+# Description: check if the program is compiled, and otherwise compile
+# $1 - additional arguments to pass to make (at the moment just
+# an optional -j arg for parallel builds).
+build_check_compile() {
+ local NAME
+ eval NAME="\${${BUILD_PROJECT}_NAME}"
+ [ ! -e "$NAME" ] || return
+
+ build_compile "$1" || exit $?
+}
+
+# Make a directory path, with mode and owner specified.
+# $1 - name of directory path
+# $2 - mode of the directories (may be empty)
+# $3 - owner of the directories (may be empty)
+mkdirhier() {
+ local REST DIR MODE OWNER
+ REST="$1"
+ MODE="$2"
+ OWNER="$3"
+ case "$REST" in
+ /*)
+ REST="${REST%/}"
+ DIR="/"
+ ;;
+ *)
+ DIR=""
+ ;;
+ esac
+ case "$REST" in
+ */)
+ ;;
+ *)
+ REST="${REST}/"
+ ;;
+ esac
+ while [ -n "$REST" ]; do
+ DIR="$DIR${REST%%/*}"
+ REST="${REST#*/}"
+ if [ ! -d "$DIR" ]; then
+ mkdir "$DIR"
+ [ -n "$MODE" ] && chmod "$MODE" "$DIR"
+ [ -n "$OWNER" ] && chown "$OWNER" "$DIR"
+ fi
+ DIR="${DIR}/"
+ done
+}
+
+# Install a file or directory
+# $1 - Source file/directory
+# $2 - Destination directory/file
+# $3 - Mode of destination file/directory
+# $4 - Owner of destination file/directory
+installsome() {
+ local SRC DEST MODE OWNDER DESTDIR SRCNAME
+ SRC="$1"
+ DEST="$2"
+ MODE="$3"
+ OWNDER="$4"
+
+ DESTDIR="${DEST%/*}"
+ if [ ! -d "$DESTDIR" ]; then
+ mkdirhier "$DESTDIR" 0755
+ fi
+ SRCNAME="${SRC##*/}"
+ cp -pr -- "$SRC" "$DEST"
+ if [ -n "$MODE" ]; then
+ if [ -d "$DEST" ]; then
+ chmod -R "$MODE" "${DEST}${SRCNAME}"
+ else
+ chmod "$MODE" "$DEST"
+ fi
+ fi
+ if [ -n "$OWNER" ]; then
+ if [ -d "$DEST" ]; then
+ chown -R "$OWNER" "${DEST}${SRCNAME}"
+ else
+ chown "$OWNER" "$DEST"
+ fi
+ fi
+}
+
+# Install the program
+build_install() {
+ eval "${TARGET}_install"
+}
+
+# Generic installation routine
+generic_install() {
+ local SRC DEST MODE OWNER
+
+ eval "${TARGET}_pre_install"
+
+ local LIB LIBS LIBDIR
+ echo "Installing system-dependent data..." >&2
+ eval LIBS="\${${BUILD_PROJECT}_INSTALL_LIBS}"
+ eval LIBDIR="\${${BUILD_PROJECT}_INSTALL_LIBDIR%/}/"
+ mkdirhier "$LIBDIR" 0755
+ for LIB in $LIBS; do
+ eval SRC="\${${BUILD_PROJECT}_INSTALL_LIB_${LIB}_SRC%/}"
+ eval DEST="\$LIBDIR\${${BUILD_PROJECT}_INSTALL_LIB_${LIB}_DEST}"
+ eval MODE="\${${BUILD_PROJECT}_INSTALL_LIB_${LIB}_MODE}"
+ eval OWNER="\${${BUILD_PROJECT}_INSTALL_LIB_${LIB}_OWNER}"
+ installsome "$SRC" "$DEST" "$MODE" "$OWNER"
+ done
+
+ local SHARE SHARED SHAREDIR
+ echo "Installing system-independent data..." >&2
+ eval SHARED="\${${BUILD_PROJECT}_INSTALL_SHARED}"
+ eval SHAREDIR="\${${BUILD_PROJECT}_INSTALL_SHAREDIR%/}/"
+ mkdirhier "$SHAREDIR" 0755
+ for SHARE in $SHARED; do
+ eval SRC="\${${BUILD_PROJECT}_INSTALL_SHARED_${SHARE}_SRC%/}"
+ eval DEST="\$SHAREDIR\${${BUILD_PROJECT}_INSTALL_SHARED_${SHARE}_DEST}"
+ eval MODE="\${${BUILD_PROJECT}_INSTALL_SHARED_${SHARE}_MODE}"
+ eval OWNER="\${${BUILD_PROJECT}_INSTALL_SHARED_${SHARE}_OWNER}"
+ installsome "$SRC" "$DEST" "$MODE" "$OWNER"
+ done
+
+ local BINS BINDIR
+ echo "Installing binaries..." >&2
+ eval BINS="\${${BUILD_PROJECT}_INSTALL_BINS}"
+ eval BINDIR="\${${BUILD_PROJECT}_INSTALL_BINDIR%/}/"
+ mkdirhier "$BINDIR" 0755
+ for BIN in $BINS; do
+ eval SRC="\${${BUILD_PROJECT}_INSTALL_BIN_${BIN}_SRC%/}"
+ eval DEST="\$BINDIR\${${BUILD_PROJECT}_INSTALL_BIN_${BIN}_DEST}"
+ eval MODE="\${${BUILD_PROJECT}_INSTALL_BIN_${BIN}_MODE}"
+ eval OWNER="\${${BUILD_PROJECT}_INSTALL_BIN_${BIN}_OWNER}"
+ installsome "$SRC" "$DEST" "$MODE" "$OWNER"
+ done
+
+ eval "${TARGET}_post_install"
+}
+
+
diff --git a/build/unix/config_functions b/build/unix/config_functions
new file mode 100644
index 0000000..23adc2d
--- /dev/null
+++ b/build/unix/config_functions
@@ -0,0 +1,1139 @@
+# Auxiliary functions for custom build system
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+BUILDLOG=/dev/null
+TEMPFILE="/tmp/build.$$.tmp"
+#KEEPTEMPFILES=keeptempfiles
+
+
+# Description: perform a command, and set the exit status to the opposite.
+# (true
+# Arguments: $1 - the command to run
+# rest - arguments to the command
+not() {
+ local CMD
+ CMD=$1
+ shift
+ "$CMD" "$@"
+ return "$(($? == 0))"
+}
+
+# Description: prints a command to stdout and then executes it
+# Arguments: command with arguments
+# Returns: the return value of the command
+echo_and_perform() {
+ cat << EOF
+$@
+EOF
+ "$@"
+}
+
+# Description: Get the contents of a variable with a specific name,
+# but don't expand it. (evalVar does expand it)
+# Use this instead of 'eval', so that you won't have to
+# worry about escaping.
+# NB. this function only works on global variables.
+# Arguments: $1 - the name of the variable
+# Returns: 0
+# Prints: the value of the variable
+getVar() {
+ local RESULT
+ eval RESULT=\$$1
+ cat << EOF
+$RESULT
+EOF
+}
+
+# Description: Get the contents of a variable with a specific name,
+# and expand it. (getVar doesn't expand it)
+# Use this instead of 'eval', so that you won't have to
+# worry about escaping.
+# NB. this function only works on global variables.
+# Arguments: $1 - the name of the variable
+# Returns: 0
+# Prints: the value of the variable
+evalVar() {
+ local RESULT
+ eval RESULT=\$$1
+ eval RESULT=\""$RESULT"\"
+ cat << EOF
+$RESULT
+EOF
+}
+
+# Description: Set the value of a variable with a specific name,
+# and expand it.
+# Use this instead of 'eval', so that you won't have to
+# worry about escaping.
+# NB. this function only works on global variables.
+# Arguments: $1 - the name of the variable
+# $2 - the value to assign to it (will not be expanded)
+setVar() {
+ eval $1=\$2
+}
+
+# Description: delete the files passed as arguments, unless
+# $KEEPTEMPFILES is set.
+deleteTempFiles() {
+ if [ -n "$KEEPTEMPFILES" ]; then
+ return
+ fi
+
+ rm -f -- "$@"
+}
+
+# Description: read text from stdin to use as a c file to compile
+# Arguments: $1 - CFLAGS to use for compilation (optional)
+# $2 - LDFLAGS to use for linking (optional)
+# Returns: 0 - if compile successful
+# something else - if compile failed
+try_compile_c() {
+ local SYSTEM_FLAGS RESULT
+
+ if [ -z "$COMPILE_C" ]; then
+ echo "Fatal: Program \$COMPILE_C is not defined!" >&2
+ exit 1
+ fi
+
+ SYSTEM_FLAGS="$SYSTEM_BUILD_CFLAGS $SYSTEM_BUILD_LDFLAGS $SYSTEM_HOST_CFLAGS $SYSTEM_HOST_LDFLAGS"
+ cat > "$TEMPFILE.c"
+
+ echo_and_perform $COMPILE_C $SYSTEM_FLAGS $1 "$TEMPFILE.c" \
+ -o "$TEMPFILE.c.o" >> "$BUILDLOG" 2>&1
+ RESULT=$?
+
+ if [ $RESULT -eq 0 ]; then
+ echo_and_perform $LINK $SYSTEM_FLAGS $2 "$TEMPFILE.c.o" \
+ -o "$TEMPFILE.out" >> "$BUILDLOG" 2>&1
+ RESULT=$?
+ fi
+
+ if [ $RESULT -ne 0 ]; then
+ echo "Failed program was:" >> "$BUILDLOG"
+ echo "+++ START $TEMPFILE.c" >> "$BUILDLOG"
+ cat "$TEMPFILE.c" >> "$BUILDLOG"
+ echo "+++ END $TEMPFILE.c" >> "$BUILDLOG"
+ fi
+
+ deleteTempFiles "$TEMPFILE.c $TEMPFILE.c.o $TEMPFILE.out"
+
+ echo >> "$BUILDLOG"
+ return $RESULT
+}
+
+# Description: read text from stdin to use as a c file to compile
+# Arguments: $1 - CFLAGS to use for compilation (optional)
+# $2 - LDFLAGS to use for linking (optional)
+# Returns: 128 - if compiling or linking failed
+# otherwise - exit status of the program
+try_compile_and_run_c() {
+ local SYSTEM_FLAGS RESULT
+
+ if [ -z "$COMPILE_C" ]; then
+ echo "Fatal: Program \$COMPILE_C is not defined!" >&2
+ exit 1
+ fi
+
+ SYSTEM_FLAGS="$SYSTEM_BUILD_CFLAGS $SYSTEM_BUILD_LDFLAGS $SYSTEM_HOST_CFLAGS $SYSTEM_HOST_LDFLAGS"
+ cat > "$TEMPFILE.c"
+
+ echo_and_perform $COMPILE_C $SYSTEM_FLAGS $1 "$TEMPFILE.c" \
+ -o "$TEMPFILE.c.o" >> "$BUILDLOG" 2>&1
+ RESULT=$?
+
+ if [ $RESULT -eq 0 ]; then
+ echo_and_perform $LINK $SYSTEM_FLAGS $2 "$TEMPFILE.c.o" \
+ -o "$TEMPFILE.out" >> "$BUILDLOG" 2>&1
+ RESULT=$?
+ fi
+
+ if [ $RESULT -eq 0 ]; then
+ "$TEMPFILE.out"
+ RESULT=$?
+ fi
+
+ deleteTempFiles "$TEMPFILE.c $TEMPFILE.c.o $TEMPFILE.out"
+
+ echo >> "$BUILDLOG"
+ return $RESULT
+}
+
+# Description: Output a message to stderr, unless BUILD_SILENT is set
+# Arguments: the message
+build_message() {
+ if [ -z "$BUILD_SILENT" ]; then
+ cat >&2 << EOF
+$@
+EOF
+ fi
+}
+
+# Description: check if a string is lexicographically before
+# another string
+# Arguments: $1 - the first string
+# $2 - the second string
+# Returns: 0 if $1 is lexicographically before $2
+# 1 otherwise
+lexlt() {
+ # The 'test' builtin in some sh shells can't do this.
+ # To execute the non-builtin 'test' you need the path, and
+ # that differs per system (/bin/test or /usr/bin/test).
+ # It says '>=' instead of '<' because expr prints '1' for true,
+ # and sh uses 0.
+ return `expr "$1" ">=" "$2"`
+}
+
+# Description: Check if one version is sufficiently new.
+# Arguments: $1 - the required version
+# $2 - the available version
+# Returns: 0 - if $2 is at least as new as $1
+# 1 - if $2 is older than $1, or the two strings don't compare
+version_match() {
+ # To compare the two version strings, the numbers in the version
+ # compared numerically, and all string parts should be equal.
+ local REST1 REST2 NUM1 NUM2 STR1 STR2 CHECKNUMS
+ REST1=$1
+ REST2=$2
+ CHECKNUMS=0
+
+ while [ -n "$REST1" -a -n "$REST2" ]; do
+ NUM1=`$SED -e 's/^\([0-9]*\).*$/\1/' << EOF
+$REST1
+EOF
+`
+ NUM2=`$SED -e 's/^\([0-9]*\).*$/\1/' << EOF
+$REST2
+EOF
+`
+ REST1=`$SED -e 's/^[0-9]*//' << EOF
+$REST1
+EOF
+`
+ REST2=`$SED -e 's/^[0-9]*//' << EOF
+$REST2
+EOF
+`
+ if [ "(" -n "$NUM1" -a -z "$NUM2" ")" -o \
+ "(" -z "$NUM1" -a -n "$NUM2" ")" ]; then
+ # Only one of the versions contains a number here. No match.
+ return 1
+ fi
+ if [ "(" -n "$NUM1" -a -z "$NUM2" ")" -o \
+ "(" -z "$NUM1" -a -n "$NUM2" ")" ]; then
+ # Only one of the versions contains a number here. No match.
+ return 1
+ fi
+
+ if [ "$CHECKNUMS" -eq 0 ]; then
+ if [ "$NUM1" -gt "$NUM2" ]; then
+ # Too old.
+ return 1
+ fi
+ if [ "$NUM1" -lt "$NUM2" ]; then
+ # A major number is larger. Minor numbers may be smaller.
+ CHECKNUMS=1
+ fi
+ fi
+
+ STR1=`$SED -e 's/^\([^0-9]*\).*$/\1/' << EOF
+$REST1
+EOF
+`
+ STR2=`$SED -e 's/^\([^0-9]*\).*$/\1/' << EOF
+$REST2
+EOF
+`
+ REST1=`$SED -e 's/^[^0-9]*//' << EOF
+$REST1
+EOF
+`
+ REST2=`$SED -e 's/^[^0-9]*//' << EOF
+$REST2
+EOF
+`
+
+ if [ "x$STR1" "!=" "x$STR2" ]; then
+ # String parts don't match
+ return 1
+ fi
+ done
+
+ if [ -n "$REST1" -o -n "$REST2" ]; then
+ # One version string contains extra information. No match.
+ return 1
+ fi
+
+ return 0
+}
+
+# Description: try to detect a list of program dependencies.
+# This only makes sure the PROG_xxx_PRESENT flag is set.
+# It does not bail out if a dependency is not found.
+# This function is called for programs in
+# PROG_xxx_DEPEND_DETECT_BIN or LIB_xxx_DEPEND_DETECT_BIN.
+# Using have_program in the respective depend functions would
+# not save the PRESENT flag, as it is executed in a subshell.
+# Arguments: $1 - the list of programs
+detect_dependencies_program() {
+ local PROGS PROG
+ PROGS=$1
+ for PROG in $PROGS; do
+ have_program "$PROG"
+ done
+}
+
+# Description: try to detect a list of library dependencies.
+# This only makes sure the LIB_xxx_PRESENT flag is set.
+# It does not bail out if a dependency is not found.
+# This function is called for libraries in
+# PROG_xxx_DEPEND_DETECT_LIB or LIB_xxx_DEPEND_DETECT_LIB.
+# Using have_library in the respective depend functions would
+# not save the PRESENT flag, as it is executed in a subshell.
+# Arguments: $1 - the list of libaries
+detect_dependencies_library() {
+ local LIBS LIB
+ LIBS=$1
+ for LIB in $LIBS; do
+ have_library "$LIB"
+ done
+}
+
+# Description: check if a program is present in the path or as a built-in
+# command.
+# Arguments: $1 - The name of the program as it is executed
+# Returns: 0 - if the command is found
+# 1 - if the command is not found
+have_command() {
+ type "$1" > /dev/null 2>&1 || false
+ # The '|| false' is because if 'type' does not recognise the
+ # command, it might return a different value than 1, on some
+ # shells.
+}
+
+# Description: check if a program is present in the path or as a built-in
+# command.
+# Arguments: $1 - The name of the program as used in config_proginfo after
+# "BIN_"
+have_program() {
+ local PROG TEMP_NAME TEMP_FILE TEMP_VERSION TEMP_PRESENT TEMP_DETECT
+ local TEMP_DEPEND_DETECT_BIN TEMP_DEPEND_DETECT_LIB
+
+ PROG=$1
+ TEMP_NAME=`evalVar "PROG_${PROG}_NAME"`
+ if [ -z "$TEMP_NAME" ]; then
+ echo "Fatal: Program '$PROG' is not defined!" >&2
+ exit 1
+ fi
+
+ TEMP_PRESENT=`getVar "PROG_${PROG}_PRESENT"`
+ if [ -n "$TEMP_PRESENT" ]; then
+ return "$TEMP_PRESENT"
+ fi
+
+ # If a detection method is specified, try that one first.
+ TEMP_DETECT=`evalVar "PROG_${PROG}_DETECT"`
+ if [ -n "$TEMP_DETECT" ]; then
+ TEMP_DEPEND_DETECT_BIN=`evalVar "PROG_${PROG}_DEPEND_DETECT_BIN"`
+ TEMP_DEPEND_DETECT_LIB=`evalVar "PROG_${PROG}_DEPEND_DETECT_LIB"`
+ detect_dependencies_program "$TEMP_DEPEND_DETECT_BIN"
+ detect_dependencies_library "$TEMP_DEPEND_DETECT_LIB"
+ $TEMP_DETECT
+ # NB. TEMP_DETECT should make sure PROG_${PROG}_VERSION and
+ # PROG_${PROG}_FILE are set.
+ case $? in
+ 0) # Program found
+ setVar "PROG_${PROG}_PRESENT" 0
+ ;;
+ 1) # Lib not found
+ setVar "PROG_${PROG}_PRESENT" 1
+ ;;
+ 2) # Use default detection
+ ;;
+ esac
+ fi
+
+ # If detection via a specified detection method was inconclusive,
+ # or no detection method was specified, try default detection
+ # based on whether the command name exists.
+ TEMP_PRESENT=`getVar "$PROG_${PROG}_PRESENT"`
+ if [ -z "$TEMP_PRESENT" ]; then
+ TEMP_FILE=`evalVar "PROG_${PROG}_FILE"`
+ have_command "${TEMP_FILE%% *}"
+ if [ $? -eq 0 ]; then
+ # Program found
+ setVar "PROG_${PROG}_PRESENT" 0
+ fi
+ fi
+
+ # If detection has yielded no results so far, we're calling it
+ # "not found".
+ TEMP_PRESENT=`getVar "PROG_${PROG}_PRESENT"`
+ if [ -z "$TEMP_PRESENT" ]; then
+ setVar "PROG_${PROG}_PRESENT" 1
+ fi
+
+ TEMP_PRESENT=`getVar "PROG_${PROG}_PRESENT"`
+ if [ "$TEMP_PRESENT" -eq 1 ]; then
+ build_message "$TEMP_NAME not found."
+ return 1
+ fi
+
+ # We have found the program.
+ # Test whether the version is sufficient.
+ if [ $# -gt 1 ]; then
+ # Minimum version supplied
+ TEMP_VERSION=`evalVar "PROG_${PROG}_VERSION"`
+ if [ -z "$TEMP_VERSION" ]; then
+ setVar "PROG_${PROG}_PRESENT" 1
+ echo "Fatal: Could not determine the version of $TEMP_NAME" >&2
+ exit 1
+ fi
+ if not version_match "$2" "$TEMP_VERSION"; then
+ setVar "PROG_${PROG}_PRESENT" 1
+ build_message "Found version $TEMP_VERSION of $TEMP_NAME, \
+but version $2 is required!"
+ return 1
+ fi
+ setVar "PROG_${PROG}_PRESENT" 0
+ build_message "$TEMP_NAME version $TEMP_VERSION found."
+ return 0
+ fi
+
+ setVar "PROG_${PROG}_PRESENT" 0
+ build_message "$TEMP_NAME found."
+ return 0
+}
+
+# Description: check if a build tool is present, and define the appropriate
+# environment variable for it.
+# Arguments: $1 - The type of compile tool. One of PREPROC_C, MKDEP_C,
+# COMPILE_C, PREPROC_OBJC, MKDEP_OBJC, COMPILE_OBJC,
+# and LINK.
+have_build_tool() {
+ local TOOL TEMP_NAME TEMP_PRESENT DEPENDS DEPEND SUCCESS COMMAND
+
+ TOOL=$1
+
+ TEMP_NAME=`evalVar "BUILDTOOL_${TOOL}_NAME"`
+ if [ -z "$TEMP_NAME" ]; then
+ echo "Fatal: Build tool '$TOOL' is not defined!" >&2
+ exit 1
+ fi
+
+ TEMP_PRESENT=`getVar "BUILDTOOL_${TOOL}_PRESENT"`
+ if [ -n "$TEMP_PRESENT" ]; then
+ return "$TEMP_PRESENT"
+ fi
+
+ SUCCESS=0
+ DEPENDS=`getVar "BUILDTOOL_${TOOL}_DEPEND"`
+ for DEPEND in $DEPENDS; do
+ if not have_program "$DEPEND"; then
+ SUCCESS=1
+ fi
+ done
+
+ setVar "BUILDTOOL_${TOOL}_PRESENT" "$SUCCESS"
+
+ COMMAND=`evalVar "BUILDTOOL_${TOOL}_COMMAND"`
+ # Environment variables in $BUILDTOOL_xxx_COMMAND are expanded.
+
+ if [ $SUCCESS -eq 0 ]; then
+ build_message "We have a $TEMP_NAME."
+ setVar "$TOOL" "$COMMAND"
+ else
+ build_message "No $TEMP_NAME found."
+ fi
+
+ return $SUCCESS
+}
+
+have_build_tools_language() {
+ local LANGUAGE
+
+ LANGUAGE=$1
+
+ have_build_tool "PREPROC_$LANGUAGE" || return 1
+ have_build_tool "MKDEP_$LANGUAGE" || return 1
+ have_build_tool "COMPILE_$LANGUAGE" || return 1
+
+ return 0
+}
+
+
+# Description: check if a library is present on the system
+# Arguments: $1 - The name of the library as used in config_proginfo after
+# "LIB_"
+# $2 - (optional) minimum version required
+# Pre: variables LIB_${1}_NAME, LIB_${1}_CFLAGS, and
+# LIB_${1}_LDFLAGS are expected to exist. If two arguments are
+# supplied, so is LIB_${1}_VERSION.
+# Returns: 0 - if the library is found
+# 1 - if the library is not found
+have_library() {
+ local LIB TEMP_NAME TEMP_PRESENT TEMP_LDFLAGS TEMP_CFLAGS \
+ TEMP_VERSION TEMP_DETECT
+ local TEMP_DEPEND_DETECT_BIN TEMP_DEPEND_DETECT_LIB
+
+ LIB=$1
+ TEMP_NAME=`evalVar "LIB_${LIB}_NAME"`
+ if [ -z "$TEMP_NAME" ]; then
+ echo "Fatal: Library '$LIB' is not defined!" >&2
+ exit 1
+ fi
+
+ TEMP_PRESENT=`getVar "LIB_${LIB}_PRESENT"`
+ if [ -n "$TEMP_PRESENT" ]; then
+ return "$TEMP_PRESENT"
+ fi
+
+ # If a detection method is specified, try that one first.
+ TEMP_DETECT=`evalVar "LIB_${LIB}_DETECT"`
+ if [ -n "$TEMP_DETECT" ]; then
+ TEMP_DEPEND_DETECT_BIN=`evalVar "LIB_${LIB}_DEPEND_DETECT_BIN"`
+ TEMP_DEPEND_DETECT_LIB=`evalVar "LIB_${LIB}_DEPEND_DETECT_LIB"`
+ detect_dependencies_program "$TEMP_DEPEND_DETECT_BIN"
+ detect_dependencies_library "$TEMP_DEPEND_DETECT_LIB"
+ $TEMP_DETECT
+ # NB. TEMP_DETECT should make sure PROG_$LIB_VERSION is set.
+ # return value of $TEMP_DETECT is used below.
+ case $? in
+ 0) # Lib found
+ setVar "LIB_${LIB}_PRESENT" 0
+ ;;
+ 1) # Lib not found
+ setVar "LIB_${LIB}_PRESENT" 1
+ ;;
+ 2) # Use default detection
+ ;;
+ esac
+ fi
+
+ # If detection via a specified detection method was inconclusive,
+ # or no detection method was specified, try default detection
+ # based on whether we can compile against the library.
+ TEMP_PRESENT=`getVar "LIB_${LIB}_PRESENT"`
+ if [ -z "$TEMP_PRESENT" ]; then
+ TEMP_CFLAGS=`evalVar "LIB_${LIB}_CFLAGS"`
+ TEMP_LDFLAGS=`evalVar "LIB_${LIB}_LDFLAGS"`
+
+ try_compile_c "$CFLAGS $TEMP_CFLAGS" "$LDFLAGS $TEMP_LDFLAGS" << EOF
+int main(void) {
+ return 0;
+}
+EOF
+ if [ $? -eq 0 ]; then
+ # Build successful
+ setVar "LIB_${LIB}_PRESENT" 0
+ fi
+ fi
+
+ # If detection has yielded no results so far, we're calling it
+ # "not found".
+ TEMP_PRESENT=`getVar "LIB_${LIB}_PRESENT"`
+ if [ -z "$TEMP_PRESENT" ]; then
+ setVar "LIB_${LIB}_PRESENT" 1
+ fi
+
+ TEMP_PRESENT=`getVar "LIB_${LIB}_PRESENT"`
+ if [ "$TEMP_PRESENT" -eq 1 ]; then
+ build_message "$TEMP_NAME not found."
+ return 1
+ fi
+
+ # We have found the library.
+ # Test whether the version is sufficient.
+ if [ $# -gt 1 ]; then
+ # Minimum version supplied
+ TEMP_VERSION=`evalVar "LIB_${LIB}_VERSION"`
+ if [ -z "$TEMP_VERSION" ]; then
+ setVar "LIB_${LIB}_PRESENT" 1
+ echo "Fatal: Could not determine the version of $TEMP_NAME" >&2
+ exit 1
+ fi
+ if not version_match "$2" "$TEMP_VERSION"; then
+ setVar "LIB_${LIB}_PRESENT" 1
+ build_message "Found version $TEMP_VERSION of $TEMP_NAME, \
+but version $2 is required!"
+ return 1
+ fi
+ setVar "LIB_${LIB}_PRESENT" 0
+ build_message "$TEMP_NAME version $TEMP_VERSION found."
+ return 0
+ fi
+
+ setVar "LIB_${LIB}_PRESENT" 0
+ build_message "$TEMP_NAME found."
+ return 0
+}
+
+# Description: check if a library is present on the system.
+# If it is, add the appropriate flags to CFLAGS and LDFLAGS.
+# If not, bail out.
+# Arguments: $1 - The name of the library as used in config_proginfo after
+# "LIB_"
+# $2 - (optional) minimum version required
+# Pre: variables LIB_${1}_NAME, LIB_${1}_CFLAGS, and
+# LIB_${1}_LDFLAGS are expected to exist. If two arguments are
+# supplied, so is LIB_${1}_VERSION.
+use_library() {
+ local TEMP_CFLAGS TEMP_LDFLAGS
+ have_library "$@"
+ [ $? -eq 0 ] || exit 1
+
+ TEMP_CFLAGS=`evalVar "LIB_${1}_CFLAGS"`
+ TEMP_LDFLAGS=`evalVar "LIB_${1}_LDFLAGS"`
+ CFLAGS="$CFLAGS $TEMP_CFLAGS"
+ LDFLAGS="$LDFLAGS $TEMP_LDFLAGS"
+ return 0
+}
+
+# Description: check if a library are being provided by a framework outside
+# of the normal system build process.
+# If it is, mark it as found and add appropriate flags
+# If not, do nothing. Do not even mark it as not found; it might
+# be available by some other means.
+# This function is currently specific to macOS builds.
+# Arguments: $1 - The name of the library as used in config_proginfo
+# $2 - (optional, default $1) the name of the framework
+# DEPS_PATH - environment variable that if set specifies where to
+# check for the framework.
+# Pre: LIB_${1}_NAME is expected to exist.
+have_framework() {
+ local TEMP_LIBNAME LIB LIBVAR FRAMEWORK_PATH
+ # If we aren't on a Mac, there is no framework
+ if [[ "x$HOST_SYSTEM" != "xDarwin" ]]; then
+ return 1
+ fi
+ # If DEPS_PATH was not configured, there is no framework
+ if [[ -z "$DEPS_PATH" ]]; then
+ return 1
+ fi
+ LIBVAR=$1
+ LIB=$2
+ if [[ -z "$2" ]]; then
+ LIB=$LIBVAR
+ fi
+ TEMP_LIBNAME=`evalVar "LIB_${LIBVAR}_NAME"`
+ FRAMEWORK_PATH="${DEPS_PATH}/${LIB}.framework"
+ if [[ -d "$FRAMEWORK_PATH" ]]; then
+ build_message "${TEMP_LIBNAME} found in ${DEPS_PATH}."
+ setVar "LIB_${LIBVAR}_PRESENT" 0
+ setVar "LIB_${LIBVAR}_CFLAGS" "-F${DEPS_PATH} -I${FRAMEWORK_PATH}/Headers"
+ setVar "LIB_${LIBVAR}_LDFLAGS" "-F${DEPS_PATH} -framework ${LIB}"
+ return 0
+ fi
+ build_message "${TEMP_LIBNAME} not found in ${DEPS_PATH}."
+ return 1
+}
+
+# Description: check if a symbol is defined
+# Arguments: $1 - the name of the symbol
+# $2 - the C code that does the actual checking
+have_symbol_generic() {
+ local CODE DETECT EXTRA
+
+ DETECT=`evalVar "SYMBOL_${1}_DETECT"`
+ if [ -n "$DETECT" ]; then
+ $DETECT
+ return $?
+ fi
+
+ EXTRA=`evalVar "SYMBOL_${1}_EXTRA"`
+ CODE=`evalVar "SYMBOL_${1}_CODE"`
+ if [ -z "$CODE" ]; then
+ CODE=$2
+ fi
+
+ try_compile_c "$CFLAGS $TEMP_CFLAGS" "$LDFLAGS $TEMP_LDFLAGS" << EOF > /dev/null 2>&1
+#include <sys/types.h>
+$EXTRA
+
+$CODE
+EOF
+}
+
+# Description: check if a symbol is defined.
+# Arguments: $1 - the name of the symbol
+have_symbol() {
+ local SYMBOL TEMP_PRESENT CODE
+ SYMBOL=$1
+
+ TEMP_PRESENT=`getVar "SYMBOL_${SYMBOL}_PRESENT"`
+ if [ -n "$TEMP_PRESENT" ]; then
+ return "$TEMP_PRESENT"
+ fi
+
+ CODE=`cat << EOF
+int main(void) {
+ (void) $SYMBOL;
+ return 0;
+}
+EOF
+`
+
+ have_symbol_generic "$SYMBOL" "$CODE"
+ if [ $? -gt 0 ]; then
+ build_message "Symbol '$SYMBOL' not found."
+ setVar "SYMBOL_${SYMBOL}_PRESENT" 1
+ return 1
+ fi
+ build_message "Symbol '$SYMBOL' found."
+ setVar "SYMBOL_${SYMBOL}_PRESENT" 0
+ return 0
+}
+
+# Description: check if a type is present.
+# Arguments: $1 - the name of the symbol
+have_type() {
+ local TYPE TEMP_PRESENT CODE
+ TYPE=$1
+
+ TEMP_PRESENT=`getVar "TYPE_${TYPE}_PRESENT"`
+ if [ -n "$TEMP_PRESENT" ]; then
+ return "$TEMP_PRESENT"
+ fi
+
+ CODE=`cat << EOF
+int main(void) {
+ $TYPE var;
+ (void) var;
+ return 0;
+}
+EOF
+ `
+
+ have_symbol_generic "$TYPE" "$CODE"
+ if [ $? -gt 0 ]; then
+ build_message "Type '$TYPE' not found."
+ setVar "TYPE_${TYPE}_PRESENT" 1
+ return 1
+ fi
+ build_message "Type '$TYPE' found."
+ setVar "TYPE_${TYPE}_PRESENT" 0
+ return 0
+}
+
+# Description: check if a symbol is defined.
+# sets HAVE_<NAME> accordingly, where <NAME> is the capitalized
+# name of the symbol.
+# Arguments: $1 - the name of the symbol
+define_have_symbol() {
+ local NAME VALUE DEFNAME
+
+ DEFNAME=`getVar "SYMBOL_${1}_DEFNAME"`
+ if [ -z "$DEFNAME" ]; then
+ # Why not "tr [:lower:] [:upper:]"? Because the capital "i" is not
+ # "I" in Turkish... An alternative would be setting LC_CTYPE to POSIX.
+ NAME=`$TR "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ << EOF
+$1
+EOF
+ `
+ DEFNAME="HAVE_$NAME"
+ fi
+
+ if have_symbol "$1"; then
+ add_symbol "$DEFNAME" "#define $DEFNAME"
+ add_symbol "${DEFNAME}_FLAG" 1
+ else
+ add_symbol "$DEFNAME" "#undef $DEFNAME"
+ add_symbol "${DEFNAME}_FLAG" 0
+ fi
+}
+
+# Description: check if a type is present.
+# set HAVE_<NAME> accordingly, where <NAME> is the capitalized
+# name of the symbol.
+# Arguments: $1 - the name of the symbol
+define_have_type() {
+ local NAME VALUE DEFNAME
+
+ DEFNAME=`getVar "TYPE_${1}_DEFNAME"`
+ if [ -z "$DEFNAME" ]; then
+ # Why not "tr [:lower:] [:upper:]"? Because the capital "i" is not
+ # "I" in Turkish... An alternative would be setting LC_CTYPE to POSIX.
+ NAME=`$TR "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ << EOF
+$1
+EOF
+ `
+ DEFNAME="HAVE_$NAME"
+ fi
+
+ if have_type "$1"; then
+ add_symbol "$DEFNAME" "#define $DEFNAME"
+ add_symbol "${DEFNAME}_FLAG" 1
+ else
+ add_symbol "$DEFNAME" "#undef $DEFNAME"
+ add_symbol "${DEFNAME}_FLAG" 0
+ fi
+}
+
+# Description: check if a header is available.
+# Arguments: $1 - the name of the header file
+have_header() {
+ local HEADER NAME EXTRA
+ HEADER=$1
+
+ NAME=${HEADER%.h}
+ EXTRA=`evalVar "HEADER_${NAME}_EXTRA"`
+
+ try_compile_c "$CFLAGS $TEMP_CFLAGS" "$LDFLAGS $TEMP_LDFLAGS" << EOF > /dev/null 2>&1
+$EXTRA
+#include <$HEADER>
+int main(void) {
+ return 0;
+}
+EOF
+ if [ $? -gt 0 ]; then
+ build_message "Header '$HEADER' not found."
+ return 1
+ fi
+ build_message "Header '$HEADER' found."
+ return 0
+}
+
+# Description: check if a header is available.
+# sets HAVE_<NAME> accordingly, where <NAME> is the capitalized
+# name of the header file. "sys/time.h" becomes "SYS_TIME_H".
+# Arguments: $1 - the name of the header file
+define_have_header() {
+ local NAME VALUE DEFNAME
+
+ DEFNAME=`getVar "HEADER_${1%.h}_DEFNAME"`
+ if [ -z "$DEFNAME" ]; then
+ # Why not "tr [:lower:] [:upper:]"? Because the capital "i" is not
+ # "I" in Turkish... An alternative would be setting LC_CTYPE to POSIX.
+ NAME=`$TR "/.abcdefghijklmnopqrstuvwxyz" \
+ "__ABCDEFGHIJKLMNOPQRSTUVWXYZ" << EOF
+$1
+EOF
+ `
+ DEFNAME="HAVE_$NAME"
+ fi
+
+ if have_header "$1"; then
+ add_symbol "$DEFNAME" "#define $DEFNAME"
+ add_symbol "${DEFNAME}_FLAG" 1
+ else
+ add_symbol "$DEFNAME" "#undef $DEFNAME"
+ add_symbol "${DEFNAME}_FLAG" 0
+ fi
+}
+
+# Description: check if a macro is available.
+# Arguments: $1 - the name of the macro
+have_macro() {
+ local MACRO EXTRA
+ MACRO=$1
+
+ EXTRA=`evalVar "MACRO_${NAME}_EXTRA"`
+
+ try_compile_c "$CFLAGS $TEMP_CFLAGS" "$LDFLAGS $TEMP_LDFLAGS" << EOF > /dev/null 2>&1
+$EXTRA
+#ifndef $MACRO
+# error
+#endif
+int main(void) {
+ return 0;
+}
+EOF
+ if [ $? -gt 0 ]; then
+ build_message "Preprocessor macro '$MACRO' not found."
+ return 1
+ fi
+ build_message "Preprocessor macro '$MACRO' found."
+ return 0
+}
+
+# Description: check if a macro is defined
+# sets MACRO_<NAME> to a non-empty string if the macro with
+# the specified name is defined, and to an empty string if it
+# is not.
+# Arguments: $1 - the name of the symbol
+define_have_macro() {
+ local NAME
+
+ NAME=$1
+
+ if have_macro "$1"; then
+ add_symbol "MACRO_$NAME" "1"
+ else
+ add_symbol "MACRO_$NAME" ""
+ fi
+}
+
+# Description: Add a symbol to be replaced by substitute_vars
+# $HAVE_SYMBOLS will contain the variable names of all
+# symbols added by define_have_symbol and should be passed to
+# substitute_vars for the file you want them in.
+# Arguments: $1 - the symbol to add
+# $2 - the value of the symbol
+add_symbol() {
+ local NAME
+
+ eval NAME="$1"
+ eval "$NAME"=\"\$2\"
+ HAVE_SYMBOLS="$HAVE_SYMBOLS $NAME"
+}
+
+check_endianness() {
+ local ENDIAN
+
+ if [ "$BUILD_SYSTEM" '!=' "$HOST_SYSTEM" ]; then
+ case "$BUILD_HOST_ENDIAN" in
+ "")
+ build_message "Cross-compiling - assuming little-endian host."
+ ENDIAN=little
+ ;;
+ big)
+ build_message "Cross-compiling for a big-endian host."
+ ENDIAN=big
+ ;;
+ little)
+ build_message "Cross-compiling for a little-endian host."
+ ENDIAN=little
+ ;;
+ *)
+ build_message "Bad endianness specified. Use \"little\" or \"big\""
+ exit 1
+ ;;
+ esac
+ else
+ # Detect endianness
+ try_compile_and_run_c "$CFLAGS" "$LDFLAGS" << EOF
+int main(void) {
+ int i;
+
+ i = 1;
+ return *((unsigned char *) &i);
+}
+EOF
+ if [ $? -eq 0 ]; then
+ build_message "Big-endian machine detected."
+ ENDIAN=big
+ else
+ build_message "Little-endian machine detected."
+ ENDIAN=little
+ fi
+ fi
+
+ case "$ENDIAN" in
+ big)
+ add_symbol WORDS_BIGENDIAN "#define WORDS_BIGENDIAN"
+ add_symbol "WORDS_BIGENDIAN_FLAG" 1
+ ;;
+ little)
+ add_symbol WORDS_BIGENDIAN "#undef WORDS_BIGENDIAN"
+ add_symbol "WORDS_BIGENDIAN_FLAG" 0
+ ;;
+ esac
+}
+
+
+# Description: If pkg-config is installed, check if there's a pkg-config
+# entry for some binary dependency.
+# If successful, it sets the appropriate BIN_xxx_VERSION.
+# Arguments: $1 - The name of the program as it is known in
+# config_proginfo after "BIN_"
+# $2 - The name of the dependency as it would be known to
+# pkg-config.
+try_pkgconfig_prog() {
+ have_program pkgconfig || return 1
+
+ local PROG PKG_NAME TEMP_NAME
+ PROG=$1
+ PKG_NAME=$2
+
+ TEMP_NAME=`evalVar "PROG_${PROG}_NAME"`
+ if [ -z "$TEMP_NAME" ]; then
+ echo "Fatal: Program '$PROG' is not defined!" >&2
+ exit 1
+ fi
+
+ if $PROG_pkgconfig_FILE --exists "$PKG_NAME"; then
+ local TEMP_VERSION
+ TEMP_VERSION=$($PROG_pkgconfig_FILE --modversion "$PKG_NAME")
+ setVar "PROG_${PROG}_VERSION" "$TEMP_VERSION"
+ return 0
+ else
+ return 2
+ fi
+}
+
+
+# Description: If pkg-config is installed, check if there's a pkg-config
+# entry for some dependency.
+# If successful, it sets the appropriate LIB_xxx_VERSION,
+# LIB_xxx_CFLAGS and LIB_xxx_LDFLAGS.
+# Arguments: $1 - The name of the library as it is known in
+# config_proginfo after "LIB_"
+# $2 - The name of the dependency as it would be known to
+# pkg-config.
+try_pkgconfig_lib() {
+ have_program pkgconfig || return 1
+
+ local LIB PKG_NAME TEMP_NAME
+ LIB=$1
+ PKG_NAME=$2
+
+ TEMP_NAME=`evalVar "LIB_${LIB}_NAME"`
+ if [ -z "$TEMP_NAME" ]; then
+ echo "Fatal: Library '$LIB' is not defined!" >&2
+ exit 1
+ fi
+
+ if $PROG_pkgconfig_FILE --exists "$PKG_NAME"; then
+ local TEMP_VERSION TEMP_CFLAGS TEMP_LDFLAGS
+ TEMP_VERSION=$($PROG_pkgconfig_FILE --modversion "$PKG_NAME")
+ TEMP_CFLAGS=$($PROG_pkgconfig_FILE --cflags "$PKG_NAME")
+ TEMP_LDFLAGS=$($PROG_pkgconfig_FILE --libs "$PKG_NAME")
+ setVar "LIB_${LIB}_VERSION" "$TEMP_VERSION"
+ setVar "LIB_${LIB}_CFLAGS" "$TEMP_CFLAGS"
+ setVar "LIB_${LIB}_LDFLAGS" "$TEMP_LDFLAGS"
+ return 2 # Force testing using the new CFLAGS and LDFLAGS
+ #return 0
+ else
+ return 2
+ fi
+}
+
+
+# Description: substitute variables in files.
+# Every supplied variable name found between @'s in the
+# supplied files, is replaced by its value.
+# Arguments: $1 - The name of the variable which contains a list of
+# variables to substitute in the files.
+# $2 - The name of the variable which contains a list of
+# files to substitute variables in.
+# If a filename ends on .in, that filename is used as
+# source, and the filename without .in as target.
+# If a filename doesn't end on .in, that filename is used
+# as target, and the filename with .in attached as source.
+# $3 - A path to which the input file names are relative
+# $4 - A path to which the output file names are relative
+substitute_vars() {
+ local VARS VAR VALUE FILES FILE SRC_PATH DST_PATH
+
+ eval VARS=\"\$$1\"
+ eval FILES=\"\$$2\"
+ SRC_PATH="$3"
+ DST_PATH="$4"
+
+ for VAR in $VARS; do
+ # Escape all / in VAR so that we can use / as seperator char for sed
+ eval VALUE=\"\$$VAR\"
+ VALUE=$(echo "$VALUE" | $SED -e 's,\([\&/]\),\\\1,g')
+ cat << EOF
+s/@${VAR}@/${VALUE}/g
+EOF
+ done > "${TEMPFILE}.sed"
+
+ for FILE in $FILES; do
+ FILE="${FILE%.in}"
+ cp -p -- "$SRC_PATH/$FILE".in "$DST_PATH/$FILE"
+ # The copy is done so that the file modes are equal.
+ $SED -f "${TEMPFILE}.sed" < "$SRC_PATH/$FILE".in > "$DST_PATH/$FILE"
+ done
+ deleteTempFiles "${TEMPFILE}.sed"
+}
+
+# Define the build system type.
+set_build_system() {
+ BUILD_SYSTEM=`uname -s`
+}
+
+# Define the host system type.
+set_host_system() {
+ local UHOST
+
+ if [ -z "$BUILD_HOST" ]; then
+ HOST_SYSTEM=$BUILD_SYSTEM
+ else
+ case "$BUILD_HOST" in
+ *-*-*)
+ HOST_SYSTEM="${BUILD_HOST#*-}"
+ HOST_SYSTEM="${HOST_SYSTEM%-*}"
+ ;;
+ esac
+
+ # Use a single capitalization.
+ # What is used is whatever 'uname -s' would give on such a platform.
+ case "$BUILD_HOST" in
+ [Ll][Ii][Nn][Uu][Xx])
+ HOST_SYSTEM="Linux" ;;
+ [Ff][Rr][Ee][Ee][Bb][Ss][Dd])
+ HOST_SYSTEM="FreeBSD" ;;
+ [Oo][Pp][Ee][Nn][Bb][Ss][Dd])
+ HOST_SYSTEM="OpenBSD" ;;
+ [Mm][Ii][Nn][Gg][Ww]|[Mm][Ii][Nn][Gg][Ww]32)
+ HOST_SYSTEM="MINGW32" ;;
+ [Cc][Yy][Gg][Ww][Ii][Nn]*)
+ HOST_SYSTEM="CYGWIN" ;;
+ [Dd][Aa][Rr][Ww][Ii][Nn])
+ HOST_SYSTEM="Darwin" ;;
+ [Ss][Uu][Nn][Oo][Ss])
+ HOST_SYSTEM="SunOS" ;;
+ [Qq][Nn][Xx])
+ HOST_SYSTEM="QNX" ;;
+ [Cc][Ee][Gg][Cc][Cc])
+ HOST_SYSTEM="cegcc" ;;
+ [Ww][Ii][Nn][Ss][Cc][Ww])
+ HOST_SYSTEM="WINSCW" ;;
+ [Aa][Rr][Mm][Vv]5)
+ HOST_SYSTEM="ARMV5" ;;
+ [Gg][Cc][Cc][Ee])
+ HOST_SYSTEM="GCCE" ;;
+ *)
+ build_message "Warning: host type '$BUILD_HOST' unknown. Using defaults."
+ ;;
+ esac
+ fi
+
+}
+
+set_system() {
+ set_build_system
+ set_host_system
+}
+
+prepare_build_system() {
+ # Include information about programs we can detect for the build system.
+ . build/unix/config_proginfo_build
+}
+
+prepare_host_system() {
+ # Include information about programs we can detect for the host system.
+ . build/unix/config_proginfo_host
+}
+
+# Some initialisations
+HAVE_SYMBOLS=""
+
+config_requirements() {
+ # Requirements for the config program itself
+ have_program echon || exit 1
+ ECHON="$PROG_echon_FILE"
+ have_program sed || exit 1
+ SED="$PROG_sed_FILE"
+ have_program tr || exit 1
+ TR="$PROG_tr_FILE"
+ have_program make || exit 1
+ MAKE="$PROG_make_FILE"
+}
+
diff --git a/build/unix/config_proginfo_build b/build/unix/config_proginfo_build
new file mode 100644
index 0000000..b01cf09
--- /dev/null
+++ b/build/unix/config_proginfo_build
@@ -0,0 +1,336 @@
+# Set information on used programs and libraries
+# This file contains the information for the programs that test the
+# system on which the software is built.
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+##############################################################################
+# System specific build flags #
+##############################################################################
+
+
+# CFLAGS
+SYSTEM_BUILD_CFLAGS=""
+
+# CXXFLAGS
+SYSTEM_BUILD_CXXFLAGS=""
+
+# LDFLAGS
+SYSTEM_BUILD_LDFLAGS=""
+
+# Compilers etc.
+BUILDTOOL_PREPROC_C_NAME="C preprocessor"
+BUILDTOOL_MKDEP_C_NAME="C dependency generator"
+BUILDTOOL_COMPILE_C_NAME="C compiler"
+BUILDTOOL_PREPROC_CXX_NAME="C++ preprocessor"
+BUILDTOOL_MKDEP_CXX_NAME="C++ dependency generator"
+BUILDTOOL_COMPILE_CXX_NAME="C++ compiler"
+BUILDTOOL_PREPROC_OBJC_NAME="Objective-C preprocessor"
+BUILDTOOL_MKDEP_OBJC_NAME="Objective-C dependency generator"
+BUILDTOOL_COMPILE_OBJC_NAME="Objective-C compiler"
+BUILDTOOL_LINK_NAME="linker"
+useGccBuildTools() {
+ # These strings will be evaluated later.
+ BUILDTOOL_PREPROC_C_COMMAND="\$PROG_gcc_FILE -E $EXTRA_PLATFORM_GCC_FLAGS_PREPROC_C"
+ BUILDTOOL_PREPROC_C_DEPEND='gcc'
+
+ BUILDTOOL_MKDEP_C_COMMAND="\$PROG_gcc_FILE -MM $EXTRA_PLATFORM_GCC_FLAGS_MKDEP_C"
+ BUILDTOOL_MKDEP_C_DEPEND='gcc'
+
+ BUILDTOOL_COMPILE_C_COMMAND="\$PROG_gcc_FILE -c $EXTRA_PLATFORM_GCC_FLAGS_COMPILE_C"
+ BUILDTOOL_COMPILE_C_DEPEND='gcc'
+
+ BUILDTOOL_PREPROC_CXX_COMMAND="\$PROG_gcc_FILE -E $EXTRA_PLATFORM_GCC_FLAGS_PREPROC_CXX"
+ BUILDTOOL_PREPROC_CXX_DEPEND='gcc'
+
+ BUILDTOOL_MKDEP_CXX_COMMAND="\$PROG_gcc_FILE -MM $EXTRA_PLATFORM_GCC_FLAGS_MKDEP_CXX"
+ BUILDTOOL_MKDEP_CXX_DEPEND='gcc'
+
+ BUILDTOOL_COMPILE_CXX_COMMAND="\$PROG_gcc_FILE -c $EXTRA_PLATFORM_GCC_FLAGS_COMPILE_CXX"
+ BUILDTOOL_COMPILE_CXX_DEPEND='gcc'
+
+ BUILDTOOL_PREPROC_OBJC_COMMAND="\$PROG_gcc_FILE -E $EXTRA_PLATFORM_GCC_FLAGS_PREPROC_OBJC"
+ BUILDTOOL_PREPROC_OBJC_DEPEND='gcc'
+
+ BUILDTOOL_MKDEP_OBJC_COMMAND="\$PROG_gcc_FILE -MM $EXTRA_PLATFORM_GCC_FLAGS_MKDEP_OBJC"
+ BUILDTOOL_MKDEP_OBJC_DEPEND='gcc'
+
+ BUILDTOOL_COMPILE_OBJC_COMMAND="\$PROG_gcc_FILE -c $EXTRA_PLATFORM_GCC_FLAGS_COMPILE_OBJC"
+ BUILDTOOL_COMPILE_OBJC_DEPEND='gcc'
+
+ BUILDTOOL_LINK_COMMAND="\$PROG_gcc_FILE $EXTRA_PLATFORM_GCC_FLAGS_LINK"
+ BUILDTOOL_LINK_DEPEND='gcc'
+}
+case "$HOST_SYSTEM" in
+ Darwin)
+ EXTRA_PLATFORM_GCC_FLAGS_COMPILE_C='-mmacosx-version-min=10.6 -arch x86_64'
+ EXTRA_PLATFORM_GCC_FLAGS_COMPILE_OBJC='-mmacosx-version-min=10.6 -arch x86_64'
+ EXTRA_PLATFORM_GCC_FLAGS_LINK='-mmacosx-version-min=10.6 -arch x86_64'
+ useGccBuildTools
+ ;;
+ WINSCW)
+ EXTRA_WINSCW_FLAGS_COMPILE="-msgstyle gcc -gccinc -dialect c99 -relax_pointers -wchar_t off -align 4 -warnings on -w nohidevirtual,nounusedexpr -enum int -str pool -exc ms -trigraphs on -nostdinc -d _UNICODE -d __SYMBIAN32__ -d __SERIES60_30__ -d __SERIES60_3X__ -d __CW32__ -d __WINS__ -d __WINSCW__ -d __EXE__ -d __SUPPORT_CPP_EXCEPTIONS__ -I$BUILD_EPOCROOT/epoc32/include -I$BUILD_EPOCROOT/epoc32/include/stdapis -I$BUILD_EPOCROOT/epoc32/include/variant"
+ EXTRA_WINSCW_FLAGS_LINK='-library -msgstyle gcc -stdlib -noimplib -search'
+
+ BUILDTOOL_PREPROC_C_COMMAND="\$PROG_mwccsym2_FILE -E $EXTRA_WINSCW_FLAGS_COMPILE"
+ BUILDTOOL_PREPROC_C_DEPEND='mwccsym2'
+
+ BUILDTOOL_MKDEP_C_COMMAND="\$PROG_mwccsym2_FILE -MM -ext .o -gccdep $EXTRA_WINSCW_FLAGS_COMPILE"
+ BUILDTOOL_MKDEP_C_DEPEND='mwccsym2'
+
+ BUILDTOOL_COMPILE_C_COMMAND="\$PROG_mwccsym2_FILE -c $EXTRA_WINSCW_FLAGS_COMPILE"
+ BUILDTOOL_COMPILE_C_DEPEND='mwccsym2'
+
+ BUILDTOOL_PREPROC_OBJC_COMMAND="\$PROG_mwccsym2_FILE -E"
+ BUILDTOOL_PREPROC_OBJC_DEPEND='mwccsym2'
+
+ BUILDTOOL_MKDEP_OBJC_COMMAND="\$PROG_mwccsym2_FILE -MM"
+ BUILDTOOL_MKDEP_OBJC_DEPEND='mwccsym2'
+
+ BUILDTOOL_COMPILE_OBJC_COMMAND="\$PROG_mwccsym2_FILE -c"
+ BUILDTOOL_COMPILE_OBJC_DEPEND='mwccsym2'
+
+ BUILDTOOL_LINK_COMMAND="\$PROG_mwldsym2_FILE $EXTRA_WINSCW_FLAGS_LINK"
+ BUILDTOOL_LINK_DEPEND='mwldsym2'
+ ;;
+ ARMV5)
+ EXTRA_ARMV5_FLAGS_COMPILE='--gnu --apcs //inter --diag_suppress 66,161,611,654,997,1152,1300,1464,1488,6318,6331 --diag_error 1267 --arm --cpu ARM926EJ-S --fpu softvfp --exceptions --exceptions_unwind -D__MARM_INTERWORK__ --enum_is_int -Ono_known_library --fpmode ieee_no_fenv --export_all_vtbl --no_vfe --dllimport_runtime -D_UNICODE -D__SYMBIAN32__ -D__SERIES60_30__ -D__SERIES60_3X__ -D__ARMCC__ -D__EPOC32__ -D__MARM__ -D__EABI__ -D__ARMCC_2__ -D__ARMCC_2_2__ -D__MARM_ARMV5__ -D__SUPPORT_CPP_EXCEPTIONS__ -J$BUILD_EPOCROOT/epoc32/include -J$BUILD_EPOCROOT/epoc32/include/stdapis -J$BUILD_EPOCROOT/epoc32/include/variant --preinclude $BUILD_EPOCROOT/epoc32/include/RVCT2_2/RVCT2_2.h'
+ EXTRA_ARMV5_FLAGS_LINK=''
+
+ BUILDTOOL_PREPROC_C_COMMAND="\$PROG_armcc_FILE -E $EXTRA_ARMV5_FLAGS_COMPILE"
+ BUILDTOOL_PREPROC_C_DEPEND='armcc'
+
+ BUILDTOOL_MKDEP_C_COMMAND="\$PROG_armcc_FILE -MM --unix_depend_format $EXTRA_ARMV5_FLAGS_COMPILE"
+ BUILDTOOL_MKDEP_C_DEPEND='armcc'
+
+ BUILDTOOL_COMPILE_C_COMMAND="\$PROG_armcc_FILE -c $EXTRA_ARMV5_FLAGS_COMPILE"
+ BUILDTOOL_COMPILE_C_DEPEND='armcc'
+
+ BUILDTOOL_PREPROC_OBJC_COMMAND="\$PROG_armcc_FILE -E"
+ BUILDTOOL_PREPROC_OBJC_DEPEND='armcc'
+
+ BUILDTOOL_MKDEP_OBJC_COMMAND="\$PROG_armcc_FILE -MM"
+ BUILDTOOL_MKDEP_OBJC_DEPEND='armcc'
+
+ BUILDTOOL_COMPILE_OBJC_COMMAND="\$PROG_armcc_FILE -c"
+ BUILDTOOL_COMPILE_OBJC_DEPEND='armcc'
+
+ BUILDTOOL_LINK_COMMAND="\$PROG_armar_FILE $EXTRA_ARMV5_FLAGS_LINK"
+ BUILDTOOL_LINK_DEPEND='armar'
+ ;;
+ GCCE)
+ EXTRA_GCCE_FLAGS_COMPILE='-Wno-unknown-pragmas -march=armv5t -mapcs -pipe -nostdinc -msoft-float -D_UNICODE -D__GCCE__ -D__SYMBIAN32__ -D__SERIES60_30__ -D__SERIES60_3X__ -D__GCCE__ -D__EPOC32__ -D__MARM__ -D__EABI__ -D__MARM_ARMV5__ -D __REMOVE_PLATSEC_DIAGNOSTIC_STRINGS__ -x c -include $BUILD_EPOCROOT/EPOC32/INCLUDE/GCCE/GCCE.h -I$BUILD_EPOCROOT/epoc32/include -I$BUILD_EPOCROOT/epoc32/include/stdapis -I$BUILD_EPOCROOT/epoc32/include/variant'
+ EXTRA_GCCE_FLAGS_LINK=''
+
+ BUILDTOOL_PREPROC_C_COMMAND="\$PROG_gcce_FILE -E $EXTRA_GCCE_FLAGS_COMPILE"
+ BUILDTOOL_PREPROC_C_DEPEND='gcce'
+
+ BUILDTOOL_MKDEP_C_COMMAND="\$PROG_gcce_FILE -MM $EXTRA_GCCE_FLAGS_COMPILE"
+ BUILDTOOL_MKDEP_C_DEPEND='gcce'
+
+ BUILDTOOL_COMPILE_C_COMMAND="\$PROG_gcce_FILE -c $EXTRA_GCCE_FLAGS_COMPILE"
+ BUILDTOOL_COMPILE_C_DEPEND='gcce'
+
+ BUILDTOOL_PREPROC_OBJC_COMMAND="\$PROG_gcce_FILE -E"
+ BUILDTOOL_PREPROC_OBJC_DEPEND='gcce'
+
+ BUILDTOOL_MKDEP_OBJC_COMMAND="\$PROG_gcce_FILE -MM"
+ BUILDTOOL_MKDEP_OBJC_DEPEND='gcce'
+
+ BUILDTOOL_COMPILE_OBJC_COMMAND="\$PROG_gcce_FILE -c"
+ BUILDTOOL_COMPILE_OBJC_DEPEND='gcce'
+
+ BUILDTOOL_LINK_COMMAND="\$PROG_gcce_FILE $EXTRA_GCCE_FLAGS_LINK"
+ BUILDTOOL_LINK_DEPEND='gcce'
+ ;;
+ *)
+ useGccBuildTools
+ ;;
+esac
+case "$HOST_SYSTEM" in
+ Darwin)
+ BUILDTOOL_REZ_NAME="MacOS X resource compiler (Rez)"
+ BUILDTOOL_REZ_COMMAND='$PROG_Rez_FILE'
+ BUILDTOOL_REZ_DEPEND='Rez'
+ ;;
+ MINGW32*|CYGWIN*|cegcc)
+ BUILDTOOL_WINDRES_NAME="Windows resource linker (windres)"
+ BUILDTOOL_WINDRES_COMMAND='$PROG_windres_FILE'
+ BUILDTOOL_WINDRES_DEPEND='windres'
+ ;;
+esac
+
+
+##############################################################################
+# Describe the programs (possibly) used: #
+##############################################################################
+
+
+### gcc ###
+PROG_gcc_NAME="GNU C compiler"
+PROG_gcc_FILE="gcc"
+PROG_gcc_ACTION=""
+PROG_gcc_VERSION='$(gcc --version)'
+
+
+### sed ###
+PROG_sed_NAME="Sed stream editor"
+PROG_sed_FILE="sed"
+PROG_sed_ACTION=""
+PROG_sed_VERSION=''
+
+
+### echo -n ###
+PROG_echon_NAME="'echo -n' capable echo"
+PROG_echon_FILE=""
+PROG_echon_ACTION=""
+PROG_echon_VERSION=''
+PROG_echon_DETECT="echon_detect"
+echon_detect() {
+ local TEST LOCATIONS LOCATION
+
+ # Default echo (probably builtin)
+ TEST=`echo -n X`
+ if [ "$TEST" = X ]; then
+ PROG_echon_FILE="echo -n"
+ return 0;
+ fi
+
+ # External echo
+ LOCATIONS="/bin/ /usr/ucb/"
+ for LOCATION in $LOCATIONS; do
+ if [ -x ${LOCATION}echo ]; then
+ TEST=`${LOCATION}echo -n X`
+ if [ "$TEST" = X ]; then
+ PROG_echon_FILE="${LOCATION}echo -n"
+ return 0;
+ fi
+ fi
+ done
+
+ # Using printf as echo
+ TEST=`printf %s X`
+ if [ "$TEST" = X ]; then
+ PROG_echon_FILE="printf %s"
+ return 0;
+ fi
+
+ # No good echo found
+ return 1
+}
+
+
+### GNU Make ###
+PROG_make_NAME="Make"
+case "$BUILD_SYSTEM" in
+ FreeBSD|OpenBSD|SunOS)
+ PROG_make_FILE="gmake"
+ ;;
+ *)
+ PROG_make_FILE="make"
+ ;;
+esac
+PROG_make_ACTION=""
+PROG_make_VERSION=''
+
+
+### tr ###
+PROG_tr_NAME="tr"
+PROG_tr_FILE="tr"
+PROG_tr_ACTION=""
+PROG_tr_VERSION=''
+
+
+### windres (for Windows) ###
+PROG_windres_NAME=windres
+PROG_windres_FILE=windres
+PROG_windres_ACTION=""
+PROG_windres_VERSION='windres --version'
+
+
+### Rez resource compiler (for MacOS X) ###
+PROG_Rez_NAME="Rez resource compiler (Apple Developer Tools)"
+PROG_Rez_FILE="/usr/bin/Rez"
+PROG_Rez_ACTION=""
+PROG_Rez_VERSION=''
+
+
+### pkg-config ###
+PROG_pkgconfig_NAME="pkg-config"
+PROG_pkgconfig_FILE="pkg-config"
+PROG_pkgconfig_ACTION=""
+PROG_pkgconfig_VERSION='$(pkg-config --version)'
+
+
+### mwccsym2 ###
+PROG_mwccsym2_NAME="Nokia CodeWarrior C/C++ Compiler for Windows/x86"
+PROG_mwccsym2_FILE="mwccsym2"
+PROG_mwccsym2_ACTION=""
+PROG_mwccsym2_VERSION='$(mwccsym2 -version)'
+
+
+### mwldsym2 ###
+PROG_mwldsym2_NAME="Nokia CodeWarrior Linker for Windows/x86"
+PROG_mwldsym2_FILE="mwldsym2"
+PROG_mwldsym2_ACTION=""
+PROG_mwldsym2_VERSION='$(mwldsym2 -version)'
+
+
+### armcc ###
+PROG_armcc_NAME="ARM/Thumb C/C++ Compiler"
+PROG_armcc_FILE="armcc"
+PROG_armcc_ACTION=""
+PROG_armcc_VERSION='$(armcc --vsn)'
+
+
+### armlink ###
+PROG_armlink_NAME="ARM Linker"
+PROG_armlink_FILE="armlink"
+PROG_armlink_ACTION=""
+PROG_armlink_VERSION='$(armlink --vsn)'
+
+
+### armar ###
+PROG_armar_NAME="ARM Archiver"
+PROG_armar_FILE="armar"
+PROG_armar_ACTION=""
+PROG_armar_VERSION='$(armar --vsn)'
+
+
+### gcce ###
+PROG_gcce_NAME="GNU C Compiler (CodeSourcery ARM)"
+PROG_gcce_FILE="arm-none-symbianelf-gcc"
+PROG_gcce_ACTION=""
+PROG_gcce_VERSION='$(arm-none-symbianelf-gcc --version)'
+
+
+### mingw-gcc ###
+PROG_mingw_gcc_NAME="GNU C MinGW Cross-compiler"
+PROG_mingw_gcc_FILE="i686-pc-mingw32-gcc"
+PROG_mingw_gcc_ACTION=""
+PROG_mingw_gcc_VERSION='$(i686-pc-mingw32-gcc --version)'
+
+
+##############################################################################
+# Describe the libaries (possibly) used: #
+##############################################################################
+
+
+
+##############################################################################
+# Describe the symbols (possibly) used: #
+##############################################################################
diff --git a/build/unix/config_proginfo_host b/build/unix/config_proginfo_host
new file mode 100644
index 0000000..748ffbd
--- /dev/null
+++ b/build/unix/config_proginfo_host
@@ -0,0 +1,356 @@
+# Set information on used programs and libraries
+# This file contains the information for the programs that test the
+# system on which the software will be run.
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+##############################################################################
+# System specific build flags #
+##############################################################################
+
+
+# CFLAGS
+SYSTEM_HOST_CFLAGS=""
+
+# LDFLAGS
+SYSTEM_HOST_LDFLAGS=""
+
+
+##############################################################################
+# Describe the programs (possibly) used: #
+##############################################################################
+
+
+
+##############################################################################
+# Describe the libaries (possibly) used: #
+##############################################################################
+
+
+### SDL ###
+LIB_SDL_NAME="Simple DirectMedia Layer"
+case "$HOST_SYSTEM" in
+ WINSCW|GCCE)
+ LIB_SDL_CFLAGS='-I$BUILD_EPOCROOT/epoc32/include/SDL'
+ LIB_SDL_LDFLAGS=''
+ LIB_SDL_VERSION='1.2.13'
+ LIB_SDL_DETECT="true"
+ ;;
+ ARMV5)
+ LIB_SDL_CFLAGS='-J$BUILD_EPOCROOT/epoc32/include/SDL'
+ LIB_SDL_LDFLAGS=''
+ LIB_SDL_VERSION='1.2.13'
+ LIB_SDL_DETECT="true"
+ ;;
+ Darwin)
+ if not have_framework SDL; then
+ LIB_SDL_DETECT="have_command sdl-config"
+ LIB_SDL_CFLAGS='$(sdl-config --cflags)'
+ LIB_SDL_LDFLAGS='$(sdl-config --libs)'
+ LIB_SDL_VERSION='$(sdl-config --version)'
+ fi
+ LIB_SDL_LDFLAGS="$LIB_SDL_LDFLAGS -lobjc -framework Cocoa"
+ ;;
+ *)
+ LIB_SDL_DETECT="have_command sdl-config"
+ LIB_SDL_CFLAGS='$(sdl-config --cflags)'
+ LIB_SDL_LDFLAGS='$(sdl-config --libs)'
+ LIB_SDL_VERSION='$(sdl-config --version)'
+ ;;
+esac
+
+
+### SDL2 ###
+LIB_SDL2_NAME="Simple DirectMedia Layer version 2.x"
+case "$HOST_SYSTEM" in
+ WINSCW|GCCE|ARMV5)
+ LIB_SDL2_DETECT="false"
+ ;;
+ Darwin)
+ if not have_framework SDL2; then
+ LIB_SDL2_DETECT="have_command sdl2-config"
+ LIB_SDL2_CFLAGS='$(sdl2-config --cflags)'
+ LIB_SDL2_LDFLAGS='$(sdl2-config --libs)'
+ LIB_SDL2_VERSION='$(sdl2-config --version)'
+ fi
+ LIB_SDL2_LDFLAGS="$LIB_SDL2_LDFLAGS -lobjc -framework Cocoa"
+ ;;
+ *)
+ LIB_SDL2_DETECT="have_command sdl2-config"
+ LIB_SDL2_CFLAGS='$(sdl2-config --cflags)'
+ LIB_SDL2_LDFLAGS='$(sdl2-config --libs)'
+ LIB_SDL2_VERSION='$(sdl2-config --version)'
+ ;;
+esac
+
+
+### libpng ###
+LIB_libpng_NAME="libpng"
+# libpng generally integrates with pkg-config, but that also generally
+# only matters if we're building against a non-systemwide version. UQM
+# should not need to do that.
+#
+# To link against a static libpng, set LDFLAGS with an appropriate -L
+# argument before calling `build.sh uqm config`.
+case "$HOST_SYSTEM" in
+ Darwin)
+ if not have_framework libpng; then
+ LIB_libpng_CFLAGS=""
+ LIB_libpng_LDFLAGS="-lpng"
+ fi
+ ;;
+ *)
+ LIB_libpng_CFLAGS=""
+ LIB_libpng_LDFLAGS="-lpng"
+ ;;
+esac
+
+
+### OpenAL ###
+LIB_openal_NAME="OpenAL"
+LIB_openal_CFLAGS=""
+case "$HOST_SYSTEM" in
+ FreeBSD|OpenBSD)
+ LIB_openal_LDFLAGS="-L/usr/local/lib -pthread -lopenal"
+ ;;
+ MINGW32*|CYGWIN*|cegcc)
+ LIB_openal_LDFLAGS="-lopenal32"
+ ;;
+ Darwin)
+ LIB_openal_CFLAGS=''
+ LIB_openal_LDFLAGS="-framework OpenAL"
+ ;;
+ *)
+ LIB_openal_LDFLAGS="-lopenal"
+ ;;
+esac
+LIB_openal_VERSION=""
+case "$HOST_SYSTEM" in
+ Darwin)
+ ## Apple does not ship pkg-config on Mac OSX
+ ;;
+ *)
+ LIB_openal_DETECT="try_pkgconfig_lib openal openal"
+ LIB_openal_DEPEND_DETECT_BIN="pkgconfig"
+ ;;
+esac
+
+
+### OpenGL ###
+LIB_opengl_NAME="OpenGL"
+case "$HOST_SYSTEM" in
+ FreeBSD|OpenBSD)
+ LIB_opengl_CFLAGS="-I/usr/X11R6/include -D_THREAD_SAFE"
+ LIB_opengl_LDFLAGS="-L/usr/X11R6/lib -lX11 -lXext -pthread -lGL"
+ ;;
+ MINGW32*|CYGWIN*|cegcc)
+ LIB_opengl_CFLAGS=""
+ LIB_opengl_LDFLAGS="-lopengl32"
+ ;;
+ Darwin)
+ LIB_opengl_CFLAGS=""
+ LIB_opengl_LDFLAGS="-framework OpenGL"
+ ;;
+ ARMV5|WINSCW|GCCE)
+ LIB_opengl_DETECT="false"
+ ;;
+ *)
+ LIB_opengl_CFLAGS=""
+ LIB_opengl_LDFLAGS="-lGL"
+ ;;
+esac
+LIB_opengl_VERSION=""
+
+
+### Vorbisfile ###
+LIB_vorbisfile_NAME="vorbisfile"
+case "$HOST_SYSTEM" in
+ FreeBSD|OpenBSD)
+ LIB_vorbisfile_CFLAGS="-I/usr/local/include"
+ LIB_vorbisfile_LDFLAGS="-L/usr/local/lib -lvorbisfile -lvorbis"
+ ;;
+ MINGW32*|CYGWIN*|cegcc)
+ LIB_vorbisfile_CFLAGS=""
+ LIB_vorbisfile_LDFLAGS="-lvorbisfile -lvorbis -lm -logg"
+ ;;
+ Darwin)
+ if not have_framework vorbisfile Vorbis; then
+ LIB_vorbisfile_CFLAGS="-D__MACOSX__"
+ LIB_vorbisfile_LDFLAGS="-lvorbisfile -lvorbis"
+ fi
+ ;;
+ QNX)
+ LIB_vorbisfile_CFLAGS=""
+ LIB_vorbisfile_LDFLAGS="-lvorbisfile -lvorbis -logg -lm"
+ ;;
+ *)
+ LIB_vorbisfile_CFLAGS=""
+ LIB_vorbisfile_LDFLAGS="-lvorbisfile -lvorbis"
+ ;;
+esac
+LIB_vorbisfile_VERSION=""
+case "$HOST_SYSTEM" in
+ ARMV5|WINSCW|GCCE)
+ LIB_vorbisfile_DETECT="false"
+ ;;
+ Darwin)
+ ## Apple does not ship pkg-config on Mac OSX
+ ;;
+ *)
+ LIB_vorbisfile_DETECT="try_pkgconfig_lib vorbisfile vorbisfile"
+ LIB_vorbisfile_DEPEND_DETECT_BIN="pkgconfig"
+ ;;
+esac
+
+
+### Tremor ###
+LIB_tremor_NAME="tremor"
+case "$HOST_SYSTEM" in
+ FreeBSD|OpenBSD)
+ LIB_tremor_CFLAGS="-I/usr/local/include"
+ LIB_tremor_LDFLAGS="-L/usr/local/lib -lvorbisidec"
+ ;;
+ Darwin)
+ if not have_framework tremor Tremor; then
+ # Assumed values - please let me know if you can verify this.
+ LIB_tremor_CFLAGS=""
+ LIB_tremor_LDFLAGS="-framework Tremor"
+ fi
+ ;;
+ ARMV5|WINSCW|GCCE)
+ LIB_tremor_DETECT="true"
+ ;;
+ *)
+ LIB_tremor_CFLAGS=""
+ LIB_tremor_LDFLAGS="-lvorbisidec"
+ ;;
+esac
+LIB_tremor_VERSION=""
+
+
+### libmikmod ###
+LIB_libmikmod_NAME="libmikmod"
+case "$HOST_SYSTEM" in
+ ARMV5|WINSCW|GCCE)
+ LIB_libmikmod_DETECT="false"
+ ;;
+ *)
+ LIB_libmikmod_DETECT="have_command libmikmod-config"
+ LIB_libmikmod_CFLAGS="$(libmikmod-config --cflags)"
+ LIB_libmikmod_LDFLAGS="$(libmikmod-config --libs)"
+ LIB_libmikmod_VERSION="$(libmikmod-config --version)"
+ ;;
+esac
+
+
+### zlib ###
+LIB_zlib_NAME="zlib"
+LIB_zlib_CFLAGS=""
+case "$HOST_SYSTEM" in
+ MINGW32*|CYGWIN*|cegcc)
+ LIB_zlib_LDFLAGS="-lzdll"
+ ;;
+ ARMV5|WINSCW|GCCE)
+ LIB_zlib_LDFLAGS=""
+ ;;
+ *)
+ LIB_zlib_LDFLAGS="-lz"
+ ;;
+esac
+LIB_zlib_VERSION=""
+case "$HOST_SYSTEM" in
+ ARMV5|WINSCW|GCCE)
+ LIB_zlib_DETECT="true"
+ ;;
+ Darwin)
+ ## Apple does not ship pkg-config on Mac OSX
+ ;;
+ *)
+ LIB_zlib_DETECT="try_pkgconfig_lib zlib zlib"
+ LIB_zlib_DEPEND_DETECT_BIN="pkgconfig"
+ ;;
+esac
+
+
+
+### pthread ###
+LIB_pthread_NAME="pthread"
+case "$HOST_SYSTEM" in
+ Linux)
+ LIB_pthread_CFLAGS=""
+ LIB_pthread_LDFLAGS=""
+ LIB_pthread_VERSION=""
+ ;;
+ FreeBSD|OpenBSD)
+ LIB_pthread_DETECT="have_command pthread-config"
+ LIB_pthread_CFLAGS="$(pthread-config --cflags)"
+ LIB_pthread_LDFLAGS="$(pthread-config --ldflags)"
+ LIB_pthread_VERSION="$(pthread-config --version)"
+ ;;
+ WINSCW|ARMV5|GCCE)
+ LIB_pthread_DETECT="true"
+ ;;
+ *)
+ LIB_pthread_CFLAGS=""
+ LIB_pthread_LDFLAGS="-lpthread"
+ LIB_pthread_VERSION=""
+ ;;
+esac
+
+
+# Additional platform-specific libraries for networking.
+LIB_netlibs_NAME="Platform-specific network libraries"
+case "$HOST_SYSTEM" in
+ SunOS)
+ LIB_netlibs_CFLAGS=""
+ LIB_netlibs_LDFLAGS="-lsocket"
+ LIB_netlibs_VERSION=""
+ ;;
+ ARMV5|WINSCW|GCCE)
+ LIB_netlibs_DETECT="false"
+ ;;
+esac
+
+
+##############################################################################
+# Describe the symbols (possibly) used: #
+##############################################################################
+
+HEADER_regex_EXTRA="#include <sys/types.h>"
+
+SYMBOL_readdir_r_EXTRA="#include <dirent.h>"
+
+SYMBOL_setenv_EXTRA="#include <stdlib.h>"
+
+SYMBOL_strcasecmp_EXTRA="#include <strings.h>"
+
+SYMBOL_strcasecmp_DEFNAME="HAVE_STRCASECMP_UQM"
+ # HAVE_STRCASECMP would conflict with SDL (SDL_config.h).
+
+SYMBOL_stricmp_EXTRA="#include <string.h>"
+
+SYMBOL_strupr_EXTRA="#include <string.h>"
+
+SYMBOL_wchar_t_EXTRA="#include <stddef.h>"
+
+SYMBOL_wint_t_EXTRA="#include <wchar.h>"
+
+SYMBOL_iswgraph_EXTRA="#include <wctype.h>"
+
+SYMBOL_getopt_long_EXTRA="#include <getopt.h>"
+
+
diff --git a/build/unix/make/buildtools-armv5 b/build/unix/make/buildtools-armv5
new file mode 100644
index 0000000..bf73689
--- /dev/null
+++ b/build/unix/make/buildtools-armv5
@@ -0,0 +1,17 @@
+# Definitions for build tools for the makefile used by the UQM build system.
+# This file defines the build commands for ARM tools.
+
+include build/unix/make/buildtools-generic
+
+define act_mkdep_c
+ $(MKDEP_C) $(CFLAGS) -o "$(@D)/$(<F).o" "$<" --depend "$@"
+endef
+
+define act_mkdep_cxx
+ $(MKDEP_CXX) $(CXXFLAGS) -o "$(@D)/$(<F).o" "$<" --depend "$@"
+endef
+
+define act_link
+ $(LINK) --create "$@" $^ $(LDFLAGS)
+endef
+
diff --git a/build/unix/make/buildtools-gcce b/build/unix/make/buildtools-gcce
new file mode 100644
index 0000000..2a5b6ca
--- /dev/null
+++ b/build/unix/make/buildtools-gcce
@@ -0,0 +1,8 @@
+# Definitions for build tools for the makefile used by the UQM build system.
+# This file defines the build commands for GCCE tools.
+
+include build/unix/make/buildtools-generic
+
+define act_link
+ arm-none-symbianelf-ar cr "$@" $^ $(LDFLAGS)
+endef
diff --git a/build/unix/make/buildtools-generic b/build/unix/make/buildtools-generic
new file mode 100644
index 0000000..b3ea464
--- /dev/null
+++ b/build/unix/make/buildtools-generic
@@ -0,0 +1,36 @@
+# Definitions for build tools for the makefile used by the UQM build system.
+# This file defines the build commands for tools using a gcc-like syntax
+# for arguments.
+
+define act_mkdep_c
+ $(MKDEP_C) $(CFLAGS) "$<" -MT "$(@D)/$(<F).o" -MF "$@"
+endef
+
+define act_mkdep_cxx
+ $(MKDEP_CXX) $(CXXFLAGS) "$<" -MT "$(@D)/$(<F).o" -MF "$@"
+endef
+
+define act_mkdep_m
+ $(MKDEP_OBJC) $(CFLAGS) "$<" -MT "$(@D)/$(<F).o" -MF "$@"
+endef
+
+define act_windres
+ $(WINDRES) --include-dir $(dir $<) -o "$@" "$<"
+endef
+
+define act_cc
+ $(COMPILE_C) -o "$@" $(CFLAGS) "$<"
+endef
+
+define act_cxx
+ $(COMPILE_CXX) -o "$@" $(CXXFLAGS) "$<"
+endef
+
+define act_objcc
+ $(COMPILE_OBJC) -o "$@" $(CFLAGS) "$<"
+endef
+
+define act_link
+ $(LINK) -o "$@" $^ $(LDFLAGS)
+endef
+
diff --git a/build/unix/make/buildtools-winscw b/build/unix/make/buildtools-winscw
new file mode 100644
index 0000000..4a2b6bc
--- /dev/null
+++ b/build/unix/make/buildtools-winscw
@@ -0,0 +1,19 @@
+# Definitions for build tools for the makefile used by the UQM build system.
+# This file defines the build commands for Nokia CodeWarrior tools.
+
+include build/unix/make/buildtools-generic
+
+define act_mkdep_c
+ $(MKDEP_C) $(CFLAGS) "$<" -o "$@.tmp"
+ @echo -n "$(@D)/" > $@
+ @cat "$@.tmp" >> $@
+ @rm -f "$@.tmp"
+endef
+
+define act_mkdep_cxx
+ $(MKDEP_C) $(CXXFLAGS) "$<" -o "$@.tmp"
+ @echo -n "$(@D)/" > $@
+ @cat "$@.tmp" >> $@
+ @rm -f "$@.tmp"
+endef
+
diff --git a/build/unix/menu_functions b/build/unix/menu_functions
new file mode 100644
index 0000000..8de5ff4
--- /dev/null
+++ b/build/unix/menu_functions
@@ -0,0 +1,662 @@
+# Auxiliary functions for menu system for custom build system
+# Copyright (c) 2002 Serge van den Boom
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+MENU_ITEM_TYPES="MENU CHOICE INPUT CHECK"
+
+MENU_ITEM_TYPE_MENU_INIT=menu_init_menu
+MENU_ITEM_TYPE_MENU_PRINT_VALUE=menu_print_value_menu
+MENU_ITEM_TYPE_MENU_HANDLER=menu_handle_menu
+MENU_ITEM_TYPE_MENU_SAVE=menu_save_menu
+MENU_ITEM_TYPE_MENU_PROCESS=menu_process_menu
+
+MENU_ITEM_TYPE_CHOICE_INIT=menu_init_choice
+MENU_ITEM_TYPE_CHOICE_PRINT_VALUE=menu_print_value_choice
+MENU_ITEM_TYPE_CHOICE_HANDLER=menu_handle_choice
+MENU_ITEM_TYPE_CHOICE_SAVE=menu_save_choice
+MENU_ITEM_TYPE_CHOICE_PROCESS=menu_process_choice
+
+MENU_ITEM_TYPE_INPUT_INIT=menu_init_input
+MENU_ITEM_TYPE_INPUT_PRINT_VALUE=menu_print_value_input
+MENU_ITEM_TYPE_INPUT_HANDLER=menu_handle_input
+MENU_ITEM_TYPE_INPUT_SAVE=menu_save_input
+MENU_ITEM_TYPE_INPUT_PROCESS=menu_process_input
+
+MENU_ITEM_TYPE_CHECK_INIT=menu_init_check
+MENU_ITEM_TYPE_CHECK_PRINT_VALUE=menu_print_value_check
+MENU_ITEM_TYPE_CHECK_HANDLER=menu_handle_check
+MENU_ITEM_TYPE_CHECK_SAVE=menu_save_check
+MENU_ITEM_TYPE_CHECK_PROCESS=menu_process_check
+
+# Description: Check if a string can be used as a path.
+# No checks are done if the path really exists.
+# Arguments: $1 - the string to check
+# Returns: 0 if the string makes a valid path
+# 1 if the string doesn't make a valid path
+# Outputs: the path, possibly modified
+validate_path() {
+ if [ "$1" = "/" ]; then
+ echo "/"
+ return
+ fi
+ cat << EOF
+${1%/}
+EOF
+ return 0
+}
+
+# Description: Initialise a menu, setting unset values to default
+# Arguments: $1 - the type of menu item
+# $2 - the name of the menu item
+menu_init() {
+ local INIT_FUN
+
+ eval INIT_FUN="\$MENU_ITEM_TYPE_$1_INIT"
+ "$INIT_FUN" "$2"
+}
+
+# Description: Initialise this menu
+# Arguments: $1 - the name of the menu
+menu_init_menu() {
+ local MENU TEMP_ITEMS ITEM TEMP_TYPE
+
+ MENU="$1"
+ eval TEMP_ITEMS="\$MENU_${MENU}_ITEMS"
+ for ITEM in $TEMP_ITEMS; do
+ eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
+ if [ -z "$TEMP_TYPE" ]; then
+ echo "Fatal: \$MENU_${MENU}_ITEM_${ITEM}_TYPE is not defined!" >&2
+ exit 1
+ fi
+ menu_init "$TEMP_TYPE" "$ITEM"
+ done
+}
+
+# Description: Check if this choice is available
+# Arguments: $1 - the name of the choice menu
+# $2 - the name of the choice
+# Returns: 0 - if the choice is available
+# 1 - if the choice is not available
+menu_have_choice() {
+ local MENU CHOICE TEMP_VALID TEMP_PRECOND
+ MENU="$1"
+ CHOICE="$2"
+ eval TEMP_VALID="\$CHOICE_${MENU}_OPTION_${CHOICE}_VALID"
+ if [ -n "$TEMP_VALID" ]; then
+ return "$TEMP_VALID"
+ fi
+ eval TEMP_PRECOND="\$CHOICE_${MENU}_OPTION_${CHOICE}_PRECOND"
+ if [ -z "$TEMP_PRECOND" ] || $TEMP_PRECOND; then
+ eval "CHOICE_${MENU}_OPTION_${CHOICE}_VALID"=0
+ return 0
+ fi
+ eval "CHOICE_${MENU}_OPTION_${CHOICE}_VALID"=1
+ return 1
+}
+
+# Description: Initialise this choice menu
+# Arguments: $1 - the name of the choice menu
+menu_init_choice() {
+ local MENU TEMP_VALUE TEMP_DEFAULT TEMP_OPTIONS OPTION TEMP_TITLE
+ MENU="$1"
+ eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"
+ eval TEMP_DEFAULT="\$CHOICE_${MENU}_DEFAULT"
+ eval TEMP_OPTIONS="\$CHOICE_${MENU}_OPTIONS"
+ for OPTION in $TEMP_VALUE $TEMP_DEFAULT $TEMP_OPTIONS; do
+ if menu_have_choice "$MENU" "$OPTION"; then
+ eval CHOICE_${MENU}_VALUE="$OPTION"
+ eval CHOICE_${MENU}_OLD_VALUE="$OPTION"
+ return
+ fi
+ done
+ eval TEMP_VALUE="\$CHOICE_${MENU}_TITLE"
+ echo "Error: No option for '$TEMP_VALUE' is available for your system."
+ exit 1
+}
+
+# Description: Initialise this input menu
+# Arguments: $1 - the name of the input menu
+menu_init_input() {
+ local MENU TEMP_VALUE
+ MENU="$1"
+ eval TEMP_VALUE="\$INPUT_${MENU}_VALUE"
+ if [ -z "$TEMP_VALUE" ]; then
+ eval TEMP_VALUE="\$INPUT_${MENU}_DEFAULT"
+ eval INPUT_${MENU}_VALUE="\$TEMP_VALUE"
+ fi
+ eval INPUT_${MENU}_OLD_VALUE="\$TEMP_VALUE"
+}
+
+# Description: Initialise this checkbox
+# Arguments: $1 - the name of the checkbox
+menu_init_check() {
+ local CHECKBOX TEMP_VALUE TEMP_DEFAULT
+ CHECKBOX="$1"
+
+ eval TEMP_VALUE="\$CHECK_${CHECKBOX}_VALUE"
+ eval TEMP_DEFAULT="\$CHECK_${CHECKBOX}_DEFAULT"
+
+ # Make sure the default is either 'CHECKED' or 'UNCHECKED'
+ case "$TEMP_DEFAULT" in
+ yes|Yes|YES|checked|Checked|CHECKED|1|true|True|TRUE)
+ TEMP_DEFAULT=CHECKED
+ ;;
+ *)
+ TEMP_DEFAULT=UNCHECKED
+ ;;
+ esac
+ eval CHECK_${CHECKBOX}_DEFAULT="\$TEMP_DEFAULT"
+
+ if [ -z "$TEMP_VALUE" ]; then
+ TEMP_VALUE="$TEMP_DEFAULT"
+ eval CHECK_${CHECKBOX}_VALUE="\$TEMP_DEFAULT"
+ fi
+ eval CHECK_${CHECKBOX}_OLD_VALUE="\$TEMP_VALUE"
+}
+
+# Description: Print the string describing the value of a menu item.
+# Arguments: $1 - the type of menu item
+# $2 - the name of the menu item
+# Outputs: The string describing the value of the menu item
+menu_print_value() {
+ local PRINT_VALUE
+
+ eval PRINT_VALUE="\$MENU_ITEM_TYPE_$1_PRINT_VALUE"
+ "$PRINT_VALUE" "$2"
+}
+
+# Description: Print the string describing the value of this menu
+# Arguments: $1 - the name of the menu item
+# Outputs: The string describing the value of this menu
+menu_print_value_menu() {
+ echo "[...]"
+}
+
+# Description: Print the string describing the value this choice menu
+# Arguments: $1 - the name of the choice menu item
+# Outputs: The string describing the value of this choice menu
+menu_print_value_choice() {
+ local TEMP_VALUE TEMP_TITLE
+ eval TEMP_VALUE="\$CHOICE_$1_VALUE"
+ eval TEMP_TITLE=\"\$CHOICE_$1_OPTION_${TEMP_VALUE}_TITLE\"
+ cat << EOF
+$TEMP_TITLE
+EOF
+}
+
+# Description: Print the value of this input menu
+# Arguments: $1 - the name of the input menu item
+# Outputs: The value of the given input menu
+menu_print_value_input() {
+ local TEMP_VALUE
+ eval TEMP_VALUE="\$INPUT_$1_VALUE"
+ cat << EOF
+$TEMP_VALUE
+EOF
+}
+
+# Description: Print the value of this checkbox
+# Arguments: $1 - the name of the checkbox item
+# Outputs: The value of the given checkbox
+menu_print_value_check() {
+ local TEMP_VALUE TEMP_DEFAULT TEMP_FIXED RESULT EXTRA
+ eval TEMP_VALUE="\$CHECK_$1_VALUE"
+ eval TEMP_DEFAULT="\$CHECK_$1_DEFAULT"
+ eval TEMP_FIXED="\$CHECK_$1_FIXED"
+
+ if [ "$TEMP_VALUE" = "CHECKED" ]; then
+ RESULT="Yes"
+ else
+ RESULT="No"
+ fi
+
+ EXTRA=""
+ if [ "$TEMP_VALUE" = "$TEMP_DEFAULT" ]; then
+ EXTRA="default"
+ fi
+
+ if [ "$TEMP_FIXED" = "TRUE" ]; then
+ EXTRA="${EXTRA:+$EXTRA, }unchangable"
+ fi
+
+ RESULT="$RESULT${EXTRA:+ ($EXTRA)}"
+ echo "$RESULT"
+}
+
+# Description: Print the string describing the value of a menu item.
+# Arguments: $1 - the type of menu item
+# $2 - the name of the menu item
+# Outputs: The string describing the value of the menu item
+menu_handle() {
+ local HANDLER
+
+ eval HANDLER="\$MENU_ITEM_TYPE_$1_HANDLER"
+ "$HANDLER" "$2"
+}
+
+# Description: Process a menu-type menu item
+# Arguments: $1 - The name of the menu
+menu_handle_menu() {
+ local TEMP_ITEMS I CHOICE NUM_ITEMS TEMP_TYPE MENU ITEM TEMP_TITLE \
+ TEMP_TEXT
+ eval TEMP_ITEMS="\$MENU_$1_ITEMS"
+ eval TEMP_TEXT="\$MENU_$1_TEXT"
+
+ MENU="$1"
+ while :; do
+ echo
+ eval TEMP_TITLE="\$MENU_${MENU}_TITLE"
+ cat << EOF
+ $ANSI_BOLD-= $TEMP_TITLE =-$ANSI_NORMAL
+EOF
+
+ if [ -n "$TEMP_TEXT" ]; then
+ cat << EOF
+$TEMP_TEXT
+EOF
+ fi
+
+ # Count the number of options
+ I=0
+ for ITEM in $TEMP_ITEMS; do
+ I=$(($I + 1))
+ done
+ NUM_ITEMS="$I"
+
+ I=0
+ for ITEM in $TEMP_ITEMS; do
+ I=$(($I + 1))
+ eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
+ eval TEMP_TITLE="\$${TEMP_TYPE}_${ITEM}_TITLE"
+ printf " %${#NUM_ITEMS}i. %-36s %s\n" $I "$TEMP_TITLE" \
+ "$(menu_print_value $TEMP_TYPE $ITEM)"
+ done
+
+ echo
+ echo "Press a number plus <ENTER> if you want to change something, "
+ $ECHON "or just <ENTER> if everything is ok: "
+ read CHOICE
+
+ # Check if the choice was empty
+ if [ -z "$CHOICE" ]; then
+ # We're done
+ return
+ fi
+
+ # Check if what the user entered was a number
+ egrep '^[0-9]+$' << EOF > /dev/null
+$CHOICE
+EOF
+ if [ $? -ne 0 ]; then
+ echo "Invalid choice."
+ continue
+ fi
+
+ # Check if the number the user entered if valid
+ if [ "$CHOICE" -lt 1 -o "$CHOICE" -gt "$NUM_ITEMS" ]; then
+ echo "Invalid choice."
+ continue
+ fi
+
+ # Now look up the choice
+ I=0
+ for ITEM in $TEMP_ITEMS; do
+ I=$(($I + 1))
+ if [ "$I" -eq "$CHOICE" ]; then
+ eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
+ menu_handle "$TEMP_TYPE" "$ITEM"
+ break
+ fi
+ done
+ done
+}
+
+# Description: Process a choice-type menu item
+# Arguments: $1 - The name of the menu
+menu_handle_choice() {
+ local TEMP_OPTIONS I CHOICE NUM_OPTIONS TEMP_TYPE MENU OPTION \
+ TEMP_VALUE TEMP_TITLE SELECTED TEMP_TEXT
+ eval TEMP_OPTIONS="\$CHOICE_$1_OPTIONS"
+ eval TEMP_TEXT="\$CHOICE_$1_TEXT"
+
+ MENU="$1"
+ while :; do
+ echo
+ eval TEMP_TITLE="\$CHOICE_${MENU}_TITLE"
+ cat << EOF
+ $ANSI_BOLD-= $TEMP_TITLE =-$ANSI_NORMAL
+EOF
+ if [ -n "$TEMP_TEXT" ]; then
+ cat << EOF
+$TEMP_TEXT
+EOF
+ fi
+
+ eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"
+
+ # Check in advance which options are present, so that that
+ # is echoed before the menu is printed.
+ # menu_have_choice caches results.
+ # Also, count the number of options
+ I=0
+ for OPTION in $TEMP_OPTIONS; do
+ I=$(($I + 1))
+ menu_have_choice "$MENU" "$OPTION"
+ done
+ NUM_OPTIONS="$I"
+
+ I=0
+ for OPTION in $TEMP_OPTIONS; do
+ I=$(($I + 1))
+ eval TEMP_TITLE="\$CHOICE_${MENU}_OPTION_${OPTION}_TITLE"
+ if [ "$TEMP_VALUE" = "$OPTION" ]; then
+ SELECTED="-->"
+ else
+ SELECTED=" "
+ fi
+ if menu_have_choice "$MENU" "$OPTION"; then
+ printf " %${#NUM_OPTIONS}i. %s %s\n" "$I" "$SELECTED" \
+ "$TEMP_TITLE"
+ else
+ printf " %-${#NUM_OPTIONS}s %s (N/A) %s\n" "-" \
+ "$SELECTED" "$TEMP_TITLE"
+ fi
+ done
+
+ echo
+ echo "Select the option you want by typing a number plus <ENTER>"
+ $ECHON "or just <ENTER> if everything is ok: "
+ read CHOICE
+ echo
+
+ # Check if the choice was empty
+ if [ -z "$CHOICE" ]; then
+ # We're done
+ return
+ fi
+
+ # Check if what the user entered was a number
+ egrep '^[0-9]+$' << EOF > /dev/null
+$CHOICE
+EOF
+ if [ $? -ne 0 ]; then
+ echo "Invalid choice."
+ continue
+ fi
+
+ # Check if the number the user entered if valid
+ if [ "$CHOICE" -lt 1 -o "$CHOICE" -gt "$NUM_ITEMS" ]; then
+ echo "Invalid choice."
+ continue
+ fi
+
+ # Now look up the choice
+ I=0
+ for OPTION in $TEMP_OPTIONS; do
+ I=$(($I + 1))
+ if [ "$I" -eq "$CHOICE" ]; then
+ if menu_have_choice "$MENU" "$OPTION"; then
+ eval "CHOICE_${MENU}_VALUE"="$OPTION"
+ return
+ else
+ echo "That option is unavailable on your system."
+ fi
+ fi
+ done
+ done
+}
+
+# Description: Process an input-type menu item
+# Arguments: $1 - The name of the menu
+menu_handle_input() {
+ local ITEM TEMP_TITLE TEMP_VALUE TEMP_DEFAULT NEW_VALUE \
+ TEMP_VALIDATOR TEMP_TEXT
+
+ ITEM="$1"
+ eval TEMP_TITLE="\$INPUT_${ITEM}_TITLE"
+ eval TEMP_TEXT="\$INPUT_${ITEM}_TEXT"
+
+ while :; do
+ echo
+ cat << EOF
+ $ANSI_BOLD-= $TEMP_TITLE =-$ANSI_NORMAL
+EOF
+
+ if [ -n "$TEMP_TEXT" ]; then
+ cat << EOF
+$TEMP_TEXT
+EOF
+ fi
+
+ eval TEMP_VALUE="\$INPUT_${ITEM}_VALUE"
+ eval TEMP_DEFAULT="\$INPUT_${ITEM}_DEFAULT"
+ echo " Default value: $TEMP_DEFAULT"
+ echo " Current value: $TEMP_VALUE"
+ $ECHON " New value: "
+ read NEW_VALUE
+
+ # If no new value is entered, keep the old one.
+ if [ -z "$NEW_VALUE" ]; then
+ return
+ fi
+
+ # If a validator function is present, validate the new value
+ eval TEMP_VALIDATOR="\$INPUT_${ITEM}_VALIDATOR"
+ if [ -n "$TEMP_VALIDATOR" ]; then
+ NEW_VALUE=`$TEMP_VALIDATOR "$NEW_VALUE"`
+ if [ $? -ne 0 ]; then
+ echo "Invalid value"
+ continue
+ fi
+ fi
+ break
+ done
+ eval "INPUT_${ITEM}_VALUE"=\"\$NEW_VALUE\"
+}
+
+# Description: Process an checkbox-type menu item
+# Arguments: $1 - The name of the checkbox
+menu_handle_check() {
+ local CHECKBOX OLD_VALUE NEW_VALUE TEMP_FIXED
+
+ CHECKBOX="$1"
+ eval TEMP_FIXED="\$CHECK_${CHECKBOX}_FIXED"
+
+ if [ "$TEMP_FIXED" = "TRUE" ]; then
+ # Unchangable
+ return;
+ fi
+
+ eval OLD_VALUE="\$CHECK_${CHECKBOX}_VALUE"
+ if [ "$OLD_VALUE" = "CHECKED" ]; then
+ NEW_VALUE="UNCHECKED"
+ else
+ NEW_VALUE="CHECKED"
+ fi
+ eval "CHECK_${CHECKBOX}_VALUE"=\"\$NEW_VALUE\"
+}
+
+# Description: echo the current state of a menu item in a form that can be
+# executed to restore the state.
+# Arguments: $1 - the type of menu item
+# $2 - the name of the menu item
+# Outputs: The string describing the value of the menu item
+menu_save() {
+ local SAVE_FUN
+ eval SAVE_FUN="\$MENU_ITEM_TYPE_$1_SAVE"
+ "$SAVE_FUN" "$2"
+}
+
+# Description: echo the current state of a menu in a form that can be
+# executed to restore the state.
+# Arguments: $1 - the name of the menu
+# Outputs: The string describing the value of the menu
+menu_save_menu() {
+ local MENU TEMP_ITEMS ITEM TEMP_TYPE
+
+ MENU="$1"
+ eval TEMP_ITEMS="\$MENU_${MENU}_ITEMS"
+ for ITEM in $TEMP_ITEMS; do
+ eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
+ menu_save "$TEMP_TYPE" "$ITEM"
+ done
+}
+
+# Description: echo the current state of a choice menu in a form that can be
+# executed to restore the state.
+# Arguments: $1 - the name of the choice menu
+# Outputs: The string describing the value of the choice menu
+menu_save_choice() {
+ local MENU TEMP_VALUE
+ MENU="$1"
+ eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"
+ cat << EOF
+CHOICE_${MENU}_VALUE='$TEMP_VALUE'
+EOF
+}
+
+# Description: echo the current state of an input menu in a form that can be
+# executed to restore the state.
+# Arguments: $1 - the name of the input menu
+# Outputs: The string describing the value of the input menu
+menu_save_input() {
+ local MENU TEMP_VALUE
+ MENU="$1"
+ eval TEMP_VALUE="\$INPUT_${MENU}_VALUE"
+ cat << EOF
+INPUT_${MENU}_VALUE='$TEMP_VALUE'
+EOF
+}
+
+# Description: echo the current state of an check box in a form that can be
+# executed to restore the state.
+# Arguments: $1 - the name of the checkbox
+# Outputs: The string describing the value of the checkbox
+menu_save_check() {
+ local CHECKBOX TEMP_VALUE
+ CHECKBOX="$1"
+ eval TEMP_VALUE="\$CHECK_${CHECKBOX}_VALUE"
+ cat << EOF
+CHECK_${CHECKBOX}_VALUE='$TEMP_VALUE'
+EOF
+}
+
+# Description: Perform the actions associated with the choice made for a
+# menu items.
+# Arguments: $1 - the type of menu item
+# $2 - the name of the menu item
+menu_process() {
+ local PROCESS_FUN
+ eval PROCESS_FUN="\$MENU_ITEM_TYPE_$1_PROCESS"
+ "$PROCESS_FUN" "$2"
+}
+
+# Description: Perform the actions associated with the chosen menu items
+# for a menu.
+# Arguments: $1 - the name of the menu
+menu_process_menu() {
+ local MENU TEMP_ITEMS ITEM TEMP_TYPE
+
+ MENU="$1"
+ eval TEMP_ITEMS="\$MENU_${MENU}_ITEMS"
+ for ITEM in $TEMP_ITEMS; do
+ eval TEMP_TYPE="\$MENU_${MENU}_ITEM_${ITEM}_TYPE"
+ menu_process "$TEMP_TYPE" "$ITEM"
+ done
+}
+
+# Description: Perform the actions associated with the choice made for
+# a choice menu.
+# Arguments: $1 - the name of the choice menu
+menu_process_choice() {
+ local MENU TEMP_VALUE TEMP_ACTION
+ MENU="$1"
+ eval TEMP_VALUE="\$CHOICE_${MENU}_VALUE"
+ eval TEMP_ACTION="\$CHOICE_${MENU}_OPTION_${TEMP_VALUE}_ACTION"
+ if [ -n "$TEMP_ACTION" ]; then
+ $TEMP_ACTION
+ fi
+}
+
+# Description: Perform the actions associated with the input menu.
+# Arguments: $1 - the name of the input menu
+menu_process_input() {
+ # Nothing to do
+ :
+}
+
+# Description: Perform the actions associated with the status of a
+# check box.
+# Arguments: $1 - the name of the check box
+menu_process_check() {
+ local CHECKBOX TEMP_VALUE TEMP_ACTION
+ CHECKBOX="$1"
+ eval TEMP_VALUE="\$CHECK_${CHECKBOX}_VALUE"
+ eval TEMP_ACTION="\$CHECK_${CHECKBOX}_OPTION_${TEMP_VALUE}_ACTION"
+ if [ -n "$TEMP_ACTION" ]; then
+ $TEMP_ACTION
+ fi
+}
+
+# Description: Start processing a menu
+# Arguments: $1 - the type of the main menu
+# $2 - the name of the main menu
+do_menu() {
+ local MENU_TYPE START_MENU SAVE_FILE
+
+ MENU_TYPE=$1
+ START_MENU=$2
+
+ menu_init "$MENU_TYPE" "$START_MENU"
+ menu_handle "$MENU_TYPE" "$START_MENU"
+ echo
+}
+
+# Description: Load the menu settings from file
+# Arguments: $1 - the type of the menu (currently ignored)
+# $2 - the name of the menu (currently ignored)
+# $3 - the name of the file to load from
+# Returns: 0 - if the file was loaded successfully
+# 1 - if the file did not exist
+do_menu_load() {
+ SAVE_FILE=$3
+
+ if [ ! -e "$SAVE_FILE" ]; then
+ return 1
+ fi
+
+ . "$SAVE_FILE"
+ return 0
+}
+
+# Description: Save the menu settings to file
+# Arguments: $1 - the type of the menu
+# $2 - the name of the menu
+# $3 - the name of the file to save to
+# Returns: 0 - if the file was saved successfully
+do_menu_save() {
+ MENU_TYPE=$1
+ START_MENU=$2
+ SAVE_FILE=$3
+
+ echo "Saving choices..."
+ menu_save "$MENU_TYPE" "$START_MENU" > "$SAVE_FILE"
+ return 0
+}
+
+
+
+
diff --git a/build/unix/recurse b/build/unix/recurse
new file mode 100755
index 0000000..0d39efa
--- /dev/null
+++ b/build/unix/recurse
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+# Generic code for traversing the source code directory structure according to
+# the Makeinfo files.
+
+usage() {
+ cat << EOF
+Usage:
+ recurse <project> <root>
+
+With the parameters:
+ <project>
+ The name identifying the project, used as prefix in variables in
+ 'Makeproject' and 'Makeinfo'.
+
+ <root>
+ The root directory, containing 'Makeproject'.
+EOF
+}
+
+if [ $# -ne 2 ]; then
+ usage >&2
+ exit 1
+fi
+
+BUILD_PROJECT=$1
+BUILD_ROOT=${2%/}/
+
+# $1 - The prefix up to this point
+# $2 - The name of the current subdir
+recurse_subdir() {
+ local REC_PREFIX SUBDIRS SUBDIR CFILES CXXFILES HFILES MFILES RCFILES FILE
+ eval local ${BUILD_PROJECT}_CFILES \
+ ${BUILD_PROJECT}_CXXFILES \
+ ${BUILD_PROJECT}_HFILES \
+ ${BUILD_PROJECT}_MFILES \
+ ${BUILD_PROJECT}_RCFILES \
+ ${BUILD_PROJECT}_SUBDIRS
+
+ REC_PREFIX="$1$2/"
+ if [ "$REC_PREFIX" = "/" ]; then
+ REC_PREFIX=
+ fi
+
+ eval ${BUILD_PROJECT}_CFILES=
+ eval ${BUILD_PROJECT}_CXXFILES=
+ eval ${BUILD_PROJECT}_HFILES=
+ eval ${BUILD_PROJECT}_MFILES=
+ eval ${BUILD_PROJECT}_RCFILES=
+ eval ${BUILD_PROJECT}_SUBDIRS=
+
+ . "${BUILD_ROOT}${REC_PREFIX}Makeinfo"
+
+ eval CFILES=\$${BUILD_PROJECT}_CFILES
+ eval CXXFILES=\$${BUILD_PROJECT}_CXXFILES
+ eval HFILES=\$${BUILD_PROJECT}_HFILES
+ eval MFILES=\$${BUILD_PROJECT}_MFILES
+ eval RCFILES=\$${BUILD_PROJECT}_RCFILES
+
+ for FILE in $CFILES; do
+ echo "C ${OBJDIR}${REC_PREFIX}$FILE"
+ done
+ for FILE in $CXXFILES; do
+ echo "CXX ${OBJDIR}${REC_PREFIX}$FILE"
+ done
+ for FILE in $HFILES; do
+ echo "H ${OBJDIR}${REC_PREFIX}$FILE"
+ done
+ for FILE in $MFILES; do
+ echo "M ${OBJDIR}${REC_PREFIX}$FILE"
+ done
+ for FILE in $RCFILES; do
+ echo "RC ${OBJDIR}${REC_PREFIX}$FILE"
+ done
+
+ eval SUBDIRS=\$${BUILD_PROJECT}_SUBDIRS
+
+ for SUBDIR in $SUBDIRS; do
+ echo "DIRIN ${OBJDIR}${REC_PREFIX}$SUBDIR"
+ recurse_subdir "$REC_PREFIX" "$SUBDIR"
+ echo "DIROUT ${OBJDIR}${REC_PREFIX}$SUBDIR"
+ done
+}
+
+. "${BUILD_ROOT}Makeproject"
+
+recurse_subdir "" ""
+
diff --git a/build/unix/todo b/build/unix/todo
new file mode 100644
index 0000000..d156e61
--- /dev/null
+++ b/build/unix/todo
@@ -0,0 +1,15 @@
+- flag for an option to set if changing it will trigger a new 'make depend'
+- same for 'make clean'
+- Specify files to be removed on clean.
+- static compilationa (?)
+- Let the dependency index depend on the Makeinfo files
+ (What about the internal dependencies among Makeinfo)
+
+some docs
+- uqm_LDFLAGS etc are only relevant in the top dir
+- some variables are 'inherited' from parent dir
+ update: this is not true anymore. The behavior was not needed and
+ would just complicate things.
+- files are sourced (.)
+
+
diff --git a/build/unix/uqm-wrapper.in b/build/unix/uqm-wrapper.in
new file mode 100755
index 0000000..262f7f4
--- /dev/null
+++ b/build/unix/uqm-wrapper.in
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Wrapper script for starting The Ur-Quan Masters
+exec "@INSTALL_LIBDIR@uqm/uqm" "--contentdir=@INSTALL_SHAREDIR@uqm/content" "$@"
+
diff --git a/build/unix_installer/README b/build/unix_installer/README
new file mode 100644
index 0000000..23122d3
--- /dev/null
+++ b/build/unix_installer/README
@@ -0,0 +1,3 @@
+This directory contains files pertaining to generating self-extracting
+installer files for unix-like platforms.
+
diff --git a/build/unix_installer/USAGE b/build/unix_installer/USAGE
new file mode 100644
index 0000000..c73ca8c
--- /dev/null
+++ b/build/unix_installer/USAGE
@@ -0,0 +1,14 @@
+Making a release binary:
+
+- Build the game in release mode.
+
+- Change build/unix_installer/template to suit your needs.
+
+- From the 'sc2' dir of the svn tree, run the following command, changing
+ the arguments if necessary.
+ The first one is the name of the final executable, the second
+ the template to use.
+
+ build/unix_installer/buildinstaller.sh uqm-0.2-linux-dynamic.sh \
+ "build/unix_installer/template"
+
diff --git a/build/unix_installer/buildinstaller.sh b/build/unix_installer/buildinstaller.sh
new file mode 100755
index 0000000..1a14aac
--- /dev/null
+++ b/build/unix_installer/buildinstaller.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+# Script for creating self-extracting installer files for unix-like systems.
+# By Serge van den Boom, 2003-02-20
+
+TEMPDIR="/tmp/buildinstaller_$$"
+
+if [ $# -ne 2 ]; then
+ cat >&2 << EOF
+Usage: buildinstaller.sh <installername> <template>
+where 'installername' is the name you want to give the final installer
+and 'template' is the template file describing the installer.
+EOF
+ exit 1
+fi
+DESTFILE="$1"
+TEMPLATE="$2"
+
+if [ ! -d src -o ! -d build ]; then
+cat >&2 << EOF
+Error: The current directory should be the top of the cvs tree.
+ Please try again from that dir
+EOF
+ exit 1
+fi
+
+if [ ! -f uqm ]; then
+ cat >&2 << EOF
+Error: There should be an 'uqm' binary in the top of the cvs tree.
+ please recompile and try again.
+EOF
+ exit 1
+fi
+
+mkdir "$TEMPDIR" 2> /dev/null
+
+if [ "$?" -ne 0 ]; then
+ echo "Could not create temporary dir." >&2
+ exit 1
+fi
+
+. "$TEMPLATE"
+
+while read FILE DEST; do
+ DESTDIR="${TEMPDIR}/attach/${DEST%/*}"
+ if [ ! -d "$DESTDIR" ]; then
+ mkdir -p -- "$DESTDIR" || exit 1
+ fi
+ cp -- "$FILE" "${TEMPDIR}/attach/$DEST" || exit 1
+done << EOF
+$UQM_ATTACH_FILES
+EOF
+
+chmod -R go+rX "${TEMPDIR}/attach"
+
+echo "Making tar.gz file for everything except from the content."
+echo "Using maximum compression; this may take a moment."
+tar -c -C "${TEMPDIR}/attach/" -f - . | gzip -9 > "${TEMPDIR}/attach.tar.gz"
+
+{
+ cat << EOF
+UQM_VERSION="$UQM_VERSION"
+UQM_PACKAGES="$UQM_PACKAGES"
+EOF
+ for PACKAGE in $UQM_PACKAGES; do
+ eval PACKAGE_NAME="\$UQM_PACKAGE_${PACKAGE}_NAME"
+ eval PACKAGE_TITLE="\$UQM_PACKAGE_${PACKAGE}_TITLE"
+ eval PACKAGE_LOCATION="\$UQM_PACKAGE_${PACKAGE}_LOCATION"
+ eval PACKAGE_OPTIONAL="\$UQM_PACKAGE_${PACKAGE}_OPTIONAL"
+ eval PACKAGE_DEFAULT="\$UQM_PACKAGE_${PACKAGE}_DEFAULT"
+ cat << EOF
+UQM_PACKAGE_${PACKAGE}_NAME="$PACKAGE_NAME"
+UQM_PACKAGE_${PACKAGE}_TITLE="$PACKAGE_TITLE"
+UQM_PACKAGE_${PACKAGE}_LOCATION="$PACKAGE_LOCATION"
+UQM_PACKAGE_${PACKAGE}_OPTIONAL="$PACKAGE_OPTIONAL"
+UQM_PACKAGE_${PACKAGE}_DEFAULT="$PACKAGE_DEFAULT"
+EOF
+ done
+} > "${TEMPDIR}/packages"
+
+# A slow way, but a reliable way.
+ATTACHLEN=`wc -c < "${TEMPDIR}/attach.tar.gz"`
+
+LICENSE_TEXT="$(cat $UQM_LICENSE_FILE)"
+
+# Now we've got all the parts, we can make the final .sh file.
+echo "Making final executable."
+{
+ for SCRIPT in ${TEMPDIR}/packages $UQM_SCRIPT_FILES; do
+ echo "# --- Start ${SCRIPT##*/} ---"
+ # Very ugly, but I can't get sed to replace a pattern by the contents
+ # of a file.
+ FILEDATA="$(cat $SCRIPT)"
+ sed -e "s/@ATTACHLEN@/$ATTACHLEN/" \
+ -e "s/@CONTENT_FILES@/$CONTENT_FILES/" << EOF
+${FILEDATA/@LICENCE@/$LICENSE_TEXT}
+EOF
+ echo "# --- End ${SCRIPT##*/} ---"
+ done
+ cat "${TEMPDIR}/attach.tar.gz"
+} > "$DESTFILE"
+
+chmod 755 "$DESTFILE"
+
+rm -r -- "$TEMPDIR"
+
+echo "Done."
+
diff --git a/build/unix_installer/copy_mac_frameworks.pl b/build/unix_installer/copy_mac_frameworks.pl
new file mode 100755
index 0000000..8ced8f0
--- /dev/null
+++ b/build/unix_installer/copy_mac_frameworks.pl
@@ -0,0 +1,128 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use File::Basename;
+use Cwd 'abs_path';
+
+my @frameworks = ( 'Ogg', 'Vorbis', 'libpng', 'SDL2' );
+my $target = 'The Ur-Quan Masters';
+
+create_app_bundle_skeleton($target);
+copy_with_version("src/res/darwin/Info.plist", "$target.app/Contents/Info.plist");
+copy_file("src/res/darwin/PkgInfo", "$target.app/Contents");
+copy_file("src/res/darwin/The Ur-Quan Masters.icns", "$target.app/Contents/Resources");
+copy_file("content/version", "$target.app/Contents/Resources/content");
+opendir my $packagedir, "dist-packages/" or die "Could not open dist-packages directory";
+while (readdir $packagedir) {
+ if (/\.uqm$/) {
+ copy_file("dist-packages/$_", "$target.app/Contents/Resources/content/packages");
+ }
+}
+copy_file("uqm", "$target.app/Contents/MacOS/$target");
+copy_frameworks($target, @frameworks);
+relink_frameworks($target, @frameworks);
+
+
+sub create_app_bundle_skeleton {
+ my $target = shift;
+ mkdir("$target.app", 0755);
+ mkdir("$target.app/Contents", 0755);
+ mkdir("$target.app/Contents/Frameworks", 0755);
+ mkdir("$target.app/Contents/MacOS", 0755);
+ mkdir("$target.app/Contents/Resources", 0755);
+ mkdir("$target.app/Contents/Resources/content", 0755);
+ mkdir("$target.app/Contents/Resources/content/addons", 0755);
+ mkdir("$target.app/Contents/Resources/content/packages", 0755);
+}
+
+
+sub copy_frameworks {
+ my $target = shift;
+ foreach my $fw (@_) {
+ my $src = "/Library/Frameworks/$fw.framework";
+ my $dest = "$target.app/Contents/Frameworks/$fw.framework";
+ system("ditto \"$src\" \"$dest\"");
+ }
+}
+
+sub resolve_symlinks {
+ my $file = shift;
+ my $origFile = $file;
+ my $startWd = `pwd`;
+ chomp($startWd);
+
+ while (-l $file) {
+ my $nextPath = readlink($file);
+ my $origDir = dirname($file);
+ chdir($origDir);
+ $file = abs_path($nextPath);
+ }
+ chdir($startWd);
+ return $file;
+}
+
+sub relink_frameworks {
+ my $target = shift;
+ my @frameworks = @_;
+ my $execfile = "$target.app/Contents/MacOS/$target";
+
+ foreach my $fw (@frameworks) {
+ my $src = resolve_symlinks("$target.app/Contents/Frameworks/$fw.framework/$fw");
+ my $oldfwid = `otool -L '$src' | head -n 2 | tail -n 1`;
+ $oldfwid =~ s/^\s+//;
+ $oldfwid =~ s/\s.*$//g;
+ my $newfwid = $oldfwid;
+ $newfwid =~ s/^.*\/$fw.framework/\@executable_path\/..\/Frameworks\/$fw.framework/;
+
+ system("install_name_tool -id $newfwid \"$src\"");
+ foreach my $fw2 (@frameworks) {
+ next if $fw eq $fw2;
+ my $src2 = resolve_symlinks("$target.app/Contents/Frameworks/$fw2.framework/$fw2");
+ system("install_name_tool -change $oldfwid $newfwid \"$src2\"");
+ }
+ system("install_name_tool -change $oldfwid $newfwid \"$execfile\"");
+ }
+}
+
+
+sub get_uqm_version {
+ open(my $versionheader, "<", "src/uqmversion.h") or die "Could not find version file";
+ my $major_version = 0;
+ my $minor_version = 0;
+ my $patch_version = 0;
+ while (<$versionheader>) {
+ if (/^\s*#define\s+UQM_MAJOR_VERSION\s+(\d+)/) {
+ $major_version = $1;
+ }
+ if (/^\s*#define\s+UQM_MINOR_VERSION\s+(\d+)/) {
+ $minor_version = $1;
+ }
+ if (/^\s*#define\s+UQM_PATCH_VERSION\s+(\d+)/) {
+ $patch_version = $1;
+ }
+ }
+ close($versionheader) or warn "Could not close version file";
+ return "${major_version}.${minor_version}.${patch_version}";
+}
+
+
+sub copy_file {
+ my $src = shift;
+ my $dest = shift;
+ system("cp \"$src\" \"$dest\"");
+}
+
+sub copy_with_version {
+ my ($src, $dest) = @_;
+ my $uqmversion = get_uqm_version();
+ open (SRCFILE, "<", $src) or die "Could not open source file '$src'";
+ open (DESTFILE, ">", $dest) or die "Could not open destination file '$dest'";
+ while (<SRCFILE>) {
+ s/\@\@VERSION\@\@/$uqmversion/g;
+ print DESTFILE;
+ }
+ close(DESTFILE) or warn "Could not close destination file";
+ close(SRCFILE) or warn "Could not close source file";
+}
diff --git a/build/unix_installer/install.sh.in b/build/unix_installer/install.sh.in
new file mode 100644
index 0000000..d89ba03
--- /dev/null
+++ b/build/unix_installer/install.sh.in
@@ -0,0 +1,277 @@
+#!/bin/sh
+# Installation program for binaries.
+# By Serge van den Boom, 2002-11-26
+
+quithandler() {
+ echo
+ echo "Bye."
+ trap INT
+ kill -INT $$
+}
+
+trap quithandler INT
+
+umask 022
+
+cat << EOF
+-== The Ur-Quan Masters installation ==-
+
+Hi, I'm your friendly neighbourhood installation program.
+I will make you very happy, but first, we've got some business to take
+care of.
+
+EOF
+
+PAGER="${PAGER:-more}"
+type "$PAGER" > /dev/null 2>&1
+if [ "$?" -ne 0 ]; then
+ cat << EOF
+I can't find a good pager. I need one. Sorry. Here's your prompt back.
+EOF
+ exit 1
+fi
+
+type unzip > /dev/null 2>&1
+if [ "$?" -ne 0 ]; then
+ cat << EOF
+I need 'unzip' for unzipping things. You don't seem to have it.
+That means no unzipping for me. And no The Ur-Quan Masters for you.
+And that's really not acceptable, is it? So I hope you'll install it
+soon. That way we'll both be happy.
+EOF
+ exit 1
+fi
+
+echo "I need to show you something. I don't want to, but I have to."
+echo "Press enter when you're ready."
+read TEMP
+
+$PAGER << "EOF"
+@LICENCE@
+EOF
+echo "That's hard reading, isn't it? But it's pretty important stuff. "
+echo -n "Now, the big question is, do you agree to it? "
+while :; do
+ read TEMP
+ case "$TEMP" in
+ [yY][eE][sS]|[aA][gG][rR][eE][eE])
+ echo "Good. Now we can be friends."
+ break
+ ;;
+ [nN][oO])
+ cat << EOF
+No? You're sure you didn't mean 'yes'? You do realise I can't let you pass
+now? Oh well, come back if you change your mind. I'm not going anywhere.
+I AM not going anywere, right?
+EOF
+ exit 0
+ ;;
+ *)
+ echo -n "Hmm... Please type 'Yes' or 'No': "
+ ;;
+ esac
+done
+echo
+
+cat << EOF
+Now I need somewhere to put all those Ur-Quan Masters files.
+My invisible friend tells me $INPUT_install_prefix_DEFAULT is a good place.
+I would put an executable there in a bin/ dir and a lot of junk together
+in a subdir under share/ and a little bit in a subdir under lib/
+But you're the boss, so where shall I put them?"
+EOF
+while :; do
+ show_install_prefix_menu
+ echo
+
+ PREFIX="$INPUT_install_prefix_VALUE"
+ if [ "$PREFIX" = "/dev/null" ]; then
+ echo "Ok, done. That was quick, wasn't it? Well, have fun."
+ echo
+ exit 0
+ fi
+
+ if [ ! -d "$PREFIX" ]; then
+cat << EOF
+There's no such directory. If you want me to stuff it all in /dev/null,
+just say so. No need to toy with me.
+Then again, I could create that directory for you.
+EOF
+ echo -n "Would you like that? "
+
+ read TEMP2
+ echo
+ case "$TEMP2" in
+ y|Y|yes|Yes|YES)
+ mkdir -p "$PREFIX" 2> /dev/null
+ if [ "$?" -gt 0 ]; then
+ cat << EOF
+I cannot do it, captain! I don't have enough power!
+Erm... I mean 'permission denied'. Sorry about that.
+I guess we'll need to find another place.
+EOF
+ echo "So what shall it be?"
+ else
+ cat << EOF
+Ok, I've created that dir for you, even though it isn't in my job
+description. Now let's get on with the copying.
+EOF
+ break;
+ fi
+ ;;
+ n|N|no|No|NO)
+ echo "Great! That saves me some work."
+ echo "Then where DO you want me to put my files?"
+ echo "I promise I'll handle them with care."
+ ;;
+ *)
+ echo "I'll take that as a 'no'."
+ echo -n "So, where should I put my files?"
+ ;;
+ esac
+ continue
+ fi
+
+ if [ ! -w "$PREFIX" ]; then
+cat << EOF
+Ooh, a very fine place indeed. Unfortunately it's not your fine place.
+You can't write there, and for some strange cosmological coincidence,
+neither can I. I think we need another place."
+EOF
+ echo "So, what shall it be?"
+ continue
+ fi
+ break
+done
+cat << EOF
+$PREFIX it is. I can work with that.
+
+EOF
+
+if [ -d "${PREFIX}/share/uqm/content" ]; then
+ if [ -f "${PREFIX}/share/uqm/content/version" ]; then
+ read UQM_OLDVERSION < "${PREFIX}/share/uqm/content/version"
+ else
+ UQM_OLDVERSION=0.1
+ fi
+ if [ `expr "$UQM_VERSION" ">" "$UQM_OLDVERSION"` = "1" ]; then
+ # NB: 'expr' echoes '1' for true, while sh uses '0'
+ echo "Hey! I see you've got an old version there."
+ echo "You'll be happy to know this one is even better."
+ echo "(yes, this is a hardcoded string in the installer)"
+ echo
+ UQM_INSTALL=UPGRADE
+ elif [ `expr "$UQM_VERSION" "<" "$UQM_OLDVERSION"` = "1" ]; then
+ UQM_INSTALL=DOWNGRADE
+ else
+ UQM_INSTALL=REINSTALL
+ fi
+else
+ UQM_INSTALL=NEW
+fi
+
+check_content_path() {
+ local OPTIONAL TITLE HAVE FAIL
+
+ echo "Looking for packages to install..."
+ FAIL="0"
+ for PACKAGE in $UQM_PACKAGES; do
+ eval OPTIONAL="\$UQM_PACKAGE_${PACKAGE}_OPTIONAL"
+ eval TITLE="\$UQM_PACKAGE_${PACKAGE}_TITLE"
+ package_available "$PACKAGE"
+ HAVE="$?"
+ echo -n "- Package for '$TITLE'"
+ if [ "$OPTIONAL" = TRUE ]; then
+ echo -n " (optional)"
+ else
+ echo -n " (required)"
+ fi
+
+ if [ "$HAVE" -eq 0 ]; then
+ echo " found."
+ continue
+ fi
+ echo " not found."
+
+ if [ "$OPTIONAL" = FALSE ]; then
+ FAIL=1
+ fi
+ done
+ return "$FAIL"
+}
+
+CONTENT_PATH="$INPUT_content_path_DEFAULT"
+check_content_path
+if [ "$?" -ne 0 ]; then
+ cat << EOF
+I haven't found the files I need in the $CONTENT_PATH dir.
+I really can't work without them.
+If you don't have them, please press CTRL-C and quickly get it, before I get
+swapped out.
+EOF
+ echo "Otherwise, please tell me where you've hidden them."
+ while :; do
+ show_content_path_menu
+ CONTENT_PATH="$INPUT_content_path_VALUE"
+ check_content_path && break
+ echo "That's not it. Guess again."
+ done
+ echo "Yay! Found them."
+ echo
+fi
+
+set_components_menu
+show_components_menu
+
+cat << EOF
+Ok, I'm ready now to start filling your hard drive.
+It might take some time though, so don't hold your breath.
+On second thought, DO hold your breath, I like the colour blue.
+Orange too, for that matter, but I don't think you can manage that.
+EOF
+echo -n "Ready? Just say the word. Any word will do: "
+read TEMP
+echo "A word as good as any."
+echo
+
+echo "Making directories..."
+mkdir -p -- "$PREFIX"/share/uqm/content 2> /dev/null
+mkdir -p -- "$PREFIX"/bin 2> /dev/null
+
+echo "Unpacking packages..."
+for PACKAGE in `selected_packages`; do
+ echo "- $COMPFILE"
+ COMPFILE="$(package_filename $PACKAGE)"
+ eval LOCATION="\$UQM_PACKAGE_${PACKAGE}_LOCATION"
+ unzip -od "${PREFIX}${LOCATION}" "$COMPFILE"
+
+ # Next line is a workaround, as the content zips have files
+ # with the wrong permissions in them. Should be fixed for the
+ # next release.
+ chmod go+rX "${PREFIX}${LOCATION}"
+done
+
+
+echo "Unpacking other stuff..."
+tail -c @ATTACHLEN@ < "$0" | gzip -dc | tar -xf - -C "$PREFIX"
+
+echo "Creating wrapper script..."
+cat << EOF > "$PREFIX"bin/uqm
+#!/bin/sh
+# Wrapper script for starting The Ur-Quan Masters
+"${PREFIX}lib/uqm/uqm" "--contentdir=${PREFIX}share/uqm/content" "\$@"
+EOF
+chmod 755 "$PREFIX"bin/uqm
+
+cat << EOF
+
+All done. Now you can play The Ur-Quan masters.
+I told you I was going to make you very happy.
+And if you're looking for documentation, you can find some in
+${PREFIX}share/uqm/doc/. If you aren't looking for documentation, too.
+But feel free to delete them. You know where to find them.
+EOF
+
+exit 0
+
+
diff --git a/build/unix_installer/installer.config b/build/unix_installer/installer.config
new file mode 100644
index 0000000..aeb9a03
--- /dev/null
+++ b/build/unix_installer/installer.config
@@ -0,0 +1,139 @@
+MENU_main_ITEMS="components install_prefix"
+MENU_main_TITLE="The Ur-Quan Masters installation"
+MENU_main_ITEM_components_TYPE=MENU
+MENU_main_ITEM_install_prefix_TYPE=INPUT
+
+MENU_components_TITLE="Optional components"
+MENU_components_TEXT=\
+" Here you can select which components you'd like to have installed. If you
+ can spare the space, you are adviced to select all, for the maximum game
+ experience.
+"
+
+package_filename() {
+ local FILENAME PACKNAME
+
+ eval PACKNAME="\${UQM_PACKAGE_$1_NAME}"
+ # Check complete package first for reinstall or downgrade.
+ # If they're not found, we'll settle for an upgrade package,
+ # maybe it will work, maybe it won't.
+ case "$UQM_INSTALL" in
+ NEW|REINSTALL|DOWNGRADE)
+ FILENAME="${CONTENT_PATH}uqm-${UQM_VERSION}-$PACKNAME.zip"
+ if [ -f "$FILENAME" ]; then
+ # Found complete package.
+ echo "$FILENAME"
+ return 0
+ fi
+ ;;
+ esac
+
+ # A full package is needed for a new install
+ if [ "$UQM_INSTALL" = NEW ]; then
+ return 1
+ fi
+
+ # Check for upgrade package.
+ FILENAME="${CONTENT_PATH}uqm-${UQM_OLDVERSION}_to_${UQM_VERSION}-$PACKNAME.zip"
+ if [ -f "$FILENAME" ]; then
+ # Found upgrade package.
+ echo "$FILENAME"
+ return 0
+ fi
+
+ # Check complete package again, as you can upgrade with a complete
+ # package.
+ if [ "$UQM_INSTALL" = UPGRADE ]; then
+ FILENAME="${CONTENT_PATH}uqm-${UQM_VERSION}-$PACKNAME.zip"
+ if [ -f "$FILENAME" ]; then
+ # Found complete package.
+ echo "$FILENAME"
+ return 0
+ fi
+ fi
+ return 1
+}
+
+package_available() {
+ package_filename "$@" > /dev/null
+ return $?
+}
+
+set_components_menu() {
+ local TITLE OPTIONAL DEFAULT
+
+ MENU_install_components_TITLE="Optional components selection"
+ MENU_install_components_ITEMS="$UQM_PACKAGES"
+ for PACKAGE in $UQM_PACKAGES; do
+ eval TITLE="\"\${UQM_PACKAGE_${PACKAGE}_TITLE}\""
+ eval OPTIONAL="\${UQM_PACKAGE_${PACKAGE}_OPTIONAL}"
+ eval DEFAULT="\${UQM_PACKAGE_${PACKAGE}_DEFAULT}"
+ eval MENU_install_components_ITEM_${PACKAGE}_TYPE=CHECK
+
+ eval CHECK_${PACKAGE}_TITLE=\"\$TITLE\"
+ if [ "$DEFAULT" = "TRUE" ]; then
+ eval CHECK_${PACKAGE}_DEFAULT=CHECKED
+ else
+ eval CHECK_${PACKAGE}_DEFAULT=UNCHECKED
+ fi
+ if package_available "$PACKAGE"; then
+ if [ "$OPTIONAL" = "FALSE" ]; then
+ eval CHECK_${PACKAGE}_VALUE=CHECKED
+ eval CHECK_${PACKAGE}_FIXED=TRUE
+ fi
+ else
+ if [ "$OPTIONAL" = "TRUE" ]; then
+ eval CHECK_${PACKAGE}_VALUE=UNCHECKED
+ eval CHECK_${PACKAGE}_FIXED=TRUE
+ else
+ echo "Can't find '$PACKAGE' package"
+ return 1
+ fi
+ fi
+ done
+ return 0
+}
+
+show_components_menu() {
+ do_menu MENU install_components ""
+}
+
+INPUT_install_prefix_TITLE="Installation prefix"
+#INPUT_install_prefix_TEXT=\
+#" This is the top directory where the game data will be installed.
+# In the directory you specify directories like 'bin', 'lib' and 'share'
+# will be used.
+#"
+if [ "$(id -u)" -eq 0 ]; then
+ INPUT_install_prefix_DEFAULT="/usr/local/games/"
+else
+ INPUT_install_prefix_DEFAULT="$HOME/uqm/"
+fi
+INPUT_install_prefix_VALIDATOR=validate_path
+
+show_install_prefix_menu() {
+ do_menu INPUT install_prefix ""
+ INPUT_install_prefix_VALUE="${INPUT_install_prefix_VALUE%/}/"
+}
+
+INPUT_content_path_TITLE="Location of content packages"
+INPUT_content_path_DEFAULT="${PWD%/}/"
+INPUT_content_path_VALIDATOR=validate_path
+show_content_path_menu() {
+ do_menu INPUT content_path ""
+ INPUT_content_path_VALUE="${INPUT_content_path_VALUE%/}/"
+}
+
+selected_packages() {
+ local RESULT PACKAGE VALUE
+ RESULT=""
+ for PACKAGE in $UQM_PACKAGES; do
+ eval VALUE="\$CHECK_${PACKAGE}_VALUE"
+ if [ "$VALUE" = CHECKED ]; then
+ RESULT="${RESULT:+$RESULT }$PACKAGE"
+ fi
+ done
+ echo "$RESULT"
+}
+
+
diff --git a/build/unix_installer/template b/build/unix_installer/template
new file mode 100644
index 0000000..5261257
--- /dev/null
+++ b/build/unix_installer/template
@@ -0,0 +1,40 @@
+# Template file for building the UQM installer for unix Systems.
+# By Serge van den Boom, 2003-02-23
+
+UQM_VERSION="0.7"
+UQM_LICENSE_FILE="COPYING"
+
+UQM_PACKAGES="content voice 3domusic"
+UQM_PACKAGE_content_NAME="content"
+UQM_PACKAGE_content_TITLE="General game data"
+UQM_PACKAGE_content_LOCATION="share/uqm/content/packages/"
+UQM_PACKAGE_content_OPTIONAL="FALSE"
+UQM_PACKAGE_content_DEFAULT="TRUE"
+UQM_PACKAGE_voice_NAME="voice"
+UQM_PACKAGE_voice_TITLE="Spoken alien communication"
+UQM_PACKAGE_voice_LOCATION="share/uqm/content/addons/"
+UQM_PACKAGE_voice_OPTIONAL="TRUE"
+UQM_PACKAGE_voice_DEFAULT="TRUE"
+UQM_PACKAGE_3domusic_NAME="3domusic"
+UQM_PACKAGE_3domusic_TITLE="Music from the 3DO version"
+UQM_PACKAGE_3domusic_LOCATION="share/uqm/content/addons/"
+UQM_PACKAGE_3domusic_OPTIONAL="TRUE"
+UQM_PACKAGE_3domusic_DEFAULT="TRUE"
+
+UQM_SCRIPT_FILES="
+ ${UQM_TOP}build/unix/ansi
+ ${UQM_TOP}build/unix/menu_functions
+ ${UQM_TOP}build/unix_installer/installer.config
+ ${UQM_TOP}build/unix_installer/install.sh.in
+"
+
+# File destination
+UQM_ATTACH_FILES=\
+"${UQM_TOP}AUTHORS share/uqm/doc/
+${UQM_TOP}COPYING share/uqm/doc/
+${UQM_TOP}ChangeLog share/uqm/doc/
+${UQM_TOP}WhatsNew share/uqm/doc/
+${UQM_TOP}README share/uqm/doc/
+${UQM_TOP}doc/users/manual.txt share/uqm/doc/
+${UQM_TOP}uqm lib/uqm/"
+
diff --git a/build/win32_install/build-win32-installer.sh b/build/win32_install/build-win32-installer.sh
new file mode 100644
index 0000000..467d0d7
--- /dev/null
+++ b/build/win32_install/build-win32-installer.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/sh
+
+for i in uqm-installer.nsi packages.nsh orzshofixti.bmp ultron.bmp \
+ uqm-3do.cfg uqm-pc.cfg; do
+ if ! [ -f $i ]; then
+ echo "$i not found. Make sure you are running from sc2/build/win32_install."
+ exit 1
+ fi
+done
+if ! [ -f packages.nsh ]; then
+ echo "packages.nsh not found. Follow the instructions in INSTALL.pkgs"
+ echo "to regenerate it."
+ exit 1
+fi
+if ! [ -f ../../uqm.exe ]; then
+ echo "uqm.exe not found. Go build it first."
+ exit 1
+fi
+
+build_keyjam()
+{
+ make clean && \
+ make && \
+ strip keyjam.exe && \
+ cp keyjam.exe ../../sc2/build/win32_install
+ return $?
+}
+
+echo "Building the key-jammer application..."
+cd ../../../tools/keys || exit 1
+build_keyjam
+result=$?
+cd ../../sc2/build/win32_install
+if [ $result -ne 0 ] ; then
+ echo "Could not build keyjam.exe. Aborting installer creation."
+ exit 1
+fi
+cp ../../uqm.exe . || exit
+strip ./uqm.exe || exit
+echo "Identifying DLL dependencies..."
+DLLS=$(ntldd -R uqm.exe | awk '/\\bin\\/{print $3;}')
+DLLS2=$(ntldd -R keyjam.exe | awk '/\\bin\\/{print $3;}')
+DLLS="$DLLS $DLLS2"
+DLLS=$(for dll in $DLLS; do echo $dll; done | sort -u)
+
+(echo "# Autogenerated by build-win32-installer.sh";
+ echo "#"
+ echo " File \"uqm.exe\""
+ echo " File \"keyjam.exe\""
+ for dll in $DLLS; do
+ echo " File \"$dll\""
+ done) >> dlls.nsi
+
+(echo "# Autogenerated by build-win32-installer.sh";
+ echo "#"
+ echo " Delete \"\$INSTDIR\\uqm.exe\""
+ echo " Delete \"\$INSTDIR\\keyjam.exe\""
+ for dll in $DLLS; do
+ echo " Delete \"\$INSTDIR\\$(basename $dll)\""
+ done) >> undlls.nsi
+
+echo "Preparing documentation..."
+for i in AUTHORS COPYING README README-SDL WhatsNew; do
+ cp "../../$i" "$i.txt" && unix2dos "$i.txt"
+done
+cp ../../doc/users/manual.txt Manual.txt && unix2dos "Manual.txt"
+
+echo "Creating installer..."
+makensis "-XSetCompressor /SOLID lzma" uqm-installer.nsi || exit 1
+echo "Installer has been created successfully."
diff --git a/build/win32_install/orzshofixti.bmp b/build/win32_install/orzshofixti.bmp
new file mode 100644
index 0000000..982500d
--- /dev/null
+++ b/build/win32_install/orzshofixti.bmp
Binary files differ
diff --git a/build/win32_install/packages.nsh b/build/win32_install/packages.nsh
new file mode 100644
index 0000000..c95ca8c
--- /dev/null
+++ b/build/win32_install/packages.nsh
@@ -0,0 +1,23 @@
+# Autogenerated by procpkgs.sh
+#
+!define PKG_CONTENT_FILE "uqm-0.8.0-content.uqm"
+!define PKG_CONTENT_MD5SUM "6cbc9d51fa63e07c0f4d1d061136d816"
+!define PKG_CONTENT_SIZE 11276
+!define PKG_3DOMUSIC_FILE "uqm-0.8.0-3domusic.uqm"
+!define PKG_3DOMUSIC_MD5SUM "9e5801d45ca12028b486cdeb83568c02"
+!define PKG_3DOMUSIC_SIZE 18535
+!define PKG_VOICE_FILE "uqm-0.8.0-voice.uqm"
+!define PKG_VOICE_MD5SUM "fc89f77d7b66c2669abca6c157f5259a"
+!define PKG_VOICE_SIZE 112465
+!define PKG_REMIX1_FILE "uqm-remix-disc1.uqm"
+!define PKG_REMIX1_MD5SUM "09f242d8d72166d1d5ccbd3d99c93e7d"
+!define PKG_REMIX1_SIZE 49012
+!define PKG_REMIX2_FILE "uqm-remix-disc2.uqm"
+!define PKG_REMIX2_MD5SUM "fbc8bdcb709939d559d8c7216ad15cc2"
+!define PKG_REMIX2_SIZE 58869
+!define PKG_REMIX3_FILE "uqm-remix-disc3.uqm"
+!define PKG_REMIX3_MD5SUM "5ccc6d4ac301ae98e172ac6835dcdead"
+!define PKG_REMIX3_SIZE 38989
+!define PKG_REMIX4_FILE "uqm-remix-disc4-1.uqm"
+!define PKG_REMIX4_MD5SUM "3fb63f4ac514343ed4b4b5b194c413fb"
+!define PKG_REMIX4_SIZE 85867
diff --git a/build/win32_install/procpkgs.sh b/build/win32_install/procpkgs.sh
new file mode 100755
index 0000000..7b1817a
--- /dev/null
+++ b/build/win32_install/procpkgs.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Generates a .nsh file for the NSIS Windows installer with MD5 checksums
+# and file sizes of the UQM packages.
+#
+# Run this from an MSYS2 bash shell.
+#
+# The packages must be in the current directory.
+#
+# The awk, md5sum, and wc utilities must be installed, but MSYS2
+# should generally already have those in your path.
+
+NSH_FILE="packages.nsh"
+PKG_VERSION="0.8.0"
+
+CONTENT_PKG="uqm-$PKG_VERSION-content.uqm"
+MUSIC_PKG="uqm-$PKG_VERSION-3domusic.uqm"
+VOICE_PKG="uqm-$PKG_VERSION-voice.uqm"
+REMIX1_PKG="uqm-remix-disc1.uqm"
+REMIX2_PKG="uqm-remix-disc2.uqm"
+REMIX3_PKG="uqm-remix-disc3.uqm"
+REMIX4_PKG="uqm-remix-disc4-1.uqm"
+
+check_pkg() {
+ if [ ! -f "$1" ]; then
+ echo "$1 not found."
+ exit 1
+ fi
+}
+
+process_pkg() {
+ echo "Processing $1..."
+ SUM=$(md5sum "$1" | awk '{print $1}')
+ SZ=$(wc -c "$1" | awk '{print $1}')
+ SZ=$(( SZ / 1024 ))
+ {
+ echo "!define $2_FILE \"$1\""
+ echo "!define $2_MD5SUM \"$SUM\""
+ echo "!define $2_SIZE $SZ"
+ } >> $NSH_FILE
+}
+
+check_pkg $CONTENT_PKG
+check_pkg $MUSIC_PKG
+check_pkg $VOICE_PKG
+check_pkg $REMIX1_PKG
+check_pkg $REMIX2_PKG
+check_pkg $REMIX3_PKG
+check_pkg $REMIX4_PKG
+
+echo "# Autogenerated by procpkgs.sh" > $NSH_FILE
+echo "#" >> $NSH_FILE
+process_pkg $CONTENT_PKG PKG_CONTENT
+process_pkg $MUSIC_PKG PKG_3DOMUSIC
+process_pkg $VOICE_PKG PKG_VOICE
+process_pkg $REMIX1_PKG PKG_REMIX1
+process_pkg $REMIX2_PKG PKG_REMIX2
+process_pkg $REMIX3_PKG PKG_REMIX3
+process_pkg $REMIX4_PKG PKG_REMIX4
+
+echo "All packages processed. ${NSH_FILE} generated."
diff --git a/build/win32_install/ultron.bmp b/build/win32_install/ultron.bmp
new file mode 100644
index 0000000..b9d6a3a
--- /dev/null
+++ b/build/win32_install/ultron.bmp
Binary files differ
diff --git a/build/win32_install/uqm-3do.cfg b/build/win32_install/uqm-3do.cfg
new file mode 100644
index 0000000..4e002b7
--- /dev/null
+++ b/build/win32_install/uqm-3do.cfg
@@ -0,0 +1,11 @@
+3domusic = BOOLEAN:true
+textmenu = BOOLEAN:false
+textgradients = BOOLEAN:false
+subtitles = BOOLEAN:false
+iconicscan = BOOLEAN:true
+3domovies = BOOLEAN:true
+speechvol = INT32:100
+pulseshield = BOOLEAN:true
+smoothmelee = BOOLEAN:true
+smoothscroll = BOOLEAN:true
+remixmusic = BOOLEAN:false
diff --git a/build/win32_install/uqm-installer.nsi b/build/win32_install/uqm-installer.nsi
new file mode 100644
index 0000000..8b6783f
--- /dev/null
+++ b/build/win32_install/uqm-installer.nsi
@@ -0,0 +1,556 @@
+; Script generated by the HM NIS Edit Script Wizard.
+
+Var PACKAGEDIR
+Var UQMARGS
+Var MAKEICON
+Var UQMUSERDATA
+
+; HM NIS Edit Wizard helper defines
+!define PRODUCT_NAME "The Ur-Quan Masters"
+!define PRODUCT_VERSION "0.8.0"
+!define PRODUCT_WEB_SITE "http://sc2.sourceforge.net"
+!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\uqm.exe"
+!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
+!define PRODUCT_UNINST_ROOT_KEY "HKLM"
+!define PRODUCT_STARTMENU_REGVAL "NSIS:StartMenuDir"
+
+; The INSTALLER_VERSION is a suffix to the version number for installer patches or to mark
+; alpha/beta/release candidate status. In normal releases it is the empty string.
+!define INSTALLER_VERSION ""
+
+; UQM Package definitions
+!include "packages.nsh"
+
+; MUI 1.67 compatible ------
+!include "MUI.nsh"
+
+; Start using macros for block structure
+!include "LogicLib.nsh"
+
+
+; MUI Settings
+!define MUI_ABORTWARNING
+!define MUI_ICON "${NSISDIR}\Contrib\Graphics\Icons\win-install.ico"
+!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\win-uninstall.ico"
+!define MUI_WELCOMEFINISHPAGE_BITMAP "orzshofixti.bmp"
+!define MUI_HEADERIMAGE
+!define MUI_HEADERIMAGE_BITMAP "ultron.bmp"
+!define MUI_HEADERIMAGE_RIGHT
+
+; UAC support
+RequestExecutionLevel admin
+
+; Welcome page
+!insertmacro MUI_PAGE_WELCOME
+; License page
+!define MUI_LICENSEPAGE_BUTTON "Install"
+!define MUI_LICENSEPAGE_TEXT_BOTTOM "Press the Install button to continue."
+!insertmacro MUI_PAGE_LICENSE "COPYING.txt"
+; Components page
+!define MUI_COMPONENTSPAGE_TEXT_COMPLIST "You can preconfigure the options to mimic the original platforms by selecting those install types. Note that more complete installs will need to download more packages."
+!insertmacro MUI_PAGE_COMPONENTS
+; Directory page
+!insertmacro MUI_PAGE_DIRECTORY
+; Package Dictory
+!define MUI_PAGE_HEADER_TEXT "Choose Package Location"
+!define MUI_PAGE_HEADER_SUBTEXT "Choose the folder that holds packages that have already been downloaded."
+!define MUI_DIRECTORYPAGE_TEXT_TOP "Setup will look for already-downloaded content packages in the following folder. To copy them from a different folder, click Browse and select another folder. If you are doing a net install, leave this field alone. Click Next to continue."
+!define MUI_DIRECTORYPAGE_TEXT_DESTINATION "Source Folder"
+!define MUI_DIRECTORYPAGE_VARIABLE $PACKAGEDIR
+!define MUI_DIRECTORYPAGE_VERIFYONLEAVE
+!insertmacro MUI_PAGE_DIRECTORY
+; Start menu page
+var ICONS_GROUP
+!define MUI_STARTMENUPAGE_NODISABLE
+!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Games\The Ur-Quan Masters"
+!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${PRODUCT_UNINST_ROOT_KEY}"
+!define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}"
+!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${PRODUCT_STARTMENU_REGVAL}"
+!insertmacro MUI_PAGE_STARTMENU Application $ICONS_GROUP
+; Instfiles page
+!insertmacro MUI_PAGE_INSTFILES
+; Finish page
+!define MUI_FINISHPAGE_RUN_NOTCHECKED
+!define MUI_FINISHPAGE_NOREBOOTSUPPORT
+!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\README.txt"
+!define MUI_FINISHPAGE_RUN "$INSTDIR\uqm.exe"
+!define MUI_FINISHPAGE_RUN_PARAMETERS $UQMARGS
+!insertmacro MUI_PAGE_FINISH
+
+; Uninstaller pages
+!define MUI_UNCONFIRMPAGE_TEXT_TOP "This program will now uninstall The Ur-Quan Masters entirely. If you wish to preserve content or expansion packs, select Cancel now and back them up. Otherwise, press Uninstall to continue."
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+
+; Language files
+!insertmacro MUI_LANGUAGE "English"
+
+; MUI end ------
+
+Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
+OutFile "uqm-${PRODUCT_VERSION}${INSTALLER_VERSION}-installer.exe"
+InstallDir "$PROGRAMFILES\The Ur-Quan Masters\"
+InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
+ShowInstDetails show
+ShowUnInstDetails show
+AllowRootDirInstall true
+DirText "" "" "" "Please select a folder."
+InstType "Typical"
+InstType "Minimal"
+InstType "Mimic PC"
+InstType "Mimic 3DO"
+InstType "No Content"
+InstType "All Expansions"
+
+Function .onInit
+ Push $0
+ StrCpy $PACKAGEDIR $EXEDIR
+ StrCpy $UQMARGS ""
+ StrCpy $MAKEICON 0
+ ReadEnvStr $0 APPDATA
+ ${If} $0 == ""
+ ReadEnvStr $0 USERPROFILE
+ ${If} $0 == ""
+ StrCpy $UQMUSERDATA "$INSTDIR\userdata\uqm"
+ ${Else}
+ ExpandEnvStrings $UQMUSERDATA "%USERPROFILE%\Application Data\uqm"
+ ${EndIf}
+ ${Else}
+ ExpandEnvStrings $UQMUSERDATA "%APPDATA%\uqm"
+ ${EndIf}
+FunctionEnd
+
+# To use:
+# Push the file name.
+# Push the installation location.
+# It will install it from the Package Directory if necessary; otherwise it
+# will download it to a temp file and install that.
+Var DOWNLOADTARGET
+Var MANDATORY
+Var MD5SUM
+Var DOWNLOADPATH
+Function HandlePackage
+ Exch $0 # File location
+ Exch
+ Exch $1 # File name
+ Push $2
+ Push $3
+ StrCpy $R9 0 # failure count
+ # Check to make sure the file wasn't already installed
+ ${If} ${FileExists} "$0\$1"
+ md5dll::GetFileMD5 "$0\$1"
+ Pop $3
+ ${If} $MD5SUM == $3
+ MessageBox MB_ICONINFORMATION|MB_OK "The package $1 has already been installed."
+ Goto PackageDone
+ ${EndIf}
+ ${EndIf}
+ # It's not installed, so check if it's in the package dir.
+ SetOutPath "$0"
+ SetOverwrite ifdiff
+ ${If} ${FileExists} "$PACKAGEDIR\$1"
+ md5dll::GetFileMD5 "$PACKAGEDIR\$1"
+ Pop $3
+ ${If} $MD5SUM != $3
+ MessageBox MB_ICONINFORMATION|MB_OKCANCEL "The file $PACKAGEDIR\$1 appears to be corrupt. The expected MD5 sum was '$MD5SUM', but the actual MD5 sum was '$3'. Press OK to attempt to download a fresh copy from the distribution site, or Cancel to skip the package." IDOK AttemptDownload IDCANCEL PackageDone
+ ${EndIf}
+ CopyFiles "$PACKAGEDIR\$1" "$0\$1"
+ Goto PackageDone
+ ${EndIf}
+ # It's not in the package dir, but check if it's there but an over-helpful
+ # browser stuck a .zip at the end
+ ${If} ${FileExists} "$PACKAGEDIR\$1.zip"
+ md5dll::GetFileMD5 "$PACKAGEDIR\$1.zip"
+ Pop $3
+ ${If} $MD5SUM != $3
+ MessageBox MB_ICONINFORMATION|MB_OKCANCEL "The file $PACKAGEDIR\$1.zip appears to be corrupt. The expected MD5 sum was '$MD5SUM', but the actual MD5 sum was '$3'. Press OK to attempt to download a fresh copy from the distribution site, or Cancel to skip the package." IDOK AttemptDownload IDCANCEL PackageDone
+ ${EndIf}
+ CopyFiles "$PACKAGEDIR\$1.zip" "$0\$1"
+ Goto PackageDone
+ ${EndIf}
+
+ # We're now in a loop of trying to download the file until the user gives
+ # up. Since the only way to iterate through the loop more than once is to
+ # have the user reply to a message box, this loop is still marked by a
+ # label instead of being part of a Do/Loop macro.
+AttemptDownload:
+ GetTempFileName $DOWNLOADTARGET
+ Delete $DOWNLOADTARGET
+ CreateDirectory $DOWNLOADTARGET
+ inetc::get "https://downloads.sourceforge.net/project/sc2/$DOWNLOADPATH$1" "$DOWNLOADTARGET/$1" /END
+ Pop $2
+ ${If} $2 == "OK"
+ # Download completed. Confirm the MD5 sum is OK.
+ md5dll::GetFileMD5 "$DOWNLOADTARGET\$1"
+ Pop $3
+ ${If} $MD5SUM != $3
+ ${If} $MANDATORY != 0
+ StrCpy $3 "THIS IS A MANDATORY PACKAGE. Without this package, $(^Name) will NOT run."
+ ${Else}
+ StrCpy $3 "This is an optional package. $(^Name) will still run, but some content will not be available."
+ ${EndIf}
+ MessageBox MB_ICONEXCLAMATION|MB_YESNO "The downloaded file $1 doesn't match the internal MD5 sum. This probably means the download was corrupt. $3 Do you want to retry from a different mirror? (Select NO to install the downloaded package anyway - for instance, if you know that the content pack was upgraded or modified since.)" IDYES AttemptDownload
+ ${EndIf}
+ CopyFiles "$DOWNLOADTARGET\$1" "$0\$1"
+ ${Else}
+ ${If} $2 == "Cancelled"
+ StrCpy $2 "Download was canceled by user."
+ ${Else}
+ StrCpy $2 "Could not install the package $1 due to the following error: $\"$2$\"."
+ ${EndIf}
+ ${If} $MANDATORY != 0
+ StrCpy $3 "THIS IS A MANDATORY PACKAGE. Without this package, $(^Name) will NOT run."
+ ${Else}
+ StrCpy $3 "This is an optional package. $(^Name) will still run, but some content will not be available."
+ ${EndIf}
+ MessageBox MB_ICONEXCLAMATION|MB_YESNO "$2 $3 Do you want to retry from a different mirror?" IDYES AttemptDownload
+ ${EndIf}
+ RmDir /r $DOWNLOADTARGET
+PackageDone:
+ Pop $3
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+# Usage:
+# Push the file name, preferrably a full path.
+# Push the string to be appended
+# Any errors during appending will be ignored.
+Function AppendToFile
+ Exch $0 # string to append
+ Exch
+ Exch $1 # File name
+ Push $2 # using $2 for file handle
+ FileOpen $2 $1 a
+ ${Unless} ${Errors}
+ FileSeek $2 0 END # seek to end
+ FileWrite $2 $0
+ FileClose $2
+ ${EndUnless}
+ Pop $2
+ Pop $1
+ Pop $0
+FunctionEnd
+
+Function EnableRemixes
+ # If there are errors pending AppendToFile will fail
+ ClearErrors
+ Push "$UQMUSERDATA\uqm.cfg"
+ Push "remixmusic = BOOLEAN:true$\r$\n"
+ Call AppendToFile
+ ClearErrors
+FunctionEnd
+
+SectionGroup "!UQM" SECGRP01
+ Section "Executable" SEC01
+ SectionIn 1 2 3 4 5 6 RO
+ SetOutPath "$INSTDIR"
+ SetOverwrite try
+ File "AUTHORS.txt"
+ File "COPYING.txt"
+ File "Manual.txt"
+ File "README.txt"
+ File "README-SDL.txt"
+ File "WhatsNew.txt"
+!include "dlls.nsi"
+
+ SetOutPath $UQMUSERDATA
+ SetOverwrite try
+ File "uqm-pc.cfg"
+ File "uqm-3do.cfg"
+
+ # Delete old content
+ Delete "$INSTDIR\content\packages\uqm-0.3-3domusic.zip"
+ Delete "$INSTDIR\content\packages\uqm-0.3-voice.zip"
+ Delete "$INSTDIR\content\packages\uqm-0.3-content.zip"
+ Delete "$INSTDIR\content\packages\uqm-0.4.0-3domusic.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.4.0-voice.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.4.0-content.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.5.0-3domusic.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.5.0-voice.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.5.0-content.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.6.0-3domusic.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.6.0-voice.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.6.0-content.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.7.0-3domusic.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.7.0-voice.uqm"
+ Delete "$INSTDIR\content\packages\uqm-0.7.0-content.uqm"
+ # and in a case of manual install and overly helpful browsers
+ Delete "$INSTDIR\content\packages\uqm-0.7.0-3domusic.uqm.zip"
+ Delete "$INSTDIR\content\packages\uqm-0.7.0-voice.uqm.zip"
+ Delete "$INSTDIR\content\packages\uqm-0.7.0-content.uqm.zip"
+
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+
+ Section "Core Data" SEC02
+ SectionIn 1 2 3 4 6
+ CreateDirectory "$INSTDIR\content\addons"
+ SetOutPath "$INSTDIR\content"
+ SetOverwrite ifnewer
+ AddSize ${PKG_CONTENT_SIZE}
+ StrCpy $MANDATORY 1
+ StrCpy $MD5SUM "${PKG_CONTENT_MD5SUM}"
+ File "..\..\content\version"
+ StrCpy $DOWNLOADPATH "UQM/0.8/"
+ Push "${PKG_CONTENT_FILE}"
+ Push "$INSTDIR\content\packages"
+ Call HandlePackage
+
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+
+ Section "Desktop Icon" SECICON
+ SectionIn 1 2 3 4 5 6
+ StrCpy $MAKEICON 1
+ SectionEnd
+SectionGroupEnd
+
+SectionGroup /e "3DO Content" SECGRP02
+ Section "Music" SEC03
+ SectionIn 1 4 6
+ AddSize ${PKG_3DOMUSIC_SIZE}
+ StrCpy $MANDATORY 0
+ StrCpy $MD5SUM "${PKG_3DOMUSIC_MD5SUM}"
+ StrCpy $DOWNLOADPATH "UQM/0.8/"
+ Push "${PKG_3DOMUSIC_FILE}"
+ Push "$INSTDIR\content\addons"
+ Call HandlePackage
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+
+ Section "Voiceovers" SEC04
+ SectionIn 1 4 6
+ AddSize ${PKG_VOICE_SIZE}
+ StrCpy $MANDATORY 0
+ StrCpy $MD5SUM "${PKG_VOICE_MD5SUM}"
+ StrCpy $DOWNLOADPATH "UQM/0.8/"
+ Push "${PKG_VOICE_FILE}"
+ Push "$INSTDIR\content\addons"
+ Call HandlePackage
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+SectionGroupEnd
+
+SectionGroup "Modern Remixes" SECGRP03
+ Section "Pack 1" SEC05
+ SectionIn 6
+ AddSize ${PKG_REMIX1_SIZE}
+ StrCpy $MANDATORY 0
+ StrCpy $MD5SUM "${PKG_REMIX1_MD5SUM}"
+ StrCpy $DOWNLOADPATH "UQM%20Remix%20Packs/UQM%20Remix%20Pack%201/"
+ Push "${PKG_REMIX1_FILE}"
+ Push "$INSTDIR\content\addons"
+ Call HandlePackage
+ Call EnableRemixes
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+
+ Section "Pack 2" SEC06
+ SectionIn 6
+ AddSize ${PKG_REMIX2_SIZE}
+ StrCpy $MANDATORY 0
+ StrCpy $MD5SUM "${PKG_REMIX2_MD5SUM}"
+ StrCpy $DOWNLOADPATH "UQM%20Remix%20Packs/UQM%20Remix%20Pack%202/"
+ Push "${PKG_REMIX2_FILE}"
+ Push "$INSTDIR\content\addons"
+ Call HandlePackage
+ Call EnableRemixes
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+
+ Section "Pack 3" SEC07
+ SectionIn 6
+ AddSize ${PKG_REMIX3_SIZE}
+ StrCpy $MANDATORY 0
+ StrCpy $MD5SUM "${PKG_REMIX3_MD5SUM}"
+ StrCpy $DOWNLOADPATH "UQM%20Remix%20Packs/UQM%20Remix%20Pack%203/"
+ Push "${PKG_REMIX3_FILE}"
+ Push "$INSTDIR\content\addons"
+ Call HandlePackage
+ Call EnableRemixes
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+
+ Section "Pack 4" SEC08
+ SectionIn 6
+ AddSize ${PKG_REMIX4_SIZE}
+ StrCpy $MANDATORY 0
+ StrCpy $MD5SUM "${PKG_REMIX4_MD5SUM}"
+ StrCpy $DOWNLOADPATH "UQM%20Remix%20Packs/UQM%20Remix%20Pack%204/"
+ Push "${PKG_REMIX4_FILE}"
+ Push "$INSTDIR\content\addons"
+ Call HandlePackage
+ Call EnableRemixes
+ ; Shortcuts
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+ SectionEnd
+SectionGroupEnd
+
+Section -ShortcutsAndIcons
+ SetOutPath $INSTDIR
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ CreateDirectory "$SMPROGRAMS\$ICONS_GROUP"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\The Ur-Quan Masters.lnk" "$INSTDIR\uqm.exe" $UQMARGS
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\UQM (Safe Mode).lnk" "$INSTDIR\uqm.exe" "-x --safe"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\UQM (Safe OpenGL).lnk" "$INSTDIR\uqm.exe" "-o --safe"
+ CreateDirectory "$SMPROGRAMS\$ICONS_GROUP\Documentation"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Documentation\AUTHORS.lnk" "$INSTDIR\AUTHORS.txt"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Documentation\COPYING.lnk" "$INSTDIR\COPYING.txt"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Documentation\Manual.lnk" "$INSTDIR\Manual.txt"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Documentation\README.lnk" "$INSTDIR\README.txt"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Documentation\WhatsNew.lnk" "$INSTDIR\WhatsNew.txt"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Keyboard Test.lnk" "$INSTDIR\keyjam.exe"
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Saved Games.lnk" "$UQMUSERDATA\save"
+ ${If} $MAKEICON = 1
+ CreateShortCut "$DESKTOP\The Ur-Quan Masters.lnk" "$INSTDIR\uqm.exe" $UQMARGS
+ ${EndIf}
+ CreateShortCut "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk" "$INSTDIR\uninst.exe"
+ !insertmacro MUI_STARTMENU_WRITE_END
+SectionEnd
+
+Section -Set3DOConfig
+ SectionIn 4
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ SetOutPath $UQMUSERDATA
+ Delete "uqm.cfg"
+ CopyFiles "$UQMUSERDATA\uqm-3do.cfg" "$UQMUSERDATA\uqm.cfg"
+ !insertmacro MUI_STARTMENU_WRITE_END
+SectionEnd
+
+Section -SetPCConfig
+ SectionIn 3
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ SetOutPath $UQMUSERDATA
+ Delete "uqm.cfg"
+ CopyFiles "$UQMUSERDATA\uqm-pc.cfg" "$UQMUSERDATA\uqm.cfg"
+ !insertmacro MUI_STARTMENU_WRITE_END
+SectionEnd
+
+Section -SetRemixConfig
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+ !insertmacro MUI_STARTMENU_WRITE_END
+SectionEnd
+
+Section -Post
+ WriteUninstaller "$INSTDIR\uninst.exe"
+ WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\uqm.exe"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\uqm.exe"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
+ WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
+SectionEnd
+
+; Section descriptions
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SECGRP01} "The core executables and content libraries for The Ur-Quan Masters. All elements in this section must be installed for the game to be playable."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC01} "Includes the main program, all subsidiary libraries, and basic documentation for The Ur-Quan Masters. Required for play."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC02} "Graphics, sound, and the PC-edition music for The Ur-Quan Masters. Required for play. If this package is selected and not present in the packages directory, the installer will attempt to download it."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SECICON} "Adds a desktop icon linking directly to The Ur-Quan Masters."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SECGRP02} "Optional content packages containing music and sound unique to the 1993 3DO release."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC03} "Optional package which includes the remixed songs from the 3DO release. If this package is selected and not present in the packages directory, the installer will attempt to download it."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC04} "Optional package containing the voiceovers from the 3DO release. If this package is selected and not present in the packages directory, the installer will attempt to download it."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SECGRP03} "Optional content packages containing the official UQM remixes by The Precursors. Selecting any element from this group will also enable the 'remix' addon by default in the starting configuration."
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC05} `Ur-Quan Masters Remix Pack 1 - 'Super Melee!' Optional add-on music package. If this package is selected and not present in the packages directory, the installer will attempt to download it.`
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC06} `Ur-Quan Masters Remix Pack 2 - 'Neutral Aliens - Don't Shoot!' Optional add-on music package. If this package is selected and not present in the packages directory, the installer will attempt to download it.`
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC07} `Ur-Quan Masters Remix Pack 3 - 'The Ur-Quan Hierarchy.' Optional add-on music package. If this package is selected and not present in the packages directory, the installer will attempt to download it.`
+ !insertmacro MUI_DESCRIPTION_TEXT ${SEC08} `Ur-Quan Masters Remix Pack 4 - 'The New Alliance of Free Stars.' Optional add-on music package. If this package is selected and not present in the packages directory, the installer will attempt to download it.`
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+
+Function un.onUninstSuccess
+ HideWindow
+ MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer."
+FunctionEnd
+
+Function un.onInit
+ MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" IDYES +2
+ Abort
+FunctionEnd
+
+Section Uninstall
+ !insertmacro MUI_STARTMENU_GETFOLDER "Application" $ICONS_GROUP
+ Delete "$INSTDIR\uninst.exe"
+ Delete "$INSTDIR\content\packages\addons\remix\uqm-remix-pack4.zip"
+ Delete "$INSTDIR\content\packages\addons\remix\uqm-remix-pack3.zip"
+ Delete "$INSTDIR\content\packages\addons\remix\uqm-remix-pack2.zip"
+ Delete "$INSTDIR\content\packages\addons\remix\uqm-remix-pack1.zip"
+ Delete "$INSTDIR\content\addons\uqm-remix-disc4-1.uqm"
+ Delete "$INSTDIR\content\addons\uqm-remix-disc4.uqm"
+ Delete "$INSTDIR\content\addons\uqm-remix-disc3.uqm"
+ Delete "$INSTDIR\content\addons\uqm-remix-disc2.uqm"
+ Delete "$INSTDIR\content\addons\uqm-remix-disc1.uqm"
+ Delete "$INSTDIR\content\addons\${PKG_VOICE_FILE}"
+ Delete "$INSTDIR\content\addons\${PKG_3DOMUSIC_FILE}"
+ Delete "$INSTDIR\content\packages\${PKG_CONTENT_FILE}"
+ Delete "$INSTDIR\content\version"
+ Delete "$INSTDIR\zlib.dll"
+ Delete "$INSTDIR\zlib1.dll"
+ Delete "$INSTDIR\WhatsNew.txt"
+ Delete "$INSTDIR\vorbisfile.dll"
+ Delete "$INSTDIR\vorbis.dll"
+ Delete "$INSTDIR\uqm.exe"
+ Delete "$INSTDIR\keyjam.exe"
+ Delete "$INSTDIR\SDL_gfx.dll"
+ Delete "$INSTDIR\SDL_image.dll"
+ Delete "$INSTDIR\SDL.dll"
+ Delete "$INSTDIR\README.txt"
+ Delete "$INSTDIR\README-SDL.txt"
+ Delete "$INSTDIR\wrap_oal.dll"
+ Delete "$INSTDIR\OpenAL32.dll"
+ Delete "$INSTDIR\ogg.dll"
+ Delete "$INSTDIR\Manual.txt"
+ Delete "$INSTDIR\libpng13.dll"
+ Delete "$INSTDIR\libpng12.dll"
+ Delete "$INSTDIR\libpng12-0.dll"
+ Delete "$INSTDIR\COPYING.txt"
+ Delete "$INSTDIR\AUTHORS.txt"
+ Delete "$INSTDIR\stderr.txt"
+
+!include "undlls.nsi"
+
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Uninstall.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Options Configuration.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Key Configuration.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Keyboard Test.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Saved Games.lnk"
+ Delete "$DESKTOP\The Ur-Quan Masters.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\The Ur-Quan Masters.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\UQM (Safe Mode).lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\UQM (Safe OpenGL).lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Documentation\AUTHORS.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Documentation\COPYING.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Documentation\Manual.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Documentation\README.lnk"
+ Delete "$SMPROGRAMS\$ICONS_GROUP\Documentation\WhatsNew.lnk"
+
+ RMDir "$SMPROGRAMS\$ICONS_GROUP\Documentation"
+ RMDir "$SMPROGRAMS\$ICONS_GROUP"
+ RMDir "$INSTDIR\content\addons"
+ RMDir "$INSTDIR\content\packages\addons\remix"
+ RMDir "$INSTDIR\content\packages\addons"
+ RMDir "$INSTDIR\content\packages"
+ RMDir "$INSTDIR\content"
+ RMDir "$INSTDIR"
+
+ DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
+ DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
+ SetAutoClose true
+SectionEnd
diff --git a/build/win32_install/uqm-pc.cfg b/build/win32_install/uqm-pc.cfg
new file mode 100644
index 0000000..09aa53a
--- /dev/null
+++ b/build/win32_install/uqm-pc.cfg
@@ -0,0 +1,11 @@
+3domusic = BOOLEAN:false
+textmenu = BOOLEAN:true
+textgradients = BOOLEAN:true
+subtitles = BOOLEAN:true
+iconicscan = BOOLEAN:false
+3domovies = BOOLEAN:false
+positionalsfx = BOOLEAN:false
+pulseshield = BOOLEAN:false
+smoothmelee = BOOLEAN:false
+smoothscroll = BOOLEAN:false
+remixmusic = BOOLEAN:false
diff --git a/content/version b/content/version
new file mode 100644
index 0000000..8d43a3c
--- /dev/null
+++ b/content/version
@@ -0,0 +1,3 @@
+0.8.0
+
+Sat Dec 26 20:34:23 2020 -0800
diff --git a/doc/devel/aniformat b/doc/devel/aniformat
new file mode 100644
index 0000000..65703a5
--- /dev/null
+++ b/doc/devel/aniformat
@@ -0,0 +1,37 @@
+For *.ani, *.sml, *.med, *.big files, each line is in following format:
+
+filename transparent_color colormap_index hotspot_x hotspot_y
+
+Filename is string, all others are int.
+
+filename
+ relative to the directory that .ani file is in.
+
+transparent_color
+ -2 : use PNG tRNS chunk transparency specification
+ -1 : transparent color is not used; not transparency in the image
+ (this behavior is enforced, even when tRNS is present)
+ 0 : for paletted images, transparent color index 0;
+ special for truecolor images, makes RGB (0,0,0) transparent
+ N>0 : for paletted images, transparent color index N;
+ meaningless for truecolor images
+
+colormap_index
+ -1 : colormaps are not used for the image
+ for paletted images, the palette from the image itself
+ will be used
+ N : for paletted images, the palette from the colormap N will
+ be used (so some .ct file somewhere defines the palette);
+ meaningless for truecolor images
+
+hotspot_x, hotspot_y
+ can both be positive of negative (or 0)
+ the position of the image on the screen will be
+ (draw_x - hotspot_x, draw_y - hotspot_y)
+ so a positive hotspot *usually* defines the logical center of
+ an image, and a negative hotspot *usually* defines the position
+ of an image within some larger compound image; there are of course
+ exceptions to this general rule
+
+This information is parsed in libs/graphics/sdl/3do_getbody.c
+
diff --git a/doc/devel/battleinput b/doc/devel/battleinput
new file mode 100644
index 0000000..19eea87
--- /dev/null
+++ b/doc/devel/battleinput
@@ -0,0 +1,13 @@
+The UQM source now has a concept of input context for melee control.
+
+A base InputContext structure is defined in battlecontrols.h. It defines
+the common fields of all the specific structures for each control method.
+
+Among these fields is a BattleInputHandlers structure, which defines the
+functions which handle various input actions for melee, such as frame input,
+selecting the next ship, and querying readiness at the end of a battle.
+
+Structures ComputerInputContext, HumanInputContext, and NetworkInputContext
+are "derived" from InputContext and can contain additional fields for the
+specific control method.
+
diff --git a/doc/devel/blue-gas-giant-pal.png b/doc/devel/blue-gas-giant-pal.png
new file mode 100644
index 0000000..0142f21
--- /dev/null
+++ b/doc/devel/blue-gas-giant-pal.png
Binary files differ
diff --git a/doc/devel/checklist b/doc/devel/checklist
new file mode 100644
index 0000000..23eed02
--- /dev/null
+++ b/doc/devel/checklist
@@ -0,0 +1,52 @@
+ -== Checklist for releases ==-
+
+
+Preparing for the release:
+- update the 'version' file in the content dir
+- update 'uqmversion.h'
+- update uqm.lsm (needs to be done after the content packages are generated)
+- test on all relevant platforms
+- make sure the documentation files are up-to-date
+ - Clean up the ChangeLog file
+ - generate Whatsnew from the ChangeLog
+ - doc/users/manual.txt
+ - doc/users/unixinstall
+ - Contributing
+ - AUTHORS
+ - INSTALL (and platform variants)
+ - unix man pages (in the future)
+
+
+Building the release:
+- make a final build in release mode
+- for the source archive:
+ - use a clean subversion checkout.
+ Just using 'svn update' and watching the output will not be
+ enough, as the files not in repository will be ignored.
+ - be sure to include content/version
+- Building the content packages:
+ - make sure the permissions in the .zip files are ok (0644 for data files
+ and 0755 for executables and dirs)
+ - don't compress .ogg files
+
+
+Announcing the release:
+- provide md5 sums for the released files
+- add a new entry for the release in the bugs database
+- update the home page
+ - http://sc2.sourceforge.net/index.php (News item)
+ - http://sc2.sourceforge.net/downloads.php
+ - http://sc2.sourceforge.net/info.php
+ - http://sc2.sourceforge.net/WhatsNew
+ - http://sc2.sourceforge.net/screenshots.php
+- send a mail to sc2-announce
+- set the forum 'news'
+- set the #sc2 irc channel topic
+- update the version number in http://uqm.stack.nl/wiki/Template:Uqmversion
+
+
+Moving on to the next version:
+- update the snapshots page so that content diffs are generated to the
+ most recent release
+- update doc/devel/versions with the release revision
+
diff --git a/doc/devel/contentfiles b/doc/devel/contentfiles
new file mode 100644
index 0000000..fdcd478
--- /dev/null
+++ b/doc/devel/contentfiles
@@ -0,0 +1,28 @@
+<race>/*icons.0.png
+ small (16x16) icon, used for
+ - picking the next ship for combat in Supermelee
+ - picking the next ship for combat in the full game
+ - the selection grid in the Supermelee fleet editor
+ - the escorts in the status window in the full game when not in
+ battle
+ - loading a game
+ - loading a melee fleet
+ - (others?)
+
+<race>/*icons.0.png
+ large icon with shadow, used for
+ - current selection in the right middle of the Supermelee fleet editor
+ - ship status window in battle
+ - (others?)
+
+<race>/*micon.0.png
+ large icon without shadow, used for
+ - images in the fleet when in the Supermelee fleet editor
+ - images in the fleet when in the shipyard
+ - current selection in the bottom right in the shipyard
+ - (others?)
+
+<race>/*micon.1.png
+ small icon, used for
+ - representing a battle group in a solarsystem
+
diff --git a/doc/devel/debug b/doc/devel/debug
new file mode 100644
index 0000000..22be628
--- /dev/null
+++ b/doc/devel/debug
@@ -0,0 +1,74 @@
+The file src/sc2code/uqmdebug.c and src/sc2code/uqmdebug.h contain various
+debugging functions, which are included in the binary when the game
+is built in debug mode.
+These functions can be called from anywhere in the code, though some
+require the game to be in a specific state.
+The function debugKeyPressed() in uqmdebug.c is called when the debug key
+is pressed. This function is a suitable place to put various debugging
+calls.
+There are also global variables 'debugHook' and 'doInputDebugHook',
+which can be set to a function to be called from the Starcon2Main thread.
+If if is set, 'debugHook' is called the next iteration of the main game loop
+(which will occur when the current activity (IP, HyperSpace, Communication,
+Battle) changes. The game will be in a well defined state here.
+If 'doInputDebugHook' is set, the function it is set to is called from
+doInput(), which is called all throughout the game.
+By setting one of these hooks, a function can be called from the Starcon2Main
+thread, thereby eliminating threading issues that may otherwise arrise.
+The debug key can be specified in user's override.cfg by adding a line
+with a text similar to "debug.1 = STRING:key F12".
+An interactive way to access various debugging code, similar to
+the uio debug mode (see below) is in the works.
+
+The most interesting debugging functionality available at the moment is
+listed below:
+- the function equipShip()
+ When this function is called, the SIS is equiped with various useful
+ modules (a possible Precursor Bomb is left in place), all
+ fuel tanks and crew pods are filled to the maximum, you are given
+ the maximum number of landers, and all possible lander shields are
+ installed.
+- the boolean variable instantMove
+ When this variable is set to TRUE, you can select a location on the
+ star map, and your HyperSpace/Quasispace coordinates will immediately
+ be set to them. Your realspace coordinates are not changed, so if you
+ are exploring a solar system, you'll only notice it when you leave to
+ HyperSpace.
+- the function showSpheres()
+ When this function is called, all spheres of influence of the active
+ races will be shown.
+- the function activateAllShips()
+ When this function is called, it will make all ships available for
+ purchase in the shipyard.
+- the function forwardToNextEvent()
+ Fast forwards the clock until the next event. If TRUE is given as an
+ argument, the HYPERSPACE_ENCOUNTER_EVENTs, which normally happen every
+ day, are skipped.
+- the function dumpEvents()
+ Outputs information on all scheduled events to a FILE.
+- the function dumpPlanetTypes()
+ Outputs information on all planet types to a FILE.
+- the function dumpUniverseToFile()
+ Outputs information on the universe to "./PlanetInfo". This function
+ should only be called from debugHook, as threading issues would otherwise
+ arrise.
+- the functions resetCrewBattle() and resetEnergyBattle()
+ This function, when called in melee, sets the current crew or energy
+ level of the bottom player's ship to its maximum. This works both in
+ Supermelee and in the full game.
+- the function uio_debugInteractive()
+ This function is not defined in sc2code/uqmdebug.h, but in libs/uio.h.
+ This function can interactively (tty-based) display information on
+ the state of the uio file system, and modifications can be made.
+- the function dumpStrings()
+ This function prints all the game strings, and is useful to check whether
+ the various string bases, as defined in gamestr.h, are correct.
+- the function debugContexts()
+ Prints and visually displays the various graphics contexts.
+ This function should only be called from doInputDebugHook(), as threading
+ issues would otherwise arrise.
+
+
+The first version of this document was created by Serge van den Boom,
+on 2004-05-15.
+
diff --git a/doc/devel/dialogs b/doc/devel/dialogs
new file mode 100644
index 0000000..91ca306
--- /dev/null
+++ b/doc/devel/dialogs
@@ -0,0 +1,74 @@
+This file describes how the conversation system works.
+
+Functions:
+
+DoResponsePhrase(RESPONSE_REF R, RESPONSE_FUNC response_func,
+ UNICODE *ConstructStr)
+ Adds a line to the list of remarks the player may choose from.
+ R - The index for the line in the .txt dialog file.
+ response_func - the function where to continue the dialog if this
+ remark was chosen.
+ ConstructStr - A user-constructed string to display. If it is 0,
+ the string from R is used; if it is 0, the string identified
+ by R is not displayed, but R is still used to report what choice
+ a player made.
+ In practice ConstructStr is always the result of a call
+ to construct_response() and R is set to the first part of
+ the constructed phrase.
+ Note: ConstructStr must still be valid after the function calling
+ DoResponsePhrase() returns, so it can't be on the stack.
+ Also, if there are multiple constructed responses at one
+ point in the dialog, they will need separate buffers.
+ In practice, a global buffer shared_phrase_buf is used when
+ only one buffer is required.
+ Note: In practice, when R is 0, the call is always made through the
+ macro Response()
+
+Response(RESPONSE_REF R, RESPONSE_FUNC response_func)
+ Macro that calls DoResponsePhrase() with 0 for ConstructStr.
+
+construct_response(UNICODE *buf, int R, ...)
+ Concatenates strings from a dialog .txt file with strings from
+ the source. The arguments after *buf are alternatingly an int R,
+ and a UNICODE * str, until either R is -1, or str is NULL.
+ buf - the destination
+ R - The index for the line in the .txt dialog file.
+ str - A UNICODE * string.
+
+NPCPhrase_cb(int index, TFB_TrackCB cb)
+ Display the NPC speech with identifier 'index' (and play the
+ associated sample). The callback function 'cb' is called when
+ the displaying is done (usually when the sample ends).
+ index - The index for the line in the .txt dialog file.
+ cb - the callback function to called when the line is over.
+ NULL indicates no callback function.
+
+NPCPhrase(int index)
+ Display the NPC speech with identifier 'index'.
+ This is a macro to NPCPrhase_cb().
+ index - The index for the line in the .txt dialog file, or
+ GLOBAL_PLAYER_NAME for the name of the captain,
+ GLOBAL_SHIP_NAME for the name of the flagship,
+ GLOBAL_PLAYER_LOCATION, for the player's hyperspace coordinates,
+ GLOBAL_ALLIANCE_NAME, for the name of the new alliance, or
+ a negative number for the absolute number as speech
+
+DISABLE_PHRASE(int index)
+ Mark a player line as not to be offered as a choice anymore. Not in
+ this conversion, nor in any future conversations.
+ index - The index for the line in the .txt dialog file.
+
+PHRASE_ENABLED(int index)
+ Check if a player phrase was formerly marked as not to be offered anymore
+ (by call to DISABLE_PHRASE).
+ index - The index for the line in the .txt dialog file.
+
+PLAYER_SAID(RESPONSE_REF said, RESPONSE_REF test)
+ Test whether a player the player reply 'said' is equal to the reply
+ 'test'.
+ said - the response that the player selected
+ test - a response to test 'said' against
+
+
+Initial version by Serge van den Boom, 2005-01-10
+
diff --git a/doc/devel/files b/doc/devel/files
new file mode 100644
index 0000000..ea4df38
--- /dev/null
+++ b/doc/devel/files
@@ -0,0 +1,115 @@
+battle.c
+border.c Draws the screen borders.
+build.c
+cleanup.c Cleanup routines for when the game is done.
+clock.c Manages the game clock.
+collide.c Process a collision of objects in combat.
+comm.c Handles communication with aliens.
+commglue.c Helper functions for communication with aliens.
+confirm.c Asks for confirmation on exit.
+credits.c Shows the credits after a victory.
+cyborg.c Handles combat AI.
+debug.c Debugging functions.
+demo.c Code for demo mode and game journal.
+displist.c Generic doubly linked list, which can keep its own pool
+ of preallocated link structures.
+ This generic list is used in various places throughout
+ the game source, but without preallocaded link structures.
+dummy.c
+encount.c Handles what to do when a player encounters an alien.
+fmv.c Handles video sequences.
+galaxy.c Starmap? Hyperspace background?
+gameinp.c
+gameopt.c Game options menu, including save and load selection.
+gendef.c Defines functions to be called for generating star systems.
+getchar.c Routines for entering characters with the joystick.
+globdata.c Sets up global data, creates the main ship, and the radar.
+gravity.c Maths regarding gravity in combat.
+gravwell.c Handles objects that generate gravity in combat.
+grpinfo.c Handles battle groups.
+hyper.c Handles hyperspace movement and actions.
+init.c Initialization of various game data.
+intel.c Passes input of computer or player along (?)
+intro.c Alternative functions for beginning and ending sequences
+ to those in fmv, unused. (PC version?)
+ipdisp.c Something with spheres of influence?
+load.c Loading a saved game.
+loadship.c Loading of ship resource data.
+master.c Preloading of all ships?
+melee.c All supermelee menu related code.
+misc.c Miscelaneous routines related to combat but not directly to
+ the fighting.
+oscill.c Code for the oscilloscope and speech sample progress slider.
+ (during communication with aliens)
+outfit.c Things related to outfiting the starship.
+pickmele.c Ship selection in Supermelee (not in Full Game)
+pickship.c Ship selection in Full Game (not in Supermelee)
+plandata.c Some const data relating stars and planets.
+process.c Some combat calculations.
+restart.c Main menu (start new, load, supermelee).
+save.c Saving of a game.
+settings.c The back end of the game options.
+setup.c Initialisations for key config, and loading of various
+ resources.
+ship.c Ship movement in combat.
+shipstat.c Routines for showing information about ships in combat
+ (name, crew, energy). (also status.c)
+shipyard.c Things related to selection of your companion ships.
+sis.c Drawing various game state information (ship, ship name,
+ captains name, hyperspace coordinates, date, melnorme credits,
+ landers, storage bays, crew, fuel, game menu)
+starbase.c Code for the Earth starbase.
+starcon.c Code for various events (Arilou portal, Kohr-Ah genocide,
+ Pkunk exodus, Thraddash attack Kohr-Ah, Ilwrath attack
+ Thraddash, Utwig+Supox attack Kohr-Ah, Mycon colonization
+ attempt, hyperspace ship spawned, zoqfot signal, zoqfot under
+ attack, Shofixti return, Spathi build shield, Arilou give
+ the Dnyarri to the Umgah, Yehat rebellion, Slylandro plague
+ worsening)
+starmap.c Code to find a star by it's coordinates and compose star names.
+state.c Saving and loading the temporary planet information,
+ like resource information.
+status.c Maintain and draw some information like the captain picture,
+ crew, energy. (also shipstat.c)
+tactrans.c Code for some additional combat events (explosions, dying,
+ new ship, ion trail, escape).
+trans.c Integer variant of atan2().
+utils.c Various auxiliary utils, code for pausing.
+velocity.c Velocity computation routines.
+weapon.c Routines for weapons fire in combat.
+
+
+build.h
+clock.h
+coderes.h
+collide.h
+commglue.h
+demo.h
+displist.h
+element.h
+encount.h
+globdata.h
+hyper.h
+ifontres.h
+igfxres.h
+ikey_con.h
+imusicre.h
+intel.h
+ires_ind.h
+isndres.h
+istrtab.h
+melee.h
+nameref.h
+races.h
+resinst.h
+respkg.h
+restypes.h
+sis.h
+sounds.h
+starbase.h
+starcon.h
+state.h
+units.h
+velocity.h
+weapon.h
+
diff --git a/doc/devel/fontres b/doc/devel/fontres
new file mode 100644
index 0000000..c70cb6c
--- /dev/null
+++ b/doc/devel/fontres
@@ -0,0 +1,75 @@
+This is the format for resources of type GFXRES (font variantion -- these
+have the extension .fon in the original source).
+
+All fonts have exactly 96 character descriptors starting with ASCII
+code 32 (space).
+
+Everything is stored MSB first unless otherwise specified.
+
+position length meaning
+ 4 0xffffffff if the file is uncompressed.
+ Otherwise, the file is compressed. When uncompressed, the
+ file complies with the rest of the format as described
+ below.
+ 4 Unused in file, always 0x00000000
+ 1 Font leading: the vertical distance between lines of text
+ in pixels.
+ 1 Max ascender: the maximum extent of the font upwards from
+ the baseline in pixels.
+ 1 Max descender: the maximum extent of the font downwards
+ from the baseline in pixels.
+ 1 Char spacing: the number of pixels in between the
+ successive characters.
+ 1 Kern amount: the number of pixels (out of char spacing)
+ that are used for kerning (explained later). Cannot be
+ more than char spacing.
+ 96 Kerning table: each byte corresponds to a character:
+ bits 0-1: right-hand side kerning mask
+ bits 2-3: left-hand side kerning mask
+ For each pair of chars printed the code figures out whether
+ the kerning should be used by taking the right mask of a
+ preceeding char and ANDing it with the left mask of a
+ succeeding char. If the result is 0, the Kern amount is
+ subtracted from Char spacing.
+ 3 alignment padding: aligns the following char descriptors
+ on 8-byte boundary.
+
+
+Then for all 96 chars:
+Char descriptors (these are frame descriptors from .ani format and some
+bits are most likely never used):
+ 4 Type index and flags (TypeIndexAndFlags)
+ Low 2 bytes:
+ bits 0-11 is the index of this frame
+ bits 12-15 are the type flags:
+ - bit 12-13:
+ - 0: (ROM_DRAWABLE)
+ - 1: Direct drawable (RAM_DRAWABLE)
+ - 2: (SCREEN_DRAWABLE)
+ - 3: (OVERLAY_DRAWABLE)
+ High 2 bytes (frame flags):
+ - bits 0-7: global PLUT to use (probably unused)
+ - bit 12: Frame is in cel format (DATA_HARDWARE)
+ - bit 13: (DATA_COPY aka DATA_SCREEN)
+ - bit 14: (DATA_PACKED)
+ - bit 15: (X_FLIP)
+ 4 Hot spot information (definition of (0, 0) in the image):
+ - low 2 bytes: x location of hot spot
+ (probably never used)
+ - high 2 bytes: y location of hot spot
+ This is the baseline of a character. The hotspot Y aligns
+ with the baseline of the text.
+ 4 Image bounds:
+ - low 2 bytes: Image width
+ - low 2 bytes: Image height
+ 4 Offset from beginning of the char descriptor for this
+ char to the beginning of the frame data.
+
+Frames:
+Then for all frames:
+ See "Frames" in gfxres.
+
+
+Initial version 2003-09-02, by Alex Volkov
+Frame descriptors and data from gfxres by Serge van den Boom
+
diff --git a/doc/devel/generate b/doc/devel/generate
new file mode 100644
index 0000000..a7108bb
--- /dev/null
+++ b/doc/devel/generate
@@ -0,0 +1,155 @@
+Note: this file (still) describes the way in which various things were
+generated in the original source. It is still useful, but there are some
+changes:
+- all of this functionality used to be handled through a single function,
+ either the function returned by GenerateIP(), or GenerateRandomIP().
+ Now, there is a structure GenerateFunctions, which contains a function
+ for each of the type of generation. See generate.h.
+- most functions don't use the global variables pSolarSysState, pOrbitalDesc,
+ and pPlanetDesc anymore. Instead, where these are needed, they are passed
+ along through a parameter.
+
+Files related to this system:
+- planets/generate.h
+ Defines GenerateFunctions.
+- planets/generate/gendefault.c
+ Default generation functions for when no specific one is specified.
+- planets/generate/gen*.c
+ Specific generation functions.
+- gendef.c
+ Glue code.
+
+This file should eventually be updated to reflect the new system.
+See the file 'orggenerate' for a description on how the system originally
+worked.
+
+============================================================================
+
+The various universe related game data is generated through a call
+to a solar system dependant generation function.
+This function is of type PLAN_GEN_FUNC, which is a typedef to
+ void (*PLAN_GEN_FUNC) (BYTE control)
+, where the 'control' argument specifies what type of data needs to be
+generated (one of GENERATE_PLANETS, GENERATE_MOONS, GENERATE_ORBITAL,
+INIT_NPCS, REINIT_NPCS, UNINIT_NPCS, GENERATE_MINERAL, GENERATE_ENERGY,
+GENERATE_LIFE, or GENERATE_NAME).
+
+The generation function for a solar system is kept in the 'GenFunc'
+field of the SOLARSYS_STATE structure.
+The SOLARSYS_STATE structure contains the data for a solar system.
+Currently, only one SOLARSYS_STATE structure is used at once, and
+the global variable pSolarSysState points to the current one.
+The GenFunc field is initialised in ExploreSolarSys(), to the value
+returned by GenerateIP() in sc2code/gendef.c. Usually, this will be
+'GenerateRandomIP', but for some specific solar systems a (pointer to a)
+custom generation function is returned. This depends on the value of
+CurStarDescPtr->Index, which contains values such as SOL_DEFINED,
+MELNORME0_DEFINED, AQUA_HELIX_DEFINED, etc (see sc2code/encount.h
+for the complete list).
+The starmap_array in sc2code/plandata.c specifies Index for all
+the solar systems in the game.
+
+Following are the possible values of the 'control' argument to the
+generation function, with the description of how the generation function
+acts on this. As the custom generation functions often only need to
+change one specific aspect of this game data generation, they will
+often call GenerateRandomIP() for the rest.
+StarBases are handled as if they were moons.
+
+GENERATE_PLANETS
+Pre: the global variable pSolarSysState points to the relevant solar system.
+Pre: the RNG is initialised with a seed to be used for the generation.
+ In practice, this seed is generated from the HyperSpace coordinates
+ of the solar system (which are hardcoded in sc2code/plandata.c),
+ followed by exactly one call to TFB_Random().
+Post: the RNG is in an undefined state.
+This function determines how many planets the system has, and fills in
+pSolarSysState->PlanetDesc[] for all planets, including the NumPlanets
+field, which determines how many moons the planet will have.
+It also sets the random seed that is used for data generated for this planet
+(including the number of moons), based on its coordinates (which are in the
+general case randomly determined themselves).
+
+GENERATE_MOONS
+Pre: the global variable pSolarSysState points to the relevant solar system,
+ which is initialised by a GENERATE_PLANETS call.
+Pre: the RNG is initialised with a seed to be used for the generation.
+ In practice, this seed is the seed stored by GENERATE_PLANETS
+ in the rand_seed field for the planet around which the moon(s) orbit.
+Pre: pSolarSysState->pBaseDesc points to the the relevant planet
+ of pSolarSysState->PlanetDesc[].
+Post: The RNG is in an undefined state.
+This function fills in pSolarSysState->MoonDesc[] for all moons around
+the planet pointed to by pSolarSysState->pBaseDesc.
+It also sets the random seed that is used for data generated for the moon
+based on its coordinates (which are in the general case randomly determined
+themselves).
+
+GENERATE_ORBITAL
+Pre: the global variable pSolarSysState points to the relevant solar system,
+ which is initialised by a GENERATE_PLANETS call.
+Pre: pSolarSysState->pOrbitalDesc points to the relevant planet or moon from
+ pSolarSysState->PlanetDesc[] or pSolarSysState->moonDesc[]
+Pre: the planet or moon that pSolarSysState->pOrbitalDesc points to
+ is initialised by a GENERATE_PLANETS or GENERATE_MOONS call.
+This function fills in pSolarSysState->SysInfo with the characteristics
+of the planet or moon, as seen from orbit.
+It also initialises the random seeds used for the generation of bio,
+minerals, and energy nodes on the surface.
+It also sets the discovery report string (if appropriate), initialises
+the surface graphics, and start the planet music.
+For specific planets, it may initiate race communication and possibly combat,
+and will only return once these are over.
+NB. The GENERATE_ORBITAL code should be split up into separate calculation
+and activation (graphics and music) parts.
+
+GENERATE_MINERAL, GENERATE_ENERGY, GENERATE_LIFE
+Pre: the global variable pSolarSysState points to the relevant solar system,
+ which is initialised by a GENERATE_PLANETS call.
+Pre: pSolarSysState->pOrbitalDesc points to the relevant planet or moon from
+ pSolarSysState->PlanetDesc[] or pSolarSysState->moonDesc[]
+Pre: the planet or moon that pSolarSysState->pOrbitalDesc points to
+ is initialised by a GENERATE_PLANETS or GENERATE_MOONS call.
+Pre: pSolarSysState->SysInfo is filled in by a GENERATE_ORBITAL call
+This function determines the properties of one mineral deposit, energy node,
+or life form on a planet or moon. On entry the caller sets
+pSolarSysState->CurNode to the index of the requested item. This function
+will then fill in pSolarSysState->SysInfo.PlanetInfo.CurPt,
+pSolarSysState->SysInfo.PlanetInfo.CurType, and in the case of minerals also
+pSolarSysState->SysInfo.PlanetInfo.CurDensity.
+In case pSolarSysState->CurNode is set to a value larger than or equal to
+the number of items of the requested kind ((COUNT) ~0 in practice), it is
+set to the real number of nodes. In this case the CurXXX fields of
+pSolarSysState->SysInfo.PlanetInfo are set to the values corresponding to
+the largest valid CurNode index, but should probably be considered to be
+undefined.
+These functions may also change the game state, cause the lander
+to take off (by setting InTransit to true in the active PLANETSIDE_DESC
+structure), or mark an energy node as not retrieved, usually in response
+to an item having been picked up since the last call. The game makes
+a GENERATE_MINERAL, GENERATE_ENERGY or GENERATE_LIFE call (whatever
+is relevant) for each item right after it is picked up.
+
+GENERATE_NAME
+Pre: pSolarSysState is set to the relevant solar system.
+Pre: The planet is initialised by GENERATE_PLANETS.
+Pre: pSolarSysState->pBaseDesc points to the relevant planet, which should
+ be in the system.
+This function fills GLOBAL_SIS (PlanetName) with the name of the planet
+pointed to by pSolarSysState->pBaseDesc.
+It also sets the GAME_STATE flag BATTLE_PLANET to the type of this planet,
+so that it will be shown appropriately in melee if combat follows.
+There is no generate function for the names of moons. The few moons that
+are named (those in the Sol system), are handled as a special case of
+the routines that prints these names (PrintCoarseScan3DO and
+PrintCoarseScanPC).
+
+INIT_NPCS
+REINIT_NPCS
+UNINIT_NPCS
+[TODO]
+
+
+Initial version of this document created by Serge van den Boom, on 2005-07-11.
+
+
diff --git a/doc/devel/gfxlib b/doc/devel/gfxlib
new file mode 100644
index 0000000..82ce6b7
--- /dev/null
+++ b/doc/devel/gfxlib
@@ -0,0 +1,328 @@
+ The TFB Graphics Libraries
+
+ Initial documentation by Michael Martin, 4 Feb 2003
+
+The graphics system in UQM has three major subsystems:
+
+- The "legacy" system, which most of the core code uses. This
+ involves the foo_blt () routines in 3do_blt.c, and the data
+ types CONTEXT, FRAME, DRAWABLE, and possibly others. I'm
+ less familiar with this code, and want to eradicate as much of it as
+ I can, but for now, its lowest level has been rewritten as direct
+ calls to:
+
+- The TFB_Draw* commands, documented below in great detail. These
+ routines deal with the datatype TFB_Image, and the more primitive
+ TFB_Canvas. They also support drawing to one of several 'screens',
+ but since only one thread should be allowed to touch the screen,
+ the TFB_DrawScreen routines end up constructing inputs to:
+
+- The DCQ/DrawCommand library. This is a ring queue with commands for
+ rendering graphics on the actual screen. It interacts to some
+ degree with the CONTEXT datatype, but otherwise is defined entirely
+ in terms of TFB_Images and TFB_Canvases.
+
+ THE LEGACY LIBRARY
+ --------------------
+
+The datatypes the code uses directly are CONTEXT, FRAME, and FONT, all
+of which are really pointers to void. Pointers to those are PCONTEXT,
+PFRAME, and PFONT. Then there's DRAWABLE, which is a DWORD, and its
+pointer type PDRAWABLE. (These are defined in sc2code/libs/gfxlib.h.)
+I'm not sure how DRAWABLE values are transformed into actual drawable
+entities.
+
+The full structures for these are in various files in
+sc2code/libs/graphics.
+
+context.h: defines CONTEXT_DESC and PCONTEXT_DESC (and the equivalent
+ CONTEXTPTR -- insert various sorts of incomprenshible
+ muttering here),
+
+display.h: defines a DISPLAY_INTERFACE and PDISPLAY_INTERFACE type (as
+ well as a global _pCurDisplay).
+
+drawable.h: defines FRAME_DESC and DRAWABLE_DESC, and the pointer
+ types PFRAME_DESC and PDRAWABLE_DESC. FRAME_DESC has a
+ TFB_Image pointer as a member. DRAWABLE_DESC currently
+ still uses a rather annoying technique where the last
+ member of a struct is a 1-element array, more memory than
+ that is actually allocated, and the array's bounds are
+ deliberately overflowed to get at multiple frames.
+
+font.h: defines FONT_DESC and PFONT_DESC.
+
+(Details on how all these data types work is forthcoming.)
+
+ THE TFB_DRAW LIBRARY
+ ----------------------
+
+The TFB_Draw commands have a single header file:
+sc2code/libs/graphics/tfb_draw.h. This file declares the following
+data types:
+
+SCREEN: This is an enum, naming the various screens that the DrawCmd
+ library can draw to. Valid values at present are
+ TFB_SCREEN_MAIN, TFB_SCREEN_EXTRA, and TFB_SCREEN_TRANSITION.
+ These correspond to various objects that are of type TFB_Canvas.
+
+ (The maximal number of screens is provided by a bogus last
+ element, TFB_GFX_NUMSCREENS. Keep that as the last element
+ and the allocators will operate properly regardless of any
+ screens you may later want to add.)
+
+TFB_Canvas: This is, for the purposes of most of the code, a void
+ pointer. The implementations of TFB_DrawCanvas commands
+ cast them to the appropriate type. (The only
+ implementation of these commands casts them to
+ SDL_Surface*.)
+
+TFB_Palette: Four UBYTES, r, g, b, and 'unused'. This is designed at
+ present to be directly castable to SDL_Color, which most
+ things do. We should probably do something about that at
+ some point.
+
+TFB_Image: The most important image structure. This has two
+ TFB_Canvases (one for the 'core' image, one for a scaled
+ version of it), a pointer to a TFB_Palette array, a
+ colormap index, a scaling constant, a Mutex from the
+ threading library (to ensure internal consistency if
+ multiple threads are doing stuff), and a 'dirty bit' which
+ means that, if the image is scaled, the ScaledImg needs to
+ be recomputed.
+
+TFB_DrawScreen
+--------------
+
+When you wish to draw graphics directly on the screen, you call these
+routines, which enqueue the DrawCommands:
+
+----
+
+void TFB_DrawScreen_Line (int x1, int y1, int x2, int y2,
+ int r, int g, int b,
+ SCREEN dest);
+
+Draws a line from (x1, y1)-(x2, y2) of a color specified by r, g, and
+b on the specified screen.
+
+We have a known bug here in that if a corner of one of these lines is
+outside of the context's clipping rectangle, the slope of the line may
+change.
+
+----
+
+void TFB_DrawScreen_Rect (PRECT rect,
+ int r, int g, int b,
+ SCREEN dest);
+
+Draws a (filled) rectangle with the specified color on the destination
+screen. PRECT is part of the legacy library.
+
+void TFB_DrawScreen_Copy (PRECT r, SCREEN src, SCREEN dest);
+
+Copies data between screens. The PRECT defines the region to copy.
+This can be handy in saving and restoring background information.
+
+----
+
+void TFB_DrawScreen_Image (TFB_Image *img,
+ int x, int y,
+ BOOLEAN scaled,
+ TFB_Palette *palette,
+ SCREEN dest);
+
+void TFB_DrawScreen_FilledImage (TFB_Image *img,
+ int x, int y,
+ BOOLEAN scaled,
+ int r, int g, int b,
+ SCREEN dest);
+
+These two routines draw images. img, x, and y are all straightforward
+(x and y refer to the upper left of the image), and scaled indicates
+whether or not the NormalImg or ScaledImg should be used, and dest
+names the target screen.
+
+For TFB_DrawScreen_Image, the 'palette' argument refers to a
+256-element array of TFB_Palette that describes the palette to use.
+(The default is cached in TFB_Image itself.) Various techniques are
+used to cache the palette values to keep spurious palette-switch
+commands from flooding the DrawCmd queue. However, switching the
+palette many times per frame is likely to seriously degrade
+performance.
+
+TFB_DrawScreen_FilledImage draws every non-transparent pixel in img in
+the color specified by r, g, b. (Fonts and some menus do this.)
+
+----
+
+void TFB_DrawScreen_WaitForSignal (void);
+
+Puts this thread to sleep until all commands queued to this point are
+executed. Mostly used to keep from spamming the DrawCmd queue, and to
+ensure proper operation of the next two routines.
+
+----
+
+void TFB_DrawScreen_CopyToImage (TFB_Image *img,
+ PRECT lpRect,
+ SCREEN src);
+
+Load the pixels from the rectangle lpRect in screen src into img. If
+you're actually working with the pixels directly, you'll want to do a
+TFB_DrawScreen_WaitForSignal () to ensure the image has actually been
+updated. (If you're just passing it to other TFB_DrawScreen commands,
+that's unnecessary, because ordering within a thread is guaranteed.)
+
+----
+
+void TFB_DrawScreen_DeleteImage (TFB_Image *img);
+
+Deallocates all memory associated with img. This REALLY doesn't
+belong here. It should be a TFB_DrawImage command, with a requirement
+that you WaitForSignal lfirst.
+
+----
+
+TFB_DrawImage
+-------------
+
+These routines are similar to a subset of the TFB_DrawScreen commands,
+except that instead of drawing on the screen at some later time, they
+draw directly and immediately onto a TFB_Image. The arguments all
+mean the same things as they did for TFB_DrawScreen.
+
+----
+
+void TFB_DrawImage_Line (int x1, int y1, int x2, int y2,
+ int r, int g, int b,
+ TFB_Image *dest);
+
+void TFB_DrawImage_Rect (PRECT rect,
+ int r, int g, int b,
+ TFB_Image *image);
+
+void TFB_DrawImage_Image (TFB_Image *img,
+ int x, int y,
+ BOOLEAN scaled,
+ TFB_Palette *palette,
+ TFB_Image *target);
+
+void TFB_DrawImage_FilledImage (TFB_Image *img,
+ int x, int y,
+ BOOLEAN scaled,
+ int r, int g, int b,
+ TFB_Image *target);
+
+----
+
+TFB_DrawCanvas
+--------------
+
+These routines are, quite literally, identical in every way to the
+TFB_DrawImage routines, except that they draw on TFB_Canvases instead.
+They are defined in graphics-library-specific locations. There is, at
+present, only one implementation of these, in
+libs/graphics/sdl/canvas.c.
+
+void TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2,
+ int r, int g, int b,
+ TFB_Canvas dest);
+
+void TFB_DrawCanvas_Rect (PRECT rect,
+ int r, int g, int b,
+ TFB_Canvas image);
+
+void TFB_DrawCanvas_Image (TFB_Image *img,
+ int x, int y,
+ BOOLEAN scaled,
+ TFB_Palette *palette,
+ TFB_Canvas target);
+
+void TFB_DrawCanvas_FilledImage (TFB_Image *img,
+ int x, int y,
+ BOOLEAN scaled,
+ int r, int g, int b,
+ TFB_Canvas target);
+
+----
+
+Creation and Destruction of TFB_Images, TFB_Canvases, and TFB_Palettes
+----------------------------------------------------------------------
+
+Various commands exist for creating and destroying the TFB_Draw data
+types. The concept of "ownership" is critical here. If a data object
+owns a pointer inside of it, that pointer's referent is deallocated
+when the data object is deallocated. If a pointer variable owns its
+referent, it's permissible to delete it.
+
+TFB_Canvas and TFB_Palette are primitives. TFB_Image owns NormalImg,
+ScaledImg, and Palette, and will delete them when it is itself
+deleted.
+
+That said, here are the routines:
+
+---
+
+TFB_Image *TFB_DrawImage_New (TFB_Canvas canvas)
+
+Creates a new TFB_Image, which the caller then owns. The caller must
+own the canvas, and transfers ownership of that canvas to the image.
+The Palette value is automatically created (and the image owns it);
+ScaledImg will be NULL until you scale the image and draw it to the
+screen.
+
+---
+
+void TFB_DrawImage_Delete (TFB_Image *image)
+
+Deletes the image, and all non-NULL components. You must own the
+image you delete.
+
+---
+
+TFB_Canvas TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN has_alpha);
+
+TFB_Canvas TFB_DrawCanvas_New_Paletted (int w, int h,
+ TFB_Palette *palette,
+ int transparent_index);
+
+These create new TFB_Canvases, which the caller will then own. Width
+and height are straightforward. The TrueColor variant produces Canvases
+with the same color depth and pixel format as the screen. The
+has_alpha flag indicates whether or not the canvas has an alpha
+channel.
+
+The Paletted variant produces 8-bit paletted canvases. The palette
+argument is optional (it can be NULL, in which case you'll need to set
+it later - TFB_Images tend to do this when drawn), as is the
+transparent_index (if -1, there is no transparency; otherwise, it's
+the index of the transparent color).
+
+---
+
+TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas);
+
+Returns a canvas that, if possible, matches the graphics configuration
+of the screen. You must own the source canvas. If the conversion is
+possible, it makes the conversion, deletes its argument, and returns
+the converted version; if conversion is not possible, the canvas is
+returned intact.
+
+Regardless of success or failure, the caller owns the result.
+
+---
+
+TFB_Palette *TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas);
+
+Allocates and returns a 256-entry TFB_Palette array that describes the
+palette of the canvas, or returns NULL if the canvas is true-color.
+If the result is non-NULL, the caller owns the result. The caller
+need not own canvas.
+
+
+ DRAWCMD LIBRARY
+ -----------------
+
+Documentation yet to be written. UQM code shouldn't really mess with
+the DrawCmd library directly.
diff --git a/doc/devel/gfxres b/doc/devel/gfxres
new file mode 100644
index 0000000..d0b30bb
--- /dev/null
+++ b/doc/devel/gfxres
@@ -0,0 +1,64 @@
+This is the format for resources of type GFXRES (these have the extension
+.ani in the original source).
+
+Everything is stored MSB first unless otherwise specified.
+
+position length meaning
+ 4 0xffffffff if the file is uncompressed.
+ Otherwise, the file is compressed. When uncompressed, the
+ file complies with the rest of the format as described
+ below.
+ 4 Unused in file, always 0x00000000
+ 4 FlagsAndIndex:
+ bits 0-11: 1 less than the number of frames in this
+ graphics resource
+ bits 12-15: flags:
+ - bit 0: (WANT_MASK)
+ - bit 1: (WANT_PIXMAP)
+ - bit 2: (WANT_COMPRESSED)
+ - bit 3: (DOUBLE_RES)
+ bits 16-31: unknown (0x0000)
+
+Frame descriptions:
+Then for all frames:
+ 4 Type index and flags (TypeIndexAndFlags)
+ Low 2 bytes:
+ bits 0-11 is the index of this frame
+ bits 12-15 are the type flags:
+ - bit 12-13:
+ - 0: (ROM_DRAWABLE)
+ - 1: Direct drawable (RAM_DRAWABLE)
+ - 2: (SCREEN_DRAWABLE)
+ - 3: (OVERLAY_DRAWABLE)
+ High 2 bytes (frame flags):
+ - bits 0-7: global PLUT to use
+ - bit 12: Frame is in cel format (DATA_HARDWARE)
+ - bit 13: (DATA_COPY aka DATA_SCREEN)
+ - bit 14: (DATA_PACKED)
+ - bit 15: (X_FLIP)
+ 4 Hot spot information (definition of (0, 0) in the image):
+ - low 2 bytes: x location of hot spot
+ - high 2 bytes: y location of hot spot
+ 4 Image bounds:
+ - low 2 bytes: Image width
+ - low 2 bytes: Image height
+ 4 Offset from beginning of the frame description for this
+ frame to the beginning of the frame.
+
+Frames:
+Then for all frames:
+ If DATA_HARDWARE is set in the flags for this frame:
+ 4 Size of image data
+ ? .cel image data
+ If DATA_PACKED is set in the flags for this frame: (from 3do/imageint.c)
+ for every line in the image (bounds.height lines):
+ 2 number of bytes used to encode this line
+ The actual number of bytes is computed like so:
+ (number-of-bytes + 2) * 4
+ a number of packets of this format:
+
+
+
+Initial version 2002-10-22, by Serge van den Boom
+
+
diff --git a/doc/devel/gfxversions b/doc/devel/gfxversions
new file mode 100644
index 0000000..d82706e
--- /dev/null
+++ b/doc/devel/gfxversions
@@ -0,0 +1,60 @@
+Chnages between DOS and 3DO graphics.
+[Unless stated otherwise, UQM does things 3DO way]
+
+----------------------------------------------------------------------
+Melee
+----------------------------------------------------------------------
+1. Minor ship small icon changes
+Icon offset horz by 1 pixel: VUX, ZoqFot, Melnorme, Supox
+Facelifted: Utwig
+
+2. Ship/weapon rendering changes
+*Yehat*
+On DOS, Yehat shield was a set of separate shield-only images which
+were composited with the ship; on 3DO, the shield images already include
+the ship.
+*Umgah*
+On DOS, Umgah cone was a composite of a cone mask + spritz images;
+on 3DO, cone images multiplied 3x and are already a combination of
+cone + spritz.
+*Blackurq* [Kohr-Ah]
+Ship images were regenerated for 3DO with a light source
+at a different position (so the angle is diff). This improved the overall look
+of the ship, especially frame 14, which was simply ugly on DOS.
+And, of course, the FRIED is blue on 3DO and was yellow on DOS.
+*Chenjesu*
+Not really a change, med.14 frame has bad transparency info on DOS.
+*Utwig*
+DOS has apparently derelict ship frames (16-18).
+HYPOTHESIS: ship special was originally intended to be some entirely
+different.
+*Sa-Matra*
+The captain portrait is bigger in UQM (same size on DOS and 3DO).
+
+4. Misc
+lbm/stars has an extra frame (2) in 3DO, afaik, it is not used.
+
+5. Planet images
+On DOS, planet images were paletted, and there were 14 topographical types
+(2 were identical) with ~60 palettes; on 3DO, images are truecolor (except
+for Shielded planet), and the 2 identical topographies have been fixed (so
+they are different now).
+
+----------------------------------------------------------------------
+Inter-planetary (IP)
+----------------------------------------------------------------------
+1. Planets
+On DOS, IP planets were all pre-generated (all phases, colors, sizes);
+On 3DO, planet phases and size are pre-generated and rendered using colormaps
+that define planet color.
+
+2. Many changes in Planet Rotation (scan view)
+
+3. Suns
+On DOS, suns have different colors; on 3DO, they are all yellow; UQM has
+been patched to render suns in different colors like DOS.
+
+----------------------------------------------------------------------
+Misc
+----------------------------------------------------------------------
+1. SIS engine color was green-yellow on DOS and is red on 3DO
diff --git a/doc/devel/glossary b/doc/devel/glossary
new file mode 100644
index 0000000..2ecbba5
--- /dev/null
+++ b/doc/devel/glossary
@@ -0,0 +1,6 @@
+Abbreviations used in the code:
+
+IP interplanetary
+NPC non-player character
+SIS Super-Integrated-Starship (the flagship)
+
diff --git a/doc/devel/historical b/doc/devel/historical
new file mode 100644
index 0000000..63697ff
--- /dev/null
+++ b/doc/devel/historical
@@ -0,0 +1,39 @@
+This file is intended as a place to document historically interesting
+curiosities in the UQM source and content, which have been deleted from
+the current source/content tree.
+
+This file is probably not complete.
+
+
+== .res files ===
+
+The Star Control source code contained files with the extension .res, which
+contained a description of the packages (.pkg or .ndx) files to be generated.
+
+There were .res files for each ship and each race which you can communicate
+with. These files 'included' the file 'star3do.res', which itself included
+'star3do.typ'.
+
+There were also header files icode.h, igfxres.h, imusicre.h, isndres.h,
+istrtab.h, resinst.h, respkg.h and restypes.h in each of the ship and comm
+directories, which defined a few resource constants, which were presumably also
+generated from these .res files.
+
+
+== Outtake not taken out ==
+
+There was an outtake accidentally left in the original speech for the Druuge.
+It can still be found at
+ http://sc2.svn.sourceforge.net/viewvc/*checkout*/sc2/trunk/sc2/content/comm/druuge/druug011.ogg?revision=133&pathrev=1113
+
+
+== Original title screen and menu ==
+
+The original title screen -- which included the text 'Star Control' and
+'Accolade' and could hence not be used in UQM -- can be found at
+ http://sc2.svn.sourceforge.net/viewvc/sc2/trunk/sc2/content/lbm/title.tga?revision=6&pathrev=6
+The original main menu (which did not include 'setup' or 'quit' options)
+can be found at
+ http://sc2.svn.sourceforge.net/viewvc/sc2/trunk/sc2/content/lbm/newgame0.tga?revision=6&pathrev=6
+
+
diff --git a/doc/devel/input b/doc/devel/input
new file mode 100644
index 0000000..ca48e00
--- /dev/null
+++ b/doc/devel/input
@@ -0,0 +1,126 @@
+Input system notes
+
+Code that reads the input state defines a routine that goes through
+the DoInput routine. This is handled in a pseudo-OOP style.
+
+An "INPUT_STATE_DESC" has the following members:
+
+BOOLEAN (*InputFunc) (PVOID pInputState);
+COUNT MenuRepeatDelay;
+
+Although a "PVOID", pInputState has to be safely castable to
+INPUT_STATE_DESC. There are various "subclasses" that are used for
+this purpose throughout the code. They are also pased to the first
+argument of DoInput.
+
+The InputFunc returns TRUE if it still will be keeping control next
+frame.
+
+MenuRepeatDelay is an "internal" variable for making keyrepeat work
+right.
+
+"Subclasses":
+
+MENU_STATE - in starbase.h. The "pMenuState" global is usually the
+element referenced. This is used by:
+
+ encount.c: DoSelectAction
+ gameopt.c: DoGameOptions, DoSettings, DoTextEntry, DoNaming,
+ DoQuitMenu, DoPickGame
+ outfit.c: DoOutfit
+ pickship.c: DoPickBattleShip
+ restart.c: DoRestart
+ shipyard.c: DoModifyShips, DoShipYard
+ starbase.c: DoStarBase
+
+ cargo.c: DoDiscardCargo
+ devices.c: DoManipulateDevices
+ lander.c: DoPlanetSide
+ pstarmap.c: DoMoveCursor, DoFlagshipCommands
+ roster.c: DoModifyRoster
+ scan.c: DoScan, PickPlanetSide
+
+VIDEO_INPUT_STATE - in vidplayer.c. Used by TFB_DoVideoInput.
+
+ENCOUNTER_STATE - in comm.c. Used by DoCommunication.
+
+MELEE_STATE - in melee.h. Used by DoPickShip, DoLoadTeam, DoEdit, DoMelee.
+
+The game logic itself that uses DoInput treats it as a stack of state
+machines. The "current state" is in the InputFunc field of the
+argument.
+
+The state machines are (with the calls that initialize and allocate them):
+
+InitCommunication: DoCommunication
+DoMenuOptions, ExploreSolarSys: DoFlagshipCommands <-> DoDiscardCargo
+ <-> DoManipulateDevices
+Roster: DoModifyRoster
+DoMelee <-> DoEdit <-> DoLoadTeam <-> DoPickShip
+VisitStarBase: DoStarBase <-> DoOutfit <-> DoInstallModule
+ <-> DoShipyard <-> DoModifyShips
+DoStarMap: DoMoveCursor
+GameOptions: DoGameOptions <-> DoNaming <-> DoSettings <-> DoQuitMenu
+ <-> DoPickGame
+GetArmadaStarShip: DoPickBattleShip
+StartGame: DoRestart
+ScanSystem: DoScan <-> PickPlanetSide <-> DoPlanetSide
+InitEncounter: DoSelectAction
+TFB_VideoInput: TFB_DoVideoInput
+
+These are called from the following points:
+
+InitCommunication: Called by the outtakes, the starbase, and the gen*
+ files; generally anywhere where you need to
+ transfer over to a conversation sequence.
+ RaceCommunication () is the toplevel call for
+ starcon.c.
+
+DoMenuOptions: Called by the postprocess operation for the sis_ship.
+
+ExploreSolarSys: Called by Starcon2Main.
+
+VisitStarBase: Called by Starcon2Main.
+
+DoStarMap: Called internally by DoFlagshipCommands.
+
+GameOptions: Called internally by DoOutfit, DoShipyard,
+ DoSelectAction, and DoFlagshipCommands. Basically
+ handles the GAME menu wherever it appears.
+
+GetArmadaStarShip: Called internally by
+ GetEncounterStarShip. UninitShips calls
+ GetEncounterStarShip, apparently to do cleanup.
+ Everyone else calls GetNextStarShip. This is done
+ by the Battle() routine, and by the new_ship
+ function, which is assigned as a "death_func" for
+ normal ships, and a "preprocess_func" for dead
+ ships.
+
+StartGame: Called by Starcon2Main.
+
+ScanSystem: Called by DoFlagshipCommands
+
+InitEncounter: Called by InitCommunication when the player has an option.
+
+TFB_VideoInput: UQM's game logic will reach it by calling DoFMV.
+
+Starcon2Main
+------------
+
+This is a loop for the game. Right now I just have a list of
+top-level calls, and a list of checks made against
+GLOBAL(CurrentActivity). The actual significance of these things
+comes later.
+
+Activities: CHECK_LOAD, START_ENCOUNTER, CHECK_ABORT,
+ IN_INTERPLANETARY, START_INTERPLANETARY, WON_LAST_BATTLE,
+ CHECK_RESTART, IN_HYPERSPACE
+
+Game states: CHMMR_BOMB_STATE, GLOBAL_FLAGS_AND_DATA, KOHR_AH_KILLED_ALL
+
+Functions: Logo(), StartGame(), VisitStarBase(), RaceCommunication(),
+ ExploreSolarSys(), Battle(). Note that Battle() here is
+ actually for hyperspace travel. Super Melee combat is
+ handled in DoMelee, and full-game combat is handled by
+ EncounterBattle, called by InitCommunication.
diff --git a/doc/devel/livecd b/doc/devel/livecd
new file mode 100644
index 0000000..a3e3bb9
--- /dev/null
+++ b/doc/devel/livecd
@@ -0,0 +1,80 @@
+This file contains notes about considerations for developing a live CD
+for UQM.
+
+
+Information on creating boot CD's:
+- http://www.geocities.com/potato.geo/bootlinuxcd.html
+- man mkisofs
+- http://linuxfromscratch.org/~jhuntwork/livecd/
+- http://www.tldp.org/HOWTO/Bootdisk-HOWTO/
+
+
+Desirable features:
+- Both Linux native mode (when booting directly from the CD),
+ an MS Windows autorun. And perhaps even MacOS boot.
+- install from the CD (for Windows)
+- Support saving
+
+
+- Needed software:
+ - boot loader
+ syslinux: ftp://ftp.kernel.org/pub/linux/utils/boot/syslinux/
+ - init (simpleinit?)
+ - libc (including libpthread) (uClibc)?
+ - libSDL
+ - libSDL_image
+ - libpng (needed for libSDL_image)
+ - libvorbisfile
+ - libvorbis (for libvorbisfile)
+ - libogg (for libvorbis)
+ - libmikmod
+ - libm (for libvorbis)
+ - libz (for zipped content)
+ - syslogd+klogd?
+
+ Some audio library:
+ - libasound (prefered)
+
+ Some graphics output: one of
+ - x11 (larger than necessary)
+ - nanox
+ - fbcon (may be the best solution)
+ - directfb
+ - svgalib (directly accesses the VGA card. No acceleration support)
+ (may be the most portable)
+ Having hardware acceleration support may be useful.
+
+ For basic debugging:
+ - sulogin
+ - sash or busybox (minimal features)
+
+ For hardware detection:
+ - udev
+ - ...
+
+- Other needed files:
+ - boot scripts
+ - /etc/ld.so.conf
+ - ...
+
+- Sources
+ Sources must be provided in some way (check the licenses of the
+ dependencies).
+
+- Warranty disclaimer on boot
+
+
+For saving:
+- USB memory stick and other memory devices
+- Windows hard drive (where to put the files?)
+- Linux hard drive (where to put the files?)
+- Floppies? Probably not worth it.
+
+
+
+Tools for testing before burning:
+- vmware
+- QEMU (http://fabrice.bellard.free.fr/qemu/)
+
+
+
diff --git a/doc/devel/meleeteams b/doc/devel/meleeteams
new file mode 100644
index 0000000..524f2cf
--- /dev/null
+++ b/doc/devel/meleeteams
@@ -0,0 +1,56 @@
+This file describes the binary file format for SuperMelee teams.
+The saving and loading is handled in sc2code/loadmele.c.
+
+This file format will be replaced by a human-readable format in the future.
+
+A SuperMelee team consists of the following data:
+ struct TEAM_IMAGE {
+ 0 - 13 uint8_t ShipList[MELEE_FLEET_SIZE];
+ /* Originally 6*2 ships, now 7*2 */
+14 - 68 char TeamName[MAX_TEAM_CHARS + 1 + 24];
+ /* \0-terminated */
+ };
+
+The file melee.cfg contains:
+ 0 - 0 Player Control for the bottom player
+ One of:
+ 1 - Human controlled
+ 6 - Computer controlled
+ 8 - Network controlled (added later)
+ or'ed with one of:
+ 16 - Standard computer control rating (was 8 originally)
+ 32 - Good computer control rating (was 16 originally)
+ 64 - Awesome computer control rating (was 32 originally)
+ 1 - 69 TEAM_IMAGE for the top player, as described above
+70 - 70 Player Control for the top player, as byte 0
+71 - 139 TEAM_IMAGE for the bottom player, as described above
+Note the mixed up order for top and bottom player.
+
+The entries in ShipList are:
+ 0 - Androsynth Guardian
+ 1 - Arilou Skiff
+ 2 - Chenjesu Broodhome
+ 3 - Chmmr Avatar
+ 4 - Druuge Mauler
+ 5 - Human Earthling
+ 6 - Ilwrath Avenger
+ 7 - Kohr-Ah Marauder
+ 8 - Melnorme Trader
+ 9 - Mmrnmhrm X-Form
+ 10 - Mycon Podship
+ 11 - Orz Nemesis
+ 12 - Pkunk Fury
+ 13 - Shofixti Scout
+ 14 - Slylandro Probe
+ 15 - Spathi Eluder
+ 16 - Supox Blade
+ 17 - Syreen Penetrator
+ 18 - Thraddash Torch
+ 19 - Umgah Drone
+ 20 - Ur-Quan Dreadnought
+ 21 - Utwig Jugger
+ 22 - VUX Intruder
+ 23 - Yehat Terminator
+ 24 - Zoq Fot Pik Stinger
+255 - Unused slot
+
diff --git a/doc/devel/musicres b/doc/devel/musicres
new file mode 100644
index 0000000..780ee2b
--- /dev/null
+++ b/doc/devel/musicres
@@ -0,0 +1,18 @@
+This is the format for resources of type MUSICRES (these have the extension
+.mod in the original source).
+
+If the first 3 characters are 'CDA', from the next character to the first
+nul char, or 3 characters after the first period (whatever comes first) is
+used as a filename with aif audio data to play. (CDA stands for CD-audio.
+The source code mentions 'red book', but it's not (red book is the standard
+for audio CD's)). The rest of the file (just a few more chars) seems to be
+garbage.
+
+If the first 3 characters are something else, the entire file (including the
+first 3 characters) are used as a MOD file. (As the mod file uses those char
+as the title of the song, a mod file with the title beginning with 'CDA'
+will go wrong).
+
+
+Initial version of this file 2002-10-26 by Serge van den Boom.
+
diff --git a/doc/devel/netplay/notes b/doc/devel/netplay/notes
new file mode 100644
index 0000000..afac628
--- /dev/null
+++ b/doc/devel/netplay/notes
@@ -0,0 +1,21 @@
+As the game currently unfortunately works with polling, this is how
+to integrate network handling with the game.
+
+In the function called periodically by DoInput(), there should be a call
+to netInput() somewhere at the beginning, and a call to flushPacketQueues()
+somewhere at the end.
+
+netInput() checks all connections for incoming packets, and calls
+the appropriate packet handlers.
+
+flushPacketQueues() sends all pending packets on their way, for all
+connections.
+
+In between, you can call functions that enqueue packets.
+You would also check the network state here to determine whether you need to
+act on some packet that has been delivered to the local party.
+
+
+
+
+
diff --git a/doc/devel/netplay/protocol b/doc/devel/netplay/protocol
new file mode 100644
index 0000000..5b82a1f
--- /dev/null
+++ b/doc/devel/netplay/protocol
@@ -0,0 +1,336 @@
+There are several types of negotiations used to synchronised the parties
+of a network connection.
+
+- Continue when we know the other is ready ("Ready")
+ This is used when both parties need to sending information to the
+ other side, but what each party is doing does not interfere with
+ what the other party is doing.
+- Agree on changes ("Update")
+ This is used when the parties have changes to make to common data.
+- Mutual agreement on an action ("Confirm")
+ This is used to end a state where both parties are modifying
+ common data. Both parties have to agree with the data for either
+ party to continue.
+- Reset a connection. This is used to abort a game in progress and return
+ to the fleet setup menu.
+
+============================================================================
+
+"Ready" negotiation.
+
+Sometimes the parties need to notify eachother of their local state,
+and then go on when both are ready. For this purpose both parties signify
+that they are ready by sending the READY message. When a party is ready
+and has received notice that the other party is ready, it can go on.
+
+States:
+0. notReady - send nor received READY
+ !localReady && !remoteReady
+1. localReady - sent READY, not yet received READY
+ localReady && !remoteReady
+2. remoteReady - received READY, not yet sent READY
+ !localReady && remoteReady
+3. ready - sent and received READY
+
+Messages:
+- READY - "I have nothing further to send at this point"
+
+
+From state 0 (notReady):
+local decision -> Send READY, goto 1
+received READY -> goto 2
+
+From state 1 (localReady):
+received READY -> goto 3
+
+From state 2 (remoteReady):
+local decision -> Send READY goto 3
+
+
+============================================================================
+
+"Update" negotiation.
+
+During configuration, both sides may change the same properties. So that the
+two sides don't have to take turns, the changes are made locally, and then
+the changes are synchronised.
+
+To this end, each side has a state containing two copies of each property:
+ 1. The value of the property as it is locally
+ 2. The last value which it sent to the other side (until it is no longer
+ relevant for the protocol)
+
+The basic idea of the Update protocol is:
+- both sides each send and receive one packet before being allowed to
+ send another one (we'll call this a "turn" here)
+- when a packet has been sent and one has been received in a turn,
+ and the sent and received values are the same, then that value is the
+ agreed upon value. If the sent and received values differ, then a
+ tie breaker determines which one prevails.
+- when the first local change of a turn is made, the change is sent to
+ the other side
+- when a local change has been made while a packet has already been
+ sent this turn, the change will be made locally, but communicating the
+ change will be postponed
+- when a turn ends while a change has been postponed, and this change isn't
+ negated by a remote change, then the change will be sent
+- when a remote change arrives, and a packet has not been sent this turn,
+ the local state is updated with the change, and the same packet is sent
+ to the other side to confirm the change
+
+The tie breaker is required to always let one side win, and the other
+side lose, given the same property.
+Any function satisfying this requirement is usable, but the currently
+used one will return true for the side which 'owns' the property, and
+false on the other side.
+Another tie breaker could be one which always lets the same side win,
+regardless of the property.
+
+
+The protocol:
+
+From state 1, {own=x0, sent=--}:
+1a Local change x1 -> send(x1); state=2:{own=x1,sent=x1}
+1b Received UPDATE(x1) -> send(x1); state=1:{own=x1,sent=--}
+
+From state 2, {own=x0, sent=x0}:
+2a Local change x1 -> state=3:{own=x1,sent=x0}
+2b Received UPDATE(x0) -> state=1:{own=x0,sent=--}
+2c+ Received UPDATE(x1) -> state=1:{own=x0,sent=--} if winTieBreak
+2c- Received UPDATE(x1) -> state=1:{own=x1,sent=--} if !winTieBreak
+
+From state 3, {own=x1, sent=x0}:
+3a Local change x0 -> state=2:{own=x0,sent=x0}
+3b Local change !x0 -> state=3:{own=xN,sent=x0}
+3c Received UPDATE(x0) -> send(x1); state=2:{own=x1,sent=x1}
+3d+ Received UPDATE(!x0) -> send(x1); state=2:{own=x1,sent=x1} if winTieBreak
+3d- Received UPDATE(!x0) -> state=1:{own=x?,sent=--} if !winTieBreak
+
+
+Explanation:
+We keep track of the local value ('own'), and whether or not we sent a packet
+in this turn ('sent' != '--'), and if we did, the last packet which we sent
+('sent'). When we proceed to the next turn, 'sent' is set to '--'.
+
+State 1: We have not yet sent a packet this turn (which implies that we
+haven't made a local change this turn).
+1a. A local change is made. We update our local value, and send this to
+ the remote side.
+1b. A remote change arrives. We don't have any local change ourselves,
+ so we accept the remote change, and sent it back to confirm.
+ With both a packet sent and one received, the turn ends, and 'sent'
+ is set back to '--'.
+
+State 2: We have sent a packet this turn (after making a local change), and
+have not changed our local value since (or we have changed it and changed
+it back).
+2a. A local change is made. We update our local value, but we have already
+ sent a packet this turn, so we can't report it until the next turn.
+2b. A remote change arrives, and it is equal to both our local value and
+ the value which we sent to the other side this turn.
+ This remote notification may be a confirmation of our update, or a
+ coincidental identical remote change.
+ Either way, the packet acts as a confirmation, and we do not need
+ to change anything. With both a packet sent and received, the turn ends
+ and so 'sent' is set back to '--'.
+2c. A remote change arrives, and it is not equal to our local value (which
+ is the same as the value which we sent).
+ The tie breaker decides which value prevails. The same value will
+ prevail on the remote side, so there is no need to send any confirmation
+ packets. The turn ends, and 'sent' is set back to '--'.
+
+State 3: We have sent a packet this turn, and have made a local change since.
+3a. A local change is made back to the value which we sent. No action
+ is required.
+3b. A local change is made. We update our local value, but we have already
+ sent a packet this turn, so we can't report it until the next turn.
+3c. A remote change arrives, and it is equal to the value which we sent
+ (which isn't equal to the current local value).
+ The sent/received value is the accepted value and the turn ends.
+ But we have changed our value since already, so as the next turn
+ starts, we immediately send an update.
+3d. A remote change arrives, and it is not equal to the value which we sent
+ this turn.
+ + We win the tie break, so the value which we sent prevails.
+ But we have changed our value since already, so as the next turn
+ starts, we immediately send an update.
+ - We lose the tie break, so the value which the remote value sent
+ prevails. We accept the value and the turn ends.
+ (An alternative would be to consider the local change(s) which
+ we made after our change was sent as made in the following turn,
+ in which case we would send our current local value in the next turn.
+ The advantage would be that 3d- would become equal to 3d+,
+ so these could be joined (with 3c too), which saves a few
+ if-statements in the code, but this requires another packet to be sent
+ and replied to in what currently is 3d-.)
+
+
+Proof outline that this works:
+- The states can never get out of sync:
+ After both a packet has been sent and received, both sides (temporarilly)
+ have accepted the same value
+- There can be no indefinite loop without ongoing local changes.
+ Once there are no more local changes:
+ from state 3 we always go to state 2 or 1,
+ from state 2 we always go to state 1
+ from state 1 we can only go back to state 1, and only when a packet
+ has been received.
+ So eventually, both sides are in states 1. With both sides in state 1,
+ no packets have been sent in the current turn, so no more packets are
+ there to be received.
+- Both sides can make changes, as long as the side which wins the tie breaks
+ stops making changes now and then:
+ Without making local changes, a side will go to state 1 eventually.
+ If a side makes a local change, and this is received by the other side
+ while it is in state 1, then that other side will accept the change.
+
+
+The Confirm negatiation is used to finish the Update negotiation.
+This works because local changes triggered by the reception of remote changes
+are treated as local modifications for the purpose of the Confirm negotiation.
+And this can be done because whenever the Confirm negotiation is in a state
+where remote changes may be expected, local modifications are still allowed
+(possily after sending a CANCEL packet).
+
+
+============================================================================
+
+"Confirm" negotiation.
+
+Some actions (like agreeing on a configuration) require confirmation
+from both parties. This section documents the handshaking protocol involved.
+
+Each player must manually confirm the action.
+After a player has confirmed an action, he may cancel it as long as
+he hasn't received a confirmation from the other party.
+
+All messages arrive in the order sent.
+
+
+States:
+0. waiting
+ !handshake.canceling && !handshake.localOk && !handshake.remoteOk
+1. localOk (cancelable) - sent CONFIRM1 (since last CANCEL)
+ !handshake.canceling && handshake.localOk && !handshake.remoteOk
+2. remoteOk (cancelable) - received CONFIRM1
+ !handshake.canceling && !handshake.localOk && handshake.remoteOk
+3. committed - sent CONFIRM1 (since last CANCEL,
+ received CONFIRM1,
+ sent CONFIRM2 (since last CANCEL)
+ !handshake.canceling && handshake.localOk && handshake.remoteOk
+4. cancelWaiting - sent CANCEL
+ handshake.canceling && !handshake.localOk && !handshake.remoteOk
+5. cancelLocalOk - sent CANCEL and ready to send CONFIRM1,
+ but received no CANCELACK
+ handshake.canceling && handshake.localOk && !handshake.remoteOk
+6. cancelRemoteOk - sent CANCEL and received CONFIRM1,
+ but received no CANCELACK
+ handshake.canceling && !handshake.localOk && handshake.remoteOk
+7. cancelCommitted - sent CANCEL and ready to send CONFIRM2,
+ received CONFIRM1,
+ but received no CANCELACK
+ handshake.canceling && handshake.localOk && handshake.remoteOk
+8. done - sent and received CONFIRM1 and CONFIRM2
+ (since last CANCEL)
+
+
+Handshake messages:
+- CONFIRM1 - "the current local configuration is OK for me"
+- CONFIRM2 - "acknowledging your CONFIRM1; my own configuration is unchanged
+ since I sent CONFIRM1 (after the last CANCEL)"
+- CANCEL - "forget about my earlier CONFIRM1"
+- CANCELACK - "received your CANCEL"
+MESSAGE(x) indicates any other message.
+
+
+From state 0: (waiting)
+local confirmation -> Send CONFIRM1, goto 1
+local changes -> Send MESSAGE(changes) (goto 0)
+received CONFIRM1 -> goto 2
+received MESSAGE(changes) -> Process(changes) (goto 0)
+
+From state 1: (localOk)
+local cancel -> Send CANCEL, goto 4
+received CONFIRM1 -> Send CONFIRM2, goto 3
+received CONFIRM2 -> Send CONFIRM2, goto 8
+received MESSAGE(changes) -> Send CANCEL, Process(changes), goto 4
+
+From state 2: (remoteOk)
+local confirmation -> Send CONFIRM2, goto 3
+local changes -> Send MESSAGE(changes), (goto 2)
+received CANCEL -> Send CANCELACK, goto 0
+
+From state 3: (committed)
+received CONFIRM2 -> goto 8
+received CANCEL -> Send CANCELACK, goto 1
+
+From state 4: (cancelWaiting)
+local changes -> Send MESSAGE(changes), (goto 4)
+local confirmation -> goto 5
+received CONFIRM1 -> goto 6
+received CONFIRM2 -> goto 6
+received CANCELACK -> goto 0
+received MESSAGE(changes) -> Process(changes), (goto 4)
+
+From state 5: (cancelLocalOk)
+local cancel -> goto 4
+received CONFIRM1 -> goto 7
+received CONFIRM2 -> goto 7
+received CANCELACK -> SEND CONFIRM1 goto 1
+received MESSAGE(changes) -> Process(changes), goto 4
+
+From state 6: (cancelRemoteOk)
+local confirmation -> goto 7
+local changes -> Send MESSAGE(changes), (goto 6)
+received CONFIRM2 -> (goto 6)
+received CANCEL -> Send CANCELACK, goto 4
+received CANCELACK -> goto 2
+
+From state 7: (cancelCommitted)
+received CONFIRM2 -> (goto 7)
+received CANCEL -> Send CANCELACK, goto 5
+received CANCELACK -> Send CONFIRM2, goto 3
+
+On receiving local confirmation, sending CONFIRM2 is a shortcut for
+sending CONFIRM1 followed by CONFIRM2. Receiving CONFIRM2 from localOk
+and cancelLocalOk is accepted just for this shortcut.
+
+
+To prove there are no race conditions, I examine all the combinations
+of states and messages that are underway. Whenever the order of actions
+isn't fixed, the result should be the same (eg. recv(CONFIRM1) followed
+by send(CANCEL) should leave the party in the same state as when
+the send(CANCEL) preceded the recv(CONFIRM1)).
+I also check whether it is possible for packets to arrive that
+aren't expected.
+
+
+============================================================================
+
+"Reset" negotiation.
+
+See src/sc2code/netplay/proto/reset.c
+
+
+============================================================================
+
+Battle ending negotiation.
+
+This negotation consists of:
+1. a 'Ready' negotiation, before stopping sending frame data
+2. communication of each sides current battle frame count
+3. the side running behind processes more frames to catch up
+
+
+States:
+
+0. Playing
+1. localReady
+2. remoteReady
+3. countSent
+4. catchingUp
+5. awaitingCatchup
+
+// Unfinished... partially described in readyForBattleEndPlayer()
+
+
diff --git a/doc/devel/netplay/states b/doc/devel/netplay/states
new file mode 100644
index 0000000..9727172
--- /dev/null
+++ b/doc/devel/netplay/states
@@ -0,0 +1,178 @@
+== Any connected state ==
+Some packets may be sent and received in any state except
+NetState_unconnected.
+These are: PING, ACK, ABORT, RESET
+These are not listed below at each individual state.
+
+Whenever a connection is aborted, the state is returned to
+NetState_unconnected. This state transition is not listed below at each
+individual state.
+
+
+== NetState_unconnected ==
+NetState_unconnected is the initial state.
+
+NetConnection.state: NULL
+
+Packets ok to send: none
+Packets ok to receive: none
+
+Next state:
+ NetState_connecting -- connection attempt in progress
+
+
+== NetState_connecting ==
+NetState_connecting indicates that a connection is in progress.
+
+When the connection is established, the state is changed to NetState_init
+and InputFunc is set to DoNetworkInit.
+
+NetConnection.state: instance of ConnectStateData
+
+Packets ok to send: none
+Packets ok to receive: none
+
+Next state:
+ NetState_init -- connection established
+
+
+== NetState_init ==
+NetState_init is for initialising the connection before actual game
+information is sent.
+
+As this state is entered, an INIT packet is sent. When an INIT packet
+has also been received, the state is set to NetState_inSetup and
+InputFunc is set to DoMelee.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: INIT
+Packets ok to receive: INIT
+
+Next state:
+ NetState_inSetup -- received an INIT packet
+
+
+== NetState_inSetup ==
+NetState_inSetup is the state in which the fleet configuration is negotiated.
+
+This does not necessarilly mean that the fleet setup screen is visible;
+this state is also held after a battle when the battle outcome is still
+displayed.
+
+Each side may send fleet configuration changes to the other side, by means of
+FLEET and TEAMNAME packets. Agreement on configuration settings is provided
+through the Update negotiation.
+The Confirm negotiation is used to end this state and go to
+NetState_preBattle. At this time InputFunc is set to DoPreMelee.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: FLEET, TEAMNAME, HANDSHAKE0, HANDSHAKE1,
+ HANDSHAKECANCEL, HANDSHAKECANCELACK
+Packets ok to receive: FLEET, TEAMNAME, HANDSHAKE0, HANDSHAKE1,
+ HANDSHAKECANCEL, HANDSHAKECANCELACK
+
+Next state:
+ NetState_preBattle -- configuration has been confirmed
+
+
+== NetState_preBattle ==
+NetState_preBattle is used for non-interactive battle negotiations.
+
+One side sends the random seed; the other receives it.
+Both sides send their input delay value.
+The Ready negotiation is used to end this state and go to
+NetState_interBattle.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: SEEDRANDOM, INPUTDELAY, READY
+Packets ok to receive: SEEDRANDOM, INPUTDELAY, READY
+
+Next state:
+ NetState_interBattle -- ready to continue
+
+
+== NetState_interBattle ==
+NetState_interBattle is used to allow each side to do some local
+initialisations before moving on.
+The Ready negotiation is used to end this state and go to
+NetState_selectShip, or if all sides have selected a ship, to
+NetState_inBattle, or if there are no more ships in a fleet,
+to NetState_inSetup.
+
+If there are no more ships, the the Ready negotiation is used to
+enter NetState_inSetup.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: READY
+Packets ok to receive: READY
+
+Next state:
+ NetState_selectShip -- ready to select the next ship
+ NetState_inBattle -- ready to start the battle
+ NetState_inSetup -- no more ships; game over
+
+
+== NetState_selectShip ==
+NetState_selectShip is where a side may select their ship. The other
+side is waiting for notice of this selection.
+As soon as the selection has been sent or received, the state is changed
+back to NetState_interBattle.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: SELECTSHIP
+Packets ok to receive: SELECTSHIP
+
+Next state:
+ NetState_interBattle -- a selection has been made
+
+
+== NetState_inBattle ==
+NetState_inBattle is where the actual melee takes place.
+Both sides send their input until the game is over, at which point
+the Ready negotiation is used to end this state and go to
+the NetState_endingBattle state. Until the Ready negotiation has been
+completed, the simulation is continuing.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: BATTLEINPUT, READY
+Packets ok to receive: BATTLEINPUT, READY
+
+Next state:
+ NetState_endingBattle -- ready to end the battle
+
+
+== NetState_endingBattle ==
+NetState_endingBattle is where the local side waits for the remote
+battle frame count, after it has sent its own. When it arrives,
+the state changes to NetState_endingBattle2.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: BATTLEINPUT, FRAMECOUNT
+Packets ok to receive: BATTLEINPUT, FRAMECOUNT
+
+Next state:
+ NetState_endingBattle2 -- we know when to end the battle
+
+
+== NetState_endingBattle2 ==
+NetState_endingBattle2 is where the side with the lowest battle frame count
+catches up with the other other side, while the other side waits.
+The Ready negotiation is used to signal that each side is ready,
+and the state changes to NetState_interBattle.
+
+NetConnection.state: instance of BattleStateData
+
+Packets ok to send: BATTLEINPUT, READY
+Packets ok to receive: BATTLEINPUT, READY
+
+Next state:
+ NetState_interBattle -- get ready for the next ship
+
+
diff --git a/doc/devel/netplay/todo b/doc/devel/netplay/todo
new file mode 100644
index 0000000..33102c7
--- /dev/null
+++ b/doc/devel/netplay/todo
@@ -0,0 +1,120 @@
+High priority items:
+
+
+Medium-priority:
+- For the battle ending synchronisation, set the end at at least
+ getBattleInputDelay() + 1 frames in the future (instead of just 1),
+ so that there will be no hickup during the end synchronisation.
+ Also check this value for incoming packets.
+- If a player only moves away from 'Battle!' there's no need for the other
+ to have to reconfirm.
+- decent pause handling
+- make compilation of crc.c and checksum.c conditional.
+- negotiate checksum interval
+- Closing and destroying of NetConnections is a terrible mess.
+- Checks allConnected() shouldn't be needed if CHECK_ABORT is set
+ on disconnect.
+- Shorten or lengthen the time between frames depending on how full the input
+ buffer is, to prevent one side constantly stalling when the connection in
+ one direction is slower than in the other direction.
+
+
+Low-priority:
+- Some difference in pictures to indicated confirmed/unconfirmed.
+ A check mark perhaps.
+- Check whether the random seed and frame delay have been agreed before
+ continuing (in doConfirmSettings).
+- Replacement for TOS. It is IPv4 only.
+- Integrate network check functions with doInput
+ It will be easy to get rid of the separate threads then too.
+- The state changes from interBattle to interBattle. That shouldn't happen,
+ but it doesn't seem to cause any problems. Need to investigate.
+ Addition: negotiateReadyConnections() is called again just to make sure
+ all sides pass this checkpoint. This is not a problem. It should be
+ documented in STATES though.
+- More files define NETCONNECTION_INTERNAL than they should.
+- voice transmission during the game (using an external lib)
+ read ramjee94adaptive.pdf
+- Keep-alive packets. Store time of last packet sent, use alarms to determine
+ when to send the next one. Count received messages?
+- Some way to (optionally) hide your fleet setup until the start of the
+ game? With either a previously determined maximum fleet value, or
+ just display a number to the opponent.
+- (when ships stats are in the content) Negotiate the ship stats, so people
+ can play with non-default ships.
+- Pre-fleet-setup setup
+ The values negotiated could include handicapping (different values for
+ each player)
+ - hide fleets (also handicapping here)
+ - maximum number of ships per side
+ - maximum ship cost per side
+ - ship properties
+- Send taunts (prerecorded samples) at the touch of a button?
+ Use taunt add-on packages? Send the opponent's package before the start
+ of a game?
+
+
+Future improvements/optimisations:
+- For BSD sockets: use dup2() to move fds to lower values, so that less fds
+ have to be checked on select().
+- Use writev() to send multiple packets in one syscall, instead of
+ calling send() for each packet.
+- Refusing games with both parties network controlled is not always
+ necessary. In theory it should be possible to have a client work
+ as "proxy". The client can watch as the actual players play the game.
+ Checking for "loops" would be tricky (eventually there should be a human
+ or computer controller for each side).
+- Concurrent selection of ships. Note that if this is handled properly, it
+ will also be easy to take care of the "Allow Shofixti to choose last" bug.
+ Note that one party will still have to send his choice to the other side
+ first, which may be "exploited". Encryption would take care of this,
+ but at the least make sure the same player who gets to chose first
+ every time.
+- meta-server. Use HTTP? Existing libs can be used, no problems with NAT,
+ human-readable. Speed is not an issue.
+- move to UDP. Repeat past battleinput packets for each new packet that
+ is sent until they are confirmed.
+- Once the protocol is stable: register the port number with IANA
+- Per state data (in NetConnection.stateData) is unnecessarilly complicated.
+ Putting all fields directly in NetConnection simplifies things a lot.
+ It's not as generic, but this code won't be used elsewhere anyhow.
+- Send captain names to the other side
+
+
+Bugs:
+- as positions are dependant on the screen resolution, you won't be able
+ to keep sync on games with a different resolution.
+- collision detection is dependant on the images used. Using different
+ graphics packs will result in a desync.
+- Both sides need identical battle frame rates. This value is not
+ negotiated.
+- If one player closes the connection while the other player is selecting
+ a ship to put in his fleet, or loading a fleet, the "Network Control"
+ button won't be updated.
+- If one side is computer-controlled, sync loss will occur, because the AI
+ uses the RNG. It needs its own RNG context.
+- Pressing F10 to exit the supermelee setup when a connection is active
+ will cause an attempt to draw a NULL frame. (possibly the disconnect
+ feedback)
+
+
+Final actions:
+- Check out TODO, XXX, WORK tags
+- memleak testing
+ - check for and remove mtrace()/muntrace() calls.
+- update documentation
+ - FILES, also note which part of the separation they are in
+- check coding style (search for '\>(')
+- compile with maximum warnings
+
+
+To put in the announcement of Netplay:
+- Slow connections is acceptable. Packet loss isn't.
+
+
+Bugs and todos unrelated to netplay.
+- other player being able to choose the next ship after 3 seconds
+ of inactivity
+- DoRunAway() shouldn't be handled in ProcessInput()
+
+
diff --git a/doc/devel/orggenerate b/doc/devel/orggenerate
new file mode 100644
index 0000000..fce2207
--- /dev/null
+++ b/doc/devel/orggenerate
@@ -0,0 +1,133 @@
+Note: this file describes the way in which various things were generated
+in the original source. See the file 'generate' for the current system.
+
+============================================================================
+
+The various universe related game data is generated through a call
+to a solar system dependant generation function.
+This function is of type PLAN_GEN_FUNC, which is a typedef to
+ void (*PLAN_GEN_FUNC) (BYTE control)
+, where the 'control' argument specifies what type of data needs to be
+generated (one of GENERATE_PLANETS, GENERATE_MOONS, GENERATE_ORBITAL,
+INIT_NPCS, REINIT_NPCS, UNINIT_NPCS, GENERATE_MINERAL, GENERATE_ENERGY,
+GENERATE_LIFE, or GENERATE_NAME).
+
+The generation function for a solar system is kept in the 'GenFunc'
+field of the SOLARSYS_STATE structure.
+The SOLARSYS_STATE structure contains the data for a solar system.
+Currently, only one SOLARSYS_STATE structure is used at once, and
+the global variable pSolarSysState points to the current one.
+The GenFunc field is initialised in ExploreSolarSys(), to the value
+returned by GenerateIP() in sc2code/gendef.c. Usually, this will be
+'GenerateRandomIP', but for some specific solar systems a (pointer to a)
+custom generation function is returned. This depends on the value of
+CurStarDescPtr->Index, which contains values such as SOL_DEFINED,
+MELNORME0_DEFINED, AQUA_HELIX_DEFINED, etc (see sc2code/encount.h
+for the complete list).
+The starmap_array in sc2code/plandata.c specifies Index for all
+the solar systems in the game.
+
+Following are the possible values of the 'control' argument to the
+generation function, with the description of how the generation function
+acts on this. As the custom generation functions often only need to
+change one specific aspect of this game data generation, they will
+often call GenerateRandomIP() for the rest.
+StarBases are handled as if they were moons.
+
+GENERATE_PLANETS
+Pre: the global variable pSolarSysState points to the relevant solar system.
+Pre: the RNG is initialised with a seed to be used for the generation.
+ In practice, this seed is generated from the HyperSpace coordinates
+ of the solar system (which are hardcoded in sc2code/plandata.c),
+ followed by exactly one call to TFB_Random().
+Post: the RNG is in an undefined state.
+This function determines how many planets the system has, and fills in
+pSolarSysState->PlanetDesc[] for all planets, including the NumPlanets
+field, which determines how many moons the planet will have.
+It also sets the random seed that is used for data generated for this planet
+(including the number of moons), based on its coordinates (which are in the
+general case randomly determined themselves).
+
+GENERATE_MOONS
+Pre: the global variable pSolarSysState points to the relevant solar system,
+ which is initialised by a GENERATE_PLANETS call.
+Pre: the RNG is initialised with a seed to be used for the generation.
+ In practice, this seed is the seed stored by GENERATE_PLANETS
+ in the rand_seed field for the planet around which the moon(s) orbit.
+Pre: pSolarSysState->pBaseDesc points to the the relevant planet
+ of pSolarSysState->PlanetDesc[].
+Post: The RNG is in an undefined state.
+This function fills in pSolarSysState->MoonDesc[] for all moons around
+the planet pointed to by pSolarSysState->pBaseDesc.
+It also sets the random seed that is used for data generated for the moon
+based on its coordinates (which are in the general case randomly determined
+themselves).
+
+GENERATE_ORBITAL
+Pre: the global variable pSolarSysState points to the relevant solar system,
+ which is initialised by a GENERATE_PLANETS call.
+Pre: pSolarSysState->pOrbitalDesc points to the relevant planet or moon from
+ pSolarSysState->PlanetDesc[] or pSolarSysState->moonDesc[]
+Pre: the planet or moon that pSolarSysState->pOrbitalDesc points to
+ is initialised by a GENERATE_PLANETS or GENERATE_MOONS call.
+This function fills in pSolarSysState->SysInfo with the characteristics
+of the planet or moon, as seen from orbit.
+It also initialises the random seeds used for the generation of bio,
+minerals, and energy nodes on the surface.
+It also sets the discovery report string (if appropriate), initialises
+the surface graphics, and start the planet music.
+For specific planets, it may initiate race communication and possibly combat,
+and will only return once these are over.
+NB. The GENERATE_ORBITAL code should be split up into separate calculation
+and activation (graphics and music) parts.
+
+GENERATE_MINERAL, GENERATE_ENERGY, GENERATE_LIFE
+Pre: the global variable pSolarSysState points to the relevant solar system,
+ which is initialised by a GENERATE_PLANETS call.
+Pre: pSolarSysState->pOrbitalDesc points to the relevant planet or moon from
+ pSolarSysState->PlanetDesc[] or pSolarSysState->moonDesc[]
+Pre: the planet or moon that pSolarSysState->pOrbitalDesc points to
+ is initialised by a GENERATE_PLANETS or GENERATE_MOONS call.
+Pre: pSolarSysState->SysInfo is filled in by a GENERATE_ORBITAL call
+This function determines the properties of one mineral deposit, energy node,
+or life form on a planet or moon. On entry the caller sets
+pSolarSysState->CurNode to the index of the requested item. This function
+will then fill in pSolarSysState->SysInfo.PlanetInfo.CurPt,
+pSolarSysState->SysInfo.PlanetInfo.CurType, and in the case of minerals also
+pSolarSysState->SysInfo.PlanetInfo.CurDensity.
+In case pSolarSysState->CurNode is set to a value larger than or equal to
+the number of items of the requested kind ((COUNT) ~0 in practice), it is
+set to the real number of nodes. In this case the CurXXX fields of
+pSolarSysState->SysInfo.PlanetInfo are set to the values corresponding to
+the largest valid CurNode index, but should probably be considered to be
+undefined.
+These functions may also change the game state, cause the lander
+to take off (by setting InTransit to true in the active PLANETSIDE_DESC
+structure), or mark an energy node as not retrieved, usually in response
+to an item having been picked up since the last call. The game makes
+a GENERATE_MINERAL, GENERATE_ENERGY or GENERATE_LIFE call (whatever
+is relevant) for each item right after it is picked up.
+
+GENERATE_NAME
+Pre: pSolarSysState is set to the relevant solar system.
+Pre: The planet is initialised by GENERATE_PLANETS.
+Pre: pSolarSysState->pBaseDesc points to the relevant planet, which should
+ be in the system.
+This function fills GLOBAL_SIS (PlanetName) with the name of the planet
+pointed to by pSolarSysState->pBaseDesc.
+It also sets the GAME_STATE flag BATTLE_PLANET to the type of this planet,
+so that it will be shown appropriately in melee if combat follows.
+There is no generate function for the names of moons. The few moons that
+are named (those in the Sol system), are handled as a special case of
+the routines that prints these names (PrintCoarseScan3DO and
+PrintCoraseScanPC).
+
+INIT_NPCS
+REINIT_NPCS
+UNINIT_NPCS
+[TODO]
+
+
+Initial version of this document created by Serge van den Boom, on 2005-07-11.
+
+
diff --git a/doc/devel/pkgformat b/doc/devel/pkgformat
new file mode 100644
index 0000000..69a2513
--- /dev/null
+++ b/doc/devel/pkgformat
@@ -0,0 +1,174 @@
+The .pkg/.ndx format as used for Starcon.pkg on the 3DO cd.
+Acquired directly from the source.
+
+This format is used both in the file and is memory. There are some
+little differences; where this is this is the case, this is
+mentioned below in square brackets.
+
+Everything is stored LSB first.
+
+This document talks about packaged files.
+Packaged files have multiple resources stored in one file. All the resources
+in one package are stored in a single file, whose name is specified in
+p.packmem. The name may be '\0', in which case the package file itself
+is used.
+Non-packaged files have one resource per file. The name is specified
+in each resource instance info field.
+
+Main header: (resinit.c, _GetResFileData())
+position length meaning
+0x00 2 Whether or not the file is packaged (res_flags)
+ bit 0: 0 - the .pkg file is not packaged
+ 1 - the .pkg file is packaged
+0x02 4 offset from the beginning of the file where the list
+ of package information is stored (packmem_list_offs)
+0x06 4 offset from the beginning of the file where the list
+ of pathnames is stored (path_list_offs)
+0x0a 4 offset from the beginning of the file where the list
+ of file names is stored (file_list_offs)
+0x0e 2 number of packages in the file (num_packages)
+0x10 2 number of types of packages present in the file (num_types)
+0x12 4 length of this header. (index_info.header_len)
+ (unused if the .pkg file is not packaged)
+0x16 8*num_packages:
+ On position i the information for package i + 1 is
+ stored. There is no package 0.
+ 4 p.packmem_info
+ bits 0-7: number of resource types
+ bits 8-20: number of resource instances
+ bits 21-31:
+ For packaged files: index in file_list_offs of the
+ file name for this resource
+ For non-packaged files: unused (0)
+ 4 p.flags_and_data_loc
+ MSB is flags, rest is data_loc,
+ if MSB == 0, then of the data_loc only the low 16 bits
+ are used as a MEM_HANDLE [only in memory],
+ if MSB == 0xff, then the data_loc is an offset in the
+ file to the actual data
+0x16+8*num_packages Type information
+ 2*num_types
+ t.instance_count
+ The number of instances there are of this type.
+ On position i the instance count for type i + 1 is
+ stored. There's no type 0.
+packmem_list_offs (should be directly after the index info):
+ for each of the num_packages packages:
+ for each of the resource types for this package
+ (as in p.packmem_info):
+ 4 bits 0-7: The type number for this type. What that
+ number means isn't specified, and may
+ vary per .pkg file.
+ For the 3DO SC2 starcon.pkg file these are:
+ 0 - not a type
+ 1 - Graphics data (GFXRES)
+ 2 - String data (STRTAB)
+ 3 - Music data (MUSICRES)
+ 4 - Resource index (RES_INDEX)
+ 5 - Code (CODE)
+ bits 8-20: The instance number of the first instance
+ of this type in this package. Every
+ following instance has a number 1 higher
+ than the one before.
+ bits 21-31: number of resources of this type in the
+ package
+ for each of the resource instances for this package
+ (as in p.packmem_info):
+ 2 if this is a packaged file: multiply by 4 to get
+ the length of this resource. [1]
+ if this is a file that is not packaged:
+ index in file_list_offs of the file name for
+ this resource.
+ [the same position is in memory used as a MEM_HANDLE
+ to the actual data]
+path_list_offs (should be directly after the packmem list)
+ ? null terminated path strings, indexed from the file list
+ table
+file_list_offs (should be directly after the path list)
+ ? A number of file info structures. For packaged files,
+ these are indexed from the p.packmem_info (so (at most)
+ one per package). For non-packaged files, these are
+ indexed from an entry for an instance from
+ p.packmem_list (so possibly more per package)
+ These structures are in this form:
+ 13 file info:
+ 2 location relative to path_list_offs of the
+ the path to this file, or 0xffff if no path.
+ 8 file name (8 chars or null-terminated)
+ 3 extension (3 chars or null-terminated)
+
+The following section is present in the package only for packaged files.
+For non-packaged files, the fields below exist in the external file indicated
+by p.packmem_info.file_index.
+For each package p:
+p.data_loc for each type p.t:
+ for each instance p.t.i:
+ p.t.i.length
+ the data for the resource
+ As the length always is a multiple of 4, the last
+ few bytes may not belong to the resource itself.
+ Their content is unspecified.
+
+
+Resources with type 'resource index' (type 4 in the 3DO sc2 starcon.pkg file)
+are files with the same format as this .pkg file itself.
+
+
+The information above, about the format used on the 3DO, is gathered from
+the resource library sources itself.
+The information below, about the format used on the PC, and for SC1,
+is induced from the packed files themselves.
+
+
+The format used in SC1 is somewhat different. The differences are listed
+here:
+0x02 4 offset from the beginning of the file where the list
+ of pathnames is stored (path_list_offs)
+0x06 4 offset from the beginning of the file where the list
+ of file names is stored (file_list_offs)
+0x0a 4 offset from the beginning of the file where the list
+ of package information is stored (packmem_list_offs)
+0x0e 2 number of types of packages present in the file (num_types)
+0x10 2 ?
+0x12 2 number of packages in the file (num_packages)
+0x14 2 ?
+packmem_list_offs
+ 4 as for the 3DO
+ 2 if this is a packaged file: the length of this resource.
+ No multiplication by 2 or 4 as on other platforms. [1]
+
+
+The format used in the DOS version of SC2 is different yet again.
+The differences to the 3DO version are listed here:
+0x10 1 number of types of packages present in the file (num_types)
+0x11 1 unknown
+packmem_list_offs
+ 4 as for the 3DO
+ 2 if this is a packaged file: multiply by 2 to get
+ the length of this resource. [1]
+
+
+[1]. In some of the packaged .pkg files used in the Toys For Bob games,
+the length field of a resource instance is overflowed for some resources,
+even though the use of a multiplier will have had the purpose of preventing
+this. The actual size is a multiple of 0x10000 times the multiplier larger.
+This occurs in 18 packages in the PC version of Star Control II, in
+1 package of the Amiga version of Star Control 1, and in 2 packages of
+the PC version of The Horde. The PC version of Star Control 1, and the 3DO
+version of Star Control II don't have such overflows.
+The actual size of a package can be determined by looking at the
+difference in offset between two subsequent packages.
+Where the package contains only one resource instance, the extra data can
+only belong to that instance. Where a package contains more than one
+resource instance, the extra data most likely belongs to the last resource,
+as an incorrect size results in an incorrect offset for all subsequent
+instances in the package, which would most likely have been noticed due to
+corruption in the game.
+
+
+The part pointed to by file_list_offs is frequently followed by a bit of
+unused space, which may contain garbage.
+
+
+Initial version of this file 2002-10-10 by Serge van den Boom.
+
diff --git a/doc/devel/planetrender b/doc/devel/planetrender
new file mode 100644
index 0000000..aa3c908
--- /dev/null
+++ b/doc/devel/planetrender
@@ -0,0 +1,54 @@
+Planet surface rendering code
+--------------------------------------------------------------
+
+The way the planets appear on the screen (based on planet type
+definition in plandata.c) is determined by several factors:
+
+1. Topography algorithm (PlanetFrame.Type)
+2. Color map (PlanetFrame.CMapInstance)
+3. Elevation -> colormap index xlat table (PlanetFrame.XLatTabInstance)
+4. Base elevation (PlanetFrame.base_elevation)
+5. Several others not covered here: faults, craters (blemishes)
+
+First, the planet topography is generated according to the algorithm
+selected for the planet: TOPO_ALGO, CRATERED_ALGO, GAS_GIANT_ALGO.
+TOPO_ALGO and CRATERED_ALGO use a fractal algorithm to pseudo-randomly
+generate a surface, then 'craters' are added and, at the end, the map is
+dithered for CRATERED_ALGO. GAS_GIANT_ALGO is of course different
+because it needs to produce a series of ring-like structures with
+'storms' added at the end.
+
+The topographical map is a rectangle with each pixel representing an
+elevation level relative to 0, so the topography is expressed in terms
+of elevation relative to base elevation. Base elevation is applied to
+the generated map. Topography is created with elevations -128..127,
+and base elevations, currently, range from 100 to 200 (plandata.c) for
+normal worlds and are always 29 for gas giants. Since the gas giants
+do not have a real topography or a surface to speak of, their surfaces
+are rather simulated with elevations.
+
+At this point, the elevation levels are translated to the actual colors
+using the xlat table and color map. First, the elevation is translated
+to an index into the colormap using the xlat table (.xlt files), and
+after that, the actual RGB color is drawn from the colormap (.ct files)
+at this index.
+
+There are 3 colormaps per instance specified in PlanetFrame. The active
+colormap is selected from these 3 based on the surface temperature of
+the planet: cold, normal and hot. The framework is also laid down for
+3 xlat tables (one per surface temperature), but, AFAIK, distinct xlat
+tables are never actually used. Currently, there is only 1 xlat table
+per instance in each file.
+
+The colormap files (.ct) seem to be in original DOS format. Probably
+because they do not translate to the CLUT system very well. The colormaps
+are carefully crafted and synchronized across all planet types to make
+the entire system function properly. They are composed of 4 gradients of
+different hue, 32 colors each (4*32=128 colors). Gradients go from the
+most intense color to near black. For the most planet types, the three
+colormaps per instance are all the same, so that the way a planet
+surface looks does not change with the surface temperature. Notable
+exceptions are: h2o_cts_ (water world) and mtl_cts_ (metal world); there
+could be others as no byte-identical examination was done.
+See blue-gas-giant-pal.png for an example colormap.
+
diff --git a/doc/devel/planetrotate b/doc/devel/planetrotate
new file mode 100644
index 0000000..387c455
--- /dev/null
+++ b/doc/devel/planetrotate
@@ -0,0 +1,31 @@
+Planet-rotate code rev3:
+written by PhracturedBlue
+Date: Nov 1, 2002
+
+This is the code to make the planets spin when in orbit view.
+The planet will zoom in (as in the 3do version)
+Shield support is very preliminary, mosly becuase I have not figured
+out how to get the 'look' right.
+
+The planet rotation speed is controlled by ROTATION_TIME which is defined in plangen.c.
+The default is '22' which gives a speed comparable to the DOS version of the game
+
+The planet zoom speed is defined by PLANET_ZOOM_SPEED which is defined in pl_stuff.c
+
+The lighting algorithm is really just a hack. All lighting code is in plangen.c
+Currently, lighting is performed by placing the light source at a certain distance
+(LIGHT_MULT) from the center of the planet (a value of 1 would place the light at
+the planet's radius. (The angle is determined by the planet's position in orbit with
+respect to the sun). The brightest poinnt will have the original color of the topo-map.
+The light falls off as the cos(r^2/(LIGHT_RADIUS^2)).
+The light can never be dimmer than AMBIENT_LIGHT
+
+The shield is done in 2 parts: the halo and the planet-mask. Whether this is needed or
+not, I don't know, but it gives a bit more flexibility in how the effect is applied.
+
+The code works by blitting directly to an SDL_Surface, rather than using DrawBatch (and
+the rendering thread). This gives a huge performance win (My P3-1000 went from ~40fps
+using the DrawBatch to ~500fps using the current method). All lighting, planet tilt, and
+sphere->plane mapping is precomputed into the 'map_rotate' and 'phong' tables (in plangen.c)
+To optimize performance (there is no floating-poinnt code used during the actual frame
+generation)
diff --git a/doc/devel/planettopo b/doc/devel/planettopo
new file mode 100644
index 0000000..7f27a39
--- /dev/null
+++ b/doc/devel/planettopo
@@ -0,0 +1,92 @@
+Planet Topography Code: sc2code/planets/gentopo.c
+----------------------------------------------------
+
+This text covers only topography generation code. It does NOT cover the
+craters (blemishes) nor the smoothing. Variables names are the same in
+"sc2code/planets/gentopo.c".
+
+A brief overview:
+
+The algorithm works by applying several elevation/lowering steps on the
+same terrain. Each step works by randomly generating two non-intersecting
+lines, LineDDA0 and LineDDA1. That delimits two regions. Each region is
+then displaced by a certain height delta (depth_delta). That step is
+repeated several times.
+
+There's a pseudocode below, with detailed information about it in the next
+section.
+
+The DeltaTopography() function works as follows:
+
+ Function parameters:
+
+ COUNT num_iterations // Number of height displacements on the
+ // planet's surface
+ PSBYTE DepthArray // Target surface, receives topography data
+ PRECT pRect // Surface dimensions
+ SIZE depth_delta // The amount of height units to raise/lower
+ // on each displacement
+
+ for i from 1 to num_iterations
+ randomly either negates depth_delta's or not, influencing
+ the displacement direction
+ randomly pick two line segments, LineDDA0 and LineDDA1
+ increase the height of the region between LineDDA0 and
+ LineDDA1 by depth_delta
+ decrease the height of the region between LineDDA1 and
+ LineDDA0 by depth_delta (by wrapping around the surface)
+ end for
+
+Detailed information about the algorithm's steps:
+
+First, in half the steps (randomly) depth_delta is negated.
+That influences on the direction of the displacements.
+Note that in each step exactly two height displacements occur:
+a lifting and a lowering.
+
+Next, it sets both w1 and w2 to different random WORD values. Those are
+used in the line segment generation process.
+
+LineDDA0 starts on a random position on the top of the surface, and ends
+at another random position on the bottom of the surface.
+LineDDA0 never wraps around the surface, since their X coordinates are
+generated in the range 0..width-1:
+
+ LineDDA0.x_top = LOBYTE (w1) % width;
+ LineDDA0.x_bot = HIBYTE (w1) % width;
+
+LineDDA1 is generated much like the way LineDDA0 is, the difference being
+LineDDA1's x coordinates are displaced by LineDDA0 ones:
+
+ LineDDA1.x_top = (LOBYTE (w2) % (width - 1)) + LineDDA0.x_top + 1;
+ LineDDA1.x_bot = (HIBYTE (w2) % (width - 1)) + LineDDA0.x_bot + 1;
+
+Y coordinates are 0 for top, and 'height' for bottom in both line
+segments.
+
+This delimits basically two regions. One delimited by LineDDA0 on the left
+side, and LineDDA1 on the right right, which we call "0->1". The other is
+delimited by LineDDA1 on the left, and LineDDA0 on the right, which we call
+"1->0".
+Note that, since we're talking about a planet, the rightmost side of the
+surface if "connected" to the leftmost side. At least one of those regions
+"wrap around" the surface.
+
+The final step in the iteration is to increase the height of the region
+0->1 by depth_delta, and decreasing 1->0 by the same value. Note that since
+delta_depth may be negative, raising part of the surface by delta_depth
+may result in the surface being lowered.
+
+This step is repeated num_iteration times.
+
+About the terrain generation algorithm: This method of displacing
+line-delimited subsets of a surface is commonly known as Fault Formation.
+
+The DDA acronym stands for "Digital Differential Analyzer". Hence the names
+LineDDA0 and LineDDA1.
+
+The first version of this document was written by:
+Daniel Ribeiro Maciel <daniel.maciel@gmail.com>
+on 2006-11-24
+
+
diff --git a/doc/devel/plugins b/doc/devel/plugins
new file mode 100644
index 0000000..505d7d1
--- /dev/null
+++ b/doc/devel/plugins
@@ -0,0 +1,129 @@
+UQM Code DataPlate modules
+=====================================================================
+Terminology:
+------------
+cdp Code DataPlate
+ refers to either a module or the architecture
+ itself;
+
+module cdp module (ditto ^)
+
+interface a set of functions defined by either the core
+ engine or a module aimed at performing various
+ tasks on a specific part of the game
+
+itf interface; short form
+
+host the game itself or core engine; also a top-level
+ interface for use by modules
+
+interface kind a unique identifier of a particular defined
+ interface (not of the interface instance);
+ there can be several interfaces of the same
+ kind registered, but they all have to cover
+ different API versions (version ranges may
+ overlap though)
+
+standard interface interface *defined* by the core engine
+ all host, sound, video, gfx, threads, time,
+ input, io (uio), memory, resource interfaces
+ are standard, including all subdivisions like
+ 'sound mixer' and 'sound decoder';
+ a module can implement standard behavior of
+ the game or extend the game by implementing and
+ registering a corresponding interface
+
+built-in interface interface *provided* by the core engine
+ e.g. host, sound, video, gfx, etc. lib wrappers
+ anything except 'host' can theoretically be
+ implemented as a 'standard' interface rather
+ than built-in; sound and video interfaces,
+ for example, would not make sense to rip out
+ from the core, but parts of 'gfx' might be good
+ candidates (gfx should probably be more than
+ 1 interface); do not confuse 'sound' interface
+ with 'sound decoder' and 'sound mixer', mixers
+ (MixSDL and OpenAL) are also prime candidates
+
+custom interface interface *defined and provided* by a module,
+ other than a standard interface
+ a module may decide to provide (expose) some
+ interfaces so that other modules can utilize
+ them; a module will register such interface
+ the same way it would register a standard
+ interface it implements
+
+vtbl virtual table; a table of interface functions;
+ pointer to vtbl is semantically equivalent to
+ an interface pointer
+
+event a bindable point in code of the engine or any
+ module; event observer recieves event
+ notifications via event handlers it installs
+
+h_foo handle to 'foo'
+
+plate cdp module
+fork interface; something to poke a plate with
+spoon event;
+ "hit the plate with a spoon" == "notify via
+ event handler"
+
+
+Typical calling sequence:
+-------------------------
+1) something calls adlm_LoadModule(the_mod) ('the_mod' is used here
+ to identify the module and to *logically* group module properties
+ and operations performed on the module for the sake of this
+ document); the_mod gets dlopen()ed;
+
+2) adlm_LoadModule() looks up the only exported symbol 'adlminfo'.
+ This symbol is the adlm_ModuleInfo struct;
+
+3) adlm_LoadModule() verifies that the_mod is compatible with current
+ engine and API versions and calls
+ adlm_GetInterface(HOST,the_mod.api_ver) to lookup the host interface
+ with the correct API version for the module;
+
+4) adlm_LoadModule() either increments module ref-count (if the module
+ has already been loaded previously) or calls
+ the_mod.module_init(host_itf)
+
+5) the_mod.module_init() stores off the host_itf pointer and performs
+ internal initialization;
+
+6) [Optional; but not useful w/o] the_mod.module_init() calls
+ host_itf->GetInterface(kind) to get access to whatever standard or
+ custom interfaces it requires (for example, it requests a sound
+ interface to register an audio decoder);
+
+7) [Optional] the_mod.module_init() calls
+ host_itf->RegisterItf(kind,...,itf_vtbl,...) to register the custom
+ or standard (do not confuse with built-in) interfaces it wishes to
+ expose to the core engine or other modules;
+
+8) [Continuous normal operation] module calls members of the interfaces
+ it acquired to perform various game-related tasks; core engine
+ and/or other modules call members of the interfaces that module
+ exposed (if it did so);
+
+9) something calls adlm_FreeModule(h_the_mod) either when the game
+ quits or when the module is dynamically unloaded for whatever
+ reason: possibly when user decides to dump the game-mod and use
+ something else in its place or nothing at all, but for now there
+ is no mechanism to ensure safe mid-game unloading of modules --
+ interfaces are not ref-counted and anything can hold a pointer to
+ an interface at any given time (creating a safe system to do this
+ is probably not cost-effective);
+
+10) adlm_FreeModule() decrements module ref-count and, when ref-count
+ reaches 0, calls the_mod.module_term();
+
+11) the_mod.module_term() calls host_itf->UnregisterItf(h_itf) to
+ unregister the interfaces it had exposed previously; then it
+ unregisters any other objects it registered, releases all objects
+ it acquired through interfaces and destroys any objects it is
+ responsible for;
+
+12) the_mod.module_term() performs internal cleanup;
+
diff --git a/doc/devel/queues b/doc/devel/queues
new file mode 100644
index 0000000..274828e
--- /dev/null
+++ b/doc/devel/queues
@@ -0,0 +1,62 @@
+The various ship queues used in the game:
+
+GlobData.Game_state.avail_race_q:
+ Contains fleet information, ship template and other info about the
+ alien races present in the game. All races are present that are
+ defined by the enum in races.h that defines NUM_AVAILABLE_RACES,
+ except for, technically, SAMATRA_SHIP. URQUAN_PROBE_SHIP is a
+ very incomplete 'race'.
+ Elements are of type FLEET_INFO.
+ Filled in InitGameStructures().
+ Partially included in savegames.
+
+GlobData.Game_state.built_ship_q:
+ The fleet accompanying the flagship (escorts).
+ Elements are of type SHIP_FRAGMENT.
+ which_side is always GOOD_GUY here.
+ Partially included in savegames.
+
+master_q:
+ List of templates for all the ships that are available in SuperMelee.
+ Elements are of type MASTER_SHIP_INFO.
+ Sorted on the (abbreviated) race name (see doc/devel/racestrings).
+ Filled in LoadMasterShipList().
+
+GlobData.Game_state.npc_built_ship_q:
+ The npc ships in an encounter, or list of ship groups when in IP;
+ empty otherwise.
+ Elements are of type SHIP_FRAGMENT. IP group list will most likely
+ change to contain GROUP_INFO instead.
+ For encounters with an infinite number of ships, the queue consists
+ of a single ship with ShipInfo.crew_level set to (BYTE)~0.
+ and the side that this ship is on.
+ Partially included in savegames.
+
+race_q[NUM_PLAYERS]:
+ Contains the ships participating in a battle for each player.
+ Elements are of type STARSHIP.
+ Filled in BuildBattle().
+ RaceDescPtr points to a loaded ship's descriptor once the ship data
+ is loaded in spawn_ship(); NULL otherwise.
+
+
+Other queues:
+
+GlobData.Game_state.GameClock.event_q:
+ Queue of game events.
+ Elements are of type EVENT.
+
+disp_q:
+ Display queue. This contains all visible elements; everything
+ that floats in TrueSpace, HyperSpace/QuasiSpace, battle.
+ Elements are of type ELEMENT.
+
+GlobData.Game_state.encounter_q:
+ Contains information regarding the black globes flying around
+ in HyperSpace.
+ Elements are of type ENCOUNTER.
+
+
+Initial version of this document created by Serge van den Boom, on 2006-03-08.
+
+
diff --git a/doc/devel/racestrings b/doc/devel/racestrings
new file mode 100644
index 0000000..2993a65
--- /dev/null
+++ b/doc/devel/racestrings
@@ -0,0 +1,45 @@
+This file describes the use for each entry of the
+content/base/ships/<race>/<ship>.txt files.
+
+
+Entry #0:
+ Battle group name
+ Used in uppercase on the "debris scavanged" message after destroying
+ a battle group in the full game, from UninitEncounter().
+
+Entry #1:
+ Race name
+ Displayed on the starmap, from DrawStarMap() (and GetSphereRect()).
+ Used in combat (both in the full game and in SuperMelee) as the
+ name of the race, from InitShipStatus().
+
+Entry #2:
+ Abbreviated race name.
+ Used when sorting the races in LoadMasterShipList().
+
+Entry #3:
+ Ship name.
+ Unused (?)
+
+Entry #4:
+ Abbreviated ship name.
+ Unused (?)
+
+Entry #5 (NAME_OFFSET) through #20 (NAME_OFFSET + NUM_CAPTAINS_NAMES - 1):
+ The names of the captains of the ships of this race, as displayed
+ during combat.
+
+
+For some races, some plot-specific captain names follow:
+Shofixti:
+ - Entry #21: Tanaka
+ - Entry #22: Katana
+Spathi:
+ - Entry #21: Fwiffo
+
+
+Note that the race/ship name combinations shown when adding a ship to your
+fleet in the shipyard are actually images (lbm/shipyard.3.png through
+shipyard.23.png). They are displayed from DrawRaceStrings().
+
+
diff --git a/doc/devel/resources b/doc/devel/resources
new file mode 100644
index 0000000..20db1b7
--- /dev/null
+++ b/doc/devel/resources
@@ -0,0 +1,196 @@
+This file documents both the currently used resource system and the
+legacy system used in the original implementations. It also describes
+how to update the resources while keeping everything functional.
+
+ THE UQM RESOURCE SYSTEM
+ -----------------------
+
+Resources are identified by "resource IDs", which are arbitrary
+strings. The UQM convention is to treat them as paths or qualified
+names, with the period being the separator. An example would be
+"comm.arilou.dialogue", which is conceptually grouped with all other
+resources beginning with "comm." or "comm.arilou."
+
+Resources are mapped to files via "resource map files" or RMPs. These
+files are full of key-value pairs. Each line of an RMP file matches
+this format:
+
+ resource_ID = RESOURCE_TYPE:resource_data
+
+The currently defined resource types and their accompanying data are:
+
+GFXRES: A single animation. The resource data is the name of the
+ .ani file that defines it. See the "aniformat" file for
+ details.
+FONTRES: A font. The resource data is the name of the directory
+ in which the font is defined.
+MUSICRES: A music file. The resource data is the filename of the
+ relevant file in OGG or MOD format.
+SNDRES: A set of related sounds. Points to a text file,
+ traditionally suffixed ".snd", that lists each sound
+ file in the collection, one per line. These sounds are
+ in WAV format.
+STRTAB: An unadorned string table. The resource data is the file
+ name.
+BINTAB: Indexed binary data, mostly used for palettes and
+ topological data for planet terrain generation. The
+ resource data is the file name. See the "strtab" file, but
+ be aware that all information not regarding .ct or .xlt
+ files has been obsoleted.
+CONVERSATION: All data corresponding to an individual conversation
+ tree. The resource data has three components, separated
+ by colons: the conversation text (similar to the
+ STRTAB), the directory in which voiceovers may be
+ found, and the timestamp file corresponding to those
+ voiceovers. The second and third values are optional,
+ but if either is present, both must be.
+SHIP: This is an integer that specifies which ship corresponds
+ to a specified resource. The mapping of SHIP integers
+ to actual ship_info structures is done by the dummy.c
+ routines.
+STRING: A string. The resource data is the string itself.
+INT32: A 32-bit integer. The resource data is the integer as a
+ string giving its representation in decimal.
+BOOLEAN: A true/false value. Any value of the resource data that
+ is not the exact case-insensitive string "true" will be
+ treated as false.
+UNKNOWNRES: A catch-all internal type for any type the system does
+ not recognize. It is functionally equivalent to STRING,
+ mainly so that sensible debug messages may be emitted.
+
+When UQM is started, each RMP file in the content directory (but not
+its subdirectories) is read and indexed. Then, for each addon pack
+rqeuested, it reads each RMP file in the addons/(addon name)/
+directory and updates the resource index accordingly. Generally
+speaking, this will overwrite older values of the resources with
+pointers to the new content.
+
+It is permissible to have multiple RMP files in a single directory;
+however, if this is done, there should be no overlap between the
+resources defined. The UQM resource system makes no guarantees about
+the loading order of RMP files within a unit.
+
+It is not required for an addon to restrict its references to its own
+subdirectory; all paths are relative to the base of the content
+directory. This allows fonts to be shared across addons, for an "addon
+pack" to simply reorganize content within the base directory. (For
+instance, a popular modification to the original games involved
+juggling .SHP files and renaming them so that the "Earthling Cruiser"
+was actually a much more powerful ship like the Utwig Jugger or Chmmr
+Avatar. This may be effected in the current system by making an addon
+pack that redefines the relevant SHIP-typed resources.)
+
+ UPDATING THE CORE RESOURCES
+ ---------------------------
+
+The officially supported content is kept in three indices:
+
+- content/uqm.rmp: The core content.
+- content/addons/3domusic/3domusic.rmp: 3DO music.
+- content/addons/3dovoice/3dovoice.rmp: The 3DO conversations.
+
+These latter two are treated the same as any other addons, except that
+they are guaranteed to be loaded first if selected by the
+configuration file.
+
+These files do not house all the necessary data, however. The code
+itself uses a set of automatically generated header files to #define
+constants that correspond to each resource. In order to add new
+resources or change currently defined resource IDs, the .h files must
+also be updated.
+
+To keep all information in one place, the tools/resmap directory
+contains a master data file for all core resources ("resource.csv")
+and a number of Python scripts for manipulating it.
+
+If files have simply been moved, renamed, or re-typed, one may reflect
+these changes in uqm.rmp and run the "reverse_rmp.py" script. This
+will reflect changes in uqm.rmp back into resources.csv.
+
+If the header constants or resource IDs have changed, changes will
+need to propagate to the .h files. Edit or add the relevant lines to
+resources.csv and run "gen_resfiles.py". This will read resources.csv
+and regenerate uqm.rmp and all the relevant header files.
+
+The resources.csv file is a comma-separated-values file suitable for
+importing into most spreadsheet programs. It has one line for each
+resource, and each line has five "columns":
+
+ C constant, resource id, header file, resource type, resource data
+
+If a resource exists in the code, but not in the base content the
+resource type and resource data are both the string "--". The C
+constant and header file are still mandatory - otherwise UQM will not
+compile. Such resources will not be reflected in the RMP files, but
+will have their headers defined appropriately so that addon packs may
+provide values.
+
+Major reorganization of files may be done with the transform.py file;
+its usage string describes its script format.
+
+ LEGACY SYSTEM
+ -------------
+
+The resource files the resource system in SC2 uses, comes in two flavours.
+There are 'packaged' files, which contain the data in the file itself,
+and 'non-packaged' (index) files, which only point to other files.
+The internal formats are described in pkgformat, and are mostly the same.
+
+A resource file is opened by a calling OpenResFile(), after which a
+MEM_HANDLE to a resource can be acquired from it by calling GetResource().
+
+The argument of GetResource() is an integer value specifying the resource.
+There are defines for this defined in (files included from) the various
+resinst.h file. The defines are unique to a resource file.
+A resource id can be something like this: 0x00400002
+Bits 21-31 specify the package. (resources are grouped together in a
+ resource file in packages) (a number >= 0).
+Bits 0-7 specify the resource type of the resource (a number >= 1).
+Bits 8-20 specify the instance number (the number of the resource of a
+ type in a package) (a number >= 0).
+
+The resource type is a number. What kind of resource that actually is,
+is not specified in the package. The program registers functions for
+loading data of a specific type number, by calling InitResTypeVectors().
+
+In the original 3DO version, the following types are used:
+GFXRES (1) - graphics data
+STRTAB (2) - string table
+MUSICRES (3) - music and sound data
+RES_INDEX (4) - a resource 'file'
+CODE (5) - identifies code to use (the resouce file itself does not
+ contain any code)
+
+
+In the PC version of Star Control II, the following types are used
+(the names are taken from the other versions):
+KEY_CONFIG (1) - keyboard configuration
+GFXRES (2) - graphics data
+SNDRES (3) - waveform audio
+STRTAB (4) - string table
+MUSICRES (5) - mod audio
+RES_INDEX (6) - a resource 'file'
+CODE (7) - actual PC code (compressed; used for ships as well as comm;
+ may include other resources)
+ (8) - Deluxe Paint .ANM
+
+
+In the UQM project pre-resmap, the following types were used:
+KEY_CONFIG (1) - keyboard configuration (not used in the 3do packages)
+GFXRES (2) - graphics data
+FONTRES (3) - font data
+STRTAB (4) - string table
+SNDRES (5) - waveform audio
+MUSICRES (6) - music
+RES_INDEX (7) - a resource 'file'
+CODE (8) - identifies code to use (the resouce file itself does not
+ contain any code)
+
+The resource files, resinst.h, restypes.h, and respkg.h files were
+originally generated from information from the .res and .typ files. The
+resource library should be compiled with 'PACKAGING' defined for this.
+
+---
+
+Initial version of this file 2002-10-23 by Serge van den Boom.
+Updated for the 0.7.0 system by Michael Martin.
diff --git a/doc/devel/savefile b/doc/devel/savefile
new file mode 100644
index 0000000..306c1ab
--- /dev/null
+++ b/doc/devel/savefile
@@ -0,0 +1,149 @@
+ SAVEFILE FORMAT
+ ---------------
+
+This document represents a work in progress. The save format described
+here will evolve before finalization for 0.8.
+
+The old save file format used a custom compressor and was very finicky
+about padding and alignment for machines that were not even in use
+anymore. It was also extremely fragile and hard for modders to extend.
+
+The new save format seeks to alleviate these problems.
+
+ GENERAL FORMAT
+ --------------
+
+All multibyte values are little-endian. There are no alignment
+restrictions inherent in the format.
+
+The save file begins with a 32-bit identifier and version number (a
+"magic number") that identifies it as a particular version of an UQM
+save file. If the format changes in a way that older versions of UQM
+cannot read it, either the identifier or the version number should
+change.
+
+The vanilla UQM system has a save number of 0x01534d55, which, if
+interpreted as a byte stream is UMS (Ur-quan Masters Save) and a
+binary 1 (version 1). Vanilla UQM reserves the tags "UMSx" for all x
+for use to evolve the core save format. Modders are encouraged, but
+not required, to also use the fourth bit as a version number.
+
+Following the 32-bit file identifier comes a series of chunks. All
+chunks have the same general format: a 32-bit tag, similar to that of
+the file as a whole, followed by a 32-bit integer specifying the size
+of the chunk, and then that many bytes of data.
+
+Bytes in a chunk tag should all be in the range 0x20-0x7E -- that is,
+they should be printable ASCII characters. Chunks are traditionally
+referred to by their tag names.
+
+Chunks whose tag has a least significant byte in the range 0x41-0x5a,
+inclusive---that is to say, whose names start with a capital
+letter---are mandatory. If you extend the set of mandatory chunks, you
+must increase the version of the file. Tags that do not start with a
+capital letter may be ignored by other versions of UQM that otherwise
+understand that data version.
+
+(Why would you want to have ignorable bits of save file? Such chunks
+may contain helpful but ancillary information. For instance, at the
+time of this writing, there is an outstanding bug that life forms are
+un-stunned if you save and load in orbit. One could add a new chunk
+that tracks the stunned status of life forms on the planet you're in
+orbit around, and just revert to the old behavior if it's not there or
+if you're running on a version without that fix. Such an ancillary
+data chunk would have a tag like "stun" or "biot".)
+
+Note also that despite being "mandatory", it is not the case that all
+chunks will be present in all save files. Different data is saved out
+depending on the situation you saved in.
+
+Unless otherwise specified, chunks may be stored in any order in the
+save file.
+
+ CHUNK INVENTORY
+ ---------------
+
+- "Summ": Summary. This chunk must come first. This chunk
+ carries the flagship configuration information and some overview
+ information that is displayed on the savegame view screen. It is of
+ variable length, because the last element of this chunk is the name
+ of the save as chosen by the user.
+
+- "GlSt": Global State. This chunk must come second, after
+ Summ. Represents most of the data in the global state structure in
+ globdata.h. BattleGroupRef is excised from this; its value is
+ computed later on.
+
+- "GmSt": Game State. This chunk must come third, after GlSt. This is
+ the gigantic bitfield that the GET_GAME_STATE macros modify. It is
+ variably-sized; excess bytes in this array will be ignored, and if
+ there are insufficient bytes in the save file, the remaining bits
+ will be initialized to zero. Modders setting extra event flags may
+ be able to import a legacy game into a sensible state by choosing
+ their defaults judiciously.
+
+- "Evts": Events. An array of values describing scripted future
+ events.
+
+- "Enct": Encounters. Details of battle groups that are pursuing you
+ through HyperSpace.
+
+- "RacQ": Available Race Queue. Which species are active in the game,
+ where they are, etc. Corresponds to avail_race_q.
+
+- "IGpQ": Interplanetary Group Queue. Battlegroup information for
+ ships in your current star system, if you're in a current star
+ system. It is currently unclear whether or not this ever needs to
+ exist, but in keeping with legacy logic, it will appear whenever you
+ are in a star system but not in the middle of an encounter.
+
+- "NpcQ": NPC Queue. Battlegroup information for ships you are in the
+ middle of encountering. This should only appear if your loaded
+ activity is "IN_ENCOUNTER" and loading the game will trigger the red
+ alert.
+
+- "ShpQ": Ship Queue. Battlegroup information for your flagship's
+ escort fleet.
+
+- "Star": Star Description. Basic indexing information to indicate
+ which star system you are in.
+
+- "Scan": Scanner Masks. This is a semi-structured tree of DWORDs that
+ represents which planetary resources have been captured or
+ removed. It is a format roughly similar to the old star info
+ statefile format (see doc/devel/statefile) but little-endianness is
+ enforced, making this chunk endian-safe where the old statefile dump
+ was not.
+
+- "BtGp": Battle Group. Defines the relevant information for all ships
+ in a given star system. This includes an "encounter ID" - randomly
+ generated fleets have an encounter ID of zero, and ones built by the
+ plot have an 32-bit identifier. Vanilla UQM reserves the first 32
+ encounter IDs for itself, and uses 15 of them (random encounter,
+ Ur-Quan Probe, Shofixti Survivor, Zoq-Fot-Pik Emissary, Unzervalt
+ Guardian, nine Melnorme Traders, and the final boss). This also
+ includes the expiration date for random encounters and which system
+ they are relevant to. Much of this information was originally stored
+ in randgrp.dat, but it has echoes in defgrp.dat as well. The data
+ here is a ragged 2D array of a slight extension of the SHIP_FRAGMENT
+ structure. There is one BtGp chunk per defined group. (Since one of
+ these is defined at game start, and the random encounter structure
+ has values that mean 'no encounter present', there should always be
+ at least two of these chunks in any save.)
+
+- "Grps": Active Battle Groups. These are IP_GROUP structures to
+ supplement the SHIP_FRAGMENTs specified in BtGp chunks. They give
+ more detailed information about the precise location and disposition
+ of each ship in the system you are either in or most recently left.
+
+ THINGS LEFT TO DO
+ -----------------
+
+The last 448 bits in GmSt probably shouldn't exist. However, we should
+not remove them from the source base (and thus the save file) until
+after other pending commits have been merged. When this happens, we
+can break out those bits into an array of DWORDs instead. The loading
+code basically ignores those GmSt bits by overwriting them while
+loading later chunks, and GmSt is an expandable array in the first
+place, so removing those final bits from the Game State array should
+be compatible in both directions.
diff --git a/doc/devel/sc1 b/doc/devel/sc1
new file mode 100644
index 0000000..373ac16
--- /dev/null
+++ b/doc/devel/sc1
@@ -0,0 +1,186 @@
+This file documents the file formats used in Star Control 1.
+
+The .pkg files are in the format described in the file "pkgformat".
+The extracted files are described in this document.
+
+The following describes the format used in the DOS version of SC1.
+
+Resource types used in starcon.pkg:
+ 1 - key config
+ 2 - some graphics type (flags = 7)
+ 3 - some graphics type (flags = 1)
+ 4 - some graphics type (flags = 2) 5 - some graphics type (flags = 3)
+ 6 - font
+ 7 - scenario
+ 8 - palette
+ 9 - strtab
+10 - ? (sound?)
+
+
+Files of types 2, 3, 4, 5, 9, and 10 may be compressed; they have
+a header of the following format: have the following format:
+
+Uncompressed files:
+ off len
+ 0x00 1 0 (means: not compressed)
+ 0x01 1 flags, 0 in practice
+ 0x04 2 Number of bytes that follow (MSB first)
+ 0x06 start of data
+
+Compressed files:
+ off len
+ 0x00 1 6 (means: compressed)
+ 0x01 1 flags
+ bit 0: unused
+ bit 1: (table2CodeLen)
+ if not set: table2 results are shifted by 6 bits.
+ if set: table2 results are shifted by 7 bits.
+ (never set in PC SC1 files)
+ bit 2: (litencoded) set if literal bytes are Huffman encoded
+ (never set in PC SC1 files)
+ rest: unused
+ 0x02 4 uncompressed size (MSB first)
+ 0x06 Huffman table 0 (8-bit codes) (only present if litencoded set)
+ used for encoding literal bytes
+ Huffman table 1 (6-bit codes)
+ used for encoding lengths
+ Huffman table 2 (6-bit codes)
+ used for the most significant table2CodeLen bits of an offset
+
+
+
+Huffman table:
+(numbers are in bits)
+ 8 one less than the number of lengths of codes
+ in the table (lenCnt)
+ 8 * lenCnt LONIBBLE: one less than one of the code lengths
+ HINIBBLE: one less than the number of codes with
+ this length
+This is enough to construct the huffman table.
+
+TODO: document the rest of the compression format; for now, look at the
+ code of sc1-decomp.
+
+
+Resource type 1 (key config):
+ off len
+ 0x00 1 bottom player special key ('n')
+ 0x01 1 top player special key ('1')
+ 0x02 1 bottom player left key ('m')
+ 0x03 1 top player left key ('2')
+ 0x04 1 bottom player right key (',')
+ 0x05 1 top player right key ('3')
+ 0x06 1 bottom player thrust key ('.')
+ 0x07 1 top player thrust key ('4')
+ 0x08 1 bottom player fire key ('/')
+ 0x09 1 top player fire key ('5')
+ 0x0a 1 ?
+ 0x0b 1 ?
+ 0x0c 1 ?
+ 0x0d 1 ?
+ 0x0e 1 ?
+ 0x0f 1 ?
+ 0x10 1 ?
+ 0x11 1 ?
+ 0x12 1 ?
+ 0x13 1 ?
+
+
+Resource types 2, 3, 4, and 5 (graphics):
+The difference between those types is the flags parameter that is passed
+to the load functions (see above in the table of resource types).
+bit 0: ?
+bit 1: probably indicates whether a palette is used
+bit 2: ?
+rest: unused
+
+(after decompression/removing packing header)
+ off len
+ 0x00 4 "IANM"
+ 0x04 2 frame count (MSB first)
+ 0x06 2 MSB first
+ LOBYTE: bits per pixel. All SC1 files are either 1 bpp or 4 bpp.
+For every frame:
+ 2 width (MSB first)
+ 2 height (MSB first)
+For every frame:
+ 2 x hotspot
+ 2 y hotspot
+ 1 if not zero, the file includes palettes
+ 1 transparant color
+ 16 (optional) (CGA?) palette (16 entries of 1 byte)
+ 16 (optional) (EGA/Tandy?) palette (16 entries of 1 byte)
+ 16 (optional) (MCGA/VGA?) palette (16 entries of 1 byte)
+ ? pixel data (indicees in palette)
+
+
+
+Resource type 6 (font):
+ off len
+ 0x00 1 ?
+ 0x01 1 ?
+ 0x02 1 LONIBBLE:
+ HINIBBLE:
+ 0x03 48
+
+The letters are actually readable if you do 'xxd -b'; it shouldn't be too
+hard to RE this one.
+
+
+Resource type 7 (scenario):
+ off len
+ 0x00 16 Title (\0 terminated)
+ 0x10 ? Short description (\0 terminated)
+ ? ? Unknown (probably description of star systems, Space Spines,
+ fleets and bases (maybe random seeds?))
+ 0x80 840(?) Long description (\0 terminated)
+
+
+Resource type 8 (palette):
+ off len
+ 0 768 palette data:
+for each of the 256 entries (with index 0 through 255):
+ 1 red value
+ 1 green value
+ 1 blue value
+
+
+Resource type 9 (string):
+ off len
+ 0x00 2 Number of strings (MSB first)
+ 0x02 2 unknown (always 0x0000)
+for each string s:
+ 2 len[s]: length of the string (MSB first)
+for each string s:
+ len[s] String data (not '\0'-terminated)
+
+
+starcon.exe:
+compressed using lzexe
+(http://fabrice.bellard.free.fr/lzexe.html)
+Using PLINK86 to load starcon.ovl as an overlay.
+
+
+
+The Amiga version of SC1:
+
+Resource types used in starcon.pkg:
+ 1 - key config (presumed; different from the DOS version)
+ 2 - graphics
+ 3 - graphics
+ 4 - graphics
+ 5 - graphics
+ 6 - font
+ 7 - scenario (as with the DOS version)
+ 8 - strings as on DOS, but also other data in the same format
+ (presumably including sound)
+
+Files of types 2, 3, 4, 5, 8 may be compressed as in the DOS version.
+
+Graphics files are in a different file format as on DOS. I haven't worked
+on these files yet.
+
+
+Initial version created on 2007-06-01 by Serge van den Boom.
+
+
diff --git a/doc/devel/script b/doc/devel/script
new file mode 100644
index 0000000..07dbf0f
--- /dev/null
+++ b/doc/devel/script
@@ -0,0 +1,117 @@
+SCRIPT CONVENTIONS
+------------------
+
+The .txt files in the content/comm directories are unlikely to stay in
+their current form forever, as it's a rather fragile format. However,
+in the the meantime, and for those who seek to edit the script files
+(to conform to 3DO, PC, and/or director's cut versions).
+
+This file will describe the format, and how it relates to the source
+code in the src/sc2code/comm directory.
+
+The .txt files
+--------------
+
+The text files should all have a set of records, optionally separated
+by newlines. Each record has a header, then one or more lines of
+text.
+
+The header is a hash (#), an identifier in parentheses, and (if it's
+an alien voice) a tab, then the name of the file corresponding to that
+voice. Alien voices have identifiers in ALL_CAPITAL_LETTERS, while
+Zelnick's lines are in_lower_case. Often one line is broken into
+several parts - in these cases the full line is knitted together by
+the communications code (usually inserting the name of the Captain, or
+the flagship, or whatever).
+
+A modest example:
+
+#(HAVING_FUN_WITH_ILWRATH_2) thrad103.wav
+The exploding starships! The screaming crew!
+The direct hits, the cunning escapes!
+These are the moments we live for!
+Now we must return to the great battles!
+Farewell, Great Teacher.
+
+Zelnick's lines must be ON ONE LINE ONLY; line breaking is handled by
+the communication code and anything after the first newline is
+IGNORED. So don't do it while repairing his lines, and if you find
+one (a lowercase id with multiple lines following it) delete the
+newlines and make it all one line.
+
+It appears that newlines may appear in the middle of speeches;
+however, most of the lines by aliens have pauses represented by a
+single space or tab. It seems reasonable to duplicate this.
+
+When multiple records are used to define a single speech line (as
+often happens when they say your name, for instance), only the first
+line has an audio file associated with it.
+
+Lines that do not end in a punctuation mark or a space have a "..."
+added to the end of them by the communications code. If the previous
+line had "..." appended to it and this line does not START with a
+space, this line will have "..." prepended to it. The "scriptcheck"
+tool looks for all cases where "..." does not occur in midsentence.
+When editing the script files, check your intended results against
+this.
+
+The .h files (src/sc2code/comm)
+-------------------------------
+
+Each race has its own directory in src/sc2code/comm, and there is a
+file marked strings.h in each directory. This file defines a single
+enum which should list every record identifier in the appropriate
+race's .txt file over in content/comm/{race}, IN THE PRECISE ORDER
+THOSE RECORDS ARE GIVEN. Due to the non-symbolic nature of C, there
+is no relation whatsoever between the identifiers in the record and in
+the enum (though it would be reasonably straightforward to generate
+the strings.h files from the .txt files) The values of the enum are
+used in the {racec}.c files in the same directory.
+
+The scriptcheck utility also checks for proper correlation between the
+.h file and the associated .txt file. It assumes that one enum value
+is listed per line, and that the general format of the file matches
+the current one.
+
+Types of script bugs
+--------------------
+
+Script bugs can fall into three broad categories.
+
+- Voice/Speech mismatch. These are easy. The wrong audio file is
+ associated with this clip. Merely change the clip to make it fit.
+ If a clip is expected but is not specified, this appears to crash the
+ game. Example: The Zoq-Fot-Pik's "Goodbye Captain." "See ya." lines
+ had no clips associated with them, which both caused the game to loop
+ forever on those lines and misaligned all clips after that so that the
+ voices did not match the subtitles.
+
+- Subtitling error. These are also easy. The voice and text more or
+ less match, but the phrasing is slightly different. This is also
+ easy. Change the text to match. If the speech is *wildly* different,
+ make sure that it's not a misfiled audio file. NOTE: The PC and 3DO
+ versions had slightly different texts, and the texts we are working
+ with here match neither one. The voices are to be taken as the
+ ultimate authority on the 3DO text.
+
+- Communications Logic errors. Basically, this means the enum in the
+ appropriate strings.h was defined wrong, or, worse, was misused by the
+ {race}c.c file. One fixed example was the Spathi Ruling Council (Race
+ name: spahome), where strings.h defined *two* enums, one for the
+ Captain's lines, one for the Spathi. This would have horribly garbled
+ everything were it not for the fact that it made their initial
+ challenge and your responses invalid, thus causing the game to lock up
+ on your first visit. An example of one of these that made it into the
+ final game (PC version, anyway) is when the Umgah are trying to reward
+ you as Great Hero - the responses for the "I act purely from largesse"
+ and "Yep! You guys owe me BIG time" are clearly reversed. No
+ decision has yet been made on how to handle these (whether to
+ reproduce them precisely, or fix them, or only fix them in a
+ "director's cut" which would presumably require input from the
+ director) so leave them alone for now unless they crash the game. (To
+ my knowledge, the only truly serious error of this type was the Spathi
+ Homeworld, which has since been repaired.)
+
+
+
+ (Initial version: Michael Martin, 10/10/02)
diff --git a/doc/devel/sfx b/doc/devel/sfx
new file mode 100644
index 0000000..67a29d5
--- /dev/null
+++ b/doc/devel/sfx
@@ -0,0 +1,202 @@
+The Ur-Quan Masters Sound Effects
+
+Path/File/Duplicates Description
+=========================== ============================================================
+androsyn
+ androsyn/primary.wav Molecular acid (bubble sounds)
+ androsyn/secondary.wav Blazer form (comet "burner")
+
+arilou
+ arilou/primary.wav Ventral auto-aiming laser (yellow laser)
+ (lastbat/primary.wav)
+ arilou/secondary.wav Shortrange hyperdriver (transporter)
+ (lastbat/secondary.wav)
+
+blackurq
+ blackurq/fightret.wav not used
+ (urquan/fighter_get.wav)
+ blackurq/fightzip.wav not used
+ (ipanims/land_sht.wav)
+ (urquan/fighter_laser.wav)
+ blackurq/primary.wav Spinning blade
+ blackurq/secondary.wav F.R.I.E.D. Emitter plasma
+
+chenjesu
+ chenjesu/dogibark.wav D.O.G.I. hit (kind of "space" dog bark)
+ chenjesu/dogidie.wav D.O.G.I. dies (kind of "space" dog whine/yelp)
+ chenjesu/primary.wav Photon crystal shard firing
+ chenjesu/secondary.wav D.O.G.I. launching, low humming
+ chenjesu/sharpnel.wav Photon crystal shard explode (sharp sound)
+
+chmmr
+ chmmr/primary.wav Terawatt Laser (sharp "crystal" phaser beam)
+ chmmr/secondary.wav Tractor beam hum (cut resonance)
+
+druuge
+ druuge/primary.wav High-recoil cannon (a bit like shotgun)
+ (orz/primary.wav)
+ druuge/secondary.wav "Aarrghf" sacrifice scream
+
+human
+ human/primary.wav Nuclear warhead (rocket)
+ human/secondary.wav Point defence laser shot (white laser)
+
+ilwarth
+ ilwarth/cloak.wav Cloacking hum (quite bassy)
+ ilwarth/primary.wav Hellfire (flame thrower)
+ ilwarth/uncloak.wav Decloacking hum (less bassy, and sounds a bit like reversed clock.wav)
+
+ipanims
+ ipanims/land_awa.wav Lander takes of from the planet surface
+ ipanims/land_bit.wav Lander gets bitten by bio (teeth clank)
+ (vux/limpet_bite.wav)
+ ipanims/land_can.wav Bio canned by stun shot (weird sound)
+ ipanims/land_die.wav Lander explodes (long explosion)
+ (lbm/shipdies.wav)
+ ipanims/land_ful.wav Lander cargo hold full, cannot pick up ("burp")
+ ipanims/land_get.wav Lander picks up item/mineral (sounds like somone sucking)
+ ipanims/land_hit.wav Lander gets hit by something (snappy sound)
+ ipanims/land_hot.wav Firelava hot hum on planet surface
+ (thradd/secondary.wav)
+ ipanims/land_hrt.wav Lander crew dies (just "ugh" human sound)
+ ipanims/land_lau.wav Lander launching
+ (mmrnmhrm/primaryy.wav)
+ ipanims/land_lig.wav Lightning on planet surface
+ ipanims/land_qua.wav Earthquake on planet surface
+ ipanims/land_sht.wav Lander blaster shot
+ (urquan/fighter_laser.wav)
+ (blackurq/fightzip.wav)
+
+lastbat
+ lastbat/primary.wav not used
+ (arilou/primary.wav)
+ lastbat/secondary.wav not used
+ (arilou/secondary.wav)
+
+lbm
+ lbm/boom1.wav Tiny hit to planet/asteroid on melee (same sound, but higher pitch as ipanims/land_hit.wav)
+ ((ipanims/land_hit.wav))
+ lbm/boom23.wav Small hit to planet/asteroid on melee
+ lbm/boom45.wav Larger hit to planet/asteroid on melee
+ lbm/boom67.wav Big hit/explosion to planet/asteroid on melee
+ lbm/getcrew.wav Get crew back to ship (Syreen called crew) (notes: should be in syreen/ ?)
+ lbm/menusnd01.wav Enter menu item
+ lbm/menusnd02.wav Select menu item
+ lbm/menusnd03.wav Cannot do (cancel) menu item (same as sis_ship/secondary.wav, but with echo)
+ lbm/menusnd04.wav Use device
+ (yehat/secondary.wav)
+ lbm/shipdies.wav Ship explosion (long explosion)
+ (ipanims/land_die.wav)
+
+melnorme
+ melnorme/primary.wav Confusion Ray (same as melnorme/secondary.wav, but reversed and one octave higher)
+ melnorme/secondary.wav Powerblaster (synth bass slap a like)
+
+mmrnmhrm
+ mmrnmhrm/primaryx.wav Laser cannon (sharp red laser beam)
+ mmrnmhrm/primaryy.wav Rocket launcher
+ (ipanims/land_lau.wav)
+ mmrnmhrm/secondary.wav Transformer
+
+mycon
+ mycon/primary.wav Plasma (long sound)
+ mycon/secondary.wav Crew regeneration (strange howling)
+
+orz
+ orz/argh.wav High pitched "Argh"
+ orz/intruder.wav Echoed and reverbed "Intruder"
+ orz/primary.wav Howitzer cannon
+ (druuge/primary.wav)
+ orz/secondary.wav "Go!"
+ orz/zap.wav Pulse cannon zap
+
+pkunk
+ pkunk/insult01.wav "Baby"
+ pkunk/insult02.wav "Dou-Dou"
+ pkunk/insult03.wav "Fool"
+ pkunk/insult04.wav "Idiot"
+ pkunk/insult05.wav "Jerk"
+ pkunk/insult06.wav "Looser"
+ pkunk/insult07.wav "Moron"
+ pkunk/insult08.wav "Nerd"
+ pkunk/insult09.wav "Nitwit"
+ pkunk/insult10.wav "Stupid"
+ pkunk/insult11.wav "Twig"
+ pkunk/insult12.wav "Whimp"
+ pkunk/insult13.wav "Worm"
+ pkunk/insult14.wav "Dummy"
+ pkunk/primary.wav Small blaster gun
+ (spathi/primary.wav)
+ (zoqfot/primary.wav)
+ pkunk/rebirth.wav Short piece of Handel played on Pkunk resurrection
+
+shofixti
+ shofixti/primary.wav Blaster gun
+ (thradd/primary.wav)
+ shofixti/secondary.wav Self-destruct, explosion and "ayeee!" yell
+
+sis_ship
+ sis_ship/primary.wav Precursors gun
+ sis_ship/secondary.wav Cancel sound, almost the same as lbm/menusnd03.wav, but no delay echo
+ (spathi/secondary.wav)
+
+slyland
+ slyland/primary.wav Electric discharger (kind of lightning)
+ slyland/secondary.wav Robotic "Energize"
+
+spathi
+ spathi/primary.wav Small blaster gun
+ (pkunk/primary.wav)
+ (zoqfot/primary.wav)
+ spathi/secondary.wav Rear launcher
+ (sis_ship/secondary.wav)
+
+supox
+ supox/primary.wav Sprout Gun
+
+syreen
+ syreen/primary.wav Partical Beam Stiletto
+ syreen/secondary.wav Syreen call
+
+thradd
+ thradd/primary.wav Blaster gun
+ (shofixti/primary.wav)
+ thradd/secondary.wav Afterburner
+ (ipanims/land_hot.wav)
+
+umgah
+ umgah/primary.wav Antimatter (like shaver)
+ umgah/secondary.wav Retro Propulsion (Zap sound)
+
+urquan
+ urquan/fighter_get.wav Fighter returning (a bit like end of tincan hit reversed)
+ (blackurq/fightret.wav)
+ urquan/fighter_laser.wav Fighter laser
+ (ipanims/land_sht.wav)
+ (blackurq/fightzip.wav)
+ urquan/primary.wav Photon torpedo (Star Trek sound)
+ urquan/secondary.wav Low pitched voice "Launch fighters"
+
+utwig
+ utwig/primary.wav Wide area guns (Low humming)
+ utwig/secondary.wav Battery (sounds a bit like reversed water drops)
+ utwig/shieldbattgain.wav Battery gain (sounds a bit like reversed water drops, plus some hum)
+
+vux
+ vux/limpet_bite.wav Limpet attaching to ship hull
+ (ipanims/land_bit.wav)
+ vux/primary.wav Gigawatt laser (beam sound)
+ vux/secondary.wav Limpet launch (reversed *plup* sound)
+
+yehat
+ yehat/primary.wav Rapid fire pulse cannon
+ yehat/secondary.wav Shield generator
+ (lbm/mainmenu04.wav)
+
+zoqfot
+ zoqfot/primary.wav Antimatter spray gun
+ (spathi/primary.wav)
+ (pkunk/primary.wav)
+ zoqfot/secondary.wav Plasma injector tongue ("yak" voice)
+
+
diff --git a/doc/devel/statefiles b/doc/devel/statefiles
new file mode 100644
index 0000000..3007bdf
--- /dev/null
+++ b/doc/devel/statefiles
@@ -0,0 +1,123 @@
+ Ur-Quan Masters State Files
+ ---------------------------
+
+Statefiles are a legacy component of the UQM runtime and savegame
+format. They represent largish (10-64KB) chunks of semi-structured
+data that the engine used to keep on disk during gameplay, checking
+and updating it as needed. These files were then dumped into and
+restored from save files in a compressed form.
+
+In UQM as it exists now (0.7), these files exist as expandable memory
+arrays that are consulted as if they were files. UQM 0.8 will change
+the savegame format to remove the compression (64K just isn't much
+anymore) and put more structure to the data within. The statefile
+abstraction itself is not guaranteed to be retained, and indeed core
+team considers it kind of horrible and would like to destroy it.
+
+There are three state files.
+
+ STARINFO_FILE aka "starinfo.dat"
+ --------------------------------
+
+This is probably the most reasonable of the three. It keeps track of
+which star systems you have visited, and which mineral deposits and
+interesting life forms and gadgets you have harvested from the worlds
+therein.
+
+The file begins with one DWORD for every star system in the
+game. (There are 502 star systems, defined in a gigantic array in
+starmap_array in plandata.c). The DWORD is 0 if the star system is
+unvisited. It is otherwise an absolute offset within the state file.
+
+Generally speaking, UQM star systems are procedurally generated with a
+PRNG and various properties of the star including its type and
+location. Thus, the information in starmap.c is sufficient to provide
+an accounting of the number and quality of worlds in any given
+system. Each world can have up to 32 each of mineral deposits, energy
+signatures, or life forms. Thus, each world, in order, in the system
+will be listed at its offset with three DWORDS, representing mineral,
+energy, and biological entities respectively. Captured entities have a
+1 bit set.
+
+With a little over 3,800 visitable celestial bodies, this state file
+is capped at a little under 50 KB.
+
+ RANDGRPINFO_FILE aka "randgrp.dat"
+ ----------------------------------
+
+This is a "battle group file" - it tracks the information regarding
+groups of ships that you can encounter in interplanetary space. This
+file gives the location information for all ships in the last solar
+system you visited, and it tracks the fleet composition, health, &c
+for ships randomly generated due to visiting a solar system in some
+species's sphere of influence. Random battle groups are reset after a
+week in HyperSpace or upon arrival in some other star system.
+
+The data structures, and the reader/writer functions for them, are
+primarily defined in grpinfo.c.
+
+The RANDGRPINFO file starts with a GROUP_HEADER struct, specifying
+which star this information is for, when the data expires, and then 65
+offsets into the file. This is a *one-indexed array* for
+randomly-generated groups.
+
+This is because the offset for group zero points to an entirely
+different data structure: the "Group List". This gives the starmap
+depiction and location on the Interplanetary map of every battle group
+in the system, whether it is randomly generated or pre-defined. This
+comprises two bytes (the ID of the most recently encountered group,
+and then the number of groups), followed by a series of 17-byte chunks
+representing battle groups. These are a byte indicating the race
+identifier of the fleet (battle groups can be mixed-composition, but
+only one ship appears on the map at a time; this specifies that)
+followed by 16 bytes that represent an IP_GROUP structure. See the
+Read/WriteIpGroup functions in gengrp.c for the exact layout in the
+virtual file. There's a lot of wasted space there, a legacy of the
+time when these files were actually structure dumps on 32-bit machines
+with very specific memory layouts.
+
+The offsets that are not Group Zero have a similar layout, interpreted
+differently. Each of these represents a single battle group. It
+retains the two-byte prologue, the second of which specifies the
+number of 17-byte chunks to follow. The first byte here is the
+displayed race identifier, again, and then each 17-byte chunk is that
+ship's particular race ID followed by a 16-byte representation of a
+SHIP_FRAGMENT.
+
+There are no requirements about order of layout or total size of this
+file; the only requirement is that there is a GROUP_HEADER at offset
+zero and that the extents described in the valid offsets do not
+overlap.
+
+ DEFGRPINFO_FILE aka "defgrp.dat"
+ --------------------------------
+
+This is a random-access scratch space for battle groups that are
+deliberately placed by the plot. There's one GROUP_HEADER in this
+file, somewhere, for each star system that has ever had predefined
+ships and that the player has visited. There is *no guarantee of any
+kind* as to where they are, a priori.
+
+This information instead lives within the game state array, a
+155-byte-long bitfield that is mostly used for storing plot events and
+event flags. 32-bit values within this array store the relevant
+offsets for their fleets, or a 0 if they have not yet been
+encountered. In vanilla UQM, there are 14 of these values,
+corresponding to the Ur-Quan Probe, the Zoq-Fot-Pik scout, the
+Shofixti Survivor, the Unzervalt Guardian, the Sa-Matra, and the nine
+Melnorme traders in TrueSpace. Each of these has a corresponding entry
+in the game state array that ends in _OFFS0. (Due to the
+implementation of GET_GAME_STATE, these values actually are stored as
+four eight-bit values each, but this can be ignored pretty much
+everywhere but the definition of the states themselves).
+
+If one of these offsets is non-zero, then that offset in the
+DEFGRPINFO_FILE will have a SHIP_FRAGMENT-based group definition in
+the same format as the ones in RANDGRPINFO_FILE.
+
+The Game State array uses 0 as a special value to mean "this
+predefined battle group has not yet been spawned." Because of that, 0
+is not a legal offset to place a GROUP_HEADER. To ensure that no
+GROUP_HEADER is placed there, a zero is written to the first byte of
+the file. That way, the first defined group (which, for vanilla UQM,
+will always be the Ur-Quan Probe) thus begins at offset 1.
diff --git a/doc/devel/strtab b/doc/devel/strtab
new file mode 100644
index 0000000..11170c2
--- /dev/null
+++ b/doc/devel/strtab
@@ -0,0 +1,76 @@
+This is the format for resources of type STRTAB. It contains a number of
+items of unspecified data (not necessarilly strings).
+This is used for game strings (extension .txt), colour tables (.ct),
+colour translation tables (.xlt), and sound-effect tables (.snd)
+
+Everything is stored MSB first unless otherwise specified.
+
+position length meaning
+ 4 0xffffffff if the file is uncompressed.
+ Otherwise, the file is compressed. When uncompressed, the
+ file complies with the rest of the format as described
+ below.
+ 2 Unused in file, always 0x0000
+ 2 number of items in the file (StringCount)
+
+Followed by:
+ In file:
+ 4 Placeholder, always 0x00000000
+ numitems times:
+ 4 length of string
+ In memory:
+ numitems times:
+ 4 offset from &StringCount to the string
+ followed by:
+ 4 offset from &StringCount until one char past the end
+ of the last string.
+
+For .ct files an item is as follows (new 256x24bit format):
+ 1 Index of first clut in this item.
+ 1 Index of last clut in this item.
+ For each clut:
+ 256 palette entries:
+ 1 red value
+ 1 green value
+ 1 blue value
+
+For old .ct files an item is as follows (obsoleted by new 256x24bit format):
+ 1 Index of first clut in this item.
+ 1 Index of last clut in this item.
+ For each clut:
+ 32 palette entries:
+ 2 bits 0-4: blue value
+ bits 5-9: green value
+ bits 10-14: red value
+ bit 15: specifies the interpolation mode for 3DO scaling
+ (is ignored now)
+The full palette contains 256 values, which are the original 32 values
+ multiplied by 1 to 8.
+
+
+For .ct files (planet surface version; in ipanims) an item is as follows:
+ 1 Index of first color in this colortab.
+ 1 Index of last color in this colortab.
+ Then for each color (6 bits per channel):
+ 1 bits 0-5: red value
+ 1 bits 0-5: green value
+ 1 bits 0-5: blue value
+The planet surface palette files were probably copied verbatim from DOS version
+and left alone because they do no exactly fit into the whole CLUT system.
+
+For .xlt files (planet surface; in ipanims) an item is as follows:
+ 2*3 3 elevation levels (not used, probably informational)
+ 1*256 256 colormap indices each corresponding to a particular
+ elevation level 0..255; only colormap indices 128..255
+ can be used currently because colormaps only define
+ colors 128..255
+The .xlt files combined with planet surface .ct files are used to select colors
+for planet surface rendering.
+
+
+For .snd files an item is as follows:
+ N-2 Signed, 8-bit, 1 channel PCM samples of the sound
+ effect, where N = (length of string)
+ 2 Sampling frequency of the sound, *LSB first*
+ (the frequencies themselves seem to be remnants of
+ the DOS version)
diff --git a/doc/devel/threads b/doc/devel/threads
new file mode 100644
index 0000000..5d1d65b
--- /dev/null
+++ b/doc/devel/threads
@@ -0,0 +1,165 @@
+The UQM Threading and Synchronization library
+
+This document describes the function and API for the many threading and
+synchronization routines that UQM uses. People attempting to port this
+to thread systems that aren't covered by SDL (such as OS 9, for
+instance) will find the necessary API descriptions here.
+
+#defines
+
+NAMED_SYNCHRO: When #defined, all synchronization objects are named.
+This should be kept on all the time, at least until 1.0.
+
+TRACK_CONTENTION: implies NAMED_SYNCHRO. Spits out status messages
+whenever a thread goes to sleep because of an object matching
+TRACK_CONTENTION_CLASSES.
+
+TRACK_CONTENTION_CLASSES: This is an ORring of enums defined in
+threadlib.h.
+
+Constructs
+----------
+
+The operation of each construct is given; these operations map to the
+API in fairly straightforward ways.
+
+The locking constructs have an argument called "sync_class"; this
+indicates which value must be defined in TRACK_CONTENTION_CLASSES to
+see reports from this object. Valid values are SYNC_CLASS_TOPLEVEL
+for game logic synchronizers, SYNC_CLASS_AUDIO and SYNC_CLASS_VIDEO
+for audio and video systems, and SYNC_CLASS_RESOURCE for low-level
+resource allocators like memory systems.
+
+- Task
+
+This is the construct that the UQM game logic sees. In our
+multithreaded code tasks map directly to threads. A task is
+"assigned" to be started. One of its arguments demands that a stack
+size be specified; UQM ignores this.
+
+Tasks can voluntarily yield their timeslice by calling TaskSwitch().
+This is mandatory inside busywait loops; otherwise priority inversion
+problems surface under BSD and even on other systems it causes undue
+CPU utilization.
+
+Tasks have "states" that can be read or modified by various other
+threads. The only state that is ever checked in the UQM code is
+TASK_EXIT, which is set when the task needs to be shut down.
+
+Task functions themselves return an integer and take a void pointer
+that can be cast to "Task". This is a reference to the Task itself,
+and is used to check its own state and properly de-initialize itself.
+
+The last thing a Task function must do before exiting is call
+FinishTask.
+
+If another thread wishes to terminate a Task, it can call
+"ConcludeTask" upon it. ConcludeTask will wait for the Task to
+recognize the TASK_EXIT state and exit; this can produce deadlocks if
+the thread requestion the Task's termination is holding a resource
+that the concluding Task needs. Program with care.
+
+API:
+Task AssignTask (ThreadFunction task_func, SDWORD Stacksize,
+ const char *name);
+DWORD Task_SetState (Task task, DWORD state_mask);
+DWORD Task_ClearState (Task task, DWORD state_mask);
+DWORD Task_ToggleState (Task task, DWORD state_mask);
+DWORD Task_ReadState (Task task, DWORD state_mask);
+void TaskSwitch (void);
+void FinishTask (Task task);
+void ConcludeTask (Task task);
+
+
+- Thread
+
+A somewhat more powerful and low-level construct than the Task.
+Threads carry a "ThreadLocal" data object with them - at present, this
+is a single Semaphore used for signaling the thread when it wants to
+sleep until some graphics have been rendered. Threads can be created
+with arbitrary data passed as an argument (in the form of the void *),
+and can request their own ThreadLocal object. Threads can also be put
+to sleep for various amounts of time. Waiting on a thread permits one
+to block a thread until another completes.
+
+API:
+Thread CreateThread (ThreadFunction func, void *data,
+ SDWORD stackSize, const char *name);
+void SleepThread (TimePeriod timePeriod);
+void SleepThreadUntil (TimeCount wakeTime);
+void TaskSwitch (void);
+void WaitThread (Thread thread, int *status);
+
+
+- Mutex
+
+The simplest form of lock. If a thread tries to lock a mutex, it will
+sleep if the mutex is already locked, and awaken once the mutex
+becomes available. A Mutex must be unlocked by the same thread that
+locked it, and a thread must never lock a mutex it has already locked
+(without unlocking it first).
+
+API:
+Mutex CreateMutex (const char *name, DWORD syncClass);
+void DestroyMutex (Mutex sem);
+void LockMutex (Mutex sem);
+void UnlockMutex (Mutex sem);
+
+- RecursiveMutex
+
+A somewhat more powerful version of the Mutex; this mutex may be
+locked multiple times by the same thread, but then to truly release it
+it must unlock it an equal number of times. The Draw Command Queue
+(DCQ) is protected by a recursive mutex. (This construct is
+occasionally called a "reentrant mutex.")
+
+API:
+RecursiveMutex CreateRecursiveMutex (const char *name,
+ DWORD syncClass);
+void DestroyRecursiveMutex (RecursiveMutex m);
+void LockRecursiveMutex (RecursiveMutex m);
+void UnlockRecursiveMutex (RecursiveMutex m);
+int GetRecursiveMutexDepth (RecursiveMutex m);
+
+- Semaphore
+
+Semaphores superficially resemble Mutexes; they are "set" and
+"cleared". An integer is associated with each semaphore. Clearing
+the semaphore raises the value by one; setting it decreases it by one.
+Attempting to set a semaphore that is at value 0 will sleep the thread
+until the semaphore is cleared.
+
+Semaphores are used in the code to sleep until another thread is
+finished doing something. They are used heavily by the FlushGraphics
+code and the thread handling routines. A Task in the game is
+dedicated to advancing the calendar when necessary; this task is put
+to sleep by the clock semaphore when the game is paused, the player
+goes into orbit or to the menu, or any of several other events that
+act to suspend the game clock.
+
+API:
+Semaphore CreateSemaphore (DWORD initial, const char *name,
+ DWORD syncClass);
+void DestroySemaphore (Semaphore sem);
+void SetSemaphore (Semaphore sem);
+void ClearSemaphore (Semaphore sem);
+
+- CondVar
+
+If a thread waits on a condition variable, it will go to sleep until
+some other thread tells that condition variable to signal a thread (or
+broadcast to all threads) that are waiting upon that variable. Our
+condition variables are weaker than the "standard" variables.
+Condition variables are generally used in conjunction with mutexes,
+but ours do not permit this synchronization and are only suitable for
+use if the condition variable will broadcast periodically. UQM has a
+condition variable associated with the DCQ that broadcasts whenever
+the queue is empty. Threads that attempt to write to the DCQ when the
+DCQ is full wait on this condition variable.
+
+API:
+CondVar CreateCondVar (const char *name, DWORD syncClass);
+void DestroyCondVar (CondVar);
+void WaitCondVar (CondVar);
+void SignalCondVar (CondVar);
+void BroadcastCondVar (CondVar);
diff --git a/doc/devel/timing b/doc/devel/timing
new file mode 100644
index 0000000..99aa62e
--- /dev/null
+++ b/doc/devel/timing
@@ -0,0 +1,66 @@
+1. Timing and Changes
+---------------------
+The current UQM timer runs on a 120 ticks/second clock, defined by the
+constant ONE_SECOND (in timelib.h). Theoretically, all code has been
+updated to use this constant where needed, so the timer precision can
+be increased to 1000, for example, to match the native SDL timer
+resolution.
+
+The following functions and all variables and constants used with them
+have been examined and patched where necessary.
+
+Functions that have something to do with time:
+ XFormColorMap
+ XFormPLUT
+ CycleColorMap
+ FadeMusic
+ SeedRandomNumbers
+ WaitForNoInput
+ GetTimeCounter
+ SleepThread
+ SleepThreadUntil
+
+Global vars:
+ GameClock
+
+Constants changed:
+ BATTLE_FRAME_RATE (element.h)
+ NUM_DELAYS (clock.c)
+ ACCELERATION_INCREMENT (gameinp.c)
+ IP_FRAME_RATE (solarsys.c)
+
+Constants added:
+ CLOCK_BASE_FRAMERATE (clock.c)
+ STEP_ACCEL_DELAY (pstarmap.c)
+ PLANET_SIDE_RATE (lander.c)
+
+All alien comm animation definitions (frame rates) were patched to take
+ONE_SECOND into account. So if some animations start to bug all of a
+sudden, you know where to look ;).
+
+The game-clock function SetGameClockRate() changed to use the rate
+defined by CLOCK_BASE_FRAMERATE and does not use ONE_SECOND now (and is
+not supposed to).
+
+
+2. Possible Rate Changes
+------------------------
+The PLANET_SIDE_RATE and IP_FRAME_RATE can be safely adjusted at this
+time. The BATTLE_FRAME_RATE needs more work.
+
+
+3. Battle rate
+--------------
+The BATTLE_FRAME_RATE constant (currently 24 fps) can be adjusted only
+after converting the rate counters for all ships used in melee. They
+are expressed in frames and *must* take BATTLE_FRAME_RATE into account.
+
+They are:
+ ENERGY_WAIT
+ TURN_WAIT
+ THRUST_WAIT
+ WEAPON_WAIT
+ SPECIAL_WAIT
+
+There is probably more that needs to be done to make it work properly
+with an altered BATTLE_FRAME_RATE. Expect more info to appear.
diff --git a/doc/devel/versions b/doc/devel/versions
new file mode 100644
index 0000000..4b3d36a
--- /dev/null
+++ b/doc/devel/versions
@@ -0,0 +1,42 @@
+This file lists the Subversion repository numbers that coincide with
+each release. You should be able to revert to any individual release
+by doing a checkout of that version. It is also possible to check out
+a tagged version of each major release, but this is deprecated.
+
+Major releases for versions 0.6.0 and before are identified by their
+tags in the CVS tree and correspond to source-release packages; all
+microversions are identified by the point at which uqmversion.h
+changed.
+
+Version 0.1: Revision 240
+Version 0.2: Revision 847
+Version 0.21: Revision 816
+Version 0.22: Revision 913
+Version 0.23: Revision 994
+Version 0.24: Revision 1036
+Version 0.3: Revision 1208
+Version 0.31: Revision 1220
+Version 0.32: Revision 1330
+Version 0.33: Revision 1440
+Version 0.34: Revision 1619
+Version 0.4.0: Revision 1814
+Version 0.4.1: Revision 1931
+Version 0.4.2: Revision 2049
+Version 0.4.3: Revision 2160
+Version 0.4.4: Revision 2196
+Version 0.5.0: Revision 2250
+Version 0.5.1: Revision 2309
+Version 0.5.2: Revision 2392
+Version 0.5.3: Revision 2436
+Version 0.5.4: Revision 2463
+Version 0.6.0: Revision 2627
+Version 0.6.1: Revision 2652
+Version 0.6.2: Revision 2669
+Version 0.6.3: Revision 2828
+Version 0.6.4: Revision 2869
+Version 0.6.5: Revision 3110
+Version 0.6.6: Revision 3143
+Version 0.6.7: Revision 3276
+Version 0.6.8: Revision 3334
+Version 0.6.9: Revision 3537
+Version 0.7.0: Revision 3631
diff --git a/doc/devel/voiceeffects b/doc/devel/voiceeffects
new file mode 100644
index 0000000..1d53e90
--- /dev/null
+++ b/doc/devel/voiceeffects
@@ -0,0 +1,20 @@
+Slylandro Probe
+---------------------------------------------------------------------------
+The voice of the Slylandro probe is "Bruce" from MacInTalk Pro. MacInTalk
+Pro requires a 68040 Mac, a Quadra or Performa, and System 7.3 minimum.
+Basilisk II emulator can do Quadra 900 well enough.
+
+The probe speech added to UQM was processed as follows:
+1) generated on Basilisk II v0.8.0.142 running System 7.5.3 Rev 2 on
+ Quadra 900. Words like "hundred" and "point" were mid-sentence to get
+ better intonation. Names of tens -- 20, 30, etc. -- were cut from number
+ phrases "twenty-five", "thirty-five", etc.
+2) captured at 44100 Hz
+3) post-processed with Audacity 1.3 beta; filters:
+ a) GVerb: room 40m, reverb time 0.1s, damping 0.85, input bandwidth 0.75,
+ dry signal +0dB, early reflection -22dB, trail level -30dB
+ b) Amplify: +2.5dB (depends on your capture level)
+ c) Bass boost: centered at 300 Hz, +4dB
+4) chopped up and exported at 11025 Hz
+
+
diff --git a/doc/users/manual.txt b/doc/users/manual.txt
new file mode 100644
index 0000000..330d785
--- /dev/null
+++ b/doc/users/manual.txt
@@ -0,0 +1,799 @@
+THE UR-QUAN MASTERS v0.7 -- homepage: http://sc2.sf.net/
+
+Welcome to this beta release of the Ur-Quan Masters port. This
+document will tell you everything you need to play, even if you've
+never played the original.
+
+For those of you who have played the original, read the first section
+(starting the game and bug reports) and the last sections (Super Melee
+and control summary), and you'll be good to go.
+
+STARTING THE GAME
+
+Simply invoke the executable from the directory you installed UQM in
+to run the game. (Under Windows, this is uqm.exe; under Linux, it's
+uqm.) This will use the default settings. The defaults and how to
+change them are listed under COMMAND LINE OPTIONS, below.
+
+After a splash screen, you will see the main menu, which has five
+options:
+
+ - New Game: Begins a new Full Game. This is a galaxy-spanning space
+ adventure full of diplomacy, exploration, combat, high treason,
+ and low cunning. The introductory cutscenes will set the scene;
+ the THE STORY SO FAR section below provides more extensive
+ backstory for the curious.
+
+ - Load Game: Restores a Full Game session that was saved earlier.
+
+ - Super Melee!: Puts the game in Super Melee mode, where you may
+ hone your space combat skills or challenge your friends to fleet
+ battles. See the SUPER MELEE section below for details on this
+ section.
+
+ - Setup: Lets you configure many options to customize your play
+ experience. Most options will take effect once you exit the setup
+ menu; a few specially marked options require you to restart UQM.
+ Setup options are preserved across sessions. The options are
+ described under COMMAND LINE OPTIONS, below.
+
+ - Quit: Exits the program.
+
+COMMAND LINE OPTIONS
+
+The default options for an UQM install are 3DO music, 640x480 windowed
+mode, and pure SDL graphics drivers. The initial defaults may vary on
+the Windows install if you selected "Mimic PC" or "Mimic 3DO" as the
+install type. You may pass various command line options to customize
+your experience:
+
+ -r 320x240 (or --res)
+
+Sets the screen resolution. Unless --opengl is set, the only valid
+values are 640x480 and 320x240.
+
+ -f (or --fullscreen)
+
+Uses full screen mode. Pretty straightforward. Usually good to
+combine with -r 320x240 unless you're using a scaler.
+
+ -w (or --windowed)
+
+Displays the game in a window. The opposite of --fullscreen.
+
+ -o (or --opengl)
+
+Use OpenGL drivers. This produces higher-quality graphics, and may be
+faster as well -- but it also may not work on older cards. It also
+permits use of any screen resolution.
+
+ -x (or --nogl)
+
+Do not use OpenGL drivers. This is more likely to run on systems with
+poor or no 3D acceleration, and is often faster, especially on older
+or less well supported cards. Only the 320x240 and 640x480
+resolutions are available when not using OpenGL.
+
+ -k (or --keepaspectratio)
+
+Keep 4:3 aspect ratio when using custom resolutions.
+
+ -c (or --scale=mode)
+
+Graphics scaling mode (bilinear, biadapt, biadv, triscan, hq or none).
+Default is none. Try these to get smoother graphics with cost on
+performance.
+
+ -b (or --meleezoom=mode)
+
+Melee zooming mode (pc or 3do); 'step' is an alias for 'pc' and 'smooth'
+is an alias for '3do'. Default is 3do. Slower machine owners can set it
+to 'pc' to get better performance in melee.
+
+ -s (or --scanlines)
+
+Simulates interlaced displays.
+
+ -g (or --gamma)
+
+Sets gamma correction. 1.0 causes no change (unless your graphics card
+is originally set to a different value). Higher than 1.0 makes the
+image brighter, lower than 1.0 makes it darker.
+
+ -p (or --fps)
+
+Print fps information in the status window.
+
+ -C (or --configdir)
+
+Set the directory where the game will store the config data.
+
+ -n (or --contentdir)
+
+Set the directory where the game will seek its data.
+
+ -l (or --logfile)
+
+Set a file to receive the diagnostic information that would otherwise go
+to the console.
+
+ -h (or --help)
+
+Display a help message.
+
+ -M (or --musicvol)
+
+Set music volume (0-100).
+
+ -S (or --sfxvol)
+
+Set sound effects volume (0-100).
+
+ -T (or --speechvol)
+
+Set speech volume (0-100). If set to 0, the game runs in 'no speech'
+mode and the oscilloscope reacts to the music.
+
+ -m 3do (or --music 3do)
+
+Use the 3DO remixed soundtrack for songs that were in fact remixed.
+The default.
+
+ -m pc (or --music pc)
+
+Use the .MOD based PC soundtrack everywhere.
+
+ -q (or --audioquality)
+
+Can be "high", "medium", or "low". Specifies how nice the audio
+sounds. Slower machines should lower the audio quality.
+
+ --addon <addon> (no short version)
+
+Replace <addon> by the name of an add-on to enable in the game. See
+the section 'ADD-ONS' below for details.
+
+ --sound (no short version)
+
+Can be "openal", "mixsdl" or "none". Specifies which driver/mixer
+to use. "openal" is only available when it has been compiled in.
+It may produce higher-quality sound and will probably be faster,
+but it is not very stable on linux platforms, and may not work
+well with some sound cards.
+Use "none" as a last resort if you cannot get other drivers to work,
+or if you have no soundcard.
+
+ --stereosfx (no short version)
+
+Enables positional sound effects in melee. Currently works only when
+using openal.
+
+ -u (or --nosubtitles)
+
+Disables subtitles.
+
+ --cscan pc
+
+Use PC style planet information when scanning (text). Default.
+
+ --cscan 3do
+
+Use 3DO style planet information when scanning (pictograms).
+
+ --menu pc
+
+Use PC style menus (text) and 'CREW'/'BATT' in melee instead of icons.
+Default.
+
+ --menu 3do
+
+Use 3DO style menus (pictograms).
+
+ --font pc
+
+Use PC style fonts and colors.
+Default.
+
+ --font 3do
+
+Use 3DO style fonts and colors.
+
+ --scroll pc
+Scroll voice-over/subtitles 1 page at a time when using left/right arrow keys
+Default.
+
+ --scroll 3do
+Scroll voice-over/subtitles smoothly while holding down left/right arrow keys
+
+ -i 3do (or --intro 3do)
+
+Use the 3DO intro and ending movies (if you have them).
+The default.
+
+ -i pc (or --intro pc)
+
+Use the PC intro and ending sequences and slide shows. These will be also
+played if you do not have 3DO movies, regardless of -i option.
+
+ --shield pc
+
+Use PC style static slave shield graphic.
+Default.
+
+ --shield 3do
+
+Use 3DO style throbbing slave shield graphic. This somewhat increases the
+load on CPU while in orbit. Do not use if your CPU cannot handle that.
+
+ --safe (no short version)
+
+Start the game in safe mode. Safe mode will ignore stored user settings,
+like resolution, fullscreen mode, sound driver, etc. This is useful if
+you have somehow wrecked your configuration files and cannot get to the
+in-game setup menu to change the settings.
+
+
+NOT OFFICIALLY SUPPORTED OPTIONS
+
+The following options may not exist in all builds and can change without
+notice at any time.
+
+ --accel (no short version)
+
+Can be "none", "detect", "mmx", "3dnow", "sse" (also "altivec" if/when
+added; or other platforms). Specifies which platform accelerations
+to use for graphics and sound, if any. All specific platform code can
+only be used when compiled in.
+
+ --netport1 <port> (no short version)
+ --netport2 <port> (no short version)
+
+Specifies the default port that the bottom or top player respectively
+will connect to or accept incoming connections on. If this parameter
+is not specified, 21837 will be used. This value can be changed later
+in the SuperMelee Net menu. Your firewall needs to be set up to allow
+TCP connections from/to the used port.
+
+ --nethost1 <host> (no short version)
+ --nethost2 <host> (no short version)
+
+Specifies the default name or ip number of the host to connect to for
+the bottom or player. If this parameter is not specified, UQM will
+not attempt an outgoing connection, but instead listen for an incoming
+connection.
+
+ --netdelay (no short version)
+
+Set the default input delay (in frames). See the Super Melee section
+for details.
+
+
+ BUG REPORTS
+
+After several years of enthusiastic testing, UQM has dramatically
+improved its stability, but it is still beta software, and bugs
+certainly still lurk. Upon finding a problem, we'd like you to report
+it, but before you do, please do the following:
+
+- Try to isolate what causes it: "Crashes with a null dereference
+ about half the time when firing and taunting with a Pkunk" is better
+ than "Melee doesn't work." If the game crashes, notice what error
+ is produced; if the game hangs, check to see if the game-exit key
+ (F12) works.
+
+- Go to the bug database at http://bugs.uqm.stack.nl/
+ and post a report of the problem there. Search the database first if
+ it has been already posted; if we get many duplicate reports, processing
+ them eats our time from actual development. If it's been reported, and
+ you have more information, feel free to confirm that you've reproduced it
+ by adding a comment to the report. If ten people have already confirmed it,
+ though, it's probably best to treat it as duly reported.
+
+- Whenever possible, for bugs that only occur under certain conditions,
+ include a save game with your bug report that duplicates the bug.
+ In the case of a crash, a stack trace can be very helpful for us too.
+ If you don't know what a stack trace is, don't worry about it.
+
+- If your issue is more like "support request" than bug report and you
+ want help from other users, then posting it to our forum might be
+ more appropriate: http://uqm.stack.nl/forum/
+
+
+
+ THE STORY SO FAR
+
+For the past decade, Earth and the rest of the Alliance of Free Stars
+has fought the Ur-Quan and their Hierarchy of Battle Thralls. In the
+course of the War, the Earthlings discovered a factory world by the
+'Precursors' - an impossibly advanced that disappeared tens of
+thousands of years ago. This colony, Unzervalt (aka Vela I), lost all
+contact with Earth shortly after landfall.
+
+You are Captain Zelnick, a human that was born on Unzervalt and who
+possesses a remarkable knack for Precursor technology. You were the
+one who worked out how to activate the Precursor installation.
+
+It was a factory for building starships. However, Unzervalt is
+mineral-poor, and there were not enough materials available to
+construct a complete vessel. Your task is to command this craft, the
+Vindicator, and return to Earth to tell them of the abandoned colony.
+Also, if the War with the Ur-Quan continues, you must fight for Earth
+and the Alliance as best you can.
+
+There is a great deal more to this story. Asking Starbase Commander
+Hayes for background information will give you most of it.
+
+ INTERPLANETARY EXPLORATION
+
+When in a Solar system, use the thrust and steering controls to move
+about the system. Intersecting a planet will move you to the
+planetary system; flying over a planet or moon will then put you into
+orbit. From there you can talk to the inhabitants, or, if the planet
+is uninhabited, send a lander down to gather minerals, investigate
+energy readings, or capture life forms.
+
+ PLANET LANDING
+
+To land on a planet, you need to achieve orbit, then fill a planet
+lander with crew and send them down. You will usually want to scan
+the planet first. Mineral scans will indicate easily harvestable
+mineral ores and other resources. Energy scans will indicate unusual
+installations, which will effectively always be worth investigating.
+Biological scans will show where life forms are on the surface.
+
+Minerals are necessary for building up and maintaining your flagship,
+so harvest them wherever you can. There are nine varieties, each
+color coded:
+
+COMMON ELEMENTS (carbon, nitrogen) are cyan. Worth 1 resource unit
+(RU) per unit.
+
+CORROSIVES (chlorine, iodine) are red. 2 RU per unit.
+
+BASE METALS (iron, tin) are grey. These are common, and usually worth
+harvesting, but not terribly valuable. 3 RU per unit.
+
+NOBLE GASSES (argon, xenon) are blue. 4 RU per unit.
+
+RARE EARTHS (lanthanum, ytterbium) are green. 5 RU per unit.
+
+PRECIOUS ELEMENTS (gold, silver) are yellow. 6 RU per unit.
+
+RADIOACTIVES (uranium, astatine) are orange. 8 RU per unit.
+
+EXOTICS (antimatter, magnetic monopoles) are purple, and a princely 25
+RU per cargo unit.
+
+Minerals may be unloaded at Earth Starbase by talking to Commander
+Hayes, which will give you RU that you may spend to upgrade your
+flagship.
+
+However, there are many hazards on planetary surfaces. Life forms are
+often hostile, and need to be subdued with your stunner or evaded.
+Earthquakes (expanding circles) can hurt your crew, lightning may
+crisp them, or lava flows and hotspots can fry them. Be careful,
+especially on hotter or more seismically and atmospherically active
+worlds. If your crew level starts dropping dramatically, flee quickly
+with the ESCAPE key!
+
+Stunned life forms may be captured and analyzed by your planet
+landers. The information you gain from this may not be immediately
+useful, but it will eventually come in handy.
+
+Landing on a planet costs fuel, and the heavier the planet, the more
+fuel it requires. Make sure you don't spend so much fuel exploring
+planets that you can't get back to Sol!
+
+ INTERSTELLAR TRAVEL
+
+When you leave a solar system, you will push up into HyperSpace. In
+HyperSpace you can travel great distances quickly, but you must
+continuously thrust to move. Otherwise, you will gradually slow to a
+stop.
+
+While you can fly about in HyperSpace just like you do in a star
+system, the Galaxy is LARGE, and you will usually want to use the
+Auto-Pilot. To use the Auto-Pilot, select "Starmap" on the menu.
+This will show you a map of the quadrant (the galactic Core is in the
+upper right corner). To fly to a location, move the cursor there and
+press Enter. Then press Space to engage the Auto-Pilot.
+
+ DIPLOMACY
+
+When you encounter an alien starship, you will usually get a picture
+of their task force and a chance to choose between conversation and
+fighting. If you choose to fight, you will transition immediately to
+space combat (below). Otherwise, you will talk first. If talks go
+poorly, space combat will likely ensue.
+
+If the task force shows ships streaming off in all directions, you
+have reached a fortified world, and there are an unlimited number of
+starships facing you. You cannot win such a fight - if combat ensues,
+you will need to warp out.
+
+ SPACE COMBAT
+
+When combat begins, you are prompted to select a ship from your task
+force. A one-on-one space combat then begins, and continues until
+either the enemy fleet is destroyed (in which case you salvage the
+wrecks and continue the game), your flagship is destroyed (ending the
+game), or your flagship warps out of combat (consuming 5 fuel units
+but ending the encounter).
+
+Each ship has two major stats: Crew and Combat Battery. Crew are
+effectively hit points. Getting hit by weapons kills crew, and if all
+crew are eliminated, the craft is destroyed. Firing weapons typically
+requires energy from the combat batteries, which is replaced over
+time. The precise speed of energy regeneration and cost of weapons
+fire varies by ship.
+
+Space flight is *mostly* inertial (you'll drift if you stop
+thrusting), but each ship has a maximum velocity that can only be
+exceeding by "gravity whipping" around the planet. Don't hit the
+planet unless you want to take LOTS of damage.
+
+Each ship has a primary and secondary weapon mode, unique to that
+race's craft. The descriptions of those follow.
+
+ SHIP DESCRIPTIONS
+
+ Androsynth Guardian
+Primary weapon: Fires homing acid bubble clouds.
+Secondary weapon: Transforms into the 'Blazer', a comet that does
+considerable damage by ramming its opponents.
+
+ Ariloulaleelay Skiff
+Primary weapon: Auto-aiming, short-range laser
+Secondary weapon: Random teleport
+Note: The Skiff is inertia-less, and stops instantly when thrust is
+removed.
+
+ Chenjesu Broodhome
+Primary weapon: Crystal Shard. Will travel until the fire button is
+released, then shatters.
+Secondary weapon: De-energizing Offensive Guided Interceptor.
+Launches an autonomous DOGI that rams the opponent to drain their
+combat batteries.
+
+ Chmmr Avatar
+Primary weapon: Immensely powerful short-range laser
+Secondary weapon: Tractor beam.
+Note: Has three orbiting "ZapSats" that attack anything that gets in
+range.
+
+ Druuge Mauler
+Primary weapon: Long range, high-recoil cannon
+Secondary weapon: Sets one crew on fire to gain combat energy.
+
+ Earthling Cruiser
+Primary weapon: Homing nuclear missile
+Secondary weapon: Point-defense laser
+
+ Ilwrath Avenger
+Primary weapon: Short-range flamethrower
+Secondary weapon: Cloaking device
+
+ Kohr-Ah Marauder
+Primary weapon: Spinning blades that stop and home when the fire
+button is released
+Secondary weapon: Fiery Ring of Inevitable and Eternal Destruction
+(F.R.I.E.D.), a short-range corona of energy that blocks shots and
+inflicts lots of damage
+
+ Melnorme Trader
+Primary weapon: Charged shot. The longer the fire button is held, the
+stronger the shot.
+Secondary weapon: Confusion beam that scrambles enemy controls.
+
+ Mmrnmhrm X-Form
+Primary weapon: Lasers (X-form) or homing missiles (Y-form).
+Secondary weapon: Switch between X-Form and Y-Form.
+
+ Mycon Podship
+Primary weapon: Homing Plasmoid.
+Secondary weapon: Regenerate 4 crew.
+
+ Orz Nemesis
+Primary weapon: Howitzer cannon.
+Secondary weapon: Secondary with left and right arrows rotates the
+primary cannon. Secondary with Primary launches space marines that
+invade the enemy ship and kill their crew.
+
+ Pkunk Fury
+Primary weapon: Three-way cannon
+Secondary weapon: Fling insults at opponent. This is the only way the
+Pkunk can regenerate combat energy.
+Note: On occasion, a destroyed Fury will be resurrected with full fuel
+and power.
+
+ Shofixti Scout
+Primary weapon: Energy Dart.
+Secondary weapon: Glory Device. When pressed three times, the ship
+will self-destruct, inflicting vast damage on nearby vessels.
+
+ Slylandro Probe
+Primary weapon: Lighting weapon.
+Secondary weapon: Absorb a nearby asteroid and convert to combat
+power. This is the only way the Probe can recharge.
+Note: The Probe is inertia-less and always in motion. Pressing thrust
+will reverse its direction.
+
+ Spathi Eluder
+Primary weapon: Simple forward cannon.
+Secondary weapon: Backward Utilized Tracking Torpedo (B.U.T.T.), a
+homing missile fired from the rear of the vessel.
+
+ Supox Blade
+Primary weapon: Forward firing glob weapon
+Secondary weapon: Secondary + left or right will cause you to drift
+laterally, while Secondary + thrust will make you fly backwards. This
+cancels your current velocity, so be careful!
+
+ Syreen Penetrator
+Primary weapon: Particle Beam Stiletto.
+Secondary weapon: "Syreen Call" - psychic attack that induces enemy
+crew to jump ship, where you (or your opponent) may capture them to
+add to your complement
+
+ Thraddash Torch
+Primary weapon: Straightforward blaster cannon.
+Secondary weapon: Afterburner. The afterburner exhaust does more
+damage then the blaster, so use it as a weapon!
+
+ Umgah Drone
+Primary weapon: Anti-Matter cone. Does not require combat batteries to use.
+Secondary weapon: Fly backwards suddenly and at high speed.
+Note: The Drone only recharges batteries if you do not fire for a long
+time, and then the energy all returns in one lump.
+
+ Ur-Quan Dreadnought
+Primary weapon: Fusion Blast.
+Secondary weapon: Launches autonomous fighters to harrass the enemy.
+When they run low on fuel, they will fly back to the Dreadnought.
+Catch them before they expire. Each fighter requires one crew to
+pilot it, so take care not to weaken the core ship.
+
+ Utwig Jugger
+Primary weapon: Six-shot cannon. Requires no combat battery energy to fire.
+Secondary weapon: Force shield. Absorbing hits re-energizes your
+batteries. When the batteries are exhausted, the shield is
+permanently disabled until combat ends.
+
+ VUX Intruder
+Primary weapon: Gigawatt laser.
+Secondary weapon: Limpet mines that track enemy ships and slow them
+down dramatically if they hit.
+
+ Yehat Terminator
+Primary weapon: Twin autocannons.
+Secondary weapon: Force shield.
+
+ Zoq-Fot-Pik Stinger
+Primary weapon: Anti-matter spray gun.
+Secondary weapon: "Tongue attack", a point-blank range attack that
+does grievous damage.
+
+ SUPER MELEE
+
+Super Melee mode is pure combat. It's designed to let you hone your
+skills for the full game, or to challenge your friends to fleet
+matches. Selecting "Super Melee!" from the main menu will bring you
+to the super melee main screen.
+
+This screen is dominated by the fleet design screen. Move the cursor
+over a ship slot and press ENTER to change the ship assignment, or
+press DELETE to remove the ship. You may select the fleet name and
+press ENTER to edit the fleet name to something of your choice. The
+number next to the fleet name lists the fleet strength; this is simply
+the sum of the point values of all ships in the fleet.
+
+The right hand side of the screen has buttons for managing the battle.
+Each side has four buttons associated with it: LOAD, SAVE, CONTROL,
+and NET. The LOAD and SAVE buttons let you load and save fleets. A
+variety of fleets of various strengths are pre-defined, and you may
+add your own by saving fleets you design.
+
+The CONTROL button has one of five settings. HUMAN CONTROL puts the
+fleet under the control of a human player. (The precise controls for
+that player are set in the Setup menu, but the bottom player's
+controls are always the same as the full game's controls.) Then there
+are three levels of computer control:
+
+ - WEAK CYBORG is not a particularly good shot, and will only use
+ special weapons if the ship absolutely requires the special weapon
+ to function at all (Pkunk, Slylandro). This difficulty level only
+ appears in the full game when fighting crippled ships.
+
+ - GOOD CYBORG will actually use its special weapon, but it's still
+ not much of a threat. The Good cyborg provides a gentle
+ introduction to Star Control combat if you are unfamiliar with the
+ gameplay. However, you will soon wish to switch to...
+
+ - AWESOME CYBORG. The AI will fully exploit each ship's abilities,
+ and is also a tolerably good shot, compensating for inertia and
+ choosing its shots. Enemies you meet in the full game are almost
+ always piloted at this level. If a battle is giving you trouble
+ in the full game, this is the setting you want.
+
+The last control option is NETWORK CONTROL, which will be set if that
+side is controlled by a non-local opponent. To set up a network game,
+push the NET button on the side you wish to be under your opponent's
+control.
+
+In order to connect, you must agree on a port (the default is 21837,
+which should not require any change) and set a net delay in frames.
+To compensate for network lag, a keypress or keyrelease will only take
+effect after this many frames. While higher values make your ship
+seem to respond more sluggishly, they give the keypress information
+more time to reach the remote party. If the game stutters, this is
+because it is waiting for this keypress information to arrive, which
+is an indication that the input delay is too low. Super Melee runs at
+24 frames per second, each frame delayed will delay the input by about
+42ms. The delay used is the maximum of the desired value for both
+parties. The default is 2. Values lower than 4 are typically
+acceptable in terms of responsiveness. Future versions may
+automatically decide on the best value to use.
+
+Once the port and delay are set, one player must select the first
+option (Wait for incoming connection), while the other enters his
+opponent's hostname or IP address into the Host field and then pushes
+"Connect." Once the connection is established, the control scheme for
+the remote player will flip to NETWORK CONTROL to register the
+connection. To disconnect, change it away back to one of the HUMAN or
+CYBORG options. Once connected, you are both free to edit your fleets
+to provide a properly balanced battle.
+
+Once all fleets are in readiness, press the BATTLE button in the
+center right. (In a network game, both players must select it with no
+intervening fleet edits. Moving away from BATTLE or having your
+opponent change their fleet will cancel your readiness state, and you
+will need to reselect the BATTLE button.) When all players are ready,
+the battle begins.
+
+The ship selection screen is much as it would be in the full game,
+with two notable exceptions: a question mark in the upper right allows
+you to select a new ship randomly from those remaining, and a red X
+allows you to exit the combat entirely. (It is also possible to exit
+the melee at any time by pressing F10, just as one can exit anything.)
+If nobody quits, the combat will continue until one side has been
+completely destroyed. At this point, both fleets are shown so that
+one may compare initial and remaining fleet strengths. Press a key to
+return to the melee menu.
+
+To return to the main menu after finishing with Super Melee mode, push
+the QUIT button in the lower right.
+
+ GENERAL GAME CONTROLS SUMMARY
+
+F1 or PAUSE Pause game
+F10 Exit game
+F11 Toggle between fullscreen and windowed mode
+
+ FULL GAME CONTROLS SUMMARY
+
+ Space flight
+
+UP Thrust
+LEFT and RIGHT: Steer
+SPACE or RIGHT SHIFT: Main menu
+
+ Menus
+
+Arrow Keys: Scroll through selections
+ENTER or RIGHT CTRL: Make selection
+SPACE or RIGHT SHIFT: Up one level
+ or ESCAPE
+
+ Conversations
+
+LEFT and RIGHT: Rewind/Forward
+UP and DOWN: Scroll through selections
+ENTER or RIGHT CTRL: Make selection
+SPACE or RIGHT SHIFT: Skip, Show/Hide summary
+
+ Star Map
+
+Arrow Keys: Move the crosshair
+ENTER or RIGHT CTRL: Select destination
+SPACE or RIGHT SHIFT: Main menu
+Keypad +: Zoom in
+Keypad -: Zoom out
+/ (not on the keypad): Begin search.
+ (Type star or constellation name to
+ find matches)
+TAB: Jump to next match.
+
+ Space Combat
+
+UP: Thrust
+LEFT and RIGHT: Steer
+RIGHT CTRL or ENTER: Fire Primary Weapon
+RIGHT SHIFT: Fire Secondary Weapon
+ESCAPE: Emergency Warp Escape
+
+
+ Planet Exploration
+
+UP: Forward
+LEFT and RIGHT: Steer
+RIGHT CTRL or ENTER: Fire stun bolt
+RIGHT SHIFT or ESCAPE: Blast off
+
+
+ MELEE CONTROLS SUMMARY
+
+ Top Player
+
+W: Thrust
+A and D: Steer
+V: Fire Primary Weapon
+B: Fire Secondary Weapon
+
+ Bottom Player
+
+UP or ENTER: Thrust
+LEFT and RIGHT: Steer
+RIGHT CTRL or ENTER: Fire Primary Weapon
+RIGHT SHIFT: Fire Secondary Weapon
+
+
+These controls are configurable from the Setup Menu. You may define
+up to six "Input Templates" and assign a template to either or both
+players. Some commonly used key configurations are pre-defined, as
+well as popular variants. To change key bindings, select the binding
+you wish to change and press ENTER. At the dialog box, press the key
+(or joystick gesture) that you wish to assign to this action.
+
+
+ SAVED GAMES
+
+The saved games are kept in your personal directory for uqm data.
+This directory is automatically created the first time you start the
+game. The location of this directory varies per system.
+
+On Microsoft Windows systems this is a folder named 'uqm' in the
+application data folder for the current user.
+This is usually in one of the following locations:
+- "C:\Windows\Application Data\"
+ (Windows 95, 98, SE without separate users)
+- "C:\Windows\Profiles\YourName\Application Data\"
+ (Windows 95/98/SE with separate users)
+- "C:\Documents and Settings\YourName\Application Data\"
+ (Windows NT/2k/XP)
+The "Application Data" folder may be hidden. You can tell Windows to display
+hidden files and folders in the Folder Options dialog, which you can
+find in the Tools menu of any folder window.
+
+On Unix systems this personal uqm data is stored in "~/.uqm".
+
+You will generally only need this if you intend to transfer savegames
+to another computer. Note that currently games saved on an PowerPC Mac
+will not work on a PC (or an Intel Mac), and vice versa. AMD64 and
+Intel savegames may be safely transferred.
+
+
+ ADD-ONS
+
+As of version 0.3, The Ur-Quan Masters has basic support for add-on
+packages. Though it is not very elaborate yet, you can install some
+content add-ons. Add-ons created for UQM releases prior to v0.7 are not
+compatible with this release. To continue using your current add-ons,
+you will have to obtain or create new add-on packages.
+
+Inside the directory where the content is installed, in the content/
+directory, there is a directory 'addons/'. In this directory, you can
+place add-on packages, like 3domusic, 3dovoice, remix and others, or
+create new directories with .zip files to be used in addition to the
+standard content .zip files. Each add-on must contain at least one .rmp
+file to tell the game which resources the add-on provides.
+When you specify the command-line option '--addon <addon>', the .zip files
+inside the directory content/addons/<addon> will be included
+in the game. '--addon' may be specified more than once to enable multiple
+add-ons.
+
+Unlike previous releases, 3domusic and 3dovoice are now standard add-ons,
+and can be turned on and off from the in-game setup menu.
+The Precursors UQM Remix project is intended to be used as an add-on.
+If you install UQM 0.7 from the Windows installer, the remix packs
+are available as options. The remix add-on can also be turned on and off
+from the in-game setup menu.
diff --git a/doc/users/unixinstall b/doc/users/unixinstall
new file mode 100644
index 0000000..f08e26f
--- /dev/null
+++ b/doc/users/unixinstall
@@ -0,0 +1,45 @@
+This document describes the installation procedure for a installation from
+source on a Linux or BSD platform.
+
+1.
+First, make sure all the required libraries are installed.
+They are listed in the file INSTALL in the top dir.
+
+
+2.
+If you haven't done so already, download the content .uqm files packages
+that you want, from where you got this source package.
+You'll need uqm-0.7.0-content.uqm; uqm-0.7.0-3domusic.uqm (music from the 3DO
+version) and uqm-0.7.0-voice.uqm (spoken alien dialogs) are optional.
+
+Put the uqm-0.7.0-content.uqm in the directory content/packages/ which exists
+in the top directory of the source tree (the dir with (among others)
+COPYING, src/, and build.var.in).
+Put the optional music and voice packages in the content/addons/ directory.
+
+
+3.
+Run './build.sh uqm'
+Change settings in the menu if you want.
+The source building process will start, and when it ends, you should have
+a file 'uqm' in the source dir (or uqm-debug if you selected a debug build).
+You can try it out from this dir by typing './uqm' (or './uqm-debug').
+
+
+4.
+Run './build.sh uqm install', and be patient.
+Depending on what installation location you selected in the configuration
+menu, you might need root permissions here.
+
+
+5.
+Done.
+You can start the game now with the command 'uqm'.
+You may also delete the source (with the .uqm files you put in
+content/) if you want.
+
+
+If you want to recompile with other options, you can use
+'./build.sh uqm config' to reconfigure; you might need to do
+'./build.sh uqm depend' afterwards.
+
diff --git a/doc/users/uqm.6 b/doc/users/uqm.6
new file mode 100644
index 0000000..9d9b38b
--- /dev/null
+++ b/doc/users/uqm.6
@@ -0,0 +1,985 @@
+.\" (C) 1992, 1993, 2002 Toys for Bob, Inc.
+.\" The documentation may be used freely under the terms of the
+.\" Creative Commons Attribution Attribution 2.0 license,
+.\" available at http://creativecommons.org/licenses/by/2.0/
+.\"
+.\" The content may also be copied freely as part of a distribution of
+.\" The Ur-Quan Masters.
+.Dd October 28, 2014
+.Dt UQM 6
+.Os
+.Sh NAME
+.Nm uqm
+.Nd The Ur\(hyQuan Masters \(en space exploration game
+.Sh SYNOPSIS
+.Nm uqm
+.Op Fl bcCfghklMnopqsSTuwx
+.Op Fl i Ar style
+.Op Fl m Ar soundtrack
+.Op Fl r Ar resolution
+.Op Fl -addon Ar addon
+.Op Fl -cscan Ar style
+.Op Fl -font Ar font
+.Op Fl -menu Ar style
+.Op Fl -safe
+.Op Fl -scroll Ar style
+.Op Fl -shield Ar style
+.Op Fl -sound
+.Op Fl -stereosfx
+.Sh DESCRIPTION
+.Nm uqm
+is a port of the epic space game The Ur\(hyQuan Masters.
+.Pp
+The default settings for an
+.Nm uqm
+install are 3DO music, 640\(mu480 windowed mode, and pure SDL graphics drivers.
+You may pass various command line options to customize your experience.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl b , Fl -meleezoom Ar mode
+Melee zooming mode
+.Po
+.Sy pc
+or
+.Sy 3do
+.Pc ;
+.Sy step
+is an alias for
+.Sy pc
+and
+.Sy smooth
+is an alias for
+.Sy 3do .
+Default is
+.Sy 3do .
+Slower machine owners can set it to
+.Sy pc
+to get better performance in melee.
+.It Fl C Ar dir , Fl -configdir Ar dir
+Sets the directory where the game will store the config data.
+.It Fl c Ar mode , Fl -scale Ar mode
+Graphics scaling mode
+.Po
+.Cm bilinear ,
+.Cm biadapt ,
+.Cm biadv ,
+.Cm triscan ,
+.Cm hq
+or
+.Cm none
+.Pc .
+Default is
+.Cm none .
+Try these to get smoother graphics, at a performance cost.
+.It Fl f , Fl -fullscreen
+Uses full screen mode.
+The opposite of
+.Fl w .
+.It Fl g Ar val , Fl -gamma Ar val
+Sets gamma correction.
+1.0 causes no change
+(unless your graphics card is originally set to a different value).
+Higher than 1.0 makes the image brighter, lower than 1.0 makes it darker.
+.It Fl h , Fl -help
+Display a help message.
+.It Fl i Ar style , Fl -intro Ar style
+With
+.Cm 3do ,
+use the 3DO intro and ending movies (if you have them).
+With
+.Cm pc ,
+use the PC intro and ending sequences and slide shows.
+Defaults to
+.Cm 3do .
+Regardless of which option is selected, the PC sequences will be used if you do
+not have the 3DO movies.
+.It Fl k , Fl -keepaspectratio
+Keep 4:3 aspect ratio when using custom resolutions.
+.It Fl l Ar file , Fl -logfile Ar file
+Set a file to receive the diagnostic information that would otherwise go to the
+console.
+.It Fl M Ar volume , Fl -musicvol Ar volume
+Set music volume (0\(en100).
+.It Fl m Ar soundtrack , Fl -music Ar soundtrack
+Select either the 3DO remixed soundtrack with
+.Cm 3do
+or the .MOD\(hybased PC soundtrack with
+.Cm pc .
+Defaults to
+.Cm 3do
+for songs that were in fact remixed.
+.It Fl n Ar dir , Fl -contentdir Ar dir
+Set the directory where the game will seek its data.
+.It Fl o , Fl -opengl
+Use OpenGL drivers.
+This produces higher\(hyquality graphics, and may be faster as well \(en
+but it also may not work on older cards.
+It also permits use of any screen resolution.
+The opposite of
+.Fl x .
+.It Fl p , Fl -fps
+Print fps information in the status window.
+.It Fl q Ar setting , Fl -audioquality Ar setting
+Can be
+.Cm high ,
+.Cm medium ,
+or
+.Cm low .
+Specifies how nice the audio sounds.
+Slower machines should lower the audio quality.
+.It Fl r Ar resolution , Fl -res Ar resolution
+Sets the screen resolution.
+Unless
+.Fl -opengl
+is set, the only valid values are
+.Dq 640x480
+and
+.Dq 320x240 .
+.It Fl S Ar volume , Fl -sfxvol Ar volume
+Set sound effects volume (0\(en100).
+.It Fl s , Fl -scanlines
+Simulates interlaced displays.
+.It Fl T Ar volume , Fl -speechvol Ar volume
+Set speech volume (0\(en100).
+If set to 0, the game runs in
+.Sq no speech
+mode and the oscilloscope reacts to the music.
+.It Fl u , Fl -nosubtitles
+Disables subtitles.
+.It Fl w , Fl -windowed
+Displays the game in a window.
+The opposite of
+.Fl f .
+.It Fl x , Fl -nogl
+Do not use OpenGL drivers.
+This is more likely to run on systems with poor or no 3D acceleration,
+and is often faster, especially on older or less well supported cards.
+Only the 320\(mu240 and 640\(mu480 resolutions are available when not using
+OpenGL.
+The opposite of
+.Fl o .
+.It Fl -addon Ar name
+Enable the named add\(hyon in the game.
+See the
+.Sx ADD\(hyONS
+section for details.
+.It Fl -cscan Ar style
+When scanning planets, display information using text
+.Pq with Cm pc
+or pictograms
+.Pq with Cm 3do .
+Defaults to
+.Cm pc .
+.It Fl -font Ar style
+Use PC\(hystyle fonts and colors
+.Pq with Cm pc
+or 3DO\(hystyle
+.Pq with Cm 3do .
+Defaults to
+.Cm pc .
+.It Fl -menu Ar style
+In menus, display options as text
+.Pq with Cm pc
+or pictograms
+.Pq with Cm 3do .
+Defaults to
+.Cm pc .
+.It Fl -safe
+Start the game in safe mode.
+Safe mode will ignore stored user settings,
+like resolution, fullscreen mode, sound driver, etc.
+This is useful if you have somehow wrecked your configuration files and cannot
+get to the in\(hygame setup menu to change the settings.
+.It Fl -scroll Ar style
+Using the left/right arrow keys,
+scroll voiceovers and subtitles one page at a time
+.Pq with Cm pc
+or smoothly
+.Pq with Cm 3do .
+Defaults to
+.Cm pc .
+.It Fl -shield Ar style
+Use the CPU\(hyintensive, 3DO\(hystyle throbbing slave shield graphic
+.Pq with Cm 3do
+or the PC\(hystyle static slave shield graphic
+.Pq with Cm pc .
+Defaults to
+.Cm pc .
+.It Fl -sound Ar driver
+Specifies which sound driver/mixer to use.
+Can be
+.Cm openal ,
+.Cm mixsdl ,
+or
+.Cm none .
+.Cm openal
+is only available when it has been compiled in.
+It may produce higher\(hyquality sound and will probably be faster,
+but it is not very stable on Linux platforms,
+and may not work well with some sound cards.
+Use
+.Cm none
+as a last resort if you cannot get other drivers to work,
+or if you have no sound card.
+.It Fl -stereosfx
+Enables positional sound effects in melee.
+Currently works only when using openal.
+.El
+.Sh STARTING THE GAME
+Simply invoke the
+.Nm uqm
+program to run the game.
+After a splash screen, you will see the main menu, which has five options:
+.Bl -tag -width "Super Melee!"
+.It New Game
+Begins a new Full Game.
+This is a galaxy\(hyspanning space adventure full of diplomacy, exploration,
+combat, high treason, and low cunning.
+The introductory cutscenes will set the scene; the below section
+.Sx THE STORY SO FAR
+provides more extensive backstory for the curious.
+.It Load Game
+Restores a Full Game session that was saved earlier.
+.It Super Melee!
+Puts the game in Super Melee mode, where you may hone your space combat skills
+or challenge your friends to fleet battles.
+See the
+.Sx SUPER MELEE
+section below for details on this game mode.
+.It Setup
+Lets you configure many options to customize your play experience.
+Most options will take effect once you exit the setup menu;
+a few specially marked options require you to restart
+.Nm uqm .
+Setup options are preserved across sessions.
+.It Quit
+Exits the program.
+.El
+.Sh CONTROLS SUMMARY
+.Ss General Game Controls
+.Bl -tag -width "F1 or Pause" -compact
+.It Cm F1 No or Cm Pause
+Pause game
+.It Cm F10
+Exit game
+.It Cm F11
+Toggle between fullscreen and windowed mode
+.El
+.Ss Full Game Controls
+Space flight:
+.Bl -tag -width "Space or Right Shift" -compact
+.It Cm Up
+Thrust
+.It Cm Left No and Cm Right
+Steer
+.It Cm Space No or Cm Right Shift
+Main Menu
+.El
+.Pp
+Menus:
+.Bl -tag -width "Space or Right Shift or Escape" -compact
+.It Arrow keys
+Scroll through selections
+.It Cm Enter No or Cm Right Ctrl
+Make selection
+.It Cm Space No or Cm Right Shift No or Cm Escape
+Up one level
+.El
+.Pp
+Conversations:
+.Bl -tag -width "Space or Right Shift" -compact
+.It Cm Left No and Cm Right
+Rewind/Forward
+.It Cm Up No and Cm Down
+Scroll through selections
+.It Cm Enter No or Cm Right Ctrl
+Make selection
+.It Cm Space No or Cm Right Shift
+Skip, Show/Hide summary
+.El
+.Pp
+Star Map:
+.Bl -tag -width "Space or Right Shift" -compact
+.It Arrow Keys
+Move the crosshair
+.It Cm Enter No or Cm Right Ctrl
+Select destination
+.It Cm Space No or Cm Right Shift
+Main menu
+.It Keypad Cm +
+Zoom in
+.It Keypad Cm -
+Zoom out
+.It Cm /
+Begin search.
+(Type star or constellation name to find matches)
+.It Cm Tab
+Jump to next match
+.El
+.Pp
+Space Combat:
+.Bl -tag -width "Right Ctrl or Enter" -compact
+.It Cm Up
+Thrust
+.It Cm Left No and Cm Right
+Steer
+.It Cm Right Ctrl No or Cm Enter
+Fire Primary Weapon
+.It Cm Right Shift
+Fire Secondary Weapon
+.It Cm Escape
+Emergency Warp Escape
+.El
+.Pp
+Planet Exploration:
+.Bl -tag -width "Right Shift or Escape" -compact
+.It Cm Up
+Forward
+.It Cm Left No and Cm Right
+Steer
+.It Cm Right Ctrl No or Cm Enter
+Fire stun bolt
+.It Cm Right Shift No or Cm Escape
+Blast off
+.El
+.Ss Melee Controls
+Top Player:
+.Bl -tag -width "A and D" -compact
+.It Cm W
+Thrust
+.It Cm A No and Cm D
+Steer
+.It Cm V
+Fire Primary Weapon
+.It Cm B
+Fire Secondary Weapon
+.El
+.Pp
+Bottom Player:
+.Bl -tag -width "Right Ctrl or Enter" -compact
+.It Cm Up No or Cm Enter
+Thrust
+.It Cm Left No and Cm Right
+Steer
+.It Cm Right Ctrl No or Cm Enter
+Fire Primary Weapon
+.It Cm Right Shift
+Fire Secondary Weapon
+.El
+.Pp
+These controls are configurable from the Setup Menu.
+You may define up to six
+.Dq Input Templates
+and assign a template to either or both players.
+Some commonly used key configurations are pre\(hydefined,
+as well as popular variants.
+To change key bindings, select the binding you wish to change and press
+.Cm Enter .
+At the dialog box, press the key (or joystick gesture)
+that you wish to assign to this action.
+.Sh SAVED GAMES
+The saved games are kept in your personal directory for
+.Nm uqm
+data.
+This directory is automatically created the first time you start the game.
+On Unix systems this personal
+.Nm uqm
+data is stored in
+.Pa ~/.uqm .
+.Pp
+You will generally only need to know this if you intend to transfer savegames
+to another computer.
+.Sh INTERPLANETARY EXPLORATION
+When in a Solar system,
+use the thrust and steering controls to move about the system.
+Intersecting a planet will move you to the planetary system;
+flying over a planet or moon will then put you into orbit.
+From there you can talk to the inhabitants, or, if the planet is uninhabited,
+send a lander down to gather minerals, investigate energy readings,
+or capture life forms.
+.Sh PLANET LANDING
+To land on a planet, you need to achieve orbit,
+then fill a planet lander with crew and send them down.
+You will usually want to scan the planet first.
+Mineral scans will indicate easily harvestable mineral ores and other
+resources.
+Energy scans will indicate unusual installations,
+which will effectively always be worth investigating.
+Biological scans will show where life forms are on the surface.
+.Pp
+Minerals are necessary for building up and maintaining your flagship, so
+harvest them wherever you can.
+There are nine varieties, each color coded:
+.Bl -bullet
+.It
+.Em Common elements
+(carbon, nitrogen) are cyan.
+Worth 1 resource unit (RU) per unit.
+.It
+.Em Corrosives
+(chlorine, iodine) are red.
+2 RU per unit.
+.It
+.Em Base metals
+(iron, tin) are grey.
+These are common, and usually worth harvesting, but not terribly valuable.
+3 RU per unit.
+.It
+.Em Noble gases
+(argon, xenon) are blue.
+4 RU per unit.
+.It
+.Em Rare earths
+(lanthanum, ytterbium) are green.
+5 RU per unit.
+.It
+.Em Precious elements
+(gold, silver) are yellow.
+6 RU per unit.
+.It
+.Em Radioactives
+(uranium, astatine) are orange.
+8 RU per unit.
+.It
+.Em Exotics
+(antimatter, magnetic monopoles) are purple,
+and a princely 25 RU per cargo unit.
+.El
+.Pp
+Minerals may be unloaded at Earth Starbase by talking to Commander Hayes,
+which will give you RU that you may spend to upgrade your flagship.
+.Pp
+However, there are many hazards on planetary surfaces.
+Life forms are often hostile, and need to be subdued with your stunner or
+evaded.
+Earthquakes (expanding circles) can hurt your crew,
+lightning may crisp them, or lava flows and hotspots can fry them.
+Be careful,
+especially on hotter or more seismically and atmospherically active worlds.
+If your crew level starts dropping dramatically,
+flee quickly with the
+.Cm Escape
+key!
+.Pp
+Stunned life forms may be captured and analyzed by your planet landers.
+The information you gain from this may not be immediately useful,
+but it will eventually come in handy.
+.Pp
+Landing on a planet costs fuel, and the heavier the planet,
+the more fuel it requires.
+Make sure you don\(cqt spend so much fuel exploring planets that you can\(cqt
+get back to Sol!
+.Sh INTERSTELLAR TRAVEL
+When you leave a solar system, you will push up into HyperSpace.
+In HyperSpace you can travel great distances quickly,
+but you must continuously thrust to move.
+Otherwise, you will gradually slow to a stop.
+.Pp
+While you can fly about in HyperSpace just like you do in a star system,
+the Galaxy is
+.Em Large ,
+and you will usually want to use the Auto-Pilot.
+To use the Auto-Pilot, select
+.Dq Starmap
+on the menu.
+This will show you a map of the quadrant
+(the galactic Core is in the upper right corner).
+To fly to a location, move the cursor there and press
+.Cm Enter .
+Then press
+.Cm Space
+to engage the Auto-Pilot.
+.Sh DIPLOMACY
+When you encounter an alien starship, you will usually get a picture of their
+task force and a chance to choose between conversation and fighting.
+If you choose to fight, you will transition immediately to space combat
+(below).
+Otherwise, you will talk first.
+If talks go poorly, space combat will likely ensue.
+.Pp
+If the task force shows ships streaming off in all directions, you have reached
+a fortified world, and there are an unlimited number of starships facing you.
+You cannot win such a fight \(en if combat ensues, you will need to warp out.
+.Sh SPACE COMBAT
+When combat begins, you are prompted to select a ship from your task force.
+A one-on-one space combat then begins, and continues until either the enemy
+fleet is destroyed (in which case you salvage the wrecks and continue the
+game), your flagship is destroyed (ending the game), or your flagship warps
+out of combat (consuming 5 fuel units but ending the encounter).
+.Pp
+Each ship has two major stats: Crew and Combat Battery.
+Crew are effectively hit points.
+Getting hit by weapons kills crew, and if all crew are eliminated,
+the craft is destroyed.
+Firing weapons typically requires energy from the combat batteries,
+which is replaced over time.
+The precise speed of energy regeneration and cost of weapons fire varies by
+ship.
+.Pp
+Space flight is
+.Em mostly
+inertial (you\(cqll drift if you stop thrusting),
+but each ship has a maximum velocity that can only be exceeding by
+.Dq gravity whipping
+around the planet.
+Don\(cqt hit the planet unless you want to take
+.Em lots
+of damage.
+.Pp
+Each ship has a primary and secondary weapon mode,
+unique to that race\(cqs craft.
+The descriptions of those follow.
+.Ss Ship Descriptions
+.Bl -ohang
+.It Androsynth Guardian
+.Bl -inset -compact
+.It Primary weapon:
+Fires homing acid bubble clouds.
+.It Secondary weapon:
+Transforms into the
+.Sq Blazer ,
+a comet that does considerable damage by ramming its opponents.
+.El
+.It Ariloulaleelay Skiff
+.Bl -inset -compact
+.It Primary weapon:
+Auto\(hyaiming, short\(hyrange laser.
+.It Secondary weapon:
+Random teleport.
+.It Note:
+The Skiff is inertia-less, and stops instantly when thrust is removed.
+.El
+.It Chenjesu Broodhome
+.Bl -inset -compact
+.It Primary weapon:
+Crystal Shard.
+Travels until the fire button is released, then shatters.
+.It Secondary weapon:
+De\(hyenergizing Offensive Guided Interceptor.
+Launches an autonomous DOGI that rams the opponent to drain their combat
+batteries.
+.El
+.It Chmmr Avatar
+.Bl -inset -compact
+.It Primary weapon:
+Immensely powerful short-range laser.
+.It Secondary weapon:
+Tractor beam.
+.It Note:
+Has three orbiting
+.Dq ZapSats
+that attack anything that gets in range.
+.El
+.It Druuge Mauler
+.Bl -inset -compact
+.It Primary weapon:
+Long range, high\(hyrecoil cannon.
+.It Secondary weapon:
+Sets one crew on fire to gain combat energy.
+.El
+.It Earthling Cruiser
+.Bl -inset -compact
+.It Primary weapon:
+Homing nuclear missile.
+.It Secondary weapon:
+Point-defense laser.
+.El
+.It Ilwrath Avenger
+.Bl -inset -compact
+.It Primary weapon:
+Short\(hyrange flamethrower.
+.It Secondary weapon:
+Cloaking device.
+.El
+.It Kohr\(hyAh Marauder
+.Bl -inset -compact
+.It Primary weapon:
+Spinning blades that stop and home when the fire button is released.
+.It Secondary weapon:
+Fiery Ring of Inevitable and Eternal Destruction (F.R.I.E.D.), a short\(hyrange
+corona of energy that blocks shots and inflicts lots of damage.
+.El
+.It Melnorme Trader
+.Bl -inset -compact
+.It Primary weapon:
+Charged shot.
+The longer the fire button is held, the stronger the shot.
+.It Secondary weapon:
+Confusion beam that scrambles enemy controls.
+.El
+.It Mmrnmhrm X\(hyForm
+.Bl -inset -compact
+.It Primary weapon:
+Lasers (X-form) or homing missiles (Y-form).
+.It Secondary weapon:
+Switch between X-Form and Y-Form.
+.El
+.It Mycon Podship
+.Bl -inset -compact
+.It Primary weapon:
+Homing Plasmoid.
+.It Secondary weapon:
+Regenerate 4 crew.
+.El
+.It Orz Nemesis
+.Bl -inset -compact
+.It Primary weapon:
+Howitzer cannon.
+.It Secondary weapon:
+Secondary with left and right arrows rotates the primary cannon.
+Secondary with Primary launches space marines that invade the enemy ship and
+kill their crew.
+.El
+.It Pkunk Fury
+.Bl -inset -compact
+.It Primary weapon:
+Three\(hyway cannon.
+.It Secondary weapon:
+Fling insults at opponent.
+This is the only way the Pkunk can regenerate combat energy.
+.It Note:
+On occasion, a destroyed Fury will be resurrected with full fuel and power.
+.El
+.It Shofixti Scout
+.Bl -inset -compact
+.It Primary weapon:
+Energy Dart.
+.It Secondary weapon:
+Glory Device.
+When pressed three times, the ship will self\(hydestruct,
+inflicting vast damage on nearby vessels.
+.El
+.It Slylandro Probe
+.Bl -inset -compact
+.It Primary weapon:
+Lighting weapon.
+.It Secondary weapon:
+Absorb a nearby asteroid and convert to combat power.
+This is the only way the Probe can recharge.
+.It Note:
+The Probe is inertia\(hyless and always in motion.
+Pressing thrust will reverse its direction.
+.El
+.It Spathi Eluder
+.Bl -inset -compact
+.It Primary weapon:
+Simple forward cannon.
+.It Secondary weapon:
+Backward Utilized Tracking Torpedo (B.U.T.T.),
+a homing missile fired from the rear of the vessel.
+.El
+.It Supox Blade
+.Bl -inset -compact
+.It Primary weapon:
+Forward firing glob weapon.
+.It Secondary weapon:
+Secondary + left or right will cause you to drift laterally,
+while Secondary + thrust will make you fly backwards.
+This cancels your current velocity, so be careful!
+.El
+.It Syreen Penetrator
+.Bl -inset -compact
+.It Primary weapon:
+Particle Beam Stiletto.
+.It Secondary weapon:
+.Dq Syreen Call
+\(en psychic attack that induces enemy crew to jump ship,
+where you (or your opponent) may capture them to add to your complement.
+.El
+.It Thraddash Torch
+.Bl -inset -compact
+.It Primary weapon:
+Straightforward blaster cannon.
+.It Secondary weapon:
+Afterburner.
+The afterburner exhaust does more damage then the blaster,
+so use it as a weapon!
+.El
+.It Umgah Drone
+.Bl -inset -compact
+.It Primary weapon:
+Anti\(hyMatter cone.
+Does not require combat batteries to use.
+.It Secondary weapon:
+Fly backwards suddenly and at high speed.
+.It Note:
+The Drone only recharges batteries if you do not fire for a long time,
+and then the energy all returns in one lump.
+.El
+.It Ur\(hyQuan Dreadnought
+.Bl -inset -compact
+.It Primary weapon:
+Fusion Blast.
+.It Secondary weapon:
+Launches autonomous fighters to harass the enemy.
+When they run low on fuel, they will fly back to the Dreadnought.
+Catch them before they expire.
+Each fighter requires one crew to pilot it,
+so take care not to weaken the core ship.
+.El
+.It Utwig Jugger
+.Bl -inset -compact
+.It Primary weapon:
+Six\(hyshot cannon.
+Requires no combat battery energy to fire.
+.It Secondary weapon:
+Force shield.
+Absorbing hits re\(hyenergizes your batteries.
+When the batteries are exhausted,
+the shield is permanently disabled until combat ends.
+.El
+.It VUX Intruder
+.Bl -inset -compact
+.It Primary weapon:
+Gigawatt laser.
+.It Secondary weapon:
+Limpet mines that track enemy ships and slow them down dramatically if they
+hit.
+.El
+.It Yehat Terminator
+.Bl -inset -compact
+.It Primary weapon:
+Twin autocannons.
+.It Secondary weapon:
+Force shield.
+.El
+.It Zoq\(hyFot\(hyPik Stinger
+.Bl -inset -compact
+.It Primary weapon:
+Anti\(hymatter spray gun.
+.It Secondary weapon:
+.Dq Tongue attack ,
+a point\(hyblank range attack that does grievous damage.
+.El
+.El
+.Sh SUPER MELEE
+Super Melee mode is pure combat.
+It\(cqs designed to let you hone your skills for the full game,
+or to challenge your friends to fleet matches.
+Selecting
+.Dq Super Melee!
+from the main menu will bring you to the super melee main screen.
+.Pp
+This screen is dominated by the fleet design screen.
+Move the cursor over a ship slot and press
+.Cm Enter
+to change the ship assignment, or press
+.Cm Delete
+to remove the ship.
+You may select the fleet name and press
+.Cm Enter
+to edit the fleet name to something of your choice.
+The number next to the fleet name lists the fleet strength;
+this is simply the sum of the point values of all ships in the fleet.
+.Pp
+The right hand side of the screen has buttons for managing the battle.
+Each side has four buttons associated with it:
+.Cm LOAD ,
+.Cm SAVE ,
+.Cm CONTROL ,
+and
+.Cm NET .
+The
+.Cm LOAD
+and
+.Cm SAVE
+buttons let you load and save fleets.
+A variety of fleets of various strengths are pre\(hydefined,
+and you may add your own by saving fleets you design.
+.Pp
+The
+.Cm Control
+button has one of five settings.
+.Cm Human Control
+puts the fleet under the control of a human player.
+(The precise controls for that player are set in the Setup menu, but the bottom
+player\(cqs controls are always the same as the full game\(cqs controls.)
+Then there are three levels of computer control:
+.Bl -bullet
+.It
+.Cm Weak Cyborg
+is not a particularly good shot, and will only use special weapons if the ship
+absolutely requires the special weapon to function at all (Pkunk, Slylandro).
+This difficulty level only appears in the full game when fighting crippled
+ships.
+.It
+.Cm Good Cyborg
+will actually use its special weapon, but it\(cqs still not much of a threat.
+The Good cyborg provides a gentle introduction to Star Control combat if you
+are unfamiliar with the gameplay.
+However, you will soon wish to switch to...
+.It
+.Cm Awesome Cyborg .
+The AI will fully exploit each ship\(cqs abilities, and is also a tolerably
+good shot, compensating for inertia and choosing its shots.
+Enemies you meet in the full game are almost always piloted at this level.
+If a battle is giving you trouble in the full game,
+this is the setting you want.
+.El
+.Pp
+The last control option is
+.Cm Network Control ,
+which will be set if that side is controlled by a non\(hylocal opponent.
+To set up a network game, push the
+.Cm Net
+button on the side you wish to be under your opponent\(hys control.
+.Pp
+In order to connect, you must agree on a port (the default is 21837,
+which should not require any change) and set a net delay in frames.
+To compensate for network lag,
+a keypress or keyrelease will only take effect after this many frames.
+While higher values make your ship seem to respond more sluggishly,
+they give the keypress information more time to reach the remote party.
+If the game stutters, this is because it is waiting for this keypress
+information to arrive, which is an indication that the input delay is too low.
+Super Melee runs at 24 frames per second,
+each frame delayed will delay the input by about 42\ ms.
+The delay used is the maximum of the desired value for both parties.
+The default is 2.
+Values lower than 4 are typically acceptable in terms of responsiveness.
+Future versions may automatically decide on the best value to use.
+.Pp
+Once the port and delay are set, one player must select the first option
+(Wait for incoming connection), while the other enters his opponent\(hys
+hostname or IP address into the Host field and then pushes
+.Dq Connect .
+Once the connection is established, the control scheme for the remote player
+will flip to
+.Cm Network Control
+to register the connection.
+To disconnect, change it away back to one of the
+.Cm Human
+or
+.Cm Cyborg options.
+Once connected,
+you are both free to edit your fleets to provide a properly balanced battle.
+.Pp
+Once all fleets are in readiness, press the
+.Cm Battle
+button in the center right.
+(In a network game, both players must select it with no intervening fleet
+edits.
+Moving away from
+.Cm Battle
+or having your opponent change their fleet will cancel your readiness state,
+and you will need to reselect the
+.Cm Battle
+button.)
+When all players are ready, the battle begins.
+.Pp
+The ship selection screen is much as it would be in the full game, with two
+notable exceptions: a question mark in the upper right allows you to select a
+new ship randomly from those remaining,
+and a red X allows you to exit the combat entirely.
+(It is also possible to exit the melee at any time by pressing
+.Cm F10 ,
+just as one can exit anything.)
+If nobody quits,
+the combat will continue until one side has been completely destroyed.
+At this point, both fleets are shown so that one may compare initial and
+remaining fleet strengths.
+Press a key to return to the melee menu.
+.Pp
+To return to the main menu after finishing with Super Melee mode, push the
+.Cm Quit
+button in the lower right.
+.Sh THE STORY SO FAR
+For the past decade, Earth and the rest of the Alliance of Free Stars has
+fought the Ur\(hyQuan and their Hierarchy of Battle Thralls.
+In the course of the War, the Earthlings discovered a factory world by the
+.Sq Precursors
+\(en an impossibly advanced that disappeared tens of thousands of years ago.
+This colony, Unzervalt (aka Vela I),
+lost all contact with Earth shortly after landfall.
+.Pp
+You are Captain Zelnick, a human who was born on Unzervalt and who possesses a
+remarkable knack for Precursor technology.
+You were the one who worked out how to activate the Precursor installation.
+.Pp
+It was a factory for building starships.
+However, Unzervalt is mineral\(hypoor,
+and there were not enough materials available to construct a complete vessel.
+Your task is to command this craft, the Vindicator,
+and return to Earth to tell them of the abandoned colony.
+Also, if the War with the Ur\(hyQuan continues,
+you must fight for Earth and the Alliance as best you can.
+.Pp
+There is a great deal more to this story.
+Asking Starbase Commander Hayes for background information will give you most
+of it.
+.Sh ADD\(hyONS
+.Nm uqm
+has basic support for add\(hyon packages.
+Though it is not very elaborate yet, you can install some content add\(hyons.
+Add\(hyons created for UQM releases prior to v0.7 are not compatible with this
+release.
+.Pp
+Inside the directory where the content is installed, in the
+.Pa content/
+directory, there is a directory
+.Pa addons/ .
+In this directory, you can place add\(hyon packages, like
+.Sy 3domusic ,
+.Sy 3dovoice ,
+.Sy remix
+and others, or create new directories with .zip files to be used in
+addition te the standard content .zip files.
+Each add\(hyon must contain at least one .rmp file to tell the game which
+resources the add\(hyon provides.
+When you specify the command\(hy line option
+.Fl -addon Ar addon ,
+the .zip files inside the directory
+.Pa content/addons/ Ns Ar addon
+will be included in the game.
+.Fl -addon
+may be specified more than once to enable multiple add\(hyons.
+.Pp
+.Sy 3domusic ,
+.Sy 3dovoice ,
+and
+.Sy remix
+are intended to be used as add\(hyons,
+and can be turned on and off from the in\(hygame setup menu.
+.Sh BUGS
+After several years of enthusiastic testing,
+.Nm uqm
+has dramatically improved its stability, but it is still beta software,
+and bugs certainly still lurk.
+Upon finding a problem, we\(cqd like you to report it,
+but before you do, please do the following:
+.Bl -bullet
+.It
+Try to isolate what causes it:
+.Do
+Crashes with a null dereference about half the time when firing and
+taunting with a Pkunk
+.Dc
+is better than
+.Dq Melee doesn\(cqt work .
+If the game crashes, notice what error is produced;
+if the game hangs, check to see if the game\(hyexit key
+.Pq Cm F12
+works.
+.It
+Go to the
+.Lk http://bugs.uqm.stack.nl/ "bug database"
+and post a report of the problem there.
+Search the database first to see if it has been already posted; if we get many
+duplicate reports, processing them eats our time from actual development.
+If it\(cqs been reported, and you have more information, feel free to confirm
+that you\(cqve reproduced it by adding a comment to the report.
+If ten people have already confirmed it, though,
+it\(cqs probably best to treat it as duly reported.
+.It
+Whenever possible, for bugs that only occur under certain conditions,
+include a save game with your bug report that duplicates the bug.
+In the case of a crash, a stack trace can be very helpful for us too.
+If you don\(cqt know what a stack trace is, don\(cqt worry about it.
+.It
+If your issue is more like
+.Dq support request
+than bug report and you want help from other users,
+then it might be more appropriate to post it to
+.Lk http://uqm.stack.nl/forum/ "our forum" .
+.El
diff --git a/src/Makeinfo b/src/Makeinfo
new file mode 100644
index 0000000..80a2dd2
--- /dev/null
+++ b/src/Makeinfo
@@ -0,0 +1,21 @@
+uqm_SUBDIRS="libs res uqm"
+uqm_CFILES="options.c port.c uqm.c"
+uqm_HFILES="config.h endian_uqm.h options.h port.h types.h uqmversion.h"
+
+if [ "$uqm_HAVE_GETOPT_LONG" = 0 ]; then
+ uqm_SUBDIRS="$uqm_SUBDIRS getopt"
+fi
+
+case "$HOST_SYSTEM" in
+ Darwin)
+ uqm_SUBDIRS="$uqm_SUBDIRS darwin"
+ ;;
+ MSVC)
+ uqm_HFILES="$uqm_HFILES config_vc6.h"
+ ;;
+esac
+
+if [ "$uqm_HAVE_REGEX" = 0 ]; then
+ uqm_SUBDIRS="$uqm_SUBDIRS regex"
+fi
+
diff --git a/src/abxadec/Makefile b/src/abxadec/Makefile
new file mode 100644
index 0000000..35b9955
--- /dev/null
+++ b/src/abxadec/Makefile
@@ -0,0 +1,9 @@
+uqm_CFLAGS=-W -Wall -g -O0 -fPIC
+uqm_INCLUDE=-I .. -I ../sc2code -I ../sc2code/libs
+uqm_LDFLAGS=-shared
+
+abxadec.so: abxaud.c abxaud.h
+ gcc $(uqm_CFLAGS) $(uqm_INCLUDE) $(uqm_LDFLAGS) abxaud.c -o abxadec.so
+
+clean:
+ rm abxadec.so
diff --git a/src/abxadec/abxaud.c b/src/abxadec/abxaud.c
new file mode 100644
index 0000000..a943f16
--- /dev/null
+++ b/src/abxadec/abxaud.c
@@ -0,0 +1,638 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* CDP module sample
+ * .abx speech track decoder
+ */
+
+/*
+ * Derived from decoder by Serge van den Boom (svdb@stack.nl),
+ * The actual conversion code (somewhat moded) is from Toys for Bob.
+ * So far, it ignores sample rates, so it will work ok as long as all
+ * the frames have the same frequency. This is probably
+ * enough for our purposes.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+//#include "port.h"
+#include "types.h"
+#include "port.h"
+#include "libs/cdp/cdp_imem.h"
+#include "libs/cdp/cdp_iio.h"
+#include "libs/cdp/cdp_isnd.h"
+#include "libs/cdp/cdpmod.h"
+#include "abxaud.h"
+#include "endian_uqm.h"
+
+#define DATA_BUF_SIZE 0x8000
+#define DUCK_GENERAL_FPS 14.622f
+
+
+#define THIS_PTR TFB_SoundDecoder* This
+
+static const char* abxa_GetName (void);
+static bool abxa_InitModule (int flags, const TFB_DecoderFormats*);
+static void abxa_TermModule ();
+static uint32 abxa_GetStructSize (void);
+static int abxa_GetError (THIS_PTR);
+static bool abxa_Init (THIS_PTR);
+static void abxa_Term (THIS_PTR);
+static bool abxa_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void abxa_Close (THIS_PTR);
+static int abxa_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 abxa_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 abxa_GetFrame (THIS_PTR);
+
+static TFB_SoundDecoderFuncs abxa_DecoderVtbl =
+{
+ abxa_GetName,
+ abxa_InitModule,
+ abxa_TermModule,
+ abxa_GetStructSize,
+ abxa_GetError,
+ abxa_Init,
+ abxa_Term,
+ abxa_Open,
+ abxa_Close,
+ abxa_Decode,
+ abxa_Seek,
+ abxa_GetFrame,
+};
+
+static bool abxa_module_init (cdp_Module* module, cdp_Itf_Host* hostitf);
+static void abxa_module_term ();
+
+// The one and only "cdpmodinfo" symbol
+// on win32, it does not have to be named like so
+// the exported name can be overridden with .def file
+cdp_ModuleInfo CDPEXPORT CDP_INFO_SYM =
+{
+ sizeof (cdp_ModuleInfo), // size of struct for version control
+ CDPAPI_VERSION, // API version we are using
+ 1, 0, 2, // our module version
+ 0, 3, 0, // host version required, purely informational
+ CDP_MODINFO_RESERVED1, // reserved
+ "UQM", // CDP context name (we can use UQM)
+ "Abx Decoder", // CDP mod name
+ "1.0", // CDP mod version
+ "Alex Volkov", // CDP mod author
+ "http://sc2.sf.net", // CDP mod URL (do not have any yet)
+ "Sample CDP-based decoder", // CDP mod comment
+ CDP_MODINFO_RESERVED2, // reserved
+ abxa_module_init, // init entrypoint
+ abxa_module_term, // term entrypoint
+};
+
+typedef struct
+{
+ uint16 num_frames; // total number of frame
+ uint32 tot_size; // total size of decoded stream
+ uint16 frame_samps; // samples per frame
+ uint16 freq; // general sampling frequency
+
+} abxa_Header;
+
+typedef struct
+{
+ uint32 ofs; // file offset of frame
+ uint16 fsize; // compressed file size
+ uint16 usize; // uncompressed file size
+
+} abxa_FrameInfo;
+
+#define SQLCH 0x40 // Squelch byte flag
+#define RESYNC 0x80 // Resync byte flag.
+
+#define DELTAMOD 0x30 // Delta modulation bits.
+
+#define ONEBIT 0x10 // One bit delta modulate
+#define TWOBIT 0x20 // Two bit delta modulate
+#define FOURBIT 0x30 // four bit delta modulate
+
+#define MULTIPLIER 0x0F // Bottom nibble contains multiplier value.
+#define SQUELCHCNT 0x3F // Bits for squelching.
+
+typedef struct
+{
+ uint16 usize;
+ uint16 freq;
+ uint8 frame_size;
+ uint8 sqelch;
+ uint16 max_error;
+
+} abxa_FrameHeader;
+
+typedef struct
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // public read-only
+ uint32 iframe; // current frame index
+ uint32 cframes; // total count of frames
+ uint32 channels; // number of channels
+ uint32 pcm_frame; // samples per frame
+
+ // private
+ sint32 last_error;
+ uio_Stream* abx;
+ abxa_FrameInfo* frames;
+ // buffer
+ void* data;
+ uint32 maxdata;
+ uint32 cbdata;
+ uint32 dataofs;
+
+} abxa_SoundDecoder;
+
+// interfaces our decoder needs
+cdp_ItfDef game_itfs[] =
+{
+ {CDPITF_KIND_MEMORY},
+ {CDPITF_KIND_IO},
+ {CDPITF_KIND_SOUND},
+
+ {CDPITF_KIND_INVALID} // term
+};
+
+static cdp_Module* abxa_mod = NULL; // our module handle
+static cdp_Itf_Host* abxa_ihost = NULL; // HOST interface ptr
+static cdp_Itf_Memory* abxa_imem = NULL; // MEMORY interface ptr
+static cdp_Itf_Io* abxa_iio = NULL; // IO interface ptr
+static cdp_Itf_Sound* abxa_isnd = NULL; // SOUND interface ptr
+static const TFB_DecoderFormats* abxa_formats = NULL;
+static TFB_RegSoundDecoder* abxa_regdec = NULL; // registered decoder
+
+static bool
+abxa_module_init (cdp_Module* module, cdp_Itf_Host* hostitf)
+{
+ abxa_mod = module;
+ abxa_ihost = hostitf;
+
+ if (!hostitf->GetItfs (game_itfs))
+ return false;
+
+ abxa_imem = game_itfs[0].itf;
+ abxa_iio = game_itfs[1].itf;
+ abxa_isnd = game_itfs[2].itf;
+
+ abxa_regdec = abxa_isnd->RegisterDecoder ("abx", &abxa_DecoderVtbl);
+ if (!abxa_regdec)
+ {
+ fprintf (stderr, "abxa_module_init(): "
+ "Could not register audio decoder\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void
+abxa_module_term ()
+{
+ if (abxa_regdec)
+ abxa_isnd->UnregisterDecoder (abxa_regdec);
+
+ // do nothing loop for 1 trillion iterations
+}
+
+static sint32
+abxa_readHeader (abxa_SoundDecoder* abxa, abxa_Header* hdr)
+{
+ if (1 != abxa_iio->fread (&hdr->num_frames, sizeof (hdr->num_frames), 1, abxa->abx) ||
+ 1 != abxa_iio->fread (&hdr->tot_size, sizeof (hdr->tot_size), 1, abxa->abx) ||
+ 1 != abxa_iio->fread (&hdr->frame_samps, sizeof (hdr->frame_samps), 1, abxa->abx) ||
+ 1 != abxa_iio->fread (&hdr->freq, sizeof (hdr->freq), 1, abxa->abx))
+ {
+ abxa->last_error = errno;
+ return abxa_ErrBadFile;
+ }
+ // byte swap when necessary
+ hdr->num_frames = UQM_SwapLE16 (hdr->num_frames);
+ hdr->tot_size = UQM_SwapLE32 (hdr->tot_size);
+ hdr->frame_samps = UQM_SwapLE16 (hdr->frame_samps);
+ hdr->freq = UQM_SwapLE16 (hdr->freq);
+
+ return 0;
+}
+
+static signed char abxa_trans[16 * 16] =
+{
+ -8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8, // Multiplier of 1
+ -16,-14,-12,-10,-8,-6,-4,-2,2,4,6,8,10,12,14,16, // Multiplier of 2
+ -24,-21,-18,-15,-12,-9,-6,-3,3,6,9,12,15,18,21,24, // Multiplier of 3
+ -32,-28,-24,-20,-16,-12,-8,-4,4,8,12,16,20,24,28,32, // Multiplier of 4
+ -40,-35,-30,-25,-20,-15,-10,-5,5,10,15,20,25,30,35,40, // Multiplier of 5
+ -48,-42,-36,-30,-24,-18,-12,-6,6,12,18,24,30,36,42,48, // Multiplier of 6
+ -56,-49,-42,-35,-28,-21,-14,-7,7,14,21,28,35,42,49,56, // Multiplier of 7
+ -64,-56,-48,-40,-32,-24,-16,-8,8,16,24,32,40,48,56,64, // Multiplier of 8
+ -72,-63,-54,-45,-36,-27,-18,-9,9,18,27,36,45,54,63,72, // Multiplier of 9
+ -80,-70,-60,-50,-40,-30,-20,-10,10,20,30,40,50,60,70,80, // Multiplier of 10
+ -88,-77,-66,-55,-44,-33,-22,-11,11,22,33,44,55,66,77,88, // Multiplier of 11
+ -96,-84,-72,-60,-48,-36,-24,-12,12,24,36,48,60,72,84,96, // Multiplier of 12
+ -104,-91,-78,-65,-52,-39,-26,-13,13,26,39,52,65,78,91,104, // Multiplier of 13
+ -112,-98,-84,-70,-56,-42,-28,-14,14,28,42,56,70,84,98,112, // Multiplier of 14
+ -120,-105,-90,-75,-60,-45,-30,-15,15,30,45,60,75,90,105,120,// Multiplier of 15
+ -128,-112,-96,-80,-64,-48,-32,-16,16,32,48,64,80,96,112,127,// Multiplier of 16
+};
+
+static sint32
+abxa_decodeFrame (abxa_SoundDecoder* abxa, abxa_FrameHeader* hdr,
+ uint8* input, uint32 inputsize)
+{
+ uint8* inend;
+ uint8* output;
+ uint8* outptr;
+ sint16 prev;
+ sint32 outputsize;
+
+ output = outptr = abxa->data;
+ inend = input + inputsize;
+ prev = *input++; // Get initial previous data point.
+ *output++ = prev;
+
+ while (input < inend)
+ {
+ uint16 bytes;
+ uint8 sample;
+
+ sample = *input++; // Get sample.
+ if (sample & RESYNC) // Is it a resync byte?
+ {
+ //--slen; // Decrement output sample length.
+
+ prev = (sample & 0x7F) << 1; // Store resync byte.
+ *output++ = prev;
+ }
+ else if (sample & SQLCH) // Is it a squelch byte?
+ {
+ bytes = sample & SQUELCHCNT; // And off the number of squelch bytes
+ // ?? the following makes no sense, should be --slen;
+ //slen -= bytes; // Decrement total samples remaining count.
+
+ memset (output, prev, bytes);
+ output += bytes;
+ }
+ else // Must be a delta modulate byte!!
+ {
+ sint8 *base;
+
+ //slen -= hdr->frame_size; // Pulling one frame out.
+ // Compute base address to multiplier table.
+ base = abxa_trans + (sample & MULTIPLIER) * 16;
+ switch (sample & DELTAMOD) // Delta mod resolution.
+ {
+ case ONEBIT:
+ {
+ sint16 up;
+
+ up = base[8]; // Go up 1 bit.
+ for (bytes = hdr->frame_size / 8; bytes; bytes--)
+ {
+ uint8 mask;
+
+ sample = *input++;
+ for (mask = 0x80; mask; mask >>= 1)
+ {
+ if ( sample & mask )
+ prev += up;
+ else
+ prev -= up;
+ if ( prev < 0 ) prev = 0;
+ else if ( prev > 255 ) prev = 255;
+ *output++ = prev;
+ }
+ }
+ break;
+ }
+ case TWOBIT:
+ base += 6; // Base address of two bit delta's.
+ for (bytes = hdr->frame_size / 4; bytes; bytes--)
+ {
+ sample = *input++;
+
+ prev += base[sample>>6];
+ if ( prev < 0 ) prev = 0;
+ else if ( prev > 255 ) prev = 255;
+ *output++ = prev;
+
+ prev += base[(sample>>4)&0x3];
+ if ( prev < 0 ) prev = 0;
+ else if ( prev > 255 ) prev = 255;
+ *output++ = prev;
+
+ prev += base[(sample>>2)&0x3];
+ if ( prev < 0 ) prev = 0;
+ else if ( prev > 255 ) prev = 255;
+ *output++ = prev;
+
+ prev += base[sample&0x3];
+ if ( prev < 0 ) prev = 0;
+ else if ( prev > 255 ) prev = 255;
+ *output++ = prev;
+ }
+ break;
+ case FOURBIT:
+ for (bytes = hdr->frame_size / 2; bytes; bytes--)
+ {
+ sample = *input++;
+
+ prev += base[sample>>4];
+ if ( prev < 0 ) prev = 0;
+ else if ( prev > 255 ) prev = 255;
+ *output++ = prev;
+
+ prev += base[sample&0x0F];
+ if ( prev < 0 ) prev = 0;
+ else if ( prev > 255 ) prev = 255;
+ *output++ = prev;
+ }
+ break;
+ }
+ }
+ // While still audio data to decompress....
+ }
+
+ outputsize = output - outptr;
+ abxa->cbdata += outputsize;
+
+ return outputsize;
+}
+
+
+static sint32
+abxa_readNextFrame (abxa_SoundDecoder* abxa)
+{
+ abxa_FrameHeader* hdr;
+ uint8* p;
+ uint32 fsize;
+ sint32 ret;
+
+ fsize = abxa->frames[abxa->iframe].fsize;
+ abxa_iio->fseek (abxa->abx, abxa->frames[abxa->iframe].ofs, SEEK_SET);
+ // dump encoded data at the end of the buffer aligned on 8-byte
+ p = ((uint8*)abxa->data + abxa->maxdata - ((fsize + 7) & (-8)));
+ if (abxa_iio->fread (p, 1, fsize, abxa->abx) != fsize)
+ {
+ abxa->last_error = errno;
+ return abxa_ErrBadFile;
+ }
+ hdr = (abxa_FrameHeader*) p;
+ p += sizeof (abxa_FrameHeader);
+ fsize -= sizeof (abxa_FrameHeader);
+
+ hdr->usize = UQM_SwapLE16 (hdr->usize);
+ hdr->freq = UQM_SwapLE16 (hdr->freq);
+ hdr->max_error = UQM_SwapLE16 (hdr->max_error);
+
+ if (hdr->freq == 0)
+ {
+ hdr->freq = abxa->decoder.frequency;
+ }
+ else if (hdr->freq != abxa->decoder.frequency)
+ {
+ fprintf (stderr, "abxa_readNextFrame(): "
+ "WARNING: Frame frequency (%u) != global frequency (%u) "
+ "for frame %u\n",
+ hdr->freq, abxa->decoder.frequency, abxa->iframe);
+ //abxa->decoder.frequency = hdr->freq;
+ }
+
+ abxa->iframe++;
+
+ ret = abxa_decodeFrame (abxa, hdr, p, fsize);
+ if (ret > 0 && ret != hdr->usize)
+ {
+ fprintf (stderr, "abxa_readNextFrame(): "
+ "WARNING: decompressed frame size (%d) != specified size (%u) "
+ "for frame %u\n",
+ ret, hdr->usize, abxa->iframe);
+ }
+
+ return ret;
+}
+
+static sint32
+abxa_stuffBuffer (abxa_SoundDecoder* abxa, void* buf, sint32 bufsize)
+{
+ sint32 dataleft;
+
+ dataleft = abxa->cbdata - abxa->dataofs;
+ if (dataleft > 0)
+ {
+ if (dataleft > bufsize)
+ dataleft = bufsize & (-4);
+ memcpy (buf, (uint8*)abxa->data + abxa->dataofs, dataleft);
+ abxa->dataofs += dataleft;
+ }
+
+ if (abxa->cbdata > 0 && abxa->dataofs >= abxa->cbdata)
+ abxa->cbdata = abxa->dataofs = 0; // reset for new data
+
+ return dataleft;
+}
+
+
+static const char*
+abxa_GetName (void)
+{
+ return "Abx";
+}
+
+static bool
+abxa_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ abxa_formats = fmts;
+ return true;
+
+ (void)flags; // laugh at compiler warning
+}
+
+static void
+abxa_TermModule ()
+{
+ // no specific module term
+}
+
+static uint32
+abxa_GetStructSize (void)
+{
+ return sizeof (abxa_SoundDecoder);
+}
+
+static int
+abxa_GetError (THIS_PTR)
+{
+ abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+ int ret = abxa->last_error;
+ abxa->last_error = abxa_ErrNone;
+ return ret;
+}
+
+static bool
+abxa_Init (THIS_PTR)
+{
+ //abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+ /* enable if 16bit PCM abx exist
+ This->need_swap =
+ abxa_formats->big_endian != abxa_formats->want_big_endian;
+ */
+ This->need_swap = false;
+ return true;
+}
+
+static void
+abxa_Term (THIS_PTR)
+{
+ //abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+ abxa_Close (This); // ensure cleanup
+}
+
+static bool
+abxa_Open (THIS_PTR, uio_DirHandle *dir, const char *file)
+{
+ abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+ abxa_Header hdr;
+ abxa_FrameInfo* info;
+ size_t cread;
+ uint32 i;
+
+ abxa->abx = abxa_iio->fopen (dir, file, "rb");
+ if (!abxa->abx)
+ {
+ abxa->last_error = errno;
+ return false;
+ }
+ if (abxa_readHeader (abxa, &hdr))
+ {
+ abxa_Close (This);
+ return false;
+ }
+
+ abxa->cframes = hdr.num_frames;
+
+ abxa->frames = abxa_imem->malloc (abxa->cframes * sizeof (abxa_FrameInfo));
+ cread = abxa_iio->fread (abxa->frames, sizeof (abxa_FrameInfo),
+ abxa->cframes, abxa->abx);
+ if (cread != abxa->cframes)
+ {
+ abxa->last_error = abxa_ErrBadFile;
+ abxa_Close (This);
+ return false;
+ }
+
+ // byte swap when necessary
+ for (i = 0, info = abxa->frames; i < abxa->cframes; ++i, ++info)
+ {
+ info->ofs = UQM_SwapLE32 (info->ofs);
+ info->fsize = UQM_SwapLE16 (info->fsize);
+ info->usize = UQM_SwapLE16 (info->usize);
+ }
+
+ This->frequency = hdr.freq;
+ This->format = abxa_formats->mono8;
+ abxa->channels = 1;
+ abxa->pcm_frame = hdr.frame_samps;
+ abxa->data = abxa_imem->malloc (hdr.frame_samps * 2);
+ abxa->maxdata = hdr.frame_samps * 2;
+
+ // estimate
+ This->length = (float) hdr.tot_size / hdr.freq;
+
+ abxa->last_error = 0;
+
+ return true;
+}
+
+static void
+abxa_Close (THIS_PTR)
+{
+ abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+
+ if (abxa->data)
+ {
+ abxa_imem->free (abxa->data);
+ abxa->data = NULL;
+ }
+ if (abxa->frames)
+ {
+ abxa_imem->free (abxa->frames);
+ abxa->frames = NULL;
+ }
+ if (abxa->abx)
+ {
+ abxa_iio->fclose (abxa->abx);
+ abxa->abx = NULL;
+ }
+ abxa->last_error = 0;
+}
+
+static int
+abxa_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+ sint32 stuffed;
+ sint32 total = 0;
+
+ if (bufsize <= 0)
+ return abxa->last_error = abxa_ErrBadArg;
+
+ do
+ {
+ stuffed = abxa_stuffBuffer (abxa, buf, bufsize);
+ ((uint8*)buf) += stuffed;
+ bufsize -= stuffed;
+ total += stuffed;
+
+ if (bufsize > 0 && abxa->iframe < abxa->cframes)
+ {
+ stuffed = abxa_readNextFrame (abxa);
+ if (stuffed <= 0)
+ return stuffed;
+ }
+ } while (bufsize > 0 && abxa->iframe < abxa->cframes);
+
+ return total;
+}
+
+static uint32
+abxa_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+ uint32 iframe;
+
+ iframe = pcm_pos / abxa->pcm_frame;
+ if (iframe < abxa->cframes)
+ {
+ abxa->iframe = iframe;
+ abxa->cbdata = 0;
+ abxa->dataofs = 0;
+ }
+ return abxa->iframe * abxa->pcm_frame;
+}
+
+static uint32
+abxa_GetFrame (THIS_PTR)
+{
+ abxa_SoundDecoder* abxa = (abxa_SoundDecoder*) This;
+
+ // if there is nothing buffered return the actual current frame
+ // otherwise return previous
+ return abxa->dataofs == abxa->cbdata ?
+ abxa->iframe : abxa->iframe - 1;
+}
diff --git a/src/abxadec/abxaud.def b/src/abxadec/abxaud.def
new file mode 100644
index 0000000..da805a7
--- /dev/null
+++ b/src/abxadec/abxaud.def
@@ -0,0 +1,16 @@
+;////////////////////////////////////////////////////////////////////
+;// abxaud.def
+;//
+;// MODULE : abxadec.dll
+;// PURPOSE : Declaration of DLL exports
+;//
+;// This file is a part of Abx Decoder sample CDP module
+;// It is only necessary if you do not name your
+;// (only) exported symbol "cdpmodinfo"
+;// GPL applies.
+;//
+
+LIBRARY "abxadec.dll"
+
+EXPORTS
+ cdpmodinfo = abxa_mod_info @1 DATA
diff --git a/src/abxadec/abxaud.h b/src/abxadec/abxaud.h
new file mode 100644
index 0000000..e72ac07
--- /dev/null
+++ b/src/abxadec/abxaud.h
@@ -0,0 +1,34 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* CDP module sample
+ * .abx speech track decoder
+ */
+
+#ifndef ABXADEC_ABXAUD_H_
+#define ABXADEC_ABXAUD_H_
+
+typedef enum
+{
+ // positive values are the same as in errno
+ abxa_ErrNone = 0,
+ abxa_ErrUnknown = -1,
+ abxa_ErrBadFile = -2,
+ abxa_ErrBadArg = -3,
+ abxa_ErrOther = -1000,
+} abxa_Error;
+
+#endif // ABXADEC_ABXAUD_H_
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..f2ef1d8
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,21 @@
+/* This file contains some compile-time configuration options.
+ */
+
+#ifdef _MSC_VER
+ /* In this case, build.sh is not run to generate a config file, so
+ * we use a default file config_vc6.h instead.
+ * If you want anything else than the defaults, you'll have to edit
+ * that file manually. */
+# include "config_vc6.h"
+#elif defined(__SYMBIAN32__)
+# include "symbian/config.h"
+#elif defined (__MINGW32__) || defined (__CYGWIN__)
+ /* If we're compiling on MS Windows using build.sh, use
+ * config_win.h, generated from src/config_win.h.in. */
+# include "config_win.h"
+#else
+ /* If we're compiling in unix, use config_unix.h, generated from
+ * src/config_unix.h.in by build.sh. */
+# include "config_unix.h"
+#endif
+
diff --git a/src/config_unix.h.in b/src/config_unix.h.in
new file mode 100644
index 0000000..63d1e0a
--- /dev/null
+++ b/src/config_unix.h.in
@@ -0,0 +1,63 @@
+/* This file contains some compile-time configuration options for *nix
+ * systems.
+ * config_unix.h is generated from config_unix.h.in by build.sh
+ * When building on MS Windows using build.sh (MinGW, Cygwin),
+ * config_win.h is generated from src/config_win.h.in.
+ * When using MSVC on MS Windows, you'll have to edit src/config_vc6.h
+ * manually if you want anything else than the defaults.
+ */
+
+#ifndef CONFIG_UNIX_H_
+#define CONFIG_UNIX_H_
+
+/* Directory where the UQM game data is located */
+#define CONTENTDIR "@CONTENTDIR@"
+
+/* Directory where game data will be stored */
+#define USERDIR "~/.uqm/"
+
+/* Directory where config files will be stored */
+#define CONFIGDIR USERDIR
+
+/* Directory where supermelee teams will be stored */
+#define MELEEDIR "${UQM_CONFIG_DIR}/teams/"
+
+/* Directory where save games will be stored */
+#define SAVEDIR "${UQM_CONFIG_DIR}/save/"
+
+/* Defined if words are stored with the most significant byte first */
+@WORDS_BIGENDIAN@
+
+/* Defined if your system has readdir_r of its own */
+@HAVE_READDIR_R@
+
+/* Defined if your system has setenv of its own */
+@HAVE_SETENV@
+
+/* Defined if your system has strupr of its own */
+@HAVE_STRUPR@
+
+/* Defined if your system has strcasecmp of its own */
+@HAVE_STRCASECMP_UQM@
+ // Not using "HAVE_STRCASECMP" as that conflicts with SDL.
+
+/* Defined if your system has stricmp of its own */
+@HAVE_STRICMP@
+
+/* Defined if your system has getopt_long */
+@HAVE_GETOPT_LONG@
+
+/* Defined if your system has iswgraph of its own*/
+@HAVE_ISWGRAPH@
+
+/* Defined if your system has wchar_t of its own */
+@HAVE_WCHAR_T@
+
+/* Defined if your system has wint_t of its own */
+@HAVE_WINT_T@
+
+/* Defined if your system has _Bool of its own */
+@HAVE__BOOL@
+
+#endif /* CONFIG_UNIX_H_ */
+
diff --git a/src/config_vc6.h b/src/config_vc6.h
new file mode 100644
index 0000000..d3ed0e3
--- /dev/null
+++ b/src/config_vc6.h
@@ -0,0 +1,61 @@
+/* This file contains some compile-time configuration options for MS Windows
+ * systems when building using MSVC.
+ * Change the values below if you want anything other than the defaults.
+ * For *nix systems, config_unix.h is used, which is generated by build.sh
+ * from src/config_unix.h.in.
+ * When building on MS Windows using build.sh (MinGW, Cygwin),
+ * config_win.h is generated from src/config_win.h.in.
+ */
+
+#ifndef CONFIG_VC6_H_
+#define CONFIG_VC6_H_
+
+/* Directory where the UQM game data is located */
+#define CONTENTDIR "../content/"
+
+/* Directory where game data will be stored */
+//#define USERDIR "../userdata/"
+#define USERDIR "%APPDATA%/uqm/"
+
+/* Directory where config files will be stored */
+#define CONFIGDIR USERDIR
+
+/* Directory where supermelee teams will be stored */
+#define MELEEDIR "%UQM_CONFIG_DIR%/teams/"
+
+/* Directory where save games will be stored */
+#define SAVEDIR "%UQM_CONFIG_DIR%/save/"
+
+/* Define if words are stored with the most significant byte first */
+#undef WORDS_BIGENDIAN
+
+/* Defined if your system has readdir_r of its own */
+#undef HAVE_READDIR_R
+
+/* Defined if your system has setenv of its own */
+#undef HAVE_SETENV
+
+/* Defined if your system has strupr of its own */
+#define HAVE_STRUPR
+
+/* Defined if your system has strcasecmp of its own */
+#undef HAVE_STRCASECMP_UQM
+ // Not using "HAVE_STRCASECMP" as that conflicts with SDL.
+
+/* Defined if your system has stricmp of its own */
+#define HAVE_STRICMP
+
+/* Defined if your system has getopt_long */
+#undef HAVE_GETOPT_LONG
+
+/* Defined if your system has iswgraph of its own*/
+#define HAVE_ISWGRAPH
+
+/* Defined if your system has wchar_t of its own */
+#define HAVE_WCHAR_T
+
+/* Defined if your system has wint_t of its own */
+#define HAVE_WINT_T
+
+#endif /* CONFIG_VC6_H_ */
+
diff --git a/src/config_win.h.in b/src/config_win.h.in
new file mode 100644
index 0000000..3001f1f
--- /dev/null
+++ b/src/config_win.h.in
@@ -0,0 +1,65 @@
+/* This file contains some compile-time configuration options for MS Windows
+ * systems when building using build.sh (MinGW, Cygwin).
+ * config_win.h is generated from src/config_win.h.in by build.sh
+ * For building using MSVC, you'll have to edit src/config_vc6.h manually
+ * if you want anything else than the defaults.
+ * For *nix systems, config_unix.h is used, which is generated by build.sh
+ * from src/config_unix.h.in.
+ */
+
+#ifndef CONFIG_WIN_H_
+#define CONFIG_WIN_H_
+
+/* Directory where the UQM game data is located */
+#define CONTENTDIR "../content/"
+
+/* Directory where game data will be stored */
+//#define USERDIR "../userdata/"
+#define USERDIR "%APPDATA%/uqm/"
+
+/* Directory where config files will be stored */
+#define CONFIGDIR USERDIR
+
+/* Directory where supermelee teams will be stored */
+#define MELEEDIR "%UQM_CONFIG_DIR%/teams/"
+
+/* Directory where save games will be stored */
+#define SAVEDIR "%UQM_CONFIG_DIR%/save/"
+
+/* Defined if words are stored with the most significant byte first */
+@WORDS_BIGENDIAN@
+
+/* Defined if your system has readdir_r of its own */
+@HAVE_READDIR_R@
+
+/* Defined if your system has setenv of its own */
+@HAVE_SETENV@
+
+/* Defined if your system has strupr of its own */
+@HAVE_STRUPR@
+
+/* Defined if your system has strcasecmp of its own */
+@HAVE_STRCASECMP_UQM@
+ // Not using "HAVE_STRCASECMP" as that conflicts with SDL.
+
+/* Defined if your system has stricmp of its own */
+@HAVE_STRICMP@
+
+/* Defined if your system has getopt_long */
+@HAVE_GETOPT_LONG@
+
+/* Defined if your system has iswgraph of its own*/
+@HAVE_ISWGRAPH@
+
+/* Defined if your system has wchar_t of its own */
+@HAVE_WCHAR_T@
+
+/* Defined if your system has wint_t of its own */
+@HAVE_WINT_T@
+
+/* Defined if your system has _Bool of its own */
+@HAVE__BOOL@
+
+#endif /* CONFIG_WIN_H_ */
+
+
diff --git a/src/darwin/Makeinfo b/src/darwin/Makeinfo
new file mode 100644
index 0000000..6b019b2
--- /dev/null
+++ b/src/darwin/Makeinfo
@@ -0,0 +1,2 @@
+uqm_MFILES="SDLMain.m"
+uqm_HFILES="SDLMain.h"
diff --git a/src/darwin/SDLMain.h b/src/darwin/SDLMain.h
new file mode 100644
index 0000000..c56d90c
--- /dev/null
+++ b/src/darwin/SDLMain.h
@@ -0,0 +1,16 @@
+/* SDLMain.m - main entry point for our Cocoa-ized SDL app
+ Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
+ Non-NIB-Code & other changes: Max Horn <max@quendi.de>
+
+ Feel free to customize this file to suit your needs
+*/
+
+#ifndef _SDLMain_h_
+#define _SDLMain_h_
+
+#import <Cocoa/Cocoa.h>
+
+@interface SDLMain : NSObject
+@end
+
+#endif /* _SDLMain_h_ */
diff --git a/src/darwin/SDLMain.m b/src/darwin/SDLMain.m
new file mode 100644
index 0000000..4bb038e
--- /dev/null
+++ b/src/darwin/SDLMain.m
@@ -0,0 +1,404 @@
+/* SDLMain.m - main entry point for our Cocoa-ized SDL app
+ Initial Version: Darrell Walisser <dwaliss1@purdue.edu>
+ Non-NIB-Code & other changes: Max Horn <max@quendi.de>
+
+ Feel free to customize this file to suit your needs
+*/
+
+#import "port.h"
+#import SDL_INCLUDE(SDL.h)
+
+#if SDL_MAJOR_VERSION == 1
+
+#import "SDLMain.h"
+#import <sys/param.h>
+ /* for PATH_MAX */
+#import <string.h>
+ /* for strrchr() */
+#import <unistd.h>
+
+static int gArgc;
+static char **gArgv;
+static BOOL gFinderLaunch;
+
+@implementation SDLApplication
+/* Invoked from the Quit menu item */
+- (void)terminate:(id)sender
+{
+ /* Post a SDL_QUIT event */
+ SDL_Event event;
+ event.type = SDL_QUIT;
+ SDL_PushEvent (&event);
+ (void) sender; /* Get rid of unused variable warning */
+}
+
+/* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
+ but the method still is there and works. To avoid warnings, we declare
+ it ourselves here. */
+@interface NSApplication(SDL_Missing_Methods)
+- (void)setAppleMenu:(NSMenu *)menu;
+@end
+
+/* Use this flag to determine whether we use SDLMain.nib or not */
+#define SDL_USE_NIB_FILE 0
+
+/* Use this flag to determine whether we use CPS (docking) or not */
+#define SDL_USE_CPS 1
+#ifdef SDL_USE_CPS
+/* Portions of CPS.h */
+typedef struct CPSProcessSerNum
+{
+ UInt32 lo;
+ UInt32 hi;
+} CPSProcessSerNum;
+
+extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
+extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+#endif /* SDL_USE_CPS */
+
+static int gArgc;
+static char **gArgv;
+static BOOL gFinderLaunch;
+static BOOL gCalledAppMainline = FALSE;
+
+static NSString *getApplicationName(void)
+{
+ const NSDictionary *dict;
+ NSString *appName = 0;
+
+ /* Determine the application name */
+ dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
+ if (dict)
+ appName = [dict objectForKey: @"CFBundleName"];
+
+ if (![appName length])
+ appName = [[NSProcessInfo processInfo] processName];
+
+ return appName;
+}
+
+#if SDL_USE_NIB_FILE
+/* A helper category for NSString */
+@interface NSString (ReplaceSubString)
+- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
+@end
+#endif
+
+@interface NSApplication (SDLApplication)
+@end
+
+@implementation NSApplication (SDLApplication)
+/* Invoked from the Quit menu item */
+- (void)terminate:(id)sender
+{
+ /* Post a SDL_QUIT event */
+ SDL_Event event;
+ event.type = SDL_QUIT;
+ SDL_PushEvent(&event);
+}
+@end
+
+/* The main class of the application, the application's delegate */
+@implementation SDLMain
+
+/* Set the working directory to the .app's parent directory */
+- (void) setupWorkingDirectory:(BOOL)shouldChdir
+{
+ if (shouldChdir)
+ {
+ char parentdir[MAXPATHLEN];
+ CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+ CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
+ if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
+ chdir(parentdir); /* chdir to the binary app's parent */
+ }
+ CFRelease(url);
+ CFRelease(url2);
+ }
+}
+
+#if SDL_USE_NIB_FILE
+
+/* Fix menu to contain the real app name instead of "SDL App" */
+- (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
+{
+ NSRange aRange;
+ NSEnumerator *enumerator;
+ NSMenuItem *menuItem;
+
+ aRange = [[aMenu title] rangeOfString:@"SDL App"];
+ if (aRange.length != 0)
+ [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
+
+ enumerator = [[aMenu itemArray] objectEnumerator];
+ while ((menuItem = [enumerator nextObject]))
+ {
+ aRange = [[menuItem title] rangeOfString:@"SDL App"];
+ if (aRange.length != 0)
+ [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
+ if ([menuItem hasSubmenu])
+ [self fixMenu:[menuItem submenu] withAppName:appName];
+ }
+}
+
+#else
+
+static void setApplicationMenu(void)
+{
+ /* warning: this code is very odd */
+ NSMenu *appleMenu;
+ NSMenuItem *menuItem;
+ NSString *title;
+ NSString *appName;
+
+ appName = getApplicationName();
+ appleMenu = [[NSMenu alloc] initWithTitle:@""];
+
+ /* Add menu items */
+ title = [@"About " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ title = [@"Hide " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+
+ menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
+ [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+
+ [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+
+ [appleMenu addItem:[NSMenuItem separatorItem]];
+
+ title = [@"Quit " stringByAppendingString:appName];
+ [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+
+
+ /* Put menu into the menubar */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
+ [menuItem setSubmenu:appleMenu];
+ [[NSApp mainMenu] addItem:menuItem];
+
+ /* Tell the application object that this is now the application menu */
+ [NSApp setAppleMenu:appleMenu];
+
+ /* Finally give up our references to the objects */
+ [appleMenu release];
+ [menuItem release];
+}
+
+/* Create a window menu */
+static void setupWindowMenu(void)
+{
+ NSMenu *windowMenu;
+ NSMenuItem *windowMenuItem;
+ NSMenuItem *menuItem;
+
+ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+
+ /* "Minimize" item */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+ [windowMenu addItem:menuItem];
+ [menuItem release];
+
+ /* Put menu into the menubar */
+ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
+ [windowMenuItem setSubmenu:windowMenu];
+ [[NSApp mainMenu] addItem:windowMenuItem];
+
+ /* Tell the application object that this is now the window menu */
+ [NSApp setWindowsMenu:windowMenu];
+
+ /* Finally give up our references to the objects */
+ [windowMenu release];
+ [windowMenuItem release];
+}
+
+/* Replacement for NSApplicationMain */
+static void CustomApplicationMain (int argc, char **argv)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ SDLMain *sdlMain;
+
+ /* Ensure the application object is initialised */
+ [NSApplication sharedApplication];
+
+#ifdef SDL_USE_CPS
+ {
+ CPSProcessSerNum PSN;
+ /* Tell the dock about us */
+ if (!CPSGetCurrentProcess(&PSN))
+ if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+ if (!CPSSetFrontProcess(&PSN))
+ [NSApplication sharedApplication];
+ }
+#endif /* SDL_USE_CPS */
+
+ /* Set up the menubar */
+ [NSApp setMainMenu:[[NSMenu alloc] init]];
+ setApplicationMenu();
+ setupWindowMenu();
+
+ /* Create SDLMain and make it the app delegate */
+ sdlMain = [[SDLMain alloc] init];
+ [NSApp setDelegate:sdlMain];
+
+ /* Start the main event loop */
+ [NSApp run];
+
+ [sdlMain release];
+ [pool release];
+}
+
+#endif
+
+
+/*
+ * Catch document open requests...this lets us notice files when the app
+ * was launched by double-clicking a document, or when a document was
+ * dragged/dropped on the app's icon. You need to have a
+ * CFBundleDocumentsType section in your Info.plist to get this message,
+ * apparently.
+ *
+ * Files are added to gArgv, so to the app, they'll look like command line
+ * arguments. Previously, apps launched from the finder had nothing but
+ * an argv[0].
+ *
+ * This message may be received multiple times to open several docs on launch.
+ *
+ * This message is ignored once the app's mainline has been called.
+ */
+- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
+{
+ const char *temparg;
+ size_t arglen;
+ char *arg;
+ char **newargv;
+
+ if (!gFinderLaunch) /* MacOS is passing command line args. */
+ return FALSE;
+
+ if (gCalledAppMainline) /* app has started, ignore this document. */
+ return FALSE;
+
+ temparg = [filename UTF8String];
+ arglen = SDL_strlen(temparg) + 1;
+ arg = (char *) SDL_malloc(arglen);
+ if (arg == NULL)
+ return FALSE;
+
+ newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
+ if (newargv == NULL)
+ {
+ SDL_free(arg);
+ return FALSE;
+ }
+ gArgv = newargv;
+
+ SDL_strlcpy(arg, temparg, arglen);
+ gArgv[gArgc++] = arg;
+ gArgv[gArgc] = NULL;
+ return TRUE;
+}
+
+
+/* Called when the internal event loop has just started running */
+- (void) applicationDidFinishLaunching: (NSNotification *) note
+{
+ int status;
+
+ /* Set the working directory to the .app's parent directory */
+ [self setupWorkingDirectory:gFinderLaunch];
+
+#if SDL_USE_NIB_FILE
+ /* Set the main menu to contain the real app name instead of "SDL App" */
+ [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
+#endif
+
+ /* Hand off to main application code */
+ gCalledAppMainline = TRUE;
+ status = SDL_main (gArgc, gArgv);
+
+ /* We're done, thank you for playing */
+ exit(status);
+}
+@end
+
+
+@implementation NSString (ReplaceSubString)
+
+- (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
+{
+ unsigned int bufferSize;
+ unsigned int selfLen = [self length];
+ unsigned int aStringLen = [aString length];
+ unichar *buffer;
+ NSRange localRange;
+ NSString *result;
+
+ bufferSize = selfLen + aStringLen - aRange.length;
+ buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
+
+ /* Get first part into buffer */
+ localRange.location = 0;
+ localRange.length = aRange.location;
+ [self getCharacters:buffer range:localRange];
+
+ /* Get middle part into buffer */
+ localRange.location = 0;
+ localRange.length = aStringLen;
+ [aString getCharacters:(buffer+aRange.location) range:localRange];
+
+ /* Get last part into buffer */
+ localRange.location = aRange.location + aRange.length;
+ localRange.length = selfLen - localRange.location;
+ [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
+
+ /* Build output string */
+ result = [NSString stringWithCharacters:buffer length:bufferSize];
+
+ NSDeallocateMemoryPages(buffer, bufferSize);
+
+ return result;
+}
+
+@end
+
+
+
+#ifdef main
+# undef main
+#endif
+
+
+/* Main entry point to executable - should *not* be SDL_main! */
+int main (int argc, char **argv)
+{
+ /* Copy the arguments into a global variable */
+ /* This is passed if we are launched by double-clicking */
+ if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
+ gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
+ gArgv[0] = argv[0];
+ gArgv[1] = NULL;
+ gArgc = 1;
+ gFinderLaunch = YES;
+ } else {
+ int i;
+ gArgc = argc;
+ gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
+ for (i = 0; i <= argc; i++)
+ gArgv[i] = argv[i];
+ gFinderLaunch = NO;
+ }
+
+#if SDL_USE_NIB_FILE
+ NSApplicationMain (argc, argv);
+#else
+ CustomApplicationMain (argc, argv);
+#endif
+ return 0;
+}
+
+#endif
diff --git a/src/endian_uqm.h b/src/endian_uqm.h
new file mode 100644
index 0000000..057da4c
--- /dev/null
+++ b/src/endian_uqm.h
@@ -0,0 +1,136 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Endian swapping, taken from SDL-1.2.5 sources and modified
+ * Original copyright (C) Sam Lantinga
+ */
+
+#ifndef ENDIAN_UQM_H_
+#define ENDIAN_UQM_H_
+
+#include "config.h"
+#include "types.h"
+
+#if defined (__APPLE__) && defined (__GNUC__)
+// When using the MacOS gcc compiler to build universal binaries,
+// each file will be compiled once for each platform.
+// This means that checking endianness beforehand from build.sh will not do,
+// but fortunately, gcc defines __BIG_ENDIAN__ or __LITTLE_ENDIAN__ on
+// this platform.
+# if defined(__BIG_ENDIAN__)
+# undef WORDS_BIGENDIAN
+# define WORDS_BIGENDIAN
+# elif defined(__LITTLE_ENDIAN__)
+# undef WORDS_BIGENDIAN
+# else
+ // Neither __BIG_ENDIAN__ nor __LITTLE_ENDIAN__ is defined.
+ // Fallback to using the build.sh defined value.
+# endif
+#endif /* __APPLE__ */
+
+#if defined(_MSC_VER) || defined(__BORLANDC__) || \
+ defined(__DMC__) || defined(__SC__) || \
+ defined(__WATCOMC__) || defined(__LCC__)
+#ifndef __inline__
+#define __inline__ __inline
+#endif
+#endif
+
+/* The macros used to swap values */
+/* Try to use superfast macros on systems that support them */
+#ifdef linux
+#include <endian.h>
+#ifdef __arch__swab16
+#define UQM_Swap16 __arch__swab16
+#endif
+#ifdef __arch__swab32
+#define UQM_Swap32 __arch__swab32
+#endif
+#endif /* linux */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Use inline functions for compilers that support them, and static
+ functions for those that do not. Because these functions become
+ static for compilers that do not support inline functions, this
+ header should only be included in files that actually use them.
+*/
+#ifndef UQM_Swap16
+static __inline__ uint16 UQM_Swap16(uint16 D)
+{
+ return((D<<8)|(D>>8));
+}
+#endif
+#ifndef UQM_Swap32
+static __inline__ uint32 UQM_Swap32(uint32 D)
+{
+ return((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24));
+}
+#endif
+#ifdef UQM_INT64
+#ifndef UQM_Swap64
+static __inline__ uint64 UQM_Swap64(uint64 val)
+{
+ uint32 hi, lo;
+
+ /* Separate into high and low 32-bit values and swap them */
+ lo = (uint32)(val&0xFFFFFFFF);
+ val >>= 32;
+ hi = (uint32)(val&0xFFFFFFFF);
+ val = UQM_Swap32(lo);
+ val <<= 32;
+ val |= UQM_Swap32(hi);
+ return(val);
+}
+#endif
+#else
+#ifndef UQM_Swap64
+/* This is mainly to keep compilers from complaining in SDL code.
+ If there is no real 64-bit datatype, then compilers will complain about
+ the fake 64-bit datatype that SDL provides when it compiles user code.
+*/
+#define UQM_Swap64(X) (X)
+#endif
+#endif /* UQM_INT64 */
+
+
+/* Byteswap item from the specified endianness to the native endianness
+ * or vice versa.
+ */
+#ifndef WORDS_BIGENDIAN
+#define UQM_SwapLE16(X) (X)
+#define UQM_SwapLE32(X) (X)
+#define UQM_SwapLE64(X) (X)
+#define UQM_SwapBE16(X) UQM_Swap16(X)
+#define UQM_SwapBE32(X) UQM_Swap32(X)
+#define UQM_SwapBE64(X) UQM_Swap64(X)
+#else
+#define UQM_SwapLE16(X) UQM_Swap16(X)
+#define UQM_SwapLE32(X) UQM_Swap32(X)
+#define UQM_SwapLE64(X) UQM_Swap64(X)
+#define UQM_SwapBE16(X) (X)
+#define UQM_SwapBE32(X) (X)
+#define UQM_SwapBE64(X) (X)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _ENDIAN_H */
diff --git a/src/getopt/Makeinfo b/src/getopt/Makeinfo
new file mode 100644
index 0000000..281a7f2
--- /dev/null
+++ b/src/getopt/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="getopt.c getopt1.c"
+uqm_HFILES="getopt.h"
diff --git a/src/getopt/getopt.c b/src/getopt/getopt.c
new file mode 100644
index 0000000..271f634
--- /dev/null
+++ b/src/getopt/getopt.c
@@ -0,0 +1,1061 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to drepper@gnu.org
+ before changing it!
+ Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#include <config.h>
+#include <string.h>
+
+#ifndef HAVE_GETOPT_LONG
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+# ifndef const
+# define const
+# endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+# define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library. */
+
+#ifdef VMS
+# include <unixlib.h>
+# if HAVE_STRING_H - 0
+# include <string.h>
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages. */
+# if defined HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifndef _
+# define _(msgid) gettext (msgid)
+# endif
+# else
+# define _(msgid) (msgid)
+# endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+# include <string.h>
+# define my_index strchr
+#else
+
+#ifndef WIN32
+# if HAVE_STRING_H
+# include <string.h>
+# else
+# include <strings.h>
+# endif
+#else
+# include <string.h>
+#endif
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Stored original parameters.
+ XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+# define SWAP_FLAGS(ch1, ch2) \
+ if (nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+# else
+# define SWAP_FLAGS(ch1, ch2)
+# endif
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+#if defined __STDC__ && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = HMalloc (top + 1);
+ if (new_str == NULL)
+ nonoption_flags_len = nonoption_flags_max_len = 0;
+ else
+ {
+ memset (__mempcpy (new_str, __getopt_nonoption_flags,
+ nonoption_flags_max_len),
+ '\0', top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ if (posixly_correct == NULL
+ && argc == __libc_argc && argv == __libc_argv)
+ {
+ if (nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen (orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) HMalloc (nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+ '\0', nonoption_flags_max_len - len);
+ }
+ }
+ nonoption_flags_len = nonoption_flags_max_len;
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ int print_errors = opterr;
+ if (optstring[0] == ':')
+ print_errors = 0;
+
+ if (argc < 1)
+ return -1;
+
+ optarg = NULL;
+
+ if (optind == 0 || !__getopt_initialized)
+ {
+ if (optind == 0)
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring);
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only
+ || pfound->has_arg != p->has_arg
+ || pfound->flag != p->flag
+ || pfound->val != p->val)
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (print_errors)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (print_errors)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
+
+#endif /* HAVE_GETOPT_LONG */
diff --git a/src/getopt/getopt.h b/src/getopt/getopt.h
new file mode 100644
index 0000000..a1b8dd6
--- /dev/null
+++ b/src/getopt/getopt.h
@@ -0,0 +1,180 @@
+/* Declarations for getopt.
+ Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+# define _GETOPT_H 1
+#endif
+
+/* If __GNU_LIBRARY__ is not already defined, either we are being used
+ standalone, or this is the first header included in the source file.
+ If we are being used with glibc, we need to include <features.h>, but
+ that does not exist if we are standalone. So: if __GNU_LIBRARY__ is
+ not defined, include <ctype.h>, which will pull in <features.h> for us
+ if it's from glibc. (Why ctype.h? It's guaranteed to exist and it
+ doesn't flood the namespace with stuff the way some other headers do.) */
+#if !defined __GNU_LIBRARY__
+# include <ctype.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+# if (defined __STDC__ && __STDC__) || defined __cplusplus
+ const char *name;
+# else
+ char *name;
+# endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+# define no_argument 0
+# define required_argument 1
+# define optional_argument 2
+#endif /* need getopt */
+
+
+/* Get definitions and prototypes for functions to process the
+ arguments in ARGV (ARGC of them, minus the program name) for
+ options given in OPTS.
+
+ Return the option character from OPTS just read. Return -1 when
+ there are no more options. For unrecognized options, or options
+ missing arguments, `optopt' is set to the option letter, and '?' is
+ returned.
+
+ The OPTS string is a list of characters which are recognized option
+ letters, optionally followed by colons, specifying that that letter
+ takes an argument, to be placed in `optarg'.
+
+ If a letter in OPTS is followed by two colons, its argument is
+ optional. This behavior is specific to the GNU `getopt'.
+
+ The argument `--' causes premature termination of argument
+ scanning, explicitly telling `getopt' that there are no more
+ options.
+
+ If OPTS begins with `--', then non-option arguments are treated as
+ arguments to the option '\0'. This behavior is specific to the GNU
+ `getopt'. */
+
+#if (defined __STDC__ && __STDC__) || defined __cplusplus
+# ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int __argc, char *const *__argv, const char *__shortopts);
+# else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+# endif /* __GNU_LIBRARY__ */
+
+# ifndef __need_getopt
+extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+extern int getopt_long_only (int __argc, char *const *__argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int __argc, char *const *__argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind,
+ int __long_only);
+# endif
+#else /* not __STDC__ */
+extern int getopt ();
+# ifndef __need_getopt
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+# endif
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations. */
+#undef __need_getopt
+
+#endif /* getopt.h */
diff --git a/src/getopt/getopt1.c b/src/getopt/getopt1.c
new file mode 100644
index 0000000..9acca1b
--- /dev/null
+++ b/src/getopt/getopt1.c
@@ -0,0 +1,189 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#include <config.h>
+
+#ifndef HAVE_GETOPT_LONG
+#include "getopt.h"
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE. */
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
+
+#endif /* HAVE_GETOPT_LONG */
diff --git a/src/libs/Makeinfo b/src/libs/Makeinfo
new file mode 100644
index 0000000..dbaa7d4
--- /dev/null
+++ b/src/libs/Makeinfo
@@ -0,0 +1,19 @@
+uqm_SUBDIRS="callback decomp file graphics heap input list math memory
+ resource sound strings task threads time uio video log"
+if [ -n "$uqm_USE_INTERNAL_MIKMOD" ]; then
+ uqm_SUBDIRS="$uqm_SUBDIRS mikmod"
+fi
+
+if [ -n "$uqm_NETPLAY" ]; then
+ uqm_SUBDIRS="$uqm_SUBDIRS network"
+fi
+
+#if [ "$DEBUG" = 1 ]; then
+# uqm_SUBDIRS="$UQM_SUBDIRS debug"
+#fi
+
+uqm_HFILES="alarm.h async.h callback.h cdplib.h compiler.h declib.h file.h
+ gfxlib.h heap.h inplib.h list.h log.h mathlib.h md5.h memlib.h
+ misc.h net.h platform.h reslib.h sndlib.h strlib.h tasklib.h
+ threadlib.h timelib.h uio.h uioutils.h unicode.h vidlib.h"
+
diff --git a/src/libs/alarm.h b/src/libs/alarm.h
new file mode 100644
index 0000000..b4a72ca
--- /dev/null
+++ b/src/libs/alarm.h
@@ -0,0 +1,9 @@
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "callback/alarm.h"
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/libs/async.h b/src/libs/async.h
new file mode 100644
index 0000000..25b71af
--- /dev/null
+++ b/src/libs/async.h
@@ -0,0 +1,10 @@
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "callback/async.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
diff --git a/src/libs/callback.h b/src/libs/callback.h
new file mode 100644
index 0000000..025160a
--- /dev/null
+++ b/src/libs/callback.h
@@ -0,0 +1,10 @@
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "callback/callback.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
diff --git a/src/libs/callback/Makeinfo b/src/libs/callback/Makeinfo
new file mode 100644
index 0000000..8842cba
--- /dev/null
+++ b/src/libs/callback/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="alarm.c async.c callback.c"
+uqm_HFILES="alarm.h async.h callback.h"
diff --git a/src/libs/callback/alarm.c b/src/libs/callback/alarm.c
new file mode 100644
index 0000000..c9bd6ce
--- /dev/null
+++ b/src/libs/callback/alarm.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "alarm.h"
+
+#include SDL_INCLUDE(SDL.h)
+#include "libs/heap.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+
+Heap *alarmHeap;
+
+
+static inline Alarm *
+Alarm_alloc(void) {
+ return malloc(sizeof (Alarm));
+}
+
+static inline void
+Alarm_free(Alarm *alarm) {
+ free(alarm);
+}
+
+static inline int
+AlarmTime_compare(const AlarmTime t1, const AlarmTime t2) {
+ if (t1 < t2)
+ return -1;
+ if (t1 > t2)
+ return 1;
+ return 0;
+}
+
+static int
+Alarm_compare(const Alarm *a1, const Alarm *a2) {
+ return AlarmTime_compare(a1->time, a2->time);
+}
+
+void
+Alarm_init(void) {
+ assert(alarmHeap == NULL);
+ alarmHeap = Heap_new((HeapValue_Comparator) Alarm_compare,
+ 4, 4, 0.8);
+}
+
+void
+Alarm_uninit(void) {
+ assert(alarmHeap != NULL);
+
+ while (Heap_hasMore(alarmHeap)) {
+ Alarm *alarm = (Alarm *) Heap_pop(alarmHeap);
+ Alarm_free(alarm);
+ }
+ Heap_delete(alarmHeap);
+ alarmHeap = NULL;
+}
+
+static inline AlarmTime
+AlarmTime_nowMs(void) {
+ return SDL_GetTicks();
+}
+
+Alarm *
+Alarm_addAbsoluteMs(uint32 ms, AlarmCallback callback,
+ AlarmCallbackArg arg) {
+ Alarm *alarm;
+
+ assert(alarmHeap != NULL);
+
+ alarm = Alarm_alloc();
+ alarm->time = ms;
+ alarm->callback = callback;
+ alarm->arg = arg;
+
+ Heap_add(alarmHeap, (HeapValue *) alarm);
+
+ return alarm;
+}
+
+Alarm *
+Alarm_addRelativeMs(uint32 ms, AlarmCallback callback,
+ AlarmCallbackArg arg) {
+ Alarm *alarm;
+
+ assert(alarmHeap != NULL);
+
+ alarm = Alarm_alloc();
+ alarm->time = AlarmTime_nowMs() + ms;
+ alarm->callback = callback;
+ alarm->arg = arg;
+
+ Heap_add(alarmHeap, (HeapValue *) alarm);
+
+ return alarm;
+}
+
+void
+Alarm_remove(Alarm *alarm) {
+ assert(alarmHeap != NULL);
+ Heap_remove(alarmHeap, (HeapValue *) alarm);
+ Alarm_free(alarm);
+}
+
+// Process at most one alarm, if its time has come.
+// It is safe to call this function again from inside a callback function
+// that it called. It should not be called from multiple threads at once.
+bool
+Alarm_processOne(void)
+{
+ AlarmTime now;
+ Alarm *alarm;
+
+ assert(alarmHeap != NULL);
+ if (!Heap_hasMore(alarmHeap))
+ return false;
+
+ now = AlarmTime_nowMs();
+ alarm = (Alarm *) Heap_first(alarmHeap);
+ if (now < alarm->time)
+ return false;
+
+ Heap_pop(alarmHeap);
+ alarm->callback(alarm->arg);
+ Alarm_free(alarm);
+ return true;
+}
+
+#if 0
+// It is safe to call this function again from inside a callback function
+// that it called. It should not be called from multiple threads at once.
+void
+Alarm_processAll(void) {
+ AlarmTime now;
+
+ assert(alarmHeap != NULL);
+
+ now = AlarmTime_nowMs();
+ while (Heap_hasMore(alarmHeap)) {
+ Alarm *alarm = (Alarm *) Heap_first(alarmHeap);
+
+ if (now < alarm->time)
+ break;
+
+ Heap_pop(alarmHeap);
+ alarm->callback(alarm->arg);
+ Alarm_free(alarm);
+ }
+}
+#endif
+
+uint32
+Alarm_timeBeforeNextMs(void) {
+ Alarm *alarm;
+
+ if (!Heap_hasMore(alarmHeap))
+ return UINT32_MAX;
+
+ alarm = (Alarm *) Heap_first(alarmHeap);
+ return alarmTimeToMsUint32(alarm->time);
+}
+
diff --git a/src/libs/callback/alarm.h b/src/libs/callback/alarm.h
new file mode 100644
index 0000000..9f61263
--- /dev/null
+++ b/src/libs/callback/alarm.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_CALLBACK_ALARM_H_
+#define LIBS_CALLBACK_ALARM_H_
+
+#include "port.h"
+#include "types.h"
+
+typedef uint32 AlarmTime;
+static inline uint32
+alarmTimeToMsUint32(AlarmTime time) {
+ return (uint32) time;
+}
+
+typedef struct Alarm Alarm;
+typedef void *AlarmCallbackArg;
+typedef void (*AlarmCallback)(AlarmCallbackArg arg);
+
+struct Alarm {
+ size_t index;
+ // For the HeapValue 'base struct'.
+
+ AlarmTime time;
+ AlarmCallback callback;
+ AlarmCallbackArg arg;
+};
+
+void Alarm_init(void);
+void Alarm_uninit(void);
+Alarm *Alarm_addAbsoluteMs(uint32 ms, AlarmCallback callback,
+ AlarmCallbackArg arg);
+Alarm *Alarm_addRelativeMs(uint32 ms, AlarmCallback callback,
+ AlarmCallbackArg arg);
+void Alarm_remove(Alarm *alarm);
+bool Alarm_processOne(void);
+void Alarm_processAll(void);
+uint32 Alarm_timeBeforeNextMs(void);
+
+#endif /* LIBS_CALLBACK_ALARM_H_ */
+
diff --git a/src/libs/callback/async.c b/src/libs/callback/async.c
new file mode 100644
index 0000000..d901158
--- /dev/null
+++ b/src/libs/callback/async.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "async.h"
+
+#include "libs/alarm.h"
+#include "libs/callback.h"
+
+
+// Process all alarms and callbacks.
+// First, all scheduled callbacks are called.
+// Then each alarm due is called, and after each of these alarms, the
+// callbacks scheduled by this alarm are called.
+void
+Async_process(void)
+{
+ // Call pending callbacks.
+ Callback_process();
+
+ for (;;) {
+ if (!Alarm_processOne())
+ return;
+
+ // Call callbacks scheduled from the last alarm.
+ Callback_process();
+ }
+}
+
+// Returns the next time that some asynchronous callback is
+// to be called. Note that all values lower than the current time
+// should be considered as 'somewhere in the past'.
+uint32
+Async_timeBeforeNextMs(void) {
+ if (Callback_haveMore()) {
+ // Any time before the current time is ok, though we reserve 0 so
+ // that the caller may use it as a special value in its own code.
+ return 1;
+ }
+ return Alarm_timeBeforeNextMs();
+}
+
diff --git a/src/libs/callback/async.h b/src/libs/callback/async.h
new file mode 100644
index 0000000..8cfae39
--- /dev/null
+++ b/src/libs/callback/async.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _ASYNC_H
+#define _ASYNC_H
+
+#include "types.h"
+
+void Async_process(void);
+uint32 Async_timeBeforeNextMs(void);
+
+#endif /* _ASYNC_H */
+
diff --git a/src/libs/callback/callback.c b/src/libs/callback/callback.c
new file mode 100644
index 0000000..e8ae8e9
--- /dev/null
+++ b/src/libs/callback/callback.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "port.h"
+#include "types.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "libs/threadlib.h"
+
+typedef struct CallbackLink CallbackLink;
+
+#define CALLBACK_INTERNAL
+#include "callback.h"
+
+struct CallbackLink {
+ CallbackLink *next;
+ CallbackFunction callback;
+ CallbackArg arg;
+};
+
+static CallbackLink *callbacks;
+static CallbackLink **callbacksEnd;
+static CallbackLink *const *callbacksProcessEnd;
+
+static Mutex callbackListLock;
+
+static inline void
+CallbackList_lock(void) {
+ LockMutex(callbackListLock);
+}
+
+static inline void
+CallbackList_unlock(void) {
+ UnlockMutex(callbackListLock);
+}
+
+#if 0
+static inline bool
+CallbackList_isLocked(void) {
+ // TODO
+}
+#endif
+
+void
+Callback_init(void) {
+ callbacks = NULL;
+ callbacksEnd = &callbacks;
+ callbacksProcessEnd = &callbacks;
+ callbackListLock = CreateMutex("Callback List Lock", SYNC_CLASS_TOPLEVEL);
+}
+
+void
+Callback_uninit(void) {
+ // TODO: cleanup the queue?
+ DestroyMutex (callbackListLock);
+ callbackListLock = 0;
+}
+
+// Callbacks are guaranteed to be called in the order that they are queued.
+CallbackID
+Callback_add(CallbackFunction callback, CallbackArg arg) {
+ CallbackLink *link = malloc(sizeof (CallbackLink));
+ link->callback = callback;
+ link->arg = arg;
+ link->next = NULL;
+
+ CallbackList_lock();
+ *callbacksEnd = link;
+ callbacksEnd = &link->next;
+ CallbackList_unlock();
+ return (CallbackID) link;
+}
+
+
+static void
+CallbackLink_delete(CallbackLink *link) {
+ free(link);
+}
+
+// Pre: CallbackList is locked.
+static CallbackLink **
+CallbackLink_find(CallbackLink *link) {
+ CallbackLink **ptr;
+
+ //assert(CallbackList_isLocked());
+ for (ptr = &callbacks; *ptr != NULL; ptr = &(*ptr)->next) {
+ if (*ptr == link)
+ return ptr;
+ }
+ return NULL;
+}
+
+bool
+Callback_remove(CallbackID id) {
+ CallbackLink *link = (CallbackLink *) id;
+ CallbackLink **linkPtr;
+
+ CallbackList_lock();
+
+ linkPtr = CallbackLink_find(link);
+ if (linkPtr == NULL) {
+ CallbackList_unlock();
+ return false;
+ }
+
+ if (callbacksEnd == &(*linkPtr)->next)
+ callbacksEnd = linkPtr;
+ if (callbacksProcessEnd == &(*linkPtr)->next)
+ callbacksProcessEnd = linkPtr;
+ *linkPtr = (*linkPtr)->next;
+
+ CallbackList_unlock();
+
+ CallbackLink_delete(link);
+ return true;
+}
+
+static inline void
+CallbackLink_doCallback(CallbackLink *link) {
+ (link->callback)(link->arg);
+}
+
+// Call all queued callbacks currently in the queue. Callbacks queued
+// from inside the called functions will not be processed until the next
+// call of Callback_process().
+// It is allowed to remove callbacks from inside the called functions.
+// NB: Callback_process() must never be called from more than one thread
+// at the same time. It's the only sensible way to ensure that the
+// callbacks are called in the order in which they were queued.
+// It is however allowed to call Callback_process() from inside the
+// callback function called by Callback_process() itself.
+void
+Callback_process(void) {
+ CallbackLink *link;
+
+ // We set 'callbacksProcessEnd' to callbacksEnd. Callbacks added
+ // from inside a callback function will be placed after
+ // callbacksProcessEnd, and will hence not be processed this
+ // call of Callback_process().
+ CallbackList_lock();
+ callbacksProcessEnd = callbacksEnd;
+ CallbackList_unlock();
+
+ for (;;) {
+ CallbackList_lock();
+ if (callbacksProcessEnd == &callbacks) {
+ CallbackList_unlock();
+ break;
+ }
+ assert(callbacks != NULL);
+ // If callbacks == NULL, then callbacksProcessEnd == &callbacks
+ link = callbacks;
+ callbacks = link->next;
+ if (callbacksEnd == &link->next)
+ callbacksEnd = &callbacks;
+ if (callbacksProcessEnd == &link->next)
+ callbacksProcessEnd = &callbacks;
+ CallbackList_unlock();
+
+ CallbackLink_doCallback(link);
+ CallbackLink_delete(link);
+ }
+}
+
+bool
+Callback_haveMore(void) {
+ bool result;
+
+ CallbackList_lock();
+ result = (callbacks != NULL);
+ CallbackList_unlock();
+
+ return result;
+}
+
diff --git a/src/libs/callback/callback.h b/src/libs/callback/callback.h
new file mode 100644
index 0000000..e04ebe8
--- /dev/null
+++ b/src/libs/callback/callback.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_CALLBACK_CALLBACK_H_
+#define LIBS_CALLBACK_CALLBACK_H_
+
+#include "types.h"
+
+#ifdef CALLBACK_INTERNAL
+typedef CallbackLink *CallbackID;
+#else
+typedef void *CallbackID;
+ // Uniquely identifies a queued callback.
+#endif
+#define CallbackID_invalid ((CallbackID ) NULL)
+
+typedef void *CallbackArg;
+typedef void (*CallbackFunction)(CallbackArg arg);
+
+void Callback_init(void);
+void Callback_uninit(void);
+CallbackID Callback_add(CallbackFunction callback, CallbackArg arg);
+bool Callback_remove(CallbackID id);
+void Callback_process(void);
+bool Callback_haveMore(void);
+
+#endif /* LIBS_CALLBACK_CALLBACK_H_ */
+
diff --git a/src/libs/cdp/Makeinfo b/src/libs/cdp/Makeinfo
new file mode 100644
index 0000000..b2a83b5
--- /dev/null
+++ b/src/libs/cdp/Makeinfo
@@ -0,0 +1,3 @@
+uqm_CFILES="cdp.c cdpapi.c"
+uqm_HFILES="cdp_alli.h cdpapi.h cdp.h cdp_iio.h cdp_imem.h cdpint.h
+ cdp_isnd.h cdp_ivid.h cdpmod.h windl.h"
diff --git a/src/libs/cdp/cdp.c b/src/libs/cdp/cdp.c
new file mode 100644
index 0000000..ca9536e
--- /dev/null
+++ b/src/libs/cdp/cdp.c
@@ -0,0 +1,437 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP library definitions
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include "cdp.h"
+#include "port.h"
+#include "cdpint.h"
+#include "cdpmod.h"
+#include "uio.h"
+#include "uqmversion.h"
+#ifdef WIN32
+# include "windl.h"
+#else
+# include <dlfcn.h>
+#endif
+
+#define MAX_CDPS 63
+#define CDPDIR "cdps"
+
+// internal CDP module representation
+struct cdp_Module
+{
+ bool builtin; // used at least once indicator
+ bool used; // used at least once indicator
+ void* hmodule; // loaded module handle
+ uint32 refcount; // reference count
+ cdp_ModuleInfo* info; // cdp exported info
+
+};
+
+// Kernel module info
+// not a real module, and not loadable either
+// this just provides information to other modules
+cdp_ModuleInfo cdp_kernel_info =
+{
+ sizeof (cdp_ModuleInfo),
+ CDPAPI_VERSION, // API version we are using
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION, UQM_PATCH_VERSION,
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION, UQM_PATCH_VERSION,
+ CDP_MODINFO_RESERVED1,
+ "UQM", // CDP context cannonical name
+ "UQM Kernel", // CDP mod name
+# define S(i) #i
+ // CDP mod version
+ S(UQM_MAJOR_VERSION) "." S(UQM_MINOR_VERSION) UQM_EXTRA_VERSION,
+# undef S
+ "UQM Team", // CDP mod author
+ "http://sc2.sf.net", // CDP mod URL
+ "Eternal doctrine executor", // CDP mod comment
+ CDP_MODINFO_RESERVED2,
+ NULL, NULL // no entrypoints defined/needed
+};
+
+static cdp_Module cdp_modules[MAX_CDPS + 1] =
+{
+ {true, true, NULL, 1, &cdp_kernel_info},
+
+ {false, false, NULL, 0, NULL} // term
+};
+
+extern uio_DirHandle *cdpDir;
+
+static bool cdp_inited = false;
+static cdp_Error cdp_last_error = CDPERR_NONE;
+static char cdp_path[PATH_MAX] = "";
+
+cdp_Error
+cdp_GetError (void)
+{
+ cdp_Error ret = cdp_last_error;
+ cdp_last_error = CDPERR_NONE;
+ return ret;
+}
+
+bool
+cdp_Init (void)
+{
+ int i;
+ void* hkernel;
+
+ if (cdp_inited)
+ {
+ fprintf (stderr, "cdp_Init(): called when already inited\n");
+ return true;
+ }
+
+ // preprocess built-in modules
+ hkernel = dlopen (NULL, RTLD_LAZY);
+
+ for (i = 0; cdp_modules[i].builtin; ++i)
+ cdp_modules[i].hmodule = hkernel;
+
+ // clear the rest
+ //memset (cdp_modules + i, 0,
+ // sizeof (cdp_modules) - sizeof (cdp_Module) * i);
+
+ //strcpy (cdp_path, cdpDir->path);
+ cdp_InitApi ();
+ cdp_inited = true;
+
+ return true;
+}
+
+void
+cdp_Uninit (void)
+{
+ if (!cdp_inited)
+ {
+ fprintf (stderr, "cdp_Uninit(): called when not inited\n");
+ return;
+ }
+
+ cdp_UninitApi ();
+ cdp_FreeAllModules ();
+ cdp_inited = false;
+}
+
+cdp_Module*
+cdp_LoadModule (const char* modname)
+ // special value for modname: NULL - refers to kernel (UQM exe)
+{
+ void* mod;
+ char modpath[PATH_MAX];
+ const char* errstr;
+ cdp_ModuleInfo* info;
+ int i;
+ cdp_Module* cdp;
+ cdp_Module* newslot = 0;
+ cdp_Itf* ihost;
+
+ if (modname == NULL)
+ return cdp_modules;
+
+ if (!cdp_inited)
+ {
+ fprintf (stderr, "cdp_LoadModule(): called when not inited\n");
+ return 0;
+ }
+
+ // load dynamic lib
+ sprintf (modpath, "%s/%s%s", CDPDIR, modname, CDPEXT);
+ mod = dlopen (modpath, RTLD_NOW);
+ if (!mod)
+ {
+ cdp_last_error = CDPERR_NOT_FOUND;
+ return NULL;
+ }
+
+ // look it up in already loaded
+ for (i = 0, cdp = cdp_modules; cdp->used && cdp->hmodule != mod;
+ ++cdp, ++i)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !cdp->hmodule)
+ newslot = cdp;
+ }
+ if (i >= MAX_CDPS)
+ {
+ fprintf (stderr, "cdp_LoadModule(): "
+ "CDPs limit reached while loading %s\n",
+ modname);
+ dlclose (mod);
+ cdp_last_error = CDPERR_TOO_MANY;
+ return NULL;
+ }
+
+ if (cdp->hmodule)
+ { // module has already been loaded
+ cdp->refcount++;
+ return cdp;
+ }
+
+ dlerror (); // clear any error
+ info = dlsym (mod, CDP_INFO_SYM_NAME);
+ if (!info && (errstr = dlerror ()))
+ {
+ dlclose (mod);
+ cdp_last_error = CDPERR_BAD_MODULE;
+ return NULL;
+ }
+
+ if (info->size < CDP_MODINFO_MIN_SIZE || info->api_ver > CDPAPI_VERSION)
+ {
+ fprintf (stderr, "cdp_LoadModule(): "
+ "CDP %s is invalid or newer API version\n",
+ modname);
+ dlclose (mod);
+ cdp_last_error = CDPERR_UNKNOWN_VER;
+ return NULL;
+ }
+
+ ihost = cdp_GetInterface (CDPITF_KIND_HOST, info->api_ver);
+ if (!ihost)
+ {
+ fprintf (stderr, "cdp_LoadModule(): "
+ "CDP %s requested unsupported API version 0x%08x\n",
+ modname, info->api_ver);
+ dlclose (mod);
+ cdp_last_error = CDPERR_UNKNOWN_VER;
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = cdp;
+ newslot->used = true;
+ // make next one a term
+ cdp[1].builtin = false;
+ cdp[1].used = false;
+ cdp[1].hmodule = NULL;
+ cdp[1].refcount = 0;
+ }
+ newslot->hmodule = mod;
+ newslot->refcount = 1;
+ newslot->info = info;
+
+ if (!info->module_init (newslot, (cdp_Itf_Host*)ihost))
+ {
+ fprintf (stderr, "cdp_LoadModule(): "
+ "CDP %s failed to init\n",
+ modname);
+ dlclose (mod);
+ newslot->hmodule = NULL;
+ newslot->info = NULL;
+ newslot->refcount = 0;
+ cdp_last_error = CDPERR_INIT_FAILED;
+ return NULL;
+ }
+
+
+ return newslot;
+}
+
+cdp_Module*
+cdp_CheckModule (cdp_Module* module)
+{
+ if (module < cdp_modules || module >= cdp_modules + MAX_CDPS ||
+ !module->hmodule || !module->info)
+ return NULL;
+ else
+ return module;
+}
+
+void
+cdp_FreeModule (cdp_Module* module)
+{
+ cdp_Module* modslot = cdp_CheckModule (module);
+
+ if (!modslot || modslot->builtin)
+ return;
+
+ modslot->refcount--;
+ if (modslot->refcount == 0)
+ modslot->info->module_term ();
+
+ dlclose (modslot->hmodule);
+
+ if (modslot->refcount == 0)
+ {
+ modslot->hmodule = NULL;
+ modslot->info = NULL;
+ }
+}
+
+const char*
+cdp_GetModuleContext (cdp_Module* module, bool bMetaString)
+{
+ cdp_Module* modslot = cdp_CheckModule (module);
+ if (bMetaString)
+ {
+ if (!modslot)
+ return "(Error)";
+ if (!modslot->info->context_name)
+ return "(Null)";
+ }
+ else if (!modslot)
+ {
+ return NULL;
+ }
+ return modslot->info->context_name;
+}
+
+const char*
+cdp_GetModuleName (cdp_Module* module, bool bMetaString)
+{
+ cdp_Module* modslot = cdp_CheckModule (module);
+ if (bMetaString)
+ {
+ if (!modslot)
+ return "(Error)";
+ if (!modslot->info->name)
+ return "(Null)";
+ }
+ else if (!modslot)
+ {
+ return NULL;
+ }
+ return modslot->info->name;
+}
+
+uint32
+cdp_GetModuleVersion (cdp_Module* module)
+{
+ cdp_Module* modslot = cdp_CheckModule (module);
+ if (!modslot)
+ return 0;
+ return (modslot->info->ver_major << 16) | modslot->info->ver_minor;
+}
+
+const char*
+cdp_GetModuleVersionString (cdp_Module* module, bool bMetaString)
+{
+ cdp_Module* modslot = cdp_CheckModule (module);
+ if (bMetaString)
+ {
+ if (!modslot)
+ return "(Error)";
+ if (!modslot->info->ver_string)
+ return "(Null)";
+ }
+ else if (!modslot)
+ {
+ return NULL;
+ }
+ return modslot->info->ver_string;
+}
+
+const char*
+cdp_GetModuleComment (cdp_Module* module, bool bMetaString)
+{
+ cdp_Module* modslot = cdp_CheckModule (module);
+ if (bMetaString)
+ {
+ if (!modslot)
+ return "(Error)";
+ if (!modslot->info->comments)
+ return "(Null)";
+ }
+ else if (!modslot)
+ {
+ return NULL;
+ }
+ return modslot->info->comments;
+}
+
+// load-all and free-all are here temporarily until
+// configs are in place
+int
+cdp_LoadAllModules (void)
+{
+ uio_DirList *dirList;
+ int nummods = 0;
+ int i;
+
+ if (!cdp_inited)
+ {
+ fprintf (stderr, "cdp_LoadAllModules(): called when not inited\n");
+ return 0;
+ }
+
+ if (!cdpDir)
+ return 0;
+
+ fprintf (stderr, "Loading all CDPs...\n");
+
+ dirList = uio_getDirList (cdpDir, "", CDPEXT, match_MATCH_SUFFIX);
+ if (!dirList)
+ return 0;
+
+ for (i = 0; i < dirList->numNames; i++)
+ {
+ char modname[PATH_MAX];
+ char* pext;
+ cdp_Module* mod;
+
+ fprintf (stderr, "Loading CDP %s...\n", dirList->names[i]);
+ strcpy (modname, dirList->names[i]);
+ pext = strrchr (modname, '.');
+ if (pext) // strip extension
+ *pext = 0;
+
+ mod = cdp_LoadModule (modname);
+ if (mod)
+ {
+ nummods++;
+ fprintf (stderr, "\tloaded CDP: %s v%s (%s)\n",
+ cdp_GetModuleName (mod, true),
+ cdp_GetModuleVersionString (mod, true),
+ cdp_GetModuleComment (mod, true));
+ }
+ else
+ {
+ fprintf (stderr, "\tload failed, error %u\n",
+ cdp_GetError ());
+ }
+ }
+ uio_freeDirList (dirList);
+
+ return nummods;
+}
+
+void
+cdp_FreeAllModules (void)
+{
+ cdp_Module* cdp;
+
+ if (!cdp_inited)
+ {
+ fprintf (stderr, "cdp_FreeAllModules(): called when not inited\n");
+ return;
+ }
+
+ for (cdp = cdp_modules; cdp->used; ++cdp)
+ {
+ if (!cdp->builtin && cdp->hmodule)
+ cdp_FreeModule (cdp);
+ }
+}
diff --git a/src/libs/cdp/cdp.h b/src/libs/cdp/cdp.h
new file mode 100644
index 0000000..0390f07
--- /dev/null
+++ b/src/libs/cdp/cdp.h
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP library declarations
+ */
+
+#ifndef LIBS_CDP_CDP_H_
+#define LIBS_CDP_CDP_H_
+
+#include "types.h"
+#include "cdpapi.h"
+
+// these will be called by the UQM engine
+// and plugins manager
+bool cdp_Init (void);
+void cdp_Uninit (void);
+cdp_Error cdp_GetError (void);
+cdp_Module* cdp_LoadModule (const char* modname);
+void cdp_FreeModule (cdp_Module* module);
+// in the following calls when bMetaString is set
+// function will never return a NULL, instead it will
+// return a valid string -- error meta-string
+const char* cdp_GetModuleContext (cdp_Module* module, bool bMetaString);
+const char* cdp_GetModuleName (cdp_Module* module, bool bMetaString);
+uint32 cdp_GetModuleVersion (cdp_Module* module);
+const char* cdp_GetModuleVersionString (cdp_Module* module, bool bMetaString);
+const char* cdp_GetModuleComment (cdp_Module* module, bool bMetaString);
+
+int cdp_LoadAllModules (void);
+void cdp_FreeAllModules (void);
+
+#endif /* LIBS_CDP_CDP_H_ */
diff --git a/src/libs/cdp/cdp_alli.h b/src/libs/cdp/cdp_alli.h
new file mode 100644
index 0000000..31520e5
--- /dev/null
+++ b/src/libs/cdp/cdp_alli.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP All-interface list (for simplicity)
+ */
+
+#ifndef LIBS_CDP_CDP_ALLI_H_
+#define LIBS_CDP_CDP_ALLI_H_
+
+#include "cdp_iio.h"
+#include "cdp_imem.h"
+#include "cdp_isnd.h"
+#include "cdp_ivid.h"
+// TODO: add more cdp_iXXX.h here as they are defined
+
+#endif /* LIBS_CDP_CDP_ALLI_H_ */
diff --git a/src/libs/cdp/cdp_iio.h b/src/libs/cdp/cdp_iio.h
new file mode 100644
index 0000000..19e6513
--- /dev/null
+++ b/src/libs/cdp/cdp_iio.h
@@ -0,0 +1,50 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP Unified IO Interface
+ */
+
+#ifndef LIBS_CDP_CDP_IIO_H_
+#define LIBS_CDP_CDP_IIO_H_
+
+#include "types.h"
+#include "libs/uio.h"
+
+// CDP IO Interface entry points
+typedef struct
+{
+ uio_Stream* (* fopen) (uio_DirHandle *dir, const char *path,
+ const char *mode);
+ int (* fclose) (uio_Stream *stream);
+ size_t (* fread) (void *buf, size_t size, size_t nmemb,
+ uio_Stream *stream);
+ size_t (* fwrite) (const void *buf, size_t size, size_t nmemb,
+ uio_Stream *stream);
+ int (* fseek) (uio_Stream *stream, long offset, int whence);
+ long (* ftell) (uio_Stream *stream);
+ int (* fflush) (uio_Stream *stream);
+ int (* feof) (uio_Stream *stream);
+ int (* ferror) (uio_Stream *stream);
+
+} cdp_Itf_IoVtbl_v1;
+
+// the following are for the sake of module writers
+typedef cdp_Itf_IoVtbl_v1 cdp_Itf_IoVtbl;
+typedef cdp_Itf_IoVtbl cdp_Itf_Io;
+
+#endif /* LIBS_CDP_CDP_IIO_H_ */
diff --git a/src/libs/cdp/cdp_imem.h b/src/libs/cdp/cdp_imem.h
new file mode 100644
index 0000000..7d1b59c
--- /dev/null
+++ b/src/libs/cdp/cdp_imem.h
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP Memory Interface
+ */
+
+#ifndef LIBS_CDP_CDP_IMEM_H_
+#define LIBS_CDP_CDP_IMEM_H_
+
+#include "types.h"
+#include "libs/memlib.h"
+
+// CDP Memory Interface entry points
+typedef struct
+{
+ void* (* malloc) (int size);
+ void (* free) (void *p);
+ void* (* calloc) (int size);
+ void* (* realloc) (void *p, int size);
+
+} cdp_Itf_MemoryVtbl_v1;
+
+// the following are for the sake of module writers
+typedef cdp_Itf_MemoryVtbl_v1 cdp_Itf_MemoryVtbl;
+typedef cdp_Itf_MemoryVtbl cdp_Itf_Memory;
+
+#endif /* LIBS_CDP_CDP_IMEM_H_ */
diff --git a/src/libs/cdp/cdp_isnd.h b/src/libs/cdp/cdp_isnd.h
new file mode 100644
index 0000000..ae4aa94
--- /dev/null
+++ b/src/libs/cdp/cdp_isnd.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP Sound Interface
+ */
+
+#ifndef LIBS_CDP_CDP_ISND_H_
+#define LIBS_CDP_CDP_ISND_H_
+
+#include "types.h"
+#include "libs/sound/sound.h"
+#include "libs/sound/decoders/decoder.h"
+
+// CDP Sound Interface entry points
+typedef struct
+{
+ TFB_RegSoundDecoder* (* RegisterDecoder) (const char* fileext,
+ TFB_SoundDecoderFuncs*);
+ void (* UnregisterDecoder) (TFB_RegSoundDecoder*);
+ const TFB_SoundDecoderFuncs* (* LookupDecoder) (const char* fileext);
+
+} cdp_Itf_SoundVtbl_v1;
+
+// the following are for the sake of module writers
+typedef cdp_Itf_SoundVtbl_v1 cdp_Itf_SoundVtbl;
+typedef cdp_Itf_SoundVtbl cdp_Itf_Sound;
+
+#endif /* LIBS_CDP_CDP_ISND_H_ */
diff --git a/src/libs/cdp/cdp_ivid.h b/src/libs/cdp/cdp_ivid.h
new file mode 100644
index 0000000..a9fc0b2
--- /dev/null
+++ b/src/libs/cdp/cdp_ivid.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP Video Interface
+ */
+
+#ifndef LIBS_CDP_CDP_IVID_H_
+#define LIBS_CDP_CDP_IVID_H_
+
+#include "types.h"
+#include "libs/video/video.h"
+#include "libs/video/videodec.h"
+
+// CDP Video Interface entry points
+typedef struct
+{
+ TFB_RegVideoDecoder* (* RegisterDecoder) (const char* fileext,
+ TFB_VideoDecoderFuncs*);
+ void (* UnregisterDecoder) (TFB_RegVideoDecoder*);
+ const TFB_VideoDecoderFuncs* (* LookupDecoder) (const char* fileext);
+
+} cdp_Itf_VideoVtbl_v1;
+
+// the following are for the sake of module writers
+typedef cdp_Itf_VideoVtbl_v1 cdp_Itf_VideoVtbl;
+typedef cdp_Itf_VideoVtbl cdp_Itf_Video;
+
+#endif /* LIBS_CDP_CDP_IVID_H_ */
diff --git a/src/libs/cdp/cdpapi.c b/src/libs/cdp/cdpapi.c
new file mode 100644
index 0000000..e50e39d
--- /dev/null
+++ b/src/libs/cdp/cdpapi.c
@@ -0,0 +1,864 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP API definitions
+ * the API is used by both the engine and modules
+ */
+
+#include "cdp.h"
+#include "port.h"
+#include "cdpint.h"
+#include "uqmversion.h"
+
+#define MAX_REG_ITFS 255
+#define MAX_REG_EVENTS 1023
+
+static cdp_Error cdp_api_error = CDPERR_NONE;
+
+static uint32 cdp_Host_GetApiVersion (void);
+static uint32 cdp_Host_GetVersion (void);
+static cdp_Error cdp_Host_GetApiError (void);
+static cdp_Itf* cdp_Host_GetItf (const char* name);
+static bool cdp_Host_GetItfs (cdp_ItfDef* defs);
+static cdp_ItfReg* cdp_Host_RegisterItf (const char* name,
+ cdp_ApiVersion ver_from, cdp_ApiVersion ver_to,
+ cdp_Itf*, cdp_Module*);
+static void cdp_Host_UnregisterItf (cdp_ItfReg*);
+static bool cdp_Host_RegisterItfs (cdp_ItfDef* defs, cdp_Module*);
+static void cdp_Host_UnregisterItfs (cdp_ItfDef* defs);
+static cdp_Event cdp_Host_GetEvent (const char* name);
+static bool cdp_Host_GetEvents (cdp_EventDef* defs);
+static cdp_EventReg* cdp_Host_RegisterEvent (const char* name, cdp_Module*);
+static void cdp_Host_UnregisterEvent (cdp_EventReg*);
+static bool cdp_Host_RegisterEvents (cdp_EventDef* defs, cdp_Module*);
+static void cdp_Host_UnregisterEvents (cdp_EventDef* defs);
+static bool cdp_Host_SubscribeEvent (cdp_Event, cdp_EventProc, cdp_Module*);
+static void cdp_Host_UnsubscribeEvent (cdp_Event, cdp_EventProc);
+static bool cdp_Host_SubscribeEvents (cdp_EventDef* defs, cdp_Module*);
+static void cdp_Host_UnsubscribeEvents (cdp_EventDef* defs);
+static cdp_EventResult cdp_Host_FireEvent (cdp_EventReg*, uint32, void*);
+
+// Interfaces
+cdp_Itf_HostVtbl_v1 cdp_host_itf_v1 =
+{
+ cdp_Host_GetApiVersion,
+ cdp_Host_GetVersion,
+ cdp_Host_GetApiError,
+ cdp_Host_GetItf,
+ cdp_Host_GetItfs,
+ cdp_Host_RegisterItf,
+ cdp_Host_UnregisterItf,
+ cdp_Host_RegisterItfs,
+ cdp_Host_UnregisterItfs,
+ cdp_Host_GetEvent,
+ cdp_Host_GetEvents,
+ cdp_Host_RegisterEvent,
+ cdp_Host_UnregisterEvent,
+ cdp_Host_RegisterEvents,
+ cdp_Host_UnregisterEvents,
+ cdp_Host_SubscribeEvent,
+ cdp_Host_UnsubscribeEvent,
+ cdp_Host_SubscribeEvents,
+ cdp_Host_UnsubscribeEvents,
+ cdp_Host_FireEvent,
+};
+
+cdp_Itf_MemoryVtbl_v1 cdp_memory_itf_v1 =
+{
+ HMalloc,
+ HFree,
+ HCalloc,
+ HRealloc,
+};
+
+cdp_Itf_IoVtbl_v1 cdp_io_itf_v1 =
+{
+ uio_fopen,
+ uio_fclose,
+ uio_fread,
+ uio_fwrite,
+ uio_fseek,
+ uio_ftell,
+ uio_fflush,
+ uio_feof,
+ uio_ferror,
+};
+
+cdp_Itf_SoundVtbl_v1 cdp_sound_itf_v1 =
+{
+ SoundDecoder_Register,
+ SoundDecoder_Unregister,
+ SoundDecoder_Lookup,
+};
+
+cdp_Itf_VideoVtbl_v1 cdp_video_itf_v1 =
+{
+ VideoDecoder_Register,
+ VideoDecoder_Unregister,
+ VideoDecoder_Lookup,
+};
+
+// the actual interface registration struct/handle
+struct cdp_ItfReg
+{
+ bool builtin;
+ bool used;
+ const char* name;
+ cdp_ApiVersion ver_from;
+ cdp_ApiVersion ver_to;
+ cdp_Itf* itfvtbl;
+ cdp_Module* module;
+};
+
+#define CDP_DECLARE_ITF(kind,vf,vt,vtbl) \
+ {true, true, CDPITF_KIND_##kind, \
+ CDPAPI_VERSION_##vf, CDPAPI_VERSION_##vt, vtbl, NULL}
+
+// Built-in interfaces + space for loadable
+cdp_ItfReg cdp_itfs[MAX_REG_ITFS + 1] =
+{
+ CDP_DECLARE_ITF (HOST, 1, 1, &cdp_host_itf_v1),
+ CDP_DECLARE_ITF (MEMORY, 1, 1, &cdp_memory_itf_v1),
+ CDP_DECLARE_ITF (IO, 1, 1, &cdp_io_itf_v1),
+ CDP_DECLARE_ITF (SOUND, 1, 1, &cdp_sound_itf_v1),
+ CDP_DECLARE_ITF (VIDEO, 1, 1, &cdp_video_itf_v1),
+ // TODO: put newly defined built-in interfaces here
+
+ {false, false, "", 0, 0, NULL} // term
+};
+
+// event bind descriptor
+typedef struct
+{
+ cdp_EventProc proc;
+ cdp_Module* module;
+
+} cdp_EventBind;
+
+#define EVENT_BIND_GROW 16
+
+// the actual event registration struct/handle
+struct cdp_EventReg
+{
+ bool builtin;
+ bool used;
+ const char* name;
+ cdp_EventBind* binds;
+ uint32 bindslots;
+ cdp_Module* module;
+};
+
+#define CDP_DECLARE_EVENT(name) \
+ {true, true, "UQM." #name, NULL, 0, NULL}
+
+// Built-in events + space for loadable
+// a cdp_Event handle is an index into this array
+cdp_EventReg cdp_evts[MAX_REG_EVENTS + 1] =
+{
+ // sample - no real events defined yet
+ CDP_DECLARE_EVENT (PlanetSide.TouchDown),
+ CDP_DECLARE_EVENT (PlanetSide.LiftOff),
+ // TODO: put newly defined built-in events here
+
+ {false, false, "", NULL, 0, NULL} // term
+};
+
+cdp_Error
+cdp_GetApiError (void)
+{
+ cdp_Error ret = cdp_api_error;
+ cdp_api_error = CDPERR_NONE;
+ return ret;
+}
+
+bool
+cdp_InitApi (void)
+{
+ int i;
+ cdp_Module* kernel;
+
+ // preprocess built-in itfs
+ kernel = cdp_LoadModule (NULL);
+
+ for (i = 0; cdp_itfs[i].builtin; ++i)
+ {
+ cdp_itfs[i].module = kernel;
+ }
+ // clear the rest
+ //memset (cdp_itfs + i, 0,
+ // sizeof (cdp_itfs) - sizeof (cdp_ItfReg) * i);
+
+ for (i = 0; cdp_evts[i].builtin; ++i)
+ {
+ cdp_evts[i].module = kernel;
+ cdp_evts[i].bindslots = 0;
+ cdp_evts[i].binds = NULL;
+ }
+
+ return true;
+}
+
+void
+cdp_UninitApi (void)
+{
+ cdp_ItfReg* itf;
+
+ // unregister custom interfaces
+ for (itf = cdp_itfs; itf->used; ++itf)
+ {
+ if (!itf->builtin)
+ {
+ itf->used = false;
+ if (itf->name)
+ HFree (itf->name);
+ itf->name = NULL;
+ itf->itfvtbl = NULL;
+ itf->module = NULL;
+ }
+ }
+}
+
+static uint32
+cdp_Host_GetApiVersion (void)
+{
+ return CDPAPI_VERSION;
+}
+
+static uint32
+cdp_Host_GetVersion (void)
+{
+ return (UQM_MAJOR_VERSION << 20) | (UQM_MINOR_VERSION << 15) |
+ UQM_PATCH_VERSION;
+}
+
+static cdp_Error
+cdp_Host_GetApiError (void)
+{
+ return cdp_GetApiError ();
+}
+
+static char*
+cdp_MakeContextName (const char* ctx, const char* name)
+{
+ int namelen;
+ char* id_name;
+
+ namelen = strlen(ctx) + strlen(name) + 2;
+ id_name = HMalloc (namelen);
+ strcpy(id_name, ctx);
+ strcat(id_name, ".");
+ strcat(id_name, name);
+
+ return id_name;
+}
+
+/***********************************************************
+ * Interface system *
+ ***********************************************************/
+
+cdp_ItfReg*
+cdp_GetInterfaceReg (const char* name, cdp_ApiVersion api_ver)
+{
+ cdp_ItfReg* itf;
+
+ for (itf = cdp_itfs; itf->used &&
+ (!itf->name || strcasecmp(itf->name, name) != 0 ||
+ api_ver < itf->ver_from || api_ver > itf->ver_to);
+ itf++)
+ ;
+ if (!itf->name)
+ {
+ cdp_api_error = CDPERR_NO_ITF;
+ return NULL;
+ }
+
+ return itf;
+}
+
+cdp_Itf*
+cdp_GetInterface (const char* name, cdp_ApiVersion api_ver)
+{
+ cdp_ItfReg* reg;
+
+ reg = cdp_GetInterfaceReg (name, api_ver);
+ return reg ? reg->itfvtbl : NULL;
+}
+
+static cdp_Itf*
+cdp_Host_GetItf (const char* name)
+{
+ return cdp_GetInterface (name, CDPAPI_VERSION_1);
+}
+
+static bool
+cdp_Host_GetItfs (cdp_ItfDef* defs)
+{
+ cdp_ItfDef* def;
+ cdp_ItfReg* reg;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ // registration handle is not returned
+ def->reg = NULL;
+
+ reg = cdp_GetInterfaceReg (def->name, CDPAPI_VERSION_1);
+ if (reg)
+ {
+ def->itf = reg->itfvtbl;
+ def->name = reg->name; // set to cannonical name
+ def->ver_from = reg->ver_from;
+ def->ver_to = reg->ver_to;
+ def->module = reg->module;
+ }
+ else
+ {
+ def->itf = NULL;
+ def->module = NULL;
+ def->ver_from = 0;
+ def->ver_to = 0;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static cdp_ItfReg*
+cdp_Host_RegisterItf (const char* name, cdp_ApiVersion ver_from,
+ cdp_ApiVersion ver_to, cdp_Itf* itfvtbl,
+ cdp_Module* owner)
+{
+ cdp_ItfReg* itfreg;
+ cdp_ItfReg* newslot = NULL;
+ char* id_name;
+ const char* ctx;
+
+ if (!owner)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "No owner info supplied\n");
+ //return NULL;
+ }
+ if (!name || !*name || !itfvtbl)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Null or invalid interface (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+ ctx = cdp_GetModuleContext (owner, false);
+ if (!ctx)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Null or invalid context (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+
+ // TODO: review version policy (below)
+ // enforce version policy and do not allow obsolete interfaces
+ // POLICY: all modules MUST be aware of recent API changes and will not
+ // be allowed to expose interfaces that support and/or utilize obsoleted
+ // API versions
+ if (ver_from < CDPAPI_VERSION_MIN)
+ ver_from = CDPAPI_VERSION_MIN;
+ if (ver_to < CDPAPI_VERSION_MIN)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Obsolete interface %s (from %s)\n",
+ name, cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+
+ id_name = cdp_MakeContextName (ctx, name);
+
+ // check if interface already registered
+ for (itfreg = cdp_itfs; itfreg->used &&
+ (!itfreg->name || strcasecmp(itfreg->name, id_name) != 0 ||
+ ver_from < itfreg->ver_from || ver_to > itfreg->ver_to);
+ ++itfreg)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !itfreg->name)
+ newslot = itfreg;
+ }
+
+ if (itfreg >= cdp_itfs + MAX_REG_ITFS)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Interfaces limit reached\n");
+ HFree (id_name);
+ return NULL;
+ }
+ else if (itfreg->name)
+ {
+ fprintf (stderr, "cdp_Host_RegisterItf(): "
+ "Interface %s already registered for these versions, "
+ "%s denied\n",
+ name, cdp_GetModuleName (owner, true));
+ HFree (id_name);
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = itfreg;
+ newslot->used = true;
+ // make next one a term
+ itfreg[1].builtin = false;
+ itfreg[1].used = false;
+ itfreg[1].name = NULL;
+ itfreg[1].itfvtbl = NULL;
+ }
+
+ newslot->name = id_name;
+ newslot->ver_from = ver_from;
+ newslot->ver_to = ver_to;
+ newslot->itfvtbl = itfvtbl;
+ newslot->module = owner;
+
+ return newslot;
+}
+
+static void
+cdp_Host_UnregisterItf (cdp_ItfReg* itfreg)
+{
+ if (itfreg < cdp_itfs || itfreg >= cdp_itfs + MAX_REG_ITFS ||
+ !itfreg->name || !itfreg->itfvtbl)
+ {
+ fprintf (stderr, "cdp_Host_UnregisterItf(): "
+ "Invalid or expired interface passed\n");
+ return;
+ }
+
+ if (!itfreg->builtin)
+ {
+ HFree (itfreg->name);
+ }
+ itfreg->module = NULL;
+ itfreg->name = NULL;
+ itfreg->itfvtbl = NULL;
+}
+
+static bool
+cdp_Host_RegisterItfs (cdp_ItfDef* defs, cdp_Module* owner)
+{
+ cdp_ItfDef* def;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ def->reg = cdp_Host_RegisterItf (def->name, def->ver_from,
+ def->ver_to, def->itf, owner);
+ if (def->reg)
+ {
+ def->module = owner;
+ }
+ else
+ {
+ def->module = NULL;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static void
+cdp_Host_UnregisterItfs (cdp_ItfDef* defs)
+{
+ cdp_ItfDef* def;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->reg)
+ cdp_Host_UnregisterItf (def->reg);
+ }
+}
+
+/***********************************************************
+ * Event system *
+ ***********************************************************/
+
+cdp_EventReg*
+cdp_GetEventReg (const char* name)
+{
+ cdp_EventReg* evt;
+
+ for (evt = cdp_evts; evt->used &&
+ (!evt->name || strcasecmp(evt->name, name) != 0);
+ evt++)
+ ;
+ if (!evt->name)
+ {
+ cdp_api_error = CDPERR_NO_EVENT;
+ return NULL;
+ }
+
+ return evt;
+}
+
+// hopefully inlinable
+static cdp_Event
+cdp_EventFromReg (cdp_EventReg* reg)
+{
+ return (reg - cdp_evts) / sizeof (cdp_EventReg);
+}
+
+// hopefully inlinable
+static cdp_EventReg*
+cdp_RegFromEvent (cdp_Event event)
+{
+ return cdp_evts + event;
+}
+
+cdp_Event
+cdp_GetEvent (const char* name)
+{
+ cdp_EventReg* reg;
+
+ reg = cdp_GetEventReg (name);
+ return reg ? cdp_EventFromReg (reg) : CDP_EVENT_INVALID;
+}
+
+static cdp_EventBind*
+cdp_AllocEventBinds (cdp_EventBind* binds, uint32 ccur, uint32 cnew)
+{
+ cdp_EventBind* newbinds;
+ uint32 newsize;
+
+ newsize = cnew * sizeof (cdp_EventBind);
+ if (binds)
+ newbinds = HRealloc (binds, newsize);
+ else
+ newbinds = HMalloc (newsize);
+
+ if (cnew > ccur)
+ memset (newbinds + ccur, 0,
+ (cnew - ccur) * sizeof (cdp_EventBind));
+
+ return newbinds;
+}
+
+static cdp_Event
+cdp_Host_GetEvent (const char* name)
+{
+ return cdp_GetEvent (name);
+}
+
+static bool
+cdp_Host_GetEvents (cdp_EventDef* defs)
+{
+ cdp_EventDef* def;
+ cdp_EventReg* reg;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ // registration handle is not returned
+ def->reg = NULL;
+
+ reg = cdp_GetEventReg (def->name);
+ if (reg)
+ {
+ def->event = cdp_EventFromReg(reg);
+ def->name = reg->name; // set to cannonical name
+ def->module = reg->module;
+ }
+ else
+ {
+ def->event = CDP_EVENT_INVALID;
+ def->module = NULL;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static cdp_EventReg*
+cdp_Host_RegisterEvent (const char* name, cdp_Module* owner)
+{
+ cdp_EventReg* evtreg;
+ cdp_EventReg* newslot = NULL;
+ char* id_name;
+ const char* ctx;
+
+ if (!owner)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "No owner info supplied\n");
+ //return NULL;
+ }
+ if (!name || !*name)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Null or invalid event (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+ ctx = cdp_GetModuleContext (owner, false);
+ if (!ctx)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Null or invalid context (from %s)\n",
+ cdp_GetModuleName (owner, true));
+ return NULL;
+ }
+
+ id_name = cdp_MakeContextName (ctx, name);
+
+ // check if event already registered
+ for (evtreg = cdp_evts; evtreg->used &&
+ (!evtreg->name || strcasecmp(evtreg->name, id_name) != 0);
+ ++evtreg)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !evtreg->name)
+ newslot = evtreg;
+ }
+
+ if (evtreg >= cdp_evts + MAX_REG_EVENTS)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Event limit reached\n");
+ HFree (id_name);
+ return NULL;
+ }
+ else if (evtreg->name)
+ {
+ fprintf (stderr, "cdp_Host_RegisterEvent(): "
+ "Event %s already registered, "
+ "%s denied\n",
+ name, cdp_GetModuleName (owner, true));
+ HFree (id_name);
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = evtreg;
+ newslot->used = true;
+ // make next one a term
+ evtreg[1].builtin = false;
+ evtreg[1].used = false;
+ evtreg[1].name = NULL;
+ }
+
+ newslot->name = id_name;
+ newslot->module = owner;
+ newslot->binds = NULL;
+ newslot->bindslots = 0;
+
+ return newslot;
+}
+
+static void
+cdp_Host_UnregisterEvent (cdp_EventReg* evtreg)
+{
+ if (evtreg < cdp_evts || evtreg >= cdp_evts + MAX_REG_EVENTS ||
+ !evtreg->name)
+ {
+ fprintf (stderr, "cdp_Host_UnregisterEvent(): "
+ "Invalid or expired event passed\n");
+ return;
+ }
+
+ if (!evtreg->builtin)
+ {
+ HFree (evtreg->name);
+ }
+ evtreg->module = NULL;
+ evtreg->name = NULL;
+ if (evtreg->binds)
+ HFree (evtreg->binds);
+ evtreg->binds = NULL;
+ evtreg->bindslots = 0;
+}
+
+static bool
+cdp_Host_RegisterEvents (cdp_EventDef* defs, cdp_Module* owner)
+{
+ cdp_EventDef* def;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ def->reg = cdp_Host_RegisterEvent (def->name, owner);
+ if (def->reg)
+ {
+ def->module = owner;
+ }
+ else
+ {
+ def->module = NULL;
+ ++errors;
+ }
+ }
+
+ return !errors;
+}
+
+static void
+cdp_Host_UnregisterEvents (cdp_EventDef* defs)
+{
+ cdp_EventDef* def;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->reg)
+ cdp_Host_UnregisterEvent (def->reg);
+ }
+}
+
+static bool
+cdp_Host_SubscribeEvent (cdp_Event event, cdp_EventProc proc, cdp_Module* module)
+{
+ cdp_EventReg* reg = cdp_RegFromEvent (event);
+ cdp_EventBind* bind = NULL;
+ uint32 i;
+
+ if (reg < cdp_evts || reg >= cdp_evts + MAX_REG_EVENTS ||
+ !reg->name)
+ {
+ fprintf (stderr, "cdp_Host_SubscribeEvent(): "
+ "Invalid or expired event passed\n");
+ return false;
+ }
+
+ if (reg->binds)
+ {
+ // check for duplicate or find a new slot
+ for (i = 0, bind = reg->binds; i < reg->bindslots &&
+ (!bind->proc || bind->proc != proc);
+ ++i, ++bind)
+ ;
+ if (i >= reg->bindslots)
+ { // full - add more slots
+ reg->binds = cdp_AllocEventBinds (reg->binds,
+ reg->bindslots, reg->bindslots + EVENT_BIND_GROW);
+ bind = reg->binds + reg->bindslots;
+ reg->bindslots += EVENT_BIND_GROW;
+ }
+ else if (bind->proc == proc)
+ { // already bound
+ return true;
+ }
+ }
+ else
+ {
+ reg->binds = cdp_AllocEventBinds (NULL, 0, EVENT_BIND_GROW);
+ reg->bindslots = EVENT_BIND_GROW;
+ bind = reg->binds;
+ }
+
+ bind->proc = proc;
+ bind->module = module;
+
+ return true;
+}
+
+static void
+cdp_Host_UnsubscribeEvent (cdp_Event event, cdp_EventProc proc)
+{
+ cdp_EventReg* reg = cdp_RegFromEvent (event);
+ cdp_EventBind* bind = NULL;
+ uint32 i;
+
+ if (reg < cdp_evts || reg >= cdp_evts + MAX_REG_EVENTS ||
+ !reg->name)
+ { // event either expired or invalid
+ return;
+ }
+
+ if (!reg->binds || !reg->bindslots)
+ return; // hmm, no bindings
+
+ // check for duplicate or find a new slot
+ for (i = 0, bind = reg->binds; i < reg->bindslots &&
+ bind->proc != proc;
+ ++i, ++bind)
+ ;
+ if (i >= reg->bindslots)
+ return; // binding not found
+
+ bind->proc = NULL;
+ bind->module = NULL;
+}
+
+static bool
+cdp_Host_SubscribeEvents (cdp_EventDef* defs, cdp_Module* module)
+{
+ cdp_EventDef* def;
+ int errors = 0;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->event != CDP_EVENT_INVALID && def->proc)
+ if (!cdp_Host_SubscribeEvent (def->event, def->proc, module))
+ ++errors;
+ }
+ return !errors;
+}
+
+static void
+cdp_Host_UnsubscribeEvents (cdp_EventDef* defs)
+{
+ cdp_EventDef* def;
+
+ for (def = defs; def->name; ++def)
+ {
+ if (def->event != CDP_EVENT_INVALID && def->proc)
+ cdp_Host_UnsubscribeEvent (def->event, def->proc);
+ }
+}
+
+static cdp_EventResult
+cdp_Host_FireEvent (cdp_EventReg* evtreg, uint32 iparam, void* pparam)
+{
+ bool bHandled = false;
+ cdp_EventResult ret = 0;
+ cdp_Event event;
+ cdp_EventBind* bind;
+ uint32 i;
+
+ if (evtreg < cdp_evts || evtreg >= cdp_evts + MAX_REG_EVENTS ||
+ !evtreg->name)
+ {
+#ifdef DEBUG
+ fprintf (stderr, "cdp_Host_FireEvent(): Invalid event\n");
+#endif
+ return 0;
+ }
+
+ if (!evtreg->binds)
+ return 0; // no subscribers
+
+ event = cdp_EventFromReg (evtreg);
+
+ // call event procs in opposite order of binding
+ for (i = evtreg->bindslots, bind = evtreg->binds + i - 1;
+ !bHandled && i > 0;
+ --i, --bind)
+ {
+ if (bind->proc)
+ ret = bind->proc (event, iparam, pparam, &bHandled);
+ }
+ return ret;
+}
diff --git a/src/libs/cdp/cdpapi.h b/src/libs/cdp/cdpapi.h
new file mode 100644
index 0000000..e1a0e0b
--- /dev/null
+++ b/src/libs/cdp/cdpapi.h
@@ -0,0 +1,154 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP API declarations
+ * the API is used by both the engine and modules
+ */
+
+#ifndef LIBS_CDP_CDPAPI_H_
+#define LIBS_CDP_CDPAPI_H_
+
+#include "types.h"
+
+typedef enum
+{
+ CDPAPI_VERSION_1 = 0x00000001, // version 0.1
+
+ CDPAPI_VERSION = CDPAPI_VERSION_1,
+ CDPAPI_VERSION_MIN = CDPAPI_VERSION_1,
+
+} cdp_ApiVersion;
+
+typedef enum
+{
+ CDPERR_NONE = 0,
+ CDPERR_UKNOWN = 1,
+ CDPERR_NOT_FOUND = 2,
+ CDPERR_BAD_MODULE = 3,
+ CDPERR_OLD_VER = 4,
+ CDPERR_UNKNOWN_VER = 5,
+ CDPERR_TOO_MANY = 6,
+ CDPERR_INIT_FAILED = 7,
+ CDPERR_NO_ITF = 8,
+ CDPERR_DUPE_ITF = 9,
+ CDPERR_NO_EVENT = 10,
+ CDPERR_DUPE_EVENT = 11,
+ CDPERR_OTHER = 1000,
+
+} cdp_Error;
+
+typedef struct cdp_Module cdp_Module;
+typedef void cdp_Itf;
+typedef struct cdp_ItfReg cdp_ItfReg;
+
+// Interface KINDs - for convinience and uniformity
+#define CDPITF_KIND_INVALID NULL
+#define CDPITF_KIND_HOST "UQM.Host"
+#define CDPITF_KIND_MEMORY "UQM.Memory"
+#define CDPITF_KIND_IO "UQM.IO"
+#define CDPITF_KIND_THREADS "UQM.Threads"
+#define CDPITF_KIND_TIME "UQM.Time"
+#define CDPITF_KIND_INPUT "UQM.Input"
+#define CDPITF_KIND_TASK "UQM.Task"
+#define CDPITF_KIND_RESOURCE "UQM.Resource"
+#define CDPITF_KIND_SOUND "UQM.Sound"
+#define CDPITF_KIND_VIDEO "UQM.Video"
+#define CDPITF_KIND_GFX "UQM.Gfx"
+#define CDPITF_KIND_MIXER "UQM.Mixer"
+
+// Interface definition structure
+// pass an array of these to Host->GetItfs() for batch lookup
+// pass an array of these to Host->RegisterItfs() for batch registration
+typedef struct
+{
+ // fill in the first 4 members for batch registration
+ // fill in the 1st member for batch lookup
+ // terminate an array of these defs with name == NULL
+ const char* name; // interface ID
+ cdp_Itf* itf; // interface pointer
+ cdp_ApiVersion ver_from; // lowest supported version
+ cdp_ApiVersion ver_to; // highest supported version
+
+ cdp_Module* module; // owner module
+ // the following member is only set during registration
+ cdp_ItfReg* reg; // registration handle (not set on lookup)
+
+} cdp_ItfDef;
+
+typedef unsigned int cdp_Event;
+typedef struct cdp_EventReg cdp_EventReg;
+typedef intptr_t cdp_EventResult;
+
+#define CDP_EVENT_INVALID (-1)
+ // used with cdp_Event
+
+typedef cdp_EventResult (* cdp_EventProc)
+ (cdp_Event, uint32, void*, bool* pbHandled);
+
+// Event definition structure
+// pass an array of these to Host->GetItfs() for batch lookup
+typedef struct
+{
+ // fill in the 1st member for batch lookup or registration
+ // also fill in the 2nd member for batch subscription
+ // terminate an array of these defs with name == NULL
+ const char* name; // event ID
+ cdp_EventProc proc; // event proc, set to NULL for no bind
+
+ cdp_Event event; // subscribable event handle
+ cdp_Module* module; // owner module
+ // the following member is only set during registration
+ cdp_EventReg* reg; // registration handle (not set on lookup)
+
+} cdp_EventDef;
+
+// Host Interface
+// the main itf of the API, it is passed to a loaded module
+// module does everything else through this itf and itfs
+// acquired through this itf
+typedef struct
+{
+ uint32 (* GetApiVersion) (void);
+ uint32 (* GetVersion) (void);
+ cdp_Error (* GetApiError) (void);
+ cdp_Itf* (* GetItf) (const char* name);
+ bool (* GetItfs) (cdp_ItfDef* defs);
+ cdp_ItfReg* (* RegisterItf) (const char* name,
+ cdp_ApiVersion ver_from, cdp_ApiVersion ver_to,
+ cdp_Itf*, cdp_Module*);
+ void (* UnregisterItf) (cdp_ItfReg*);
+ bool (* RegisterItfs) (cdp_ItfDef* defs, cdp_Module*);
+ void (* UnregisterItfs) (cdp_ItfDef* defs);
+ cdp_Event (* GetEvent) (const char* name);
+ bool (* GetEvents) (cdp_EventDef* defs);
+ cdp_EventReg* (* RegisterEvent) (const char* name, cdp_Module*);
+ void (* UnregisterEvent) (cdp_EventReg*);
+ bool (* RegisterEvents) (cdp_EventDef* defs, cdp_Module*);
+ void (* UnregisterEvents) (cdp_EventDef* defs);
+ bool (* SubscribeEvent) (cdp_Event, cdp_EventProc, cdp_Module*);
+ void (* UnsubscribeEvent) (cdp_Event, cdp_EventProc);
+ bool (* SubscribeEvents) (cdp_EventDef* defs, cdp_Module*);
+ void (* UnsubscribeEvents) (cdp_EventDef* defs);
+ cdp_EventResult (* FireEvent) (cdp_EventReg*, uint32, void*);
+
+} cdp_Itf_HostVtbl_v1;
+
+typedef cdp_Itf_HostVtbl_v1 cdp_Itf_HostVtbl;
+typedef cdp_Itf_HostVtbl cdp_Itf_Host;
+
+#endif /* LIBS_CDP_CDPAPI_H_ */
diff --git a/src/libs/cdp/cdpint.h b/src/libs/cdp/cdpint.h
new file mode 100644
index 0000000..fbc3238
--- /dev/null
+++ b/src/libs/cdp/cdpint.h
@@ -0,0 +1,48 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP common internal definitions
+ */
+
+#ifndef LIBS_CDP_CDPINT_H_
+#define LIBS_CDP_CDPINT_H_
+
+#include "cdpapi.h"
+#include "cdp_imem.h"
+#include "cdp_iio.h"
+#include "cdp_isnd.h"
+#include "cdp_ivid.h"
+
+#ifdef WIN32
+# define CDPEXT ".dll"
+#else
+# define CDPEXT ".so"
+#endif
+
+extern cdp_Itf_HostVtbl_v1 cdp_host_itf_v1;
+extern cdp_Itf_MemoryVtbl_v1 cdp_memory_itf_v1;
+extern cdp_Itf_IoVtbl_v1 cdp_io_itf_v1;
+extern cdp_Itf_SoundVtbl_v1 cdp_sound_itf_v1;
+
+bool cdp_InitApi (void);
+void cdp_UninitApi (void);
+cdp_Error cdp_GetApiError (void);
+cdp_Itf* cdp_GetInterface (const char* name, cdp_ApiVersion);
+cdp_ItfReg* cdp_GetInterfaceReg (const char* name, cdp_ApiVersion);
+
+#endif /* _CDPISND_H */
diff --git a/src/libs/cdp/cdpmod.h b/src/libs/cdp/cdpmod.h
new file mode 100644
index 0000000..a1fdcae
--- /dev/null
+++ b/src/libs/cdp/cdpmod.h
@@ -0,0 +1,92 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP module definitions
+ * all CDP modules should #include this .h
+ */
+
+#ifndef LIBS_CDP_CDPMOD_H_
+#define LIBS_CDP_CDPMOD_H_
+
+#include "types.h"
+#include "cdpapi.h"
+
+#define CDP_INFO_SYM cdpmodinfo
+#define CDP_INFO_SYM_NAME "cdpmodinfo"
+
+// this struct will be exported from the module
+// under 'cdpmodinfo'
+typedef struct
+{
+ // mandatory, version control
+ uint32 size; // size of this structure
+ cdp_ApiVersion api_ver; // version of cdp API used, set to CDPAPI_VERSION
+ uint16 ver_major; // module version, somewhat informational
+ uint16 ver_minor;
+ uint16 ver_patch;
+ uint16 host_ver_major; // minimum host version required, purely informational
+ uint16 host_ver_minor;
+ uint16 host_ver_patch;
+
+ // reserved members: set all to 0 or use CDP_MODINFO_RESERVED1
+ uint32 _32_reserved1;
+ uint32 _32_reserved2;
+ uint32 _32_reserved3;
+ uint32 _32_reserved4;
+
+ const char* context_name;
+ // cannonical context name (in proper case)
+ // this context will be used with all exposed objects
+ // English preferred; try to keep it below 32 chars
+ // informational, human-only; these fields have no real size
+ // restriction other than to keep it reasonable
+ const char* name; // descriptive name
+ const char* ver_string; // descriptive version
+ const char* author; // go nuts
+ const char* url; // go nuts
+ const char* comments; // go nuts
+
+ // reserved members, set all to 0 or use CDP_MODINFO_RESERVED2
+ const char* _sz_reserved1;
+ const char* _sz_reserved2;
+ const char* _sz_reserved3;
+ const char* _sz_reserved4;
+
+ // mandatory, CDP entry points
+ // TODO: decide whether more EPs are necessary and if not move
+ // EPs above info-string members, abolishing _sz_reservedX
+ bool (* module_init) (cdp_Module* module, cdp_Itf_Host* hostitf);
+ void (* module_term) ();
+
+} cdp_ModuleInfo;
+
+#define CDP_MODINFO_RESERVED1 0,0,0,0
+#define CDP_MODINFO_RESERVED2 0,0,0,0
+
+// the following is defined via the last mandatory member
+#define CDP_MODINFO_MIN_SIZE \
+ ( ((uint32) &((cdp_ModuleInfo*)0)->module_term) + \
+ sizeof (((cdp_ModuleInfo*)0)->module_term) )
+
+#if defined(WIN32)
+# define CDPEXPORT __declspec(dllexport)
+#else
+# define CDPEXPORT
+#endif
+
+#endif /* LIBS_CDP_CDPMOD_H_ */
diff --git a/src/libs/cdp/windl.c b/src/libs/cdp/windl.c
new file mode 100644
index 0000000..4bc76f6
--- /dev/null
+++ b/src/libs/cdp/windl.c
@@ -0,0 +1,76 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP dlopen() & Co. WIN32 implementation
+ */
+
+#include "windl.h"
+#include "port.h"
+#define WIN32_LEAN_AND_MEAN
+//#include <windows.h>
+#include <wtypes.h>
+#include <winbase.h>
+#include <stdio.h>
+
+static uint32 wdl_last_error = 0;
+static char wdl_errstr[128] = "";
+
+void*
+dlopen (const char *filename, int flag)
+ // all defined flags are not possible on win32
+{
+ HMODULE hlib;
+
+ if (filename == NULL)
+ hlib = GetModuleHandleA(NULL);
+ else
+ hlib = LoadLibraryA (filename);
+
+ if (!hlib)
+ wdl_last_error = GetLastError ();
+
+ return hlib;
+}
+
+void*
+dlsym (void *handle, const char *symbol)
+{
+ void* ptr = GetProcAddress (handle, symbol);
+ if (!ptr)
+ wdl_last_error = GetLastError ();
+ return ptr;
+}
+
+int
+dlclose (void *handle)
+{
+ return FreeLibrary (handle);
+}
+
+char*
+dlerror (void)
+{
+ if (wdl_last_error)
+ {
+ sprintf (wdl_errstr, "Windows error %u", wdl_last_error);
+ wdl_last_error = 0;
+ return wdl_errstr;
+ }
+ else
+ return NULL;
+}
diff --git a/src/libs/cdp/windl.h b/src/libs/cdp/windl.h
new file mode 100644
index 0000000..71ca240
--- /dev/null
+++ b/src/libs/cdp/windl.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*
+ * CDP dlopen() & Co. WIN32 implementation
+ */
+
+#ifndef LIBS_CDP_WINDL_H_
+#define LIBS_CDP_WINDL_H_
+
+#include "types.h"
+
+extern void *dlopen (const char *filename, int flag);
+extern void *dlsym (void *handle, const char *symbol);
+extern int dlclose (void *handle);
+extern char *dlerror (void);
+
+/* these dlopen() flags are meaningless on win32 */
+#define RTLD_LAZY 1 /* lazy function call binding */
+#define RTLD_NOW 2 /* immediate function call binding */
+#define RTLD_GLOBAL 4 /* symbols in this dlopen'ed obj are visible to other dlopen'ed objs */
+
+#endif /* LIBS_CDP_WINDL_H_ */
diff --git a/src/libs/cdplib.h b/src/libs/cdplib.h
new file mode 100644
index 0000000..83d6a69
--- /dev/null
+++ b/src/libs/cdplib.h
@@ -0,0 +1,32 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_CDPLIB_H_
+#define LIBS_CDPLIB_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "cdp/cdp.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_CDPLIB_H_ */
diff --git a/src/libs/compiler.h b/src/libs/compiler.h
new file mode 100644
index 0000000..a53f779
--- /dev/null
+++ b/src/libs/compiler.h
@@ -0,0 +1,96 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_COMPILER_H_
+#define LIBS_COMPILER_H_
+
+#include "types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef uint8 BYTE;
+typedef uint8 UBYTE;
+typedef sint8 SBYTE;
+typedef uint16 UWORD;
+typedef sint16 SWORD;
+typedef uint32 DWORD;
+typedef sint32 SDWORD;
+
+typedef UWORD COUNT;
+typedef SWORD SIZE;
+
+typedef char UNICODE;
+
+
+typedef enum
+{
+ FALSE = 0,
+ TRUE
+} BOOLEAN;
+
+typedef void (*PVOIDFUNC) (void);
+typedef BOOLEAN (*PBOOLFUNC) (void);
+typedef BYTE (*PBYTEFUNC) (void);
+typedef UWORD (*PUWORDFUNC) (void);
+typedef SWORD (*PSWORDFUNC) (void);
+typedef DWORD (*PDWORDFUNC) (void);
+
+#define MAKE_BYTE(lo, hi) ((BYTE) (((BYTE) (hi) << (BYTE) 4) | (BYTE) (lo)))
+#define LONIBBLE(x) ((BYTE) ((BYTE) (x) & (BYTE) 0x0F))
+#define HINIBBLE(x) ((BYTE) ((BYTE) (x) >> (BYTE) 4))
+#define MAKE_WORD(lo, hi) ((UWORD) ((BYTE) (hi) << 8) | (BYTE) (lo))
+#define LOBYTE(x) ((BYTE) ((UWORD) (x)))
+#define HIBYTE(x) ((BYTE) ((UWORD) (x) >> 8))
+#define MAKE_DWORD(lo, hi) (((DWORD) (hi) << 16) | (UWORD) (lo))
+#define LOWORD(x) ((UWORD) ((DWORD) (x)))
+#define HIWORD(x) ((UWORD) ((DWORD) (x) >> 16))
+
+
+// To be moved to port.h:
+// _ALIGNED_ANY specifies an alignment suitable for any type
+// _ALIGNED_ON specifies a caller-supplied alignment (should be a power of 2)
+#if defined(__GNUC__)
+# define _PACKED __attribute__((packed))
+# define _ALIGNED_ANY __attribute__((aligned))
+# define _ALIGNED_ON(bytes) __attribute__((aligned(bytes)))
+#elif defined(_MSC_VER)
+# define _ALIGNED_ANY
+//# define _ALIGNED_ON(bytes) __declspec(align(bytes))
+ // __declspec(align(bytes)) expects a constant. 'sizeof (type)'
+ // will not do. This is something that needs some attention,
+ // once we find someone with a 64 bits Windows machine.
+ // Leaving it alone for now.
+# define _PACKED
+# define _ALIGNED_ON(bytes)
+#elif defined(__ARMCC__)
+# define _PACKED __attribute__((packed))
+# define _ALIGNED_ANY __attribute__((aligned))
+# define _ALIGNED_ON(bytes) __attribute__((aligned(bytes)))
+#elif defined(__WINSCW__)
+# define _PACKED
+# define _ALIGNED_ANY
+# define _ALIGNED_ON(bytes)
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_COMPILER_H_ */
diff --git a/src/libs/declib.h b/src/libs/declib.h
new file mode 100644
index 0000000..80d767c
--- /dev/null
+++ b/src/libs/declib.h
@@ -0,0 +1,57 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_DECLIB_H_
+#define LIBS_DECLIB_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct _LZHCODE_DESC* DECODE_REF;
+
+enum
+{
+ FILE_STREAM = 0,
+ MEMORY_STREAM
+};
+typedef BYTE STREAM_TYPE;
+
+enum
+{
+ STREAM_READ = 0,
+ STREAM_WRITE
+};
+typedef BYTE STREAM_MODE;
+
+extern DECODE_REF copen (void *InStream, STREAM_TYPE SType,
+ STREAM_MODE SMode);
+extern DWORD cclose (DECODE_REF DecodeRef);
+extern void cfilelength (DECODE_REF DecodeRef, DWORD *pfilelen);
+extern COUNT cread (void *pStr, COUNT size, COUNT count,
+ DECODE_REF DecodeRef);
+extern COUNT cwrite (const void *pStr, COUNT size, COUNT count,
+ DECODE_REF DecodeRef);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_DECLIB_H_ */
diff --git a/src/libs/decomp/Makeinfo b/src/libs/decomp/Makeinfo
new file mode 100644
index 0000000..699a24e
--- /dev/null
+++ b/src/libs/decomp/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="lzdecode.c lzencode.c update.c"
+uqm_HFILES="lzh.h"
diff --git a/src/libs/decomp/lzdecode.c b/src/libs/decomp/lzdecode.c
new file mode 100644
index 0000000..3b64a90
--- /dev/null
+++ b/src/libs/decomp/lzdecode.c
@@ -0,0 +1,415 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+ /*
+ * LZHUF.C English version 1.0
+ * Based on Japanese version 29-NOV-1988
+ * LZSS coded by Haruhiko OKUMURA
+ * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
+ * Edited and translated to English by Kenji RIKITAKE
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "lzh.h"
+#include "libs/reslib.h"
+
+PLZHCODE_DESC _lpCurCodeDesc;
+STREAM_TYPE _StreamType;
+BYTE* _Stream;
+UWORD _workbuf;
+BYTE _workbuflen;
+
+/* get one bit */
+static SWORD
+GetBit (void)
+{
+ SWORD i;
+
+ while (_workbuflen <= 8)
+ {
+ if ((i = InChar ()) < 0)
+ i = 0;
+ _workbuf |= i << (8 - _workbuflen);
+ _workbuflen += 8;
+ }
+ i = (_workbuf & 0xFFFF) >> (16 - 1);
+ _workbuf = (_workbuf << 1) & 0xFFFF;
+ _workbuflen--;
+
+ return (i);
+}
+
+static UWORD
+GetBits (BYTE num_bits)
+{
+ SWORD i;
+
+ while (_workbuflen <= 8)
+ {
+ if ((i = InChar ()) < 0)
+ i = 0;
+ _workbuf |= i << (8 - _workbuflen);
+ _workbuflen += 8;
+ }
+ i = (_workbuf & 0xFFFF) >> (16 - num_bits);
+ _workbuf = (_workbuf << num_bits) & 0xFFFF;
+ _workbuflen -= num_bits;
+
+ return (i);
+}
+
+/* initialize freq tree */
+
+void
+StartHuff (void)
+{
+ COUNT i, j;
+
+ for (i = 0; i < N_CHAR; i++)
+ {
+ _lpCurCodeDesc->freq[i] = 1;
+ _lpCurCodeDesc->son[i] = i + T;
+ _lpCurCodeDesc->prnt[i + T] = i;
+ }
+ i = 0; j = N_CHAR;
+ while (j <= R)
+ {
+ _lpCurCodeDesc->freq[j] = _lpCurCodeDesc->freq[i] + _lpCurCodeDesc->freq[i + 1];
+ _lpCurCodeDesc->son[j] = i;
+ _lpCurCodeDesc->prnt[i] = _lpCurCodeDesc->prnt[i + 1] = j;
+ i += 2; j++;
+ }
+ _lpCurCodeDesc->freq[T] = 0xffff;
+ _lpCurCodeDesc->prnt[R] = 0;
+}
+
+DECODE_REF
+copen (void *InStream, STREAM_TYPE SType, STREAM_MODE SMode)
+{
+ DWORD StreamLength;
+
+ _StreamType = SType;
+ _Stream = InStream;
+ if (SMode == STREAM_WRITE) /* writing */
+ {
+ OutChar (0); /* skip future StreamLength */
+ OutChar (0);
+ OutChar (0);
+ OutChar (0);
+
+ StreamLength = 0;
+ }
+ else /* reading */
+ {
+ BYTE lobyte, hibyte;
+ UWORD loword, hiword;
+
+ lobyte = (BYTE)InChar ();
+ hibyte = (BYTE)InChar ();
+ loword = MAKE_WORD (lobyte, hibyte);
+ lobyte = (BYTE)InChar ();
+ hibyte = (BYTE)InChar ();
+ hiword = MAKE_WORD (lobyte, hibyte);
+
+ StreamLength = MAKE_DWORD (loword, hiword);
+ }
+
+ if (StreamLength == 0xFFFFFFFF
+ || (_lpCurCodeDesc = AllocCodeDesc ()) == NULL)
+ {
+ FreeCodeDesc (_lpCurCodeDesc);
+ _lpCurCodeDesc = NULL;
+ }
+ else
+ {
+ _lpCurCodeDesc->Stream = _Stream;
+ _lpCurCodeDesc->StreamType = _StreamType;
+ _lpCurCodeDesc->StreamMode = SMode;
+ _lpCurCodeDesc->StreamLength = StreamLength;
+ _lpCurCodeDesc->buf_index = N - F;
+ memset (&_lpCurCodeDesc->text_buf[0], ' ', N - F);
+
+ StartHuff ();
+ }
+
+ return ((DECODE_REF)_lpCurCodeDesc);
+}
+
+DWORD
+cclose (PLZHCODE_DESC lpCodeDesc)
+{
+ _lpCurCodeDesc = lpCodeDesc;
+ if (_lpCurCodeDesc)
+ {
+ DWORD StreamIndex;
+
+ if (_lpCurCodeDesc->CleanupFunc)
+ (*_lpCurCodeDesc->CleanupFunc) ();
+
+ StreamIndex = lpCodeDesc->StreamIndex;
+ FreeCodeDesc (lpCodeDesc);
+ _lpCurCodeDesc = NULL;
+
+ return (StreamIndex);
+ }
+
+ return (0);
+}
+
+void
+cfilelength (PLZHCODE_DESC lpCodeDesc, DWORD *pfilelen)
+{
+ if (lpCodeDesc == 0)
+ *pfilelen = 0;
+ else
+ *pfilelen = lpCodeDesc->StreamLength;
+}
+
+ /* decoder table */
+static const BYTE d_code[256] =
+{
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 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, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
+ 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D,
+ 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F,
+ 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11,
+ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13,
+ 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15,
+ 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17,
+ 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B,
+ 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F,
+ 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23,
+ 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27,
+ 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B,
+ 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+};
+static const BYTE d_len[256] =
+{
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+};
+
+ /* decode upper 6 bits from given table */
+#define DecodePosition(p) \
+{ \
+ while (_workbuflen <= 8) \
+ { \
+ *(p) = InChar (); \
+ _workbuf |= *(p) << (8 - _workbuflen); \
+ _workbuflen += 8; \
+ } \
+ *(p) = HIBYTE (_workbuf); \
+ _workbuf = (_workbuf << 8) & 0xFFFF; \
+ _workbuflen -= 8; \
+ \
+ /* input lower 6 bits directly */ \
+ j = d_len[*(p)]; \
+ *(p) = ((UWORD)d_code[*(p)] << 6) \
+ | (((*(p) << j) | GetBits (j)) & 0x3f); \
+}
+
+ /* start searching tree from the root to leaves.
+ * choose node #(son[]) if input bit == 0
+ * else choose #(son[]+1) (input bit == 1)
+ */
+#define DecodeChar(c) \
+{ \
+ for (*(c) = lpCodeDesc->son[R]; \
+ *(c) < T; \
+ *(c) = lpCodeDesc->son[*(c) + GetBit ()]) \
+ ; \
+ _update (*(c)); \
+ *(c) -= T; \
+}
+
+COUNT
+cread (void *buf, COUNT size, COUNT count, PLZHCODE_DESC lpCodeDesc)
+{
+ COUNT r, j, i;
+ BYTE *lpStr;
+
+ if ((_lpCurCodeDesc = lpCodeDesc) == 0)
+ return (0);
+
+ size *= count;
+ if (lpCodeDesc->StreamIndex + size > lpCodeDesc->StreamLength)
+ {
+ size /= count;
+ count = (COUNT)((lpCodeDesc->StreamLength
+ - lpCodeDesc->StreamIndex) / size);
+
+ size *= count;
+ }
+
+ if (size == 0)
+ return (0);
+
+ lpStr = (BYTE*)buf;
+ _StreamType = lpCodeDesc->StreamType;
+
+ _Stream = lpCodeDesc->Stream;
+ _workbuf = lpCodeDesc->workbuf;
+ _workbuflen = lpCodeDesc->workbuflen;
+
+ lpCodeDesc->StreamIndex += size;
+ r = lpCodeDesc->buf_index;
+ j = lpCodeDesc->bytes_left;
+ if (j)
+ {
+ lpCodeDesc->bytes_left = 0;
+ i = lpCodeDesc->restart_index;
+
+ goto ReenterRun;
+ }
+
+ do
+ {
+ COUNT c;
+
+ DecodeChar (&c);
+
+ if (c < 256)
+ {
+ size--;
+
+ *lpStr++ = lpCodeDesc->text_buf[r++ & (N - 1)] = (BYTE)c;
+ }
+ else
+ {
+ COUNT copy_size;
+
+ //i is a COUNT;
+ DecodePosition(&i);
+ i = r - i - 1;
+ j = c - 255 + THRESHOLD;
+ReenterRun:
+ if (j > size)
+ {
+ lpCodeDesc->bytes_left = j - size;
+ lpCodeDesc->restart_index = i + size;
+ j = size;
+ }
+
+ size -= j;
+ do
+ {
+ COUNT loc_size;
+
+ i &= (N - 1);
+ r &= (N - 1);
+ if ((i < r && i + j > r) || (i > r && i + j > r + N))
+ copy_size = (r - i) & (N - 1);
+ else if ((copy_size = j) > N)
+ copy_size = N;
+
+ loc_size = copy_size;
+ if (i + loc_size > N)
+ {
+ COUNT k;
+
+ k = N - i;
+ memcpy (lpStr, &lpCodeDesc->text_buf[i], k);
+ lpStr += k;
+ loc_size -= k;
+ i = 0;
+ }
+
+ memcpy (lpStr, &lpCodeDesc->text_buf[i], loc_size);
+ lpStr += loc_size;
+ i += loc_size;
+
+ lpStr -= copy_size;
+
+ loc_size = copy_size;
+ if (r + loc_size > N)
+ {
+ COUNT k;
+
+ k = N - r;
+ memcpy (&lpCodeDesc->text_buf[r], lpStr, k);
+ lpStr += k;
+ loc_size -= k;
+ r = 0;
+ }
+
+ memcpy (&lpCodeDesc->text_buf[r], lpStr, loc_size);
+ lpStr += loc_size;
+ r += loc_size;
+ } while (j -= copy_size);
+ }
+ } while (size);
+
+ lpCodeDesc->buf_index = r;
+ lpCodeDesc->Stream = _Stream;
+ lpCodeDesc->workbuf = _workbuf;
+ lpCodeDesc->workbuflen = _workbuflen;
+
+ return (count);
+}
+
diff --git a/src/libs/decomp/lzencode.c b/src/libs/decomp/lzencode.c
new file mode 100644
index 0000000..e26f344
--- /dev/null
+++ b/src/libs/decomp/lzencode.c
@@ -0,0 +1,468 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+ /*
+ * LZHUF.C English version 1.0
+ * Based on Japanese version 29-NOV-1988
+ * LZSS coded by Haruhiko OKUMURA
+ * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI
+ * Edited and translated to English by Kenji RIKITAKE
+ */
+
+#include <stdio.h>
+#include "lzh.h"
+#include "libs/reslib.h"
+
+static UWORD match_position, match_length;
+static SWORD *lson;
+static SWORD *rson;
+static SWORD *dad;
+static SWORD *encode_arrays;
+
+#define AllocEncodeArrays() \
+ HCalloc ( \
+ (((N + 1) + (N + 257) + (N + 1)) \
+ * sizeof (lson[0])))
+#define FreeCodeArrays HFree
+
+static BOOLEAN
+InitTree (void)
+{
+ if ((encode_arrays = AllocEncodeArrays ()) == NULL)
+ {
+ FreeCodeArrays (encode_arrays);
+ encode_arrays = NULL;
+ return (FALSE);
+ }
+ else
+ {
+ SWORD i;
+
+ lson = encode_arrays;
+ rson = lson + (N + 1);
+ dad = rson + (N + 257);
+
+ for (i = N + 1; i <= N + 256; i++)
+ rson[i] = NIL; /* root */
+ for (i = 0; i < N; i++)
+ dad[i] = NIL; /* node */
+
+ return (TRUE);
+ }
+}
+
+static void
+InsertNode (SWORD r)
+{
+ SWORD p, cmp;
+ BYTE *lpBuf;
+
+ cmp = 1;
+ lpBuf = _lpCurCodeDesc->text_buf;
+ p = N + 1 + lpBuf[r];
+ rson[r] = lson[r] = NIL;
+ match_length = 0;
+ for (;;)
+ {
+ UWORD i;
+
+ if (cmp >= 0)
+ {
+ if (rson[p] != NIL)
+ p = rson[p];
+ else
+ {
+ rson[p] = r;
+ dad[r] = p;
+ return;
+ }
+ }
+ else
+ {
+ if (lson[p] != NIL)
+ p = lson[p];
+ else
+ {
+ lson[p] = r;
+ dad[r] = p;
+ return;
+ }
+ }
+
+ i = F;
+ {
+ SWORD _r, _p;
+
+ _r = r;
+ _p = p;
+ while (--i && (cmp = lpBuf[++_r] - lpBuf[++_p]) == 0)
+ ;
+ }
+ if ((i = F - i) > THRESHOLD)
+ {
+ if (i > match_length)
+ {
+ match_position = ((r - p) & (N - 1)) - 1;
+ if ((match_length = i) >= F)
+ break;
+ }
+ else if (i == match_length)
+ {
+ if ((i = ((r - p) & (N - 1)) - 1) < match_position)
+ {
+ match_position = i;
+ }
+ }
+ }
+ }
+ dad[r] = dad[p];
+ lson[r] = lson[p];
+ rson[r] = rson[p];
+ dad[lson[p]] = r;
+ dad[rson[p]] = r;
+ if (rson[dad[p]] == p)
+ rson[dad[p]] = r;
+ else
+ lson[dad[p]] = r;
+ dad[p] = NIL; /* remove p */
+}
+
+static void
+DeleteNode (SWORD p)
+{
+ SWORD q;
+
+ if (dad[p] == NIL)
+ return; /* unregistered */
+ if (rson[p] == NIL)
+ q = lson[p];
+ else if (lson[p] == NIL)
+ q = rson[p];
+ else
+ {
+ q = lson[p];
+ if (rson[q] != NIL)
+ {
+ do
+ {
+ q = rson[q];
+ } while (rson[q] != NIL);
+ rson[dad[q]] = lson[q];
+ dad[lson[q]] = dad[q];
+ lson[q] = lson[p];
+ dad[lson[p]] = q;
+ }
+ rson[q] = rson[p];
+ dad[rson[p]] = q;
+ }
+ dad[q] = dad[p];
+ if (rson[dad[p]] == p)
+ rson[dad[p]] = q;
+ else
+ lson[dad[p]] = q;
+ dad[p] = NIL;
+}
+
+static void
+Putcode (SWORD l, UWORD c)
+{
+ _workbuf |= c >> _workbuflen;
+ if ((_workbuflen += l) >= 8)
+ {
+ OutChar ((BYTE)(_workbuf >> 8));
+ ++_lpCurCodeDesc->StreamIndex;
+ if ((_workbuflen -= 8) >= 8)
+ {
+ OutChar ((BYTE)(_workbuf));
+ ++_lpCurCodeDesc->StreamIndex;
+ _workbuflen -= 8;
+ _workbuf = c << (l - _workbuflen);
+ }
+ else
+ {
+ _workbuf <<= 8;
+ }
+ _workbuf &= 0xFFFF;
+ }
+}
+
+static void
+EncodeChar (UWORD c)
+{
+ UWORD i;
+ SWORD j, k;
+
+ i = 0;
+ j = 0;
+ k = _lpCurCodeDesc->prnt[c + T];
+
+ /* search connections from leaf node to the root */
+ do
+ {
+ i >>= 1;
+
+ /*
+ if node's address is odd, output 1
+ else output 0
+ */
+ if (k & 1)
+ i += 0x8000;
+
+ j++;
+ } while ((k = _lpCurCodeDesc->prnt[k]) != R);
+ Putcode (j, i);
+ _update (c + T);
+}
+
+static void
+EncodePosition (UWORD c)
+{
+ UWORD i;
+ /*
+ * Tables for encoding/decoding upper 6 bits of
+ * sliding dictionary pointer
+ */
+ /* encoder table */
+ static const BYTE p_len[64] =
+ {
+ 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+ };
+
+ static const BYTE p_code[64] =
+ {
+ 0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68,
+ 0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C,
+ 0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC,
+ 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE,
+ 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE,
+ 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
+ };
+
+ /* output upper 6 bits with encoding */
+ i = c >> 6;
+ Putcode (p_len[i], (UWORD)p_code[i] << 8);
+
+ /* output lower 6 bits directly */
+ Putcode (6, (c & 0x3f) << 10);
+}
+
+static void
+UninitTree (void)
+{
+ if (_workbuflen)
+ {
+ OutChar ((BYTE)(_workbuf >> 8));
+ ++_lpCurCodeDesc->StreamIndex;
+ }
+
+ FreeCodeArrays (encode_arrays);
+ encode_arrays = NULL;
+ lson = NULL;
+ rson = NULL;
+ dad = NULL;
+}
+
+static void
+_encode_cleanup (void)
+{
+ UWORD r, s, last_match_length, len;
+
+ _StreamType = _lpCurCodeDesc->StreamType;
+ _Stream = _lpCurCodeDesc->Stream;
+ _workbuf = _lpCurCodeDesc->workbuf;
+ _workbuflen = _lpCurCodeDesc->workbuflen;
+
+ r = _lpCurCodeDesc->buf_index;
+ s = _lpCurCodeDesc->restart_index;
+ last_match_length = _lpCurCodeDesc->bytes_left;
+ if (_lpCurCodeDesc->StreamLength >= F)
+ len = F;
+ else
+ {
+ UWORD i;
+
+ for (i = 1; i <= F; i++)
+ InsertNode (r - i);
+ InsertNode (r);
+
+ len = (UWORD)_lpCurCodeDesc->StreamLength;
+ }
+
+ while (1)
+ {
+ while (last_match_length--)
+ {
+ DeleteNode (s);
+ if (--len == 0)
+ {
+ BYTE lobyte, hibyte;
+ UWORD loword, hiword;
+
+ UninitTree ();
+
+ _lpCurCodeDesc->StreamIndex += 4;
+ /* rewind */
+ if (_lpCurCodeDesc->StreamType == FILE_STREAM)
+ SeekResFile ((uio_Stream *)_Stream,
+ -(int)_lpCurCodeDesc->StreamIndex, SEEK_CUR);
+ else /* _lpCurCodeDesc->StreamType == MEMORY_STREAM */
+ _Stream = (BYTE*)_Stream - _lpCurCodeDesc->StreamIndex;
+
+ loword = LOWORD (_lpCurCodeDesc->StreamLength);
+ lobyte = LOBYTE (loword);
+ hibyte = HIBYTE (loword);
+ OutChar (lobyte);
+ OutChar (hibyte);
+ hiword = HIWORD (_lpCurCodeDesc->StreamLength);
+ lobyte = LOBYTE (hiword);
+ hibyte = HIBYTE (hiword);
+ OutChar (lobyte);
+ OutChar (hibyte);
+
+ return;
+ }
+ s = (s + 1) & (N - 1);
+ r = (r + 1) & (N - 1);
+ InsertNode (r);
+ }
+ if (match_length > len)
+ match_length = len;
+ if (match_length <= THRESHOLD)
+ {
+ match_length = 1;
+ EncodeChar (_lpCurCodeDesc->text_buf[r]);
+ }
+ else
+ {
+ EncodeChar (255 - THRESHOLD + match_length);
+ EncodePosition (match_position);
+ }
+ last_match_length = match_length;
+ }
+}
+
+COUNT
+cwrite (const void *buf, COUNT size, COUNT count, PLZHCODE_DESC lpCodeDesc)
+{
+ UWORD r, s, last_match_length;
+ BYTE *lpBuf;
+ const BYTE *lpStr;
+
+ if ((_lpCurCodeDesc = lpCodeDesc) == 0
+ || (size *= count) == 0)
+ return (0);
+
+ _StreamType = lpCodeDesc->StreamType;
+ _Stream = lpCodeDesc->Stream;
+ _workbuf = lpCodeDesc->workbuf;
+ _workbuflen = lpCodeDesc->workbuflen;
+ lpStr = (const BYTE *) buf;
+ lpBuf = lpCodeDesc->text_buf;
+
+ r = lpCodeDesc->buf_index;
+ s = lpCodeDesc->restart_index;
+ last_match_length = lpCodeDesc->bytes_left;
+ if (last_match_length)
+ {
+ lpCodeDesc->StreamLength += size;
+ goto EncodeRestart;
+ }
+ else if (lpCodeDesc->StreamLength < F)
+ {
+ UWORD i;
+
+ if ((i = (UWORD)lpCodeDesc->StreamLength) == 0)
+ {
+ if (!InitTree ())
+ return (0);
+
+ _lpCurCodeDesc->StreamIndex = 0;
+ lpCodeDesc->CleanupFunc = _encode_cleanup;
+ }
+
+ lpCodeDesc->StreamLength += size;
+
+ for (; i < F && size; ++i, --size)
+ lpBuf[r + i] = *lpStr++;
+ if (i < F)
+ goto EncodeExit;
+
+ for (i = 1; i <= F; i++)
+ InsertNode (r - i);
+ InsertNode (r);
+ if (size == 0)
+ goto EncodeExit;
+ }
+ else
+ lpCodeDesc->StreamLength += size;
+
+ do
+ {
+ if (match_length > F)
+ match_length = F;
+ if (match_length <= THRESHOLD)
+ {
+ match_length = 1;
+ EncodeChar (lpBuf[r]);
+ }
+ else
+ {
+ EncodeChar (255 - THRESHOLD + match_length);
+ EncodePosition (match_position);
+ }
+ last_match_length = match_length;
+EncodeRestart:
+ while (last_match_length && size)
+ {
+ BYTE c;
+
+ --size;
+ --last_match_length;
+
+ DeleteNode (s);
+ c = *lpStr++;
+ lpBuf[s] = c;
+ if (s < F - 1)
+ lpBuf[s + N] = c;
+ s = (s + 1) & (N - 1);
+ r = (r + 1) & (N - 1);
+ InsertNode (r);
+ }
+ } while (last_match_length == 0);
+
+EncodeExit:
+ lpCodeDesc->buf_index = r;
+ lpCodeDesc->restart_index = s;
+ lpCodeDesc->bytes_left = last_match_length;
+
+ lpCodeDesc->Stream = _Stream;
+ lpCodeDesc->workbuf = _workbuf;
+ lpCodeDesc->workbuflen = _workbuflen;
+
+ return (count);
+}
+
diff --git a/src/libs/decomp/lzh.h b/src/libs/decomp/lzh.h
new file mode 100644
index 0000000..8ebdef4
--- /dev/null
+++ b/src/libs/decomp/lzh.h
@@ -0,0 +1,91 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_DECOMP_LZH_H_
+#define LIBS_DECOMP_LZH_H_
+
+#include "libs/declib.h"
+#include "libs/memlib.h"
+
+/* LZSS Parameters */
+
+#define N 4096 /* Size of string buffer */
+#define F 16 /* Size of look-ahead buffer */
+//#define F 60 /* Size of look-ahead buffer */
+#define THRESHOLD 2
+#define NIL N /* End of tree's node */
+
+/* Huffman coding parameters */
+
+#define N_CHAR (256 - THRESHOLD + F)
+ /* character code (= 0..N_CHAR-1) */
+#define T (N_CHAR * 2 - 1) /* Size of table */
+#define R (T - 1) /* root position */
+#define MAX_FREQ 0x8000
+ /* update when cumulative frequency */
+
+struct _LZHCODE_DESC
+{
+ COUNT buf_index, restart_index, bytes_left;
+ BYTE text_buf[N + F - 1];
+ /* reconstruct freq tree */
+ COUNT freq[T + 1]; /* cumulative freq table */
+ /*
+ * pointing parent nodes.
+ * area [T..(T + N_CHAR - 1)] are pointers for leaves
+ */
+ COUNT prnt[T + N_CHAR];
+ /* pointing children nodes (son[], son[] + 1)*/
+ COUNT son[T];
+ UWORD workbuf;
+ BYTE workbuflen;
+
+ STREAM_TYPE StreamType;
+
+ void *Stream;
+ DWORD StreamIndex, StreamLength;
+
+ STREAM_MODE StreamMode;
+ PVOIDFUNC CleanupFunc;
+};
+
+typedef struct _LZHCODE_DESC LZHCODE_DESC;
+typedef LZHCODE_DESC *PLZHCODE_DESC;
+
+#define InChar() (_StreamType == FILE_STREAM ? \
+ GetResFileChar ((uio_Stream *)_Stream) : \
+ (int)*_Stream++)
+#define OutChar(c) (_StreamType == FILE_STREAM ? \
+ PutResFileChar ((c), (uio_Stream *)_Stream) : \
+ (*_Stream++ = (BYTE)(c)))
+
+
+#define AllocCodeDesc() HCalloc (sizeof (LZHCODE_DESC))
+#define FreeCodeDesc HFree
+
+extern void _update (COUNT c);
+extern void StartHuff (void);
+
+extern PLZHCODE_DESC _lpCurCodeDesc;
+extern STREAM_TYPE _StreamType;
+extern BYTE* _Stream;
+extern UWORD _workbuf;
+extern BYTE _workbuflen;
+
+#endif /* LIBS_DECOMP_LZH_H_ */
+
diff --git a/src/libs/decomp/update.c b/src/libs/decomp/update.c
new file mode 100644
index 0000000..13de988
--- /dev/null
+++ b/src/libs/decomp/update.c
@@ -0,0 +1,115 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "lzh.h"
+
+static void
+reconst (void)
+{
+ COUNT i, j;
+
+ /* halven cumulative freq for leaf nodes */
+ j = 0;
+ for (i = 0; i < T; i++)
+ {
+ if (_lpCurCodeDesc->son[i] >= T)
+ {
+ _lpCurCodeDesc->freq[j] = (_lpCurCodeDesc->freq[i] + 1) >> 1;
+ _lpCurCodeDesc->son[j] = _lpCurCodeDesc->son[i];
+ j++;
+ }
+ }
+ /* make a tree : first, connect children nodes */
+ for (i = 0, j = N_CHAR; j < T; i += 2, j++)
+ {
+ SWORD k;
+ UWORD f, l;
+
+ k = i + 1;
+ f = _lpCurCodeDesc->freq[j] = _lpCurCodeDesc->freq[i] + _lpCurCodeDesc->freq[k];
+ for (k = j - 1; f < _lpCurCodeDesc->freq[k]; k--)
+ ;
+ k++;
+ l = (j - k);
+
+ memmove (_lpCurCodeDesc->freq + k + 1, _lpCurCodeDesc->freq + k,
+ sizeof(_lpCurCodeDesc->freq[0]) * l);
+ _lpCurCodeDesc->freq[k] = f;
+ memmove (_lpCurCodeDesc->son + k + 1, _lpCurCodeDesc->son + k,
+ sizeof(_lpCurCodeDesc->son[0]) * l);
+ _lpCurCodeDesc->son[k] = i;
+ }
+ /* connect parent nodes */
+ for (i = 0; i < T; i++)
+ {
+ if ((j = _lpCurCodeDesc->son[i]) >= T)
+ _lpCurCodeDesc->prnt[j] = i;
+ else
+ _lpCurCodeDesc->prnt[j] = _lpCurCodeDesc->prnt[j + 1] = i;
+ }
+}
+
+
+/* update freq tree */
+
+void
+_update (COUNT c)
+{
+ PLZHCODE_DESC lpCD;
+
+ if ((lpCD = _lpCurCodeDesc)->freq[R] == MAX_FREQ)
+ reconst ();
+
+ c = lpCD->prnt[c];
+ do
+ {
+ COUNT i, l;
+
+ i = ++lpCD->freq[c];
+
+ /* swap nodes to keep the tree freq-ordered */
+ if (i > lpCD->freq[l = c + 1])
+ {
+ COUNT j;
+
+ while (i > lpCD->freq[++l])
+ ;
+ l--;
+ lpCD->freq[c] = lpCD->freq[l];
+ lpCD->freq[l] = i;
+
+ i = lpCD->son[c];
+ j = lpCD->son[l];
+ lpCD->son[l] = i;
+ lpCD->son[c] = j;
+
+ lpCD->prnt[i] = l;
+ if (i < T)
+ lpCD->prnt[i + 1] = l;
+
+ lpCD->prnt[j] = c;
+ if (j < T)
+ lpCD->prnt[j + 1] = c;
+
+ c = l;
+ }
+ } while ((c = lpCD->prnt[c]) != 0); /* do it until reaching the root */
+}
+
+
diff --git a/src/libs/file.h b/src/libs/file.h
new file mode 100644
index 0000000..df8de5e
--- /dev/null
+++ b/src/libs/file.h
@@ -0,0 +1,95 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Contains file handling code
+
+#ifndef LIBS_FILE_H_
+#define LIBS_FILE_H_
+
+#include "port.h"
+#include "libs/uio.h"
+
+// for bool
+#include "types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if 0
+// from temp.h
+void initTempDir (void);
+void unInitTempDir (void);
+char *tempFilePath (const char *filename);
+extern uio_DirHandle *tempDir;
+#endif
+
+
+// from dirs.h
+int mkdirhier (const char *path);
+const char *getHomeDir (void);
+int createDirectory (const char *dir, int mode);
+
+int expandPath (char *dest, size_t len, const char *src, int what);
+// values for 'what':
+#define EP_HOME 1
+ // Expand '~' for home dirs.
+#define EP_ABSOLUTE 2
+ // Make paths absolute
+#define EP_ENVVARS 4
+ // Expand environment variables.
+#define EP_DOTS 8
+ // Process ".." and "."
+#define EP_SLASHES 16
+ // Consider backslashes as path component separators.
+ // They will be replaced by slashes. Windows UNC paths will always
+ // start with "\\server\share", with backslashes.
+#define EP_SINGLESEP 32
+ // Replace multiple consecutive path separators by a single one.
+#define EP_ALL (EP_HOME | EP_ENVVARS | EP_ABSOLUTE | EP_DOTS | EP_SLASHES \
+ EP_SINGLESEP)
+ // Everything
+// Everything except Windows style backslashes on Unix Systems:
+#ifdef WIN32
+# define EP_ALL_SYSTEM (EP_HOME | EP_ENVVARS | EP_ABSOLUTE | EP_DOTS | \
+ EP_SLASHES | EP_SINGLESEP)
+#else
+# define EP_ALL_SYSTEM (EP_HOME | EP_ENVVARS | EP_ABSOLUTE | EP_DOTS | \
+ EP_SINGLESEP)
+#endif
+
+// from files.h
+int copyFile (uio_DirHandle *srcDir, const char *srcName,
+ uio_DirHandle *dstDir, const char *newName);
+bool fileExists (const char *name);
+bool fileExists2(uio_DirHandle *dir, const char *fileName);
+#ifdef HAVE_UNC_PATHS
+size_t skipUNCServerShare(const char *inPath);
+#endif /* HAVE_UNC_PATHS */
+
+#ifdef HAVE_DRIVE_LETTERS
+static inline int isDriveLetter(int c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+#endif /* HAVE_DRIVE_LETTERS */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_FILE_H_ */
+
diff --git a/src/libs/file/Makeinfo b/src/libs/file/Makeinfo
new file mode 100644
index 0000000..b4ea11d
--- /dev/null
+++ b/src/libs/file/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="dirs.c files.c"
+uqm_HFILES="filintrn.h"
diff --git a/src/libs/file/dirs.c b/src/libs/file/dirs.c
new file mode 100644
index 0000000..39a44f5
--- /dev/null
+++ b/src/libs/file/dirs.c
@@ -0,0 +1,830 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Contains code handling directories
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "port.h"
+#include "config.h"
+#include "filintrn.h"
+#include "libs/compiler.h"
+#include "libs/memlib.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#ifdef HAVE_DRIVE_LETTERS
+# include <ctype.h>
+ // For tolower()
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef WIN32
+# include <direct.h>
+ // For _getdcwd()
+#else
+# include <pwd.h>
+ // For getpwuid()
+#endif
+
+/* Try to find a suitable value for %APPDATA% if it isn't defined on
+ * Windows.
+ */
+#define APPDATA_FALLBACK
+
+
+static char *expandPathAbsolute (char *dest, size_t destLen, const char *src,
+ size_t *skipSrc, int what);
+static char *strrchr2(const char *start, int c, const char *end);
+
+
+int
+createDirectory(const char *dir, int mode)
+{
+ return MKDIR(dir, mode);
+}
+
+// make all components of the path if they don't exist already
+// returns 0 on success, -1 on failure.
+// on failure, some parts may still have been created.
+int
+mkdirhier (const char *path)
+{
+ char *buf; // buffer
+ char *ptr; // end of the string in buf
+ const char *pathstart; // start of a component of path
+ const char *pathend; // first char past the end of a component of path
+ size_t len;
+ struct stat statbuf;
+
+ len = strlen (path);
+ buf = HMalloc (len + 2); // one extra for possibly added '/'
+
+ ptr = buf;
+ pathstart = path;
+
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(pathstart[0]) && pathstart[1] == ':')
+ {
+ // Driveletter + semicolon on Windows.
+ // Copy as is; don't try to create directories for it.
+ *(ptr++) = *(pathstart++);
+ *(ptr++) = *(pathstart++);
+
+ ptr[0] = '/';
+ ptr[1] = '\0';
+ if (stat (buf, &statbuf) == -1)
+ {
+ log_add (log_Error, "Can't stat \"%s\": %s", buf, strerror (errno));
+ goto err;
+ }
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef HAVE_UNC_PATHS
+ if (pathstart[0] == '\\' && pathstart[1] == '\\')
+ {
+ // Universal Naming Convention path. (\\server\share\...)
+ // Copy the server part as is; don't try to create directories for
+ // it, or stat it. Don't create a dir for the share either.
+ *(ptr++) = *(pathstart++);
+ *(ptr++) = *(pathstart++);
+
+ // Copy the server part
+ while (*pathstart != '\0' && *pathstart != '\\' && *pathstart != '/')
+ *(ptr++) = *(pathstart++);
+
+ if (*pathstart == '\0')
+ {
+ log_add (log_Error, "Incomplete UNC path \"%s\"", pathstart);
+ goto err;
+ }
+
+ // Copy the path seperator.
+ *(ptr++) = *(pathstart++);
+
+ // Copy the share part
+ while (*pathstart != '\0' && *pathstart != '\\' && *pathstart != '/')
+ *(ptr++) = *(pathstart++);
+
+ ptr[0] = '/';
+ ptr[1] = '\0';
+ if (stat (buf, &statbuf) == -1)
+ {
+ log_add (log_Error, "Can't stat \"%s\": %s", buf, strerror (errno));
+ goto err;
+ }
+ }
+#else
+ {
+ // Making sure that there is an 'else' case if HAVE_DRIVE_LETTERS is
+ // defined.
+ }
+#endif /* HAVE_UNC_PATHS */
+
+ if (*pathstart == '/')
+ *(ptr++) = *(pathstart++);
+
+ if (*pathstart == '\0') {
+ // path exists completely, nothing more to do
+ goto success;
+ }
+
+ // walk through the path as long as the components exist
+ while (1)
+ {
+ pathend = strchr (pathstart, '/');
+ if (pathend == NULL)
+ pathend = path + len;
+ memcpy(ptr, pathstart, pathend - pathstart);
+ ptr += pathend - pathstart;
+ *ptr = '\0';
+
+ if (stat (buf, &statbuf) == -1)
+ {
+ if (errno == ENOENT)
+ break;
+#ifdef __SYMBIAN32__
+ // XXX: HACK: If we don't have access to a directory, we can
+ // still have access to the underlying entries. We don't
+ // actually know whether the entry is a directory, but I know of
+ // no way to find out. We just pretend that it is; if we were
+ // wrong, an error will occur when we try to do something with
+ // the directory. That /should/ not be a problem, as any such
+ // action should have its own error checking.
+ if (errno != EACCES)
+#endif
+ {
+ log_add (log_Error, "Can't stat \"%s\": %s", buf,
+ strerror (errno));
+ goto err;
+ }
+ }
+
+ if (*pathend == '\0')
+ goto success;
+
+ *ptr = '/';
+ ptr++;
+ pathstart = pathend + 1;
+ while (*pathstart == '/')
+ pathstart++;
+ // pathstart is the next non-slash character
+
+ if (*pathstart == '\0')
+ goto success;
+ }
+
+ // create all components left
+ while (1)
+ {
+ if (createDirectory (buf, 0777) == -1)
+ {
+ log_add (log_Error, "Error: Can't create %s: %s", buf,
+ strerror (errno));
+ goto err;
+ }
+
+ if (*pathend == '\0')
+ break;
+
+ *ptr = '/';
+ ptr++;
+ pathstart = pathend + 1;
+ while (*pathstart == '/')
+ pathstart++;
+ // pathstart is the next non-slash character
+
+ if (*pathstart == '\0')
+ break;
+
+ pathend = strchr (pathstart, '/');
+ if (pathend == NULL)
+ pathend = path + len;
+
+ memcpy (ptr, pathstart, pathend - pathstart);
+ ptr += pathend - pathstart;
+ *ptr = '\0';
+ }
+
+success:
+ HFree (buf);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+ HFree (buf);
+ errno = savedErrno;
+ }
+ return -1;
+}
+
+// Get the user's home dir
+// returns a pointer to a static buffer from either getenv() or getpwuid().
+const char *
+getHomeDir (void)
+{
+#ifdef WIN32
+ return getenv ("HOME");
+#else
+ const char *home;
+ struct passwd *pw;
+
+ home = getenv ("HOME");
+ if (home != NULL)
+ return home;
+
+ pw = getpwuid (getuid ());
+ if (pw == NULL)
+ return NULL;
+ // NB: pw points to a static buffer.
+
+ return pw->pw_dir;
+#endif
+}
+
+// Performs various types of string expansions on a path.
+// 'what' is an OR'd compination of the folowing flags, which
+// specify what type of exmansions will be performed.
+// EP_HOME - Expand '~' for home dirs.
+// EP_ABSOLUTE - Make relative paths absolute
+// EP_ENVVARS - Expand environment variables
+// EP_DOTS - Process ".." and "."
+// EP_SLASHES - Consider backslashes as path component separators.
+// They will be replaced by slashes.
+// EP_SINGLESEP - Replace multiple consecutive path seperators (which POSIX
+// considers equivalent to a single one) by a single one.
+// Additionally, there's EP_ALL, which indicates all of the above,
+// and EP_ALL_SYSTEM, which does the same as EP_ALL, with the exception
+// of EP_SLASHES, which will only be included if the operating system
+// accepts backslashes as path terminators.
+// Returns 0 on success.
+// Returns -1 on failure, setting errno.
+int
+expandPath (char *dest, size_t len, const char *src, int what)
+{
+ char *destptr, *destend;
+ char *buf = NULL;
+ char *bufptr, *bufend;
+ const char *srcend;
+
+#define CHECKLEN(bufname, n) \
+ if (bufname##ptr + (n) >= bufname##end) \
+ { \
+ errno = ENAMETOOLONG; \
+ goto err; \
+ } \
+ else \
+ (void) 0
+
+ destptr = dest;
+ destend = dest + len;
+
+ if (what & EP_ENVVARS)
+ {
+ buf = HMalloc (len);
+ bufptr = buf;
+ bufend = buf + len;
+ while (*src != '\0')
+ {
+ switch (*src)
+ {
+#ifdef WIN32
+ case '%':
+ {
+ /* Environment variable substitution in Windows */
+ const char *end; // end of env var name in src
+ const char *envVar;
+ char *envName;
+ size_t envNameLen, envVarLen;
+
+ src++;
+ end = strchr (src, '%');
+ if (end == NULL)
+ {
+ errno = EINVAL;
+ goto err;
+ }
+
+ envNameLen = end - src;
+ envName = HMalloc (envNameLen + 1);
+ memcpy (envName, src, envNameLen + 1);
+ envName[envNameLen] = '\0';
+ envVar = getenv (envName);
+ HFree (envName);
+
+ if (envVar == NULL)
+ {
+#ifdef APPDATA_FALLBACK
+ if (strncmp (src, "APPDATA", envNameLen) != 0)
+ {
+ // Substitute an empty string
+ src = end + 1;
+ break;
+ }
+
+ // fallback for when the APPDATA env var is not set
+ // Using SHGetFolderPath or SHGetSpecialFolderPath
+ // is problematic (not everywhere available).
+ log_add (log_Warning, "Warning: %%APPDATA%% is not set. "
+ "Falling back to \"%%USERPROFILE%%\\Application "
+ "Data\"");
+ envVar = getenv ("USERPROFILE");
+ if (envVar != NULL)
+ {
+#define APPDATA_STRING "\\Application Data"
+ envVarLen = strlen (envVar);
+ CHECKLEN (buf,
+ envVarLen + sizeof (APPDATA_STRING) - 1);
+ strcpy (bufptr, envVar);
+ bufptr += envVarLen;
+ strcpy (bufptr, APPDATA_STRING);
+ bufptr += sizeof (APPDATA_STRING) - 1;
+ src = end + 1;
+ break;
+ }
+
+ // fallback to "./userdata"
+#define APPDATA_FALLBACK_STRING ".\\userdata"
+ log_add (log_Warning,
+ "Warning: %%USERPROFILE%% is not set. "
+ "Falling back to \"%s\" for %%APPDATA%%",
+ APPDATA_FALLBACK_STRING);
+ CHECKLEN (buf, sizeof (APPDATA_FALLBACK_STRING) - 1);
+ strcpy (bufptr, APPDATA_FALLBACK_STRING);
+ bufptr += sizeof (APPDATA_FALLBACK_STRING) - 1;
+ src = end + 1;
+ break;
+
+#else /* !defined (APPDATA_FALLBACK) */
+ // Substitute an empty string
+ src = end + 1;
+ break;
+#endif /* APPDATA_FALLBACK */
+ }
+
+ envVarLen = strlen (envVar);
+ CHECKLEN (buf, envVarLen);
+ strcpy (bufptr, envVar);
+ bufptr += envVarLen;
+ src = end + 1;
+ break;
+ }
+#endif
+#ifndef WIN32
+ case '$':
+ {
+ const char *end;
+ char *envName;
+ size_t envNameLen;
+ const char *envVar;
+ size_t envVarLen;
+
+ src++;
+ if (*src == '{')
+ {
+ src++;
+ end = strchr(src, '}');
+ if (end == NULL)
+ {
+ errno = EINVAL;
+ goto err;
+ }
+ envNameLen = end - src;
+ end++; // Skip the '}'
+ }
+ else
+ {
+ end = src;
+ while ((*end >= 'A' && *end <= 'Z') ||
+ (*end >= 'a' && *end <= 'z') ||
+ (*end >= '0' && *end <= '9') ||
+ *end == '_')
+ end++;
+ envNameLen = end - src;
+ }
+
+ envName = HMalloc (envNameLen + 1);
+ memcpy (envName, src, envNameLen + 1);
+ envName[envNameLen] = '\0';
+ envVar = getenv (envName);
+ HFree (envName);
+
+ if (envVar != NULL)
+ {
+ envVarLen = strlen (envVar);
+ CHECKLEN (buf, envVarLen);
+ memcpy (bufptr, envVar, envVarLen);
+ bufptr += envVarLen;
+ }
+
+ src = end;
+ break;
+ }
+#endif
+ default:
+ CHECKLEN(buf, 1);
+ *(bufptr++) = *(src++);
+ break;
+ } // switch
+ } // while
+ *bufptr = '\0';
+ src = buf;
+ srcend = bufptr;
+ } // if (what & EP_ENVVARS)
+ else
+ srcend = src + strlen (src);
+
+ if (what & EP_HOME)
+ {
+ if (src[0] == '~')
+ {
+ const char *home;
+ size_t homelen;
+
+ if (src[1] != '/')
+ {
+ errno = EINVAL;
+ goto err;
+ }
+
+ home = getHomeDir ();
+ if (home == NULL)
+ {
+ errno = ENOENT;
+ goto err;
+ }
+ homelen = strlen (home);
+
+ if (what & EP_ABSOLUTE) {
+ size_t skip;
+ destptr = expandPathAbsolute (dest, destend - dest,
+ home, &skip, what);
+ if (destptr == NULL)
+ {
+ // errno is set
+ goto err;
+ }
+ home += skip;
+ what &= ~EP_ABSOLUTE;
+ // The part after the '~' should not be seen
+ // as absolute.
+ }
+
+ CHECKLEN (dest, homelen);
+ memcpy (destptr, home, homelen);
+ destptr += homelen;
+ src++; /* skip the ~ */
+ }
+ }
+
+ if (what & EP_ABSOLUTE)
+ {
+ size_t skip;
+ destptr = expandPathAbsolute (destptr, destend - destptr, src,
+ &skip, what);
+ if (destptr == NULL)
+ {
+ // errno is set
+ goto err;
+ }
+ src += skip;
+ }
+
+ CHECKLEN (dest, srcend - src);
+ memcpy (destptr, src, srcend - src + 1);
+ // The +1 is for the '\0'. It is already taken into account by
+ // CHECKLEN.
+
+ if (what & EP_SLASHES)
+ {
+ /* Replacing backslashes in path by slashes. */
+ destptr = dest;
+#ifdef HAVE_UNC_PATHS
+ {
+ // A UNC path should always start with two backslashes
+ // and have a backslash in between the server and share part.
+ size_t skip = skipUNCServerShare (destptr);
+ if (skip != 0)
+ {
+ char *slash = (char *) memchr (destptr + 2, '/', skip - 2);
+ if (slash)
+ *slash = '\\';
+ destptr += skip;
+ }
+ }
+#endif /* HAVE_UNC_PATHS */
+ while (*destptr != '\0')
+ {
+ if (*destptr == '\\')
+ *destptr = '/';
+ destptr++;
+ }
+ }
+
+ if (what & EP_DOTS) {
+ // At this point backslashes are already replaced by slashes if they
+ // are specified to be path seperators.
+ // Note that the path can only get smaller, so no size checks
+ // need to be done.
+ char *pathStart;
+ // Start of the first path component, after any
+ // leading slashes or drive letters.
+ char *startPart;
+ char *endPart;
+
+ pathStart = dest;
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(pathStart[0]) && (pathStart[1] == ':'))
+ {
+ pathStart += 2;
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef HAVE_UNC_PATHS
+ {
+ // Test for a Universal Naming Convention path.
+ pathStart += skipUNCServerShare(pathStart);
+ }
+#else
+ {
+ // Making sure that there is an 'else' case if HAVE_DRIVE_LETTERS is
+ // defined.
+ }
+#endif /* HAVE_UNC_PATHS */
+ if (pathStart[0] == '/')
+ pathStart++;
+
+ startPart = pathStart;
+ destptr = pathStart;
+ for (;;)
+ {
+ endPart = strchr(startPart, '/');
+ if (endPart == NULL)
+ endPart = startPart + strlen(startPart);
+
+ if (endPart - startPart == 1 && startPart[0] == '.')
+ {
+ // Found "." as path component. Ignore this component.
+ }
+ else if (endPart - startPart == 2 &&
+ startPart[0] == '.' && startPart[1] == '.')
+ {
+ // Found ".." as path component. Remove the previous
+ // component, and ignore this one.
+ char *lastSlash;
+ lastSlash = strrchr2(pathStart, '/', destptr - 1);
+ if (lastSlash == NULL)
+ {
+ if (destptr == pathStart)
+ {
+ // We ran out of path components to back out of.
+ errno = EINVAL;
+ goto err;
+ }
+ destptr = pathStart;
+ }
+ else
+ {
+ destptr = lastSlash;
+ if (*endPart == '/')
+ destptr++;
+ }
+ }
+ else
+ {
+ // A normal path component; copy it.
+ // Using memmove as source and destination may overlap.
+ memmove(destptr, startPart, endPart - startPart);
+ destptr += (endPart - startPart);
+ if (*endPart == '/')
+ {
+ *destptr = '/';
+ destptr++;
+ }
+ }
+ if (*endPart == '\0')
+ break;
+ startPart = endPart + 1;
+ }
+ *destptr = '\0';
+ }
+
+ if (what & EP_SINGLESEP)
+ {
+ char *srcptr;
+ srcptr = dest;
+ destptr = dest;
+ while (*srcptr != '\0')
+ {
+ char ch = *srcptr;
+ *(destptr++) = *(srcptr++);
+ if (ch == '/')
+ {
+ while (*srcptr == '/')
+ srcptr++;
+ }
+ }
+ *destptr = '\0';
+ }
+
+ HFree (buf);
+ return 0;
+
+err:
+ if (buf != NULL) {
+ int savedErrno = errno;
+ HFree (buf);
+ errno = savedErrno;
+ }
+ return -1;
+}
+
+#if defined(HAVE_DRIVE_LETTERS) && defined(HAVE_CWD_PER_DRIVE)
+ // This code is only needed if we have a current working directory
+ // per drive.
+// letter is 0 based: 0 = A, 1 = B, ...
+static bool
+driveLetterExists(int letter)
+{
+ unsigned long drives;
+
+ drives = _getdrives ();
+
+ return ((drives >> letter) & 1) != 0;
+}
+#endif /* if defined(HAVE_DRIVE_LETTERS) && defined(HAVE_CWD_PER_DRIVE) */
+
+// helper for expandPath, expanding an absolute path
+// returns a pointer to the end of the filled in part of dest.
+static char *
+expandPathAbsolute (char *dest, size_t destLen, const char *src,
+ size_t *skipSrc, int what)
+{
+ const char *orgSrc;
+
+ if (src[0] == '/' || ((what & EP_SLASHES) && src[0] == '\\'))
+ {
+ // Path is already absolute; nothing to do
+ *skipSrc = 0;
+ return dest;
+ }
+
+ orgSrc = src;
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(src[0]) && (src[1] == ':'))
+ {
+ int letter;
+
+ if (src[2] == '/' || src[2] == '\\')
+ {
+ // Path is already absolute (of the form "d:/"); nothing to do
+ *skipSrc = 0;
+ return dest;
+ }
+
+ // Path is of the form "d:path", without a (back)slash after the
+ // semicolon.
+
+#ifdef REJECT_DRIVE_PATH_WITHOUT_SLASH
+ // We reject paths of the form "d:foo/bar".
+ errno = EINVAL;
+ return NULL;
+#elif defined(HAVE_CWD_PER_DRIVE)
+ // Paths of the form "d:foo/bar" are treated as "foo/bar" relative
+ // to the working directory of d:.
+ letter = tolower(src[0]) - 'a';
+
+ // _getdcwd() should only be called on drives that exist.
+ // This is weird though, because it means a race condition
+ // in between the existance check and the call to _getdcwd()
+ // cannot be avoided, unless a drive still exists for Windows
+ // when the physical drive is removed.
+ if (!driveLetterExists (letter))
+ {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ // Get the working directory for a specific drive.
+ if (_getdcwd (letter + 1, dest, destLen) == NULL)
+ {
+ // errno is set
+ return NULL;
+ }
+
+ src += 2;
+#else /* if !defined(HAVE_CWD_PER_DRIVE) */
+ // We treat paths of the form "d:foo/bar" as "d:/foo/bar".
+ if (destLen < 3) {
+ errno = ERANGE;
+ return NULL;
+ }
+ dest[0] = src[0];
+ dest[1] = ':';
+ dest[2] = '/';
+ *skipSrc = 2;
+ dest += 3;
+ return dest;
+#endif /* HAVE_CWD_PER_DRIVE */
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+ {
+ // Relative dir
+ if (getcwd (dest, destLen) == NULL)
+ {
+ // errno is set
+ return NULL;
+ }
+ }
+
+ {
+ size_t tempLen;
+ tempLen = strlen (dest);
+ if (tempLen == 0)
+ {
+ // getcwd() or _getdcwd() returned a 0-length string.
+ errno = ENOENT;
+ return NULL;
+ }
+ dest += tempLen;
+ destLen -= tempLen;
+ }
+ if (dest[-1] != '/'
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ && dest[-1] != '\\'
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+ )
+ {
+ // Need to add a slash.
+ // There's always space, as we overwrite the '\0' that getcwd()
+ // always returns.
+ dest[0] = '/';
+ dest++;
+ destLen--;
+ }
+
+ *skipSrc = (size_t) (src - orgSrc);
+ return dest;
+}
+
+// As strrchr, but starts searching from the indicated end of the string.
+static char *
+strrchr2(const char *start, int c, const char *end) {
+ for (;;) {
+ end--;
+ if (end < start)
+ return (char *) NULL;
+ if (*end == c)
+ return (char *) unconst(end);
+ }
+}
+
+#ifdef HAVE_UNC_PATHS
+// returns 0 if the path is not a valid UNC path.
+// Does not skip trailing slashes.
+size_t
+skipUNCServerShare(const char *inPath) {
+ const char *path = inPath;
+
+ // Skip the initial two backslashes.
+ if (path[0] != '\\' || path[1] != '\\')
+ return (size_t) 0;
+ path += 2;
+
+ // Skip the server part.
+ while (*path != '\\' && *path != '/') {
+ if (*path == '\0')
+ return (size_t) 0;
+ path++;
+ }
+
+ // Skip the seperator.
+ path++;
+
+ // Skip the share part.
+ while (*path != '\0' && *path != '\\' && *path != '/')
+ path++;
+
+ return (size_t) (path - inPath);
+}
+#endif /* HAVE_UNC_PATHS */
+
+
diff --git a/src/libs/file/files.c b/src/libs/file/files.c
new file mode 100644
index 0000000..5ce7dac
--- /dev/null
+++ b/src/libs/file/files.c
@@ -0,0 +1,165 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Contains code handling files
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "port.h"
+#include "libs/uio.h"
+#include "config.h"
+#include "types.h"
+#include "filintrn.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+
+static int copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uint8 *buf);
+
+bool
+fileExists (const char *name)
+{
+ return access (name, F_OK) == 0;
+}
+
+bool
+fileExists2(uio_DirHandle *dir, const char *fileName)
+{
+ uio_Stream *stream;
+
+ stream = uio_fopen (dir, fileName, "rb");
+ if (stream == NULL)
+ return 0;
+
+ uio_fclose (stream);
+ return 1;
+}
+
+/*
+ * Copy a file with path srcName to a file with name newName.
+ * If the destination already exists, the operation fails.
+ * Links are followed.
+ * Special files (fifos, char devices, block devices, etc) will be
+ * read as long as there is data available and the destination will be
+ * a regular file with that data.
+ * The new file will have the same permissions as the old.
+ * If an error occurs during copying, an attempt will be made to
+ * remove the copy.
+ */
+int
+copyFile (uio_DirHandle *srcDir, const char *srcName,
+ uio_DirHandle *dstDir, const char *newName)
+{
+ uio_Handle *src, *dst;
+ struct stat sb;
+#define BUFSIZE 65536
+ uint8 *buf, *bufPtr;
+ ssize_t numInBuf, numWritten;
+
+ src = uio_open (srcDir, srcName, O_RDONLY
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (src == NULL)
+ return -1;
+
+ if (uio_fstat (src, &sb) == -1)
+ return copyError (src, NULL, NULL, NULL, NULL);
+
+ dst = uio_open (dstDir, newName, O_WRONLY | O_CREAT | O_EXCL
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (dst == NULL)
+ return copyError (src, NULL, NULL, NULL, NULL);
+
+ buf = HMalloc(BUFSIZE);
+ // This was originally a statically allocated buffer,
+ // but as this function might be run from a thread with
+ // a small stack, this is better.
+ while (1)
+ {
+ numInBuf = uio_read (src, buf, BUFSIZE);
+ if (numInBuf == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (src, dst, dstDir, newName, buf);
+ }
+ if (numInBuf == 0)
+ break;
+
+ bufPtr = buf;
+ do
+ {
+ numWritten = uio_write (dst, bufPtr, numInBuf);
+ if (numWritten == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (src, dst, dstDir, newName, buf);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ } while (numInBuf > 0);
+ }
+
+ HFree (buf);
+ uio_close (src);
+ uio_close (dst);
+ errno = 0;
+ return 0;
+}
+
+/*
+ * Closes srcHandle if it's not -1.
+ * Closes dstHandle if it's not -1.
+ * Removes unlinkpath from the unlinkHandle dir if it's not NULL.
+ * Frees 'buf' if not NULL.
+ * Always returns -1.
+ * errno is what was before the call.
+ */
+static int
+copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uint8 *buf)
+{
+ int savedErrno;
+
+ savedErrno = errno;
+
+ log_add (log_Debug, "Error while copying: %s", strerror (errno));
+
+ if (srcHandle != NULL)
+ uio_close (srcHandle);
+
+ if (dstHandle != NULL)
+ uio_close (dstHandle);
+
+ if (unlinkPath != NULL)
+ uio_unlink (unlinkHandle, unlinkPath);
+
+ if (buf != NULL)
+ HFree(buf);
+
+ errno = savedErrno;
+ return -1;
+}
+
diff --git a/src/libs/file/filintrn.h b/src/libs/file/filintrn.h
new file mode 100644
index 0000000..2c6512a
--- /dev/null
+++ b/src/libs/file/filintrn.h
@@ -0,0 +1,24 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Contains code handling temporary files and dirs
+
+#ifndef _FILEINTRN_H
+
+#include "../file.h"
+
+#endif /* _FILEINTRN_H */
+
diff --git a/src/libs/file/temp.c b/src/libs/file/temp.c
new file mode 100644
index 0000000..3253e0d
--- /dev/null
+++ b/src/libs/file/temp.c
@@ -0,0 +1,199 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Contains code handling temporary files and dirs
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#ifdef WIN32
+# include <io.h>
+#endif
+#include <string.h>
+#include <time.h>
+#include "filintrn.h"
+#include "libs/timelib.h"
+#include "port.h"
+#include "libs/compiler.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+static char *tempDirName;
+uio_DirHandle *tempDir;
+
+static void
+removeTempDir (void)
+{
+ rmdir (tempDirName);
+}
+
+// Try if the null-terminated path 'dir' to a directory is valid
+// as temp path.
+// On success, 'buf' will be filled with the path, with a trailing /,
+// null-terminated, and 0 is returned.
+// On failure, EINVAL, ENAMETOOLONG, or one of the errors access() can return
+// is returned, and the contents of buf is unspecified.
+static int
+tryTempDir (char *buf, size_t buflen, const char *dir)
+{
+ size_t len;
+ int haveSlash;
+
+ if (dir == NULL)
+ return EINVAL;
+
+ if (dir[0] == '\0')
+ return EINVAL;
+
+ len = strlen (dir);
+ haveSlash = (dir[len - 1] == '/'
+#ifdef WIN32
+ || dir[len - 1] == '\\'
+#endif
+ );
+ if ((haveSlash ? len : len + 1) >= buflen)
+ return ENAMETOOLONG;
+
+ strcpy (buf, dir);
+#if 0
+ //def WIN32
+ {
+ char *bufPtr;
+ for (bufPtr = buf; *bufPtr != '\0'; bufPtr++)
+ {
+ if (*bufPtr == '\\')
+ *bufPtr = '/';
+ }
+ }
+#endif
+ if (!haveSlash)
+ {
+ buf[len] = '/';
+ len++;
+ buf[len] = '\0';
+ }
+ if (access (buf, R_OK | W_OK) == -1)
+ return errno;
+
+ return 0;
+}
+
+static void
+getTempDir (char *buf, size_t buflen) {
+ char cwd[PATH_MAX];
+
+ if (tryTempDir (buf, buflen, getenv("TMP")) &&
+ tryTempDir (buf, buflen, getenv("TEMP")) &&
+#if !defined(WIN32) || defined (__CYGWIN__)
+ tryTempDir (buf, buflen, "/tmp/") &&
+ tryTempDir (buf, buflen, "/var/tmp/") &&
+#endif
+ tryTempDir (buf, buflen, getcwd (cwd, sizeof cwd)))
+ {
+ log_add (log_Fatal, "Fatal Error: Cannot find a suitable location "
+ "to store temporary files.");
+ exit (EXIT_FAILURE);
+ }
+}
+
+// Sets the global var 'tempDir'
+static int
+mountTempDir(const char *name) {
+ static uio_AutoMount *autoMount[] = { NULL };
+ uio_MountHandle *tempHandle;
+ extern uio_Repository *repository;
+
+ tempHandle = uio_mountDir (repository, "/tmp/",
+ uio_FSTYPE_STDIO, NULL, NULL, name, autoMount,
+ uio_MOUNT_TOP, NULL);
+ if (tempHandle == NULL) {
+ int saveErrno = errno;
+ log_add (log_Fatal, "Fatal error: Couldn't mount temp dir '%s': "
+ "%s", name, strerror (errno));
+ errno = saveErrno;
+ return -1;
+ }
+
+ tempDir = uio_openDir (repository, "/tmp", 0);
+ if (tempDir == NULL) {
+ int saveErrno = errno;
+ log_add (log_Fatal, "Fatal error: Could not open temp dir: %s",
+ strerror (errno));
+ errno = saveErrno;
+ return -1;
+ }
+ return 0;
+}
+
+#define NUM_TEMP_RETRIES 16
+ // Number of files to try to open before giving up.
+void
+initTempDir (void) {
+ size_t len;
+ DWORD num;
+ int i;
+ char *tempPtr;
+ // Pointer to the location in the tempDirName string where the
+ // path to the temp dir ends and the dir starts.
+
+ tempDirName = HMalloc (PATH_MAX);
+ getTempDir (tempDirName, PATH_MAX - 21);
+ // reserve 8 chars for dirname, 1 for slash, and 12 for filename
+ len = strlen(tempDirName);
+
+ num = ((DWORD) time (NULL));
+// num = GetTimeCounter () % 0xffffffff;
+ tempPtr = tempDirName + len;
+ for (i = 0; i < NUM_TEMP_RETRIES; i++)
+ {
+ sprintf (tempPtr, "%08x", num + i);
+ if (createDirectory (tempDirName, 0700) == -1)
+ continue;
+
+ // Success, we've got a temp dir.
+ tempDirName = HRealloc (tempDirName, len + 9);
+ atexit (removeTempDir);
+ if (mountTempDir (tempDirName) == -1)
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+ // Failure, could not make a temporary directory.
+ log_add (log_Fatal, "Fatal error: Cannot get a name for a temporary "
+ "directory.");
+ exit (EXIT_FAILURE);
+}
+
+void
+unInitTempDir (void) {
+ uio_closeDir(tempDir);
+ // the removing of the dir is handled via atexit
+}
+
+// return the path to a file in the temp dir with the specified filename.
+// returns a pointer to a static buffer.
+char *
+tempFilePath (const char *filename) {
+ static char file[PATH_MAX];
+
+ if (snprintf (file, PATH_MAX, "%s/%s", tempDirName, filename) == -1) {
+ log_add (log_Fatal, "Path to temp file too long.");
+ exit (EXIT_FAILURE);
+ }
+ return file;
+}
+
+
diff --git a/src/libs/gfxlib.h b/src/libs/gfxlib.h
new file mode 100644
index 0000000..9a16a69
--- /dev/null
+++ b/src/libs/gfxlib.h
@@ -0,0 +1,474 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GFXLIB_H_
+#define LIBS_GFXLIB_H_
+
+#include "port.h"
+#include "libs/compiler.h"
+
+typedef struct Color Color;
+struct Color {
+ BYTE r;
+ BYTE g;
+ BYTE b;
+ BYTE a;
+};
+
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct context_desc CONTEXT_DESC;
+typedef struct frame_desc FRAME_DESC;
+typedef struct font_desc FONT_DESC;
+typedef struct drawable_desc DRAWABLE_DESC;
+
+typedef CONTEXT_DESC *CONTEXT;
+typedef FRAME_DESC *FRAME;
+typedef FONT_DESC *FONT;
+typedef DRAWABLE_DESC *DRAWABLE;
+
+typedef UWORD TIME_VALUE;
+
+#define TIME_SHIFT 8
+#define MAX_TIME_VALUE ((1 << TIME_SHIFT) + 1)
+
+typedef SWORD COORD;
+
+static inline bool
+sameColor(Color c1, Color c2)
+{
+ return c1.r == c2.r &&
+ c1.g == c2.g &&
+ c1.b == c2.b &&
+ c1.a == c2.a;
+}
+
+// Transform a 5-bits color component to an 8-bits color component.
+// Form 1, calculates '(r5 / 31.0) * 255.0, highest value is 0xff:
+#define CC5TO8(c) (((c) << 3) | ((c) >> 2))
+// Form 2, calculates '(r5 / 32.0) * 256.0, highest value is 0xf8:
+//#define CC5TO8(c) ((c) << 3)
+
+#define BUILD_COLOR(col, c256) col
+ // BUILD_COLOR used to combine a 15-bit RGB color tripple with a
+ // destination VGA palette index into a 32-bit value.
+ // Now, it is an empty wrapper which returns the first argument,
+ // which is of type Color, and ignores the second argument,
+ // the palette index.
+ //
+ // It is a remnant of 8bpp hardware paletted display (VGA).
+ // The palette index would be overwritten with the RGB value
+ // and the drawing op would use this index on screen.
+ // The palette indices 0-15, as used in DOS SC2, are unchanged
+ // from the standard VGA palette and are identical to 16-color EGA.
+ // Various frames, borders, menus, etc. frequently refer to these
+ // first 16 colors and normally do not change the RGB values from
+ // the standard ones (see colors.h; most likely unchanged from SC1)
+ // The palette index is meaningless in UQM for the most part.
+ // New code should just use index 0.
+
+// Turn a 15 bits color into a 24-bits color.
+// r, g, and b are each 5-bits color components.
+static inline Color
+colorFromRgb15 (BYTE r, BYTE g, BYTE b)
+{
+ Color c;
+ c.r = CC5TO8 (r);
+ c.g = CC5TO8 (g);
+ c.b = CC5TO8 (b);
+ c.a = 0xff;
+
+ return c;
+}
+#define MAKE_RGB15(r, g, b) colorFromRgb15 ((r), (g), (b))
+
+#ifdef NOTYET /* Need C'99 support */
+#define MAKE_RGB15(r, g, b) (Color) { \
+ .r = CC5TO8 (r), \
+ .g = CC5TO8 (g), \
+ .b = CC5TO8 (b), \
+ .a = 0xff \
+ }
+#endif
+
+// Temporary, until we can use C'99 features. Then MAKE_RGB15 will be usable
+// anywhere.
+// This define is intended for global initialisations, where the
+// expression must be constant.
+#define MAKE_RGB15_INIT(r, g, b) { \
+ CC5TO8 (r), \
+ CC5TO8 (g), \
+ CC5TO8 (b), \
+ 0xff \
+ }
+
+static inline Color
+buildColorRgba (BYTE r, BYTE g, BYTE b, BYTE a)
+{
+ Color c;
+ c.r = r;
+ c.g = g;
+ c.b = b;
+ c.a = a;
+
+ return c;
+}
+#define BUILD_COLOR_RGBA(r, g, b, a) \
+ buildColorRgba ((r), (g), (b), (a))
+
+
+typedef BYTE CREATE_FLAGS;
+// WANT_MASK is deprecated (and non-functional). It used to generate a bitmap
+// of changed pixels for a target DRAWABLE, so that DRAW_SUBTRACTIVE could
+// paint background pixels over them, i.e. a revert draw. The backgrounds
+// are fully erased now instead.
+#define WANT_MASK (CREATE_FLAGS)(1 << 0)
+#define WANT_PIXMAP (CREATE_FLAGS)(1 << 1)
+// MAPPED_TO_DISPLAY is deprecated but still checked by LoadDisplayPixmap().
+// Its former use was to indicate a pre-scaled graphic for the display.
+#define MAPPED_TO_DISPLAY (CREATE_FLAGS)(1 << 2)
+#define WANT_ALPHA (CREATE_FLAGS)(1 << 3)
+
+typedef struct extent
+{
+ COORD width, height;
+} EXTENT;
+
+typedef struct point
+{
+ COORD x, y;
+} POINT;
+
+typedef struct stamp
+{
+ POINT origin;
+ FRAME frame;
+} STAMP;
+
+typedef struct rect
+{
+ POINT corner;
+ EXTENT extent;
+} RECT;
+
+typedef struct line
+{
+ POINT first, second;
+} LINE;
+
+static inline POINT
+MAKE_POINT (COORD x, COORD y)
+{
+ POINT pt = {x, y};
+ return pt;
+}
+
+static inline bool
+pointsEqual (POINT p1, POINT p2)
+{
+ return p1.x == p2.x && p1.y == p2.y;
+}
+
+static inline bool
+extentsEqual (EXTENT e1, EXTENT e2)
+{
+ return e1.width == e2.width && e1.height == e2.height;
+}
+
+static inline bool
+rectsEqual (RECT r1, RECT r2)
+{
+ return pointsEqual (r1.corner, r2.corner)
+ && extentsEqual (r1.extent, r2.extent);
+}
+
+static inline bool
+pointWithinRect (RECT r, POINT p)
+{
+ return p.x >= r.corner.x && p.y >= r.corner.y
+ && p.x < r.corner.x + r.extent.width
+ && p.y < r.corner.y + r.extent.height;
+}
+
+typedef enum
+{
+ ALIGN_LEFT,
+ ALIGN_CENTER,
+ ALIGN_RIGHT
+} TEXT_ALIGN;
+
+typedef enum
+{
+ VALIGN_TOP,
+ VALIGN_MIDDLE,
+ VALIGN_BOTTOM
+} TEXT_VALIGN;
+
+typedef struct text
+{
+ POINT baseline;
+ const UNICODE *pStr;
+ TEXT_ALIGN align;
+ COUNT CharCount;
+} TEXT;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "libs/strlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef STRING_TABLE COLORMAP_REF;
+typedef STRING COLORMAP;
+// COLORMAPPTR is really a pointer to colortable entry structure
+// which is documented in doc/devel/strtab, .ct files section
+typedef void *COLORMAPPTR;
+
+#include "graphics/prim.h"
+
+
+typedef BYTE BATCH_FLAGS;
+// This flag is currently unused but it might make sense to restore it
+#define BATCH_BUILD_PAGE (BATCH_FLAGS)(1 << 0)
+
+typedef struct
+{
+ TIME_VALUE last_time_val;
+ POINT EndPoint;
+ STAMP IntersectStamp;
+} INTERSECT_CONTROL;
+
+typedef BYTE INTERSECT_CODE;
+
+#define INTERSECT_LEFT (INTERSECT_CODE)(1 << 0)
+#define INTERSECT_TOP (INTERSECT_CODE)(1 << 1)
+#define INTERSECT_RIGHT (INTERSECT_CODE)(1 << 2)
+#define INTERSECT_BOTTOM (INTERSECT_CODE)(1 << 3)
+#define INTERSECT_NOCLIP (INTERSECT_CODE)(1 << 7)
+#define INTERSECT_ALL_SIDES (INTERSECT_CODE)(INTERSECT_LEFT | \
+ INTERSECT_TOP | \
+ INTERSECT_RIGHT | \
+ INTERSECT_BOTTOM)
+
+typedef POINT HOT_SPOT;
+
+extern HOT_SPOT MAKE_HOT_SPOT (COORD, COORD);
+
+extern INTERSECT_CODE BoxIntersect (RECT *pr1, RECT *pr2, RECT *printer);
+extern void BoxUnion (RECT *pr1, RECT *pr2, RECT *punion);
+
+typedef enum
+{
+ FadeAllToWhite = 250,
+ FadeSomeToWhite,
+ FadeAllToBlack,
+ FadeAllToColor,
+ FadeSomeToBlack,
+ FadeSomeToColor
+} ScreenFadeType;
+
+typedef enum
+{
+ DRAW_REPLACE = 0,
+ // Pixels in the target FRAME are replaced entirely.
+ // Non-stamp primitives with Color.a < 255 to RGB targets are
+ // equivalent to DRAW_ALPHA with (DrawMode.factor = Color.a),
+ // except the Text primitives.
+ // DrawMode.factor: ignored
+ // Text: supported (except DRAW_ALPHA via Color.a)
+ // RGBA sources (WANT_ALPHA): per-pixel alpha blending performed
+ // RGBA targets (WANT_ALPHA): replace directly supported
+ DRAW_ADDITIVE,
+ // Pixel channels of the source FRAME or Color channels of
+ // a primitive are modulated by (DrawMode.factor / 255) and added
+ // to the pixel channels of the target FRAME.
+ // DrawMode.factor range: -32767..32767 (negative values make
+ // draw subtractive); 255 = 1:1 ratio
+ // Text: not yet supported
+ // RGBA sources (WANT_ALPHA): alpha channel ignored
+ // RGBA targets (WANT_ALPHA): not yet supported
+ DRAW_ALPHA,
+ // Pixel channels of the source FRAME or Color channels of
+ // a primitive are modulated by (DrawMode.factor / 255) and added
+ // to the pixel channels of the target FRAME, modulated by
+ // (1 - DrawMode.factor / 255)
+ // DrawMode.factor range: 0..255; 255 = fully opaque
+ // Text: supported
+ // RGBA sources (WANT_ALPHA): alpha channel ignored
+ // RGBA targets (WANT_ALPHA): not yet supported
+
+ DRAW_DEFAULT = DRAW_REPLACE,
+} DrawKind;
+
+typedef struct
+{
+ BYTE kind;
+ SWORD factor;
+} DrawMode;
+
+#define DRAW_REPLACE_MODE MAKE_DRAW_MODE (DRAW_REPLACE, 0)
+#define DRAW_FACTOR_1 0xff
+
+static inline DrawMode
+MAKE_DRAW_MODE (DrawKind kind, SWORD factor)
+{
+ DrawMode mode;
+ mode.kind = kind;
+ mode.factor = factor;
+ return mode;
+}
+
+extern CONTEXT SetContext (CONTEXT Context);
+extern Color SetContextForeGroundColor (Color Color);
+extern Color GetContextForeGroundColor (void);
+extern Color SetContextBackGroundColor (Color Color);
+extern Color GetContextBackGroundColor (void);
+extern FRAME SetContextFGFrame (FRAME Frame);
+extern FRAME GetContextFGFrame (void);
+// Context cliprect defines the drawing bounds. Additionally, all
+// drawing positions (x,y) are relative to the cliprect corner.
+extern BOOLEAN SetContextClipRect (RECT *pRect);
+// The returned rect is always filled in. If the context cliprect
+// is undefined, the returned rect has foreground frame dimensions.
+extern BOOLEAN GetContextClipRect (RECT *pRect);
+// The actual origin will be orgOffset + context ClipRect.corner
+extern POINT SetContextOrigin (POINT orgOffset);
+extern DrawMode SetContextDrawMode (DrawMode);
+extern DrawMode GetContextDrawMode (void);
+// 'area' may be NULL to copy the entire CONTEXT cliprect
+// 'area' is relative to the CONTEXT cliprect
+extern DRAWABLE CopyContextRect (const RECT* area);
+
+extern TIME_VALUE DrawablesIntersect (INTERSECT_CONTROL *pControl0,
+ INTERSECT_CONTROL *pControl1, TIME_VALUE max_time_val);
+extern void DrawStamp (STAMP *pStamp);
+extern void DrawFilledStamp (STAMP *pStamp);
+extern void DrawPoint (POINT *pPoint);
+extern void DrawRectangle (RECT *pRect);
+extern void DrawFilledRectangle (RECT *pRect);
+extern void DrawLine (LINE *pLine);
+extern void font_DrawText (TEXT *pText);
+extern void font_DrawTracedText (TEXT *pText, Color text, Color trace);
+extern void DrawBatch (PRIMITIVE *pBasePrim, PRIM_LINKS PrimLinks,
+ BATCH_FLAGS BatchFlags);
+extern void BatchGraphics (void);
+extern void UnbatchGraphics (void);
+extern void FlushGraphics (void);
+extern void ClearDrawable (void);
+#ifdef DEBUG
+extern CONTEXT CreateContextAux (const char *name);
+#define CreateContext(name) CreateContextAux((name))
+#else /* if !defined(DEBUG) */
+extern CONTEXT CreateContextAux (void);
+#define CreateContext(name) CreateContextAux()
+#endif /* !defined(DEBUG) */
+extern BOOLEAN DestroyContext (CONTEXT ContextRef);
+extern DRAWABLE CreateDisplay (CREATE_FLAGS CreateFlags, SIZE *pwidth,
+ SIZE *pheight);
+extern DRAWABLE CreateDrawable (CREATE_FLAGS CreateFlags, SIZE width,
+ SIZE height, COUNT num_frames);
+extern BOOLEAN DestroyDrawable (DRAWABLE Drawable);
+extern BOOLEAN GetFrameRect (FRAME Frame, RECT *pRect);
+#ifdef DEBUG
+extern const char *GetContextName (CONTEXT context);
+extern CONTEXT GetFirstContext (void);
+extern CONTEXT GetNextContext (CONTEXT context);
+extern size_t GetContextCount (void);
+#endif /* DEBUG */
+
+extern HOT_SPOT SetFrameHot (FRAME Frame, HOT_SPOT HotSpot);
+extern HOT_SPOT GetFrameHot (FRAME Frame);
+extern BOOLEAN InstallGraphicResTypes (void);
+extern DRAWABLE LoadGraphicFile (const char *pStr);
+extern FONT LoadFontFile (const char *pStr);
+extern void *LoadGraphicInstance (RESOURCE res);
+extern DRAWABLE LoadDisplayPixmap (const RECT *area, FRAME frame);
+extern FRAME SetContextFontEffect (FRAME EffectFrame);
+extern FONT SetContextFont (FONT Font);
+extern BOOLEAN DestroyFont (FONT FontRef);
+// The returned pRect is relative to the context drawing origin
+extern BOOLEAN TextRect (TEXT *pText, RECT *pRect, BYTE *pdelta);
+extern BOOLEAN GetContextFontLeading (SIZE *pheight);
+extern BOOLEAN GetContextFontLeadingWidth (SIZE *pwidth);
+extern COUNT GetFrameCount (FRAME Frame);
+extern COUNT GetFrameIndex (FRAME Frame);
+extern FRAME SetAbsFrameIndex (FRAME Frame, COUNT FrameIndex);
+extern FRAME SetRelFrameIndex (FRAME Frame, SIZE FrameOffs);
+extern FRAME SetEquFrameIndex (FRAME DstFrame, FRAME SrcFrame);
+extern FRAME IncFrameIndex (FRAME Frame);
+extern FRAME DecFrameIndex (FRAME Frame);
+extern DRAWABLE CopyFrameRect (FRAME Frame, const RECT *area);
+extern DRAWABLE CloneFrame (FRAME Frame);
+extern DRAWABLE RotateFrame (FRAME Frame, int angle_deg);
+extern DRAWABLE RescaleFrame (FRAME, int width, int height);
+// This pair works for both paletted and trucolor frames
+extern BOOLEAN ReadFramePixelColors (FRAME frame, Color *pixels,
+ int width, int height);
+extern BOOLEAN WriteFramePixelColors (FRAME frame, const Color *pixels,
+ int width, int height);
+// This pair only works for paletted frames
+extern BOOLEAN ReadFramePixelIndexes (FRAME frame, BYTE *pixels,
+ int width, int height);
+extern BOOLEAN WriteFramePixelIndexes (FRAME frame, const BYTE *pixels,
+ int width, int height);
+extern void SetFrameTransparentColor (FRAME, Color);
+
+// If the frame is an active SCREEN_DRAWABLE, this call must be
+// preceeded by FlushGraphics() for draw commands to have taken effect
+extern Color GetFramePixel (FRAME, POINT pixelPt);
+
+extern FRAME CaptureDrawable (DRAWABLE Drawable);
+extern DRAWABLE ReleaseDrawable (FRAME Frame);
+
+extern DRAWABLE GetFrameParentDrawable (FRAME Frame);
+
+extern BOOLEAN SetColorMap (COLORMAPPTR ColorMapPtr);
+extern DWORD XFormColorMap (COLORMAPPTR ColorMapPtr, SIZE TimeInterval);
+extern DWORD FadeScreen (ScreenFadeType fadeType, SIZE TimeInterval);
+extern void FlushColorXForms (void);
+#define InitColorMapResources InitStringTableResources
+#define LoadColorMapFile LoadStringTableFile
+#define LoadColorMapInstance LoadStringTableInstance
+#define CaptureColorMap CaptureStringTable
+#define ReleaseColorMap ReleaseStringTable
+#define DestroyColorMap DestroyStringTable
+#define GetColorMapRef GetStringTable
+#define GetColorMapCount GetStringTableCount
+#define GetColorMapIndex GetStringTableIndex
+#define SetAbsColorMapIndex SetAbsStringTableIndex
+#define SetRelColorMapIndex SetRelStringTableIndex
+#define GetColorMapLength GetStringLengthBin
+
+extern COLORMAPPTR GetColorMapAddress (COLORMAP);
+
+void SetSystemRect (const RECT *pRect);
+void ClearSystemRect (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_GFXLIB_H_ */
diff --git a/src/libs/graphics/Makeinfo b/src/libs/graphics/Makeinfo
new file mode 100644
index 0000000..1a9f7ee
--- /dev/null
+++ b/src/libs/graphics/Makeinfo
@@ -0,0 +1,12 @@
+if [ "$uqm_GFXMODULE" = "sdl" ]; then
+ uqm_SUBDIRS="sdl"
+fi
+
+uqm_CFILES="boxint.c clipline.c cmap.c context.c drawable.c filegfx.c
+ bbox.c dcqueue.c gfxload.c
+ font.c frame.c gfx_common.c intersec.c loaddisp.c
+ pixmap.c resgfx.c tfb_draw.c tfb_prim.c widgets.c"
+
+uqm_HFILES="bbox.h cmap.h context.h dcqueue.h drawable.h drawcmd.h font.h
+ gfx_common.h gfxintrn.h prim.h tfb_draw.h tfb_prim.h widgets.h"
+
diff --git a/src/libs/graphics/bbox.c b/src/libs/graphics/bbox.c
new file mode 100644
index 0000000..ce57d32
--- /dev/null
+++ b/src/libs/graphics/bbox.c
@@ -0,0 +1,133 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/graphics/bbox.h"
+
+TFB_BoundingBox TFB_BBox;
+int maxWidth;
+int maxHeight;
+
+void
+TFB_BBox_Init (int width, int height)
+{
+ maxWidth = width;
+ maxHeight = height;
+ TFB_BBox.clip.extent.width = width;
+ TFB_BBox.clip.extent.height = height;
+}
+
+void
+TFB_BBox_Reset (void)
+{
+ TFB_BBox.valid = 0;
+}
+
+void
+TFB_BBox_SetClipRect (const RECT *r)
+{
+ if (!r)
+ { /* No clipping -- full rect */
+ TFB_BBox.clip.corner.x = 0;
+ TFB_BBox.clip.corner.y = 0;
+ TFB_BBox.clip.extent.width = maxWidth;
+ TFB_BBox.clip.extent.height = maxHeight;
+ return;
+ }
+
+ TFB_BBox.clip = *r;
+
+ /* Make sure the cliprect is sane */
+ if (TFB_BBox.clip.corner.x < 0)
+ TFB_BBox.clip.corner.x = 0;
+
+ if (TFB_BBox.clip.corner.y < 0)
+ TFB_BBox.clip.corner.y = 0;
+
+ if (TFB_BBox.clip.corner.x + TFB_BBox.clip.extent.width > maxWidth)
+ TFB_BBox.clip.extent.width = maxWidth - TFB_BBox.clip.corner.x;
+
+ if (TFB_BBox.clip.corner.y + TFB_BBox.clip.extent.height > maxHeight)
+ TFB_BBox.clip.extent.height = maxHeight - TFB_BBox.clip.corner.y;
+}
+
+void
+TFB_BBox_RegisterPoint (int x, int y)
+{
+ int x1 = TFB_BBox.clip.corner.x;
+ int y1 = TFB_BBox.clip.corner.y;
+ int x2 = TFB_BBox.clip.corner.x + TFB_BBox.clip.extent.width - 1;
+ int y2 = TFB_BBox.clip.corner.y + TFB_BBox.clip.extent.height - 1;
+
+ /* Constrain coordinates */
+ if (x < x1) x = x1;
+ if (x >= x2) x = x2;
+ if (y < y1) y = y1;
+ if (y >= y2) y = y2;
+
+ /* Is this the first point? If so, set a pixel-region and return. */
+ if (!TFB_BBox.valid)
+ {
+ TFB_BBox.valid = 1;
+ TFB_BBox.region.corner.x = x;
+ TFB_BBox.region.corner.y = y;
+ TFB_BBox.region.extent.width = 1;
+ TFB_BBox.region.extent.height = 1;
+ return;
+ }
+
+ /* Otherwise expand the rectangle if necessary. */
+ x1 = TFB_BBox.region.corner.x;
+ y1 = TFB_BBox.region.corner.y;
+ x2 = TFB_BBox.region.corner.x + TFB_BBox.region.extent.width - 1;
+ y2 = TFB_BBox.region.corner.y + TFB_BBox.region.extent.height - 1;
+
+ if (x < x1) {
+ TFB_BBox.region.corner.x = x;
+ TFB_BBox.region.extent.width += x1 - x;
+ }
+ if (y < y1) {
+ TFB_BBox.region.corner.y = y;
+ TFB_BBox.region.extent.height += y1 - y;
+ }
+ if (x > x2) {
+ TFB_BBox.region.extent.width += x - x2;
+ }
+ if (y > y2) {
+ TFB_BBox.region.extent.height += y - y2;
+ }
+}
+
+void
+TFB_BBox_RegisterRect (const RECT *r)
+{
+ /* RECT will still register as a corner point of the cliprect even
+ * if it does not intersect with the cliprect at all. This is not
+ * a problem, as more is not less. */
+ TFB_BBox_RegisterPoint (r->corner.x, r->corner.y);
+ TFB_BBox_RegisterPoint (r->corner.x + r->extent.width - 1,
+ r->corner.y + r->extent.height - 1);
+}
+
+void
+TFB_BBox_RegisterCanvas (TFB_Canvas c, int x, int y)
+{
+ RECT r;
+ r.corner.x = x;
+ r.corner.y = y;
+ TFB_DrawCanvas_GetExtent (c, &r.extent);
+ TFB_BBox_RegisterRect (&r);
+}
diff --git a/src/libs/graphics/bbox.h b/src/libs/graphics/bbox.h
new file mode 100644
index 0000000..33d3000
--- /dev/null
+++ b/src/libs/graphics/bbox.h
@@ -0,0 +1,46 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef BBOX_H_INCL__
+#define BBOX_H_INCL__
+
+#include "libs/gfxlib.h"
+#include "libs/graphics/tfb_draw.h"
+
+/* Bounding Box operations. These operations are NOT synchronized.
+ * However, they should only be accessed by TFB_FlushGraphics and
+ * TFB_SwapBuffers, or the routines that they exclusively call -- all
+ * of which are only callable by the thread that is permitted to touch
+ * the screen. No explicit locks should therefore be required. */
+
+typedef struct {
+ int valid; // If zero, the next point registered becomes the region
+ RECT region; // The actual modified rectangle
+ RECT clip; // Points outside of this rectangle are pushed to
+ // the closest border point
+} TFB_BoundingBox;
+
+extern TFB_BoundingBox TFB_BBox;
+
+void TFB_BBox_RegisterPoint (int x, int y);
+void TFB_BBox_RegisterRect (const RECT *r);
+void TFB_BBox_RegisterCanvas (TFB_Canvas c, int x, int y);
+
+void TFB_BBox_Init (int width, int height);
+void TFB_BBox_Reset (void);
+void TFB_BBox_SetClipRect (const RECT *r);
+
+#endif /* BBOX_H_INCL__ */
diff --git a/src/libs/graphics/boxint.c b/src/libs/graphics/boxint.c
new file mode 100644
index 0000000..0500311
--- /dev/null
+++ b/src/libs/graphics/boxint.c
@@ -0,0 +1,183 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+#undef MIN
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+#undef MAX
+#define MAX(a, b) (((a) >= (b)) ? (a) : (b))
+
+INTERSECT_CODE
+BoxIntersect (RECT *pr1, RECT *pr2, RECT *pinter)
+{
+ INTERSECT_CODE intersect_code;
+ COORD x1;
+ SIZE w1, w2, delta;
+
+ intersect_code = INTERSECT_NOCLIP;
+
+ x1 = pr1->corner.x - pr2->corner.x;
+
+ w1 = pr1->extent.width;
+ w2 = pr2->extent.width;
+ if ((delta = w2 - x1) <= w1)
+ {
+ if (delta != w1)
+ {
+ w1 = delta;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_RIGHT;
+ }
+ if (x1 <= 0)
+ {
+ if (x1 < 0)
+ {
+ w1 += x1;
+ x1 = 0;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_LEFT;
+ }
+
+ if (w1 > 0)
+ {
+#define h2 w2
+ COORD y1;
+ SIZE h1;
+
+ y1 = pr1->corner.y - pr2->corner.y;
+
+ h1 = pr1->extent.height;
+ h2 = pr2->extent.height;
+ if ((delta = h2 - y1) <= h1)
+ {
+ if (delta != h1)
+ {
+ h1 = delta;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_BOTTOM;
+ }
+ if (y1 <= 0)
+ {
+ if (y1 < 0)
+ {
+ h1 += y1;
+ y1 = 0;
+ intersect_code &= ~INTERSECT_NOCLIP;
+ }
+ intersect_code |= INTERSECT_TOP;
+ }
+
+ if (h1 > 0)
+ {
+ pinter->corner.x = x1 + pr2->corner.x;
+ pinter->corner.y = y1 + pr2->corner.y;
+ pinter->extent.width = w1;
+ pinter->extent.height = h1;
+
+ return (intersect_code);
+ }
+#undef h2
+ }
+
+ return ((INTERSECT_CODE)0);
+}
+
+void
+BoxUnion (RECT *pr1, RECT *pr2, RECT *punion)
+{
+#if NEVER // Part of lower FIXME.
+ COORD x2, y2, w2, h2;
+#endif // NEVER
+
+ // Union is A AND B, put together, correct? Returns a bigger box that
+ // encompasses the two.
+ punion->corner.x = MIN(pr1->corner.x, pr2->corner.x);
+ punion->corner.y = MIN(pr1->corner.y, pr2->corner.y);
+
+ punion->extent.width = MAX(pr1->corner.x + pr1->extent.width,
+ pr2->corner.x + pr2->extent.width) - punion->corner.x;
+ punion->extent.height = MAX(pr1->corner.y + pr1->extent.height,
+ pr2->corner.y + pr2->extent.height) - punion->corner.y;
+
+
+#if NEVER // FIXME - I think this is broken, but keeping it around for reference
+ // FIXME - just in case.
+
+#if 1 /* alter based on 0 widths */
+
+ x2 =
+ (pr1->corner.x < pr2->corner.x)? pr1->corner.x : pr2->corner.x;
+
+ y2 =
+ (pr1->corner.y < pr2->corner.y)? pr1->corner.y : pr2->corner.y;
+
+ w2 = (
+ ((pr1->corner.x + pr1->extent.width) > (pr2->corner.x + pr2->extent.width))?
+ (pr1->corner.x + pr1->extent.width) : (pr2->corner.x + pr2->extent.width)
+ ) - punion->corner.x;
+
+ h2 = (
+ ((pr1->corner.y + pr1->extent.height) > (pr2->corner.y + pr2->extent.height))?
+ (pr1->corner.y + pr1->extent.height) : (pr2->corner.y + pr2->extent.height)
+ ) - punion->corner.y;
+#else
+ SIZE delta;
+ COORD x1, y1, w1, h1;
+
+ x1 = pr1->corner.x;
+ w1 = pr1->extent.width;
+ x2 = pr2->corner.x;
+ w2 = pr2->extent.width;
+ if ((delta = x1 - x2) >= 0)
+ w1 += delta;
+ else
+ {
+ w2 -= delta;
+ x2 += delta;
+ }
+
+ y1 = pr1->corner.y;
+ h1 = pr1->extent.height;
+ y2 = pr2->corner.y;
+ h2 = pr2->extent.height;
+ if ((delta = y1 - y2) >= 0)
+ h1 += delta;
+ else
+ {
+ h2 -= delta;
+ y2 += delta;
+ }
+
+ if ((delta = w1 - w2) > 0)
+ w2 += delta;
+ if ((delta = h1 - h2) > 0)
+ h2 += delta;
+#endif
+
+ punion->corner.x = x2;
+ punion->corner.y = y2;
+ punion->extent.width = w2;
+ punion->extent.height = h2;
+
+#endif // NEVER
+}
+
diff --git a/src/libs/graphics/clipline.c b/src/libs/graphics/clipline.c
new file mode 100644
index 0000000..ab2d7dd
--- /dev/null
+++ b/src/libs/graphics/clipline.c
@@ -0,0 +1,241 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+INTERSECT_CODE
+_clip_line (const RECT *pClipRect, BRESENHAM_LINE *pLine)
+{
+ COORD p;
+ COORD x0, y0, xmin, ymin, xmax, ymax;
+ SIZE abs_delta_x, abs_delta_y;
+ INTERSECT_CODE intersect_code;
+
+ xmin = pClipRect->corner.x;
+ ymin = pClipRect->corner.y;
+ xmax = pClipRect->corner.x + pClipRect->extent.width - 1;
+ ymax = pClipRect->corner.y + pClipRect->extent.height - 1;
+ if (pLine->first.x <= pLine->second.x)
+ pLine->end_points_exchanged = FALSE;
+ else
+ {
+ p = pLine->first.x;
+ pLine->first.x = pLine->second.x;
+ pLine->second.x = p;
+
+ p = pLine->first.y;
+ pLine->first.y = pLine->second.y;
+ pLine->second.y = p;
+
+ pLine->end_points_exchanged = TRUE;
+ }
+
+ if (pLine->first.x > xmax || pLine->second.x < xmin ||
+ (pLine->first.y > ymax && pLine->second.y > ymax) ||
+ (pLine->first.y < ymin && pLine->second.y < ymin))
+ return ((INTERSECT_CODE)0);
+
+ intersect_code = INTERSECT_NOCLIP;
+ x0 = y0 = 0;
+ abs_delta_x = (pLine->second.x - pLine->first.x) << 1;
+ abs_delta_y = (pLine->second.y - pLine->first.y) << 1;
+ pLine->abs_delta_x = abs_delta_x;
+ pLine->abs_delta_y = abs_delta_y;
+ if (abs_delta_y == 0)
+ {
+ if (pLine->first.x < xmin)
+ {
+ pLine->first.x = xmin;
+ intersect_code |= INTERSECT_LEFT;
+ }
+ if (pLine->second.x > xmax)
+ {
+ pLine->second.x = xmax;
+ intersect_code |= INTERSECT_RIGHT;
+ }
+ }
+ else if (abs_delta_x == 0)
+ {
+ if (abs_delta_y < 0)
+ {
+ p = pLine->first.y;
+ pLine->first.y = pLine->second.y;
+ pLine->second.y = p;
+
+ pLine->abs_delta_y =
+ abs_delta_y = -abs_delta_y;
+ }
+
+ if (pLine->first.y < ymin)
+ {
+ pLine->first.y = ymin;
+ intersect_code |= INTERSECT_TOP;
+ }
+ if (pLine->second.y > ymax)
+ {
+ pLine->second.y = ymax;
+ intersect_code |= INTERSECT_BOTTOM;
+ }
+ }
+ else
+ {
+ COORD x1, y1;
+
+ p = pLine->first.x;
+ x1 = pLine->second.x - p;
+ xmin = xmin - p;
+ xmax = xmax - p;
+
+ p = pLine->first.y;
+ if (abs_delta_y > 0)
+ {
+ y1 = pLine->second.y - p;
+ ymin = ymin - p;
+ ymax = ymax - p;
+ }
+ else
+ {
+ y1 = p - pLine->second.y;
+ ymin = p - ymin;
+ ymax = p - ymax;
+
+ p = ymin;
+ ymin = ymax;
+ ymax = p;
+ abs_delta_y = -abs_delta_y;
+ }
+
+ if (abs_delta_x > abs_delta_y)
+ {
+ SIZE half_dx;
+
+ half_dx = abs_delta_x >> 1;
+ if (x0 < xmin)
+ {
+ if ((y0 = (COORD)(((long)abs_delta_y *
+ (x0 = xmin) + half_dx) / abs_delta_x)) > ymax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_LEFT;
+ }
+ if (x1 > xmax)
+ {
+ if ((y1 = (COORD)(((long)abs_delta_y *
+ (x1 = xmax) + half_dx) / abs_delta_x)) < ymin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_RIGHT;
+ }
+ if (y0 < ymin)
+ {
+ if ((x0 = (COORD)(((long)abs_delta_x *
+ (y0 = ymin) - half_dx + (abs_delta_y - 1)) /
+ abs_delta_y)) > xmax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_TOP;
+ intersect_code &= ~INTERSECT_LEFT;
+ }
+ if (y1 > ymax)
+ {
+ if ((x1 = (COORD)(((long)abs_delta_x *
+ ((y1 = ymax) + 1) - half_dx + (abs_delta_y - 1)) /
+ abs_delta_y) - 1) < xmin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_BOTTOM;
+ intersect_code &= ~INTERSECT_RIGHT;
+ }
+ }
+ else
+ {
+ SIZE half_dy;
+
+ half_dy = abs_delta_y >> 1;
+ if (y0 < ymin)
+ {
+ if ((x0 = (COORD)(((long)abs_delta_x *
+ (y0 = ymin) + half_dy) / abs_delta_y)) > xmax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_TOP;
+ }
+ if (y1 > ymax)
+ {
+ if ((x1 = (COORD)(((long)abs_delta_x *
+ (y1 = ymax) + half_dy) / abs_delta_y)) < xmin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_BOTTOM;
+ }
+ if (x0 < xmin)
+ {
+ if ((y0 = (COORD)(((long)abs_delta_y *
+ (x0 = xmin) - half_dy + (abs_delta_x - 1)) /
+ abs_delta_x)) > ymax)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_LEFT;
+ intersect_code &= ~INTERSECT_TOP;
+ }
+ if (x1 > xmax)
+ {
+ if ((y1 = (COORD)(((long)abs_delta_y *
+ ((x1 = xmax) + 1) - half_dy + (abs_delta_x - 1)) /
+ abs_delta_x) - 1) < ymin)
+ return ((INTERSECT_CODE)0);
+ intersect_code |= INTERSECT_RIGHT;
+ intersect_code &= ~INTERSECT_BOTTOM;
+ }
+ }
+
+ pLine->second.x = pLine->first.x + x1;
+ pLine->first.x += x0;
+ if (pLine->abs_delta_y > 0)
+ {
+ pLine->second.y = pLine->first.y + y1;
+ pLine->first.y += y0;
+ }
+ else
+ {
+ INTERSECT_CODE y_code;
+
+ pLine->second.y = pLine->first.y - y1;
+ pLine->first.y -= y0;
+
+ y_code = (INTERSECT_CODE)(intersect_code
+ & (INTERSECT_TOP | INTERSECT_BOTTOM));
+ if (y_code && y_code != (INTERSECT_TOP | INTERSECT_BOTTOM))
+ intersect_code ^= (INTERSECT_TOP | INTERSECT_BOTTOM);
+ }
+ }
+
+ if (!(intersect_code & INTERSECT_ALL_SIDES))
+ {
+ if (abs_delta_x > abs_delta_y)
+ pLine->error_term = -(SIZE)(abs_delta_x >> 1);
+ else
+ pLine->error_term = -(SIZE)(abs_delta_y >> 1);
+ }
+ else
+ {
+ intersect_code &= ~INTERSECT_NOCLIP;
+ if (abs_delta_x > abs_delta_y)
+ pLine->error_term = (SIZE)((x0 * (long)abs_delta_y) -
+ (y0 * (long)abs_delta_x)) - (abs_delta_x >> 1);
+ else
+ pLine->error_term = (SIZE)((y0 * (long)abs_delta_x) -
+ (x0 * (long)abs_delta_y)) - (abs_delta_y >> 1);
+ }
+
+ return (pLine->intersect_code = intersect_code);
+}
+
diff --git a/src/libs/graphics/cmap.c b/src/libs/graphics/cmap.c
new file mode 100644
index 0000000..53cd13f
--- /dev/null
+++ b/src/libs/graphics/cmap.c
@@ -0,0 +1,663 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/graphics/cmap.h"
+#include "libs/threadlib.h"
+#include "libs/timelib.h"
+#include "libs/inplib.h"
+#include "libs/strlib.h"
+ // for GetStringAddress()
+#include "libs/log.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+typedef struct xform_control
+{
+ int CMapIndex; // -1 means unused
+ COLORMAPPTR CMapPtr;
+ SIZE Ticks;
+ DWORD StartTime;
+ DWORD EndTime;
+ Color OldCMap[NUMBER_OF_PLUTVALS];
+} XFORM_CONTROL;
+
+#define MAX_XFORMS 16
+static struct
+{
+ XFORM_CONTROL TaskControl[MAX_XFORMS];
+ volatile int Highest;
+ // 'pending' is Highest >= 0
+ Mutex Lock;
+} XFormControl;
+
+static int fadeAmount = FADE_NORMAL_INTENSITY;
+static int fadeDelta;
+static TimeCount fadeStartTime;
+static sint32 fadeInterval;
+static Mutex fadeLock;
+
+#define SPARE_COLORMAPS 20
+
+// Colormaps are rapidly replaced in some parts of the game, so
+// it pays to have some spares on hand
+static TFB_ColorMap *poolhead;
+static int poolcount;
+
+static TFB_ColorMap * colormaps[MAX_COLORMAPS];
+static int mapcount;
+static Mutex maplock;
+
+
+static void release_colormap (TFB_ColorMap *map);
+static void delete_colormap (TFB_ColorMap *map);
+
+
+void
+InitColorMaps (void)
+{
+ int i;
+
+ // init colormaps
+ maplock = CreateMutex ("Colormaps Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+
+ // init xform control
+ XFormControl.Highest = -1;
+ XFormControl.Lock = CreateMutex ("Transform Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+ for (i = 0; i < MAX_XFORMS; ++i)
+ XFormControl.TaskControl[i].CMapIndex = -1;
+
+ fadeLock = CreateMutex ("Fade Lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+}
+
+void
+UninitColorMaps (void)
+{
+ int i;
+ TFB_ColorMap *next;
+
+ for (i = 0; i < MAX_COLORMAPS; ++i)
+ {
+ TFB_ColorMap *map = colormaps[i];
+ if (!map)
+ continue;
+ release_colormap (map);
+ colormaps[i] = 0;
+ }
+
+ // free spares
+ for ( ; poolhead; poolhead = next, --poolcount)
+ {
+ next = poolhead->next;
+ delete_colormap (poolhead);
+ }
+
+ DestroyMutex (fadeLock);
+ // uninit xform control
+ DestroyMutex (XFormControl.Lock);
+
+ // uninit colormaps
+ DestroyMutex (maplock);
+}
+
+static inline TFB_ColorMap *
+alloc_colormap (void)
+ // returns an addrefed object
+{
+ TFB_ColorMap *map;
+
+ if (poolhead)
+ { // have some spares
+ map = poolhead;
+ poolhead = map->next;
+ --poolcount;
+ }
+ else
+ { // no spares, need a new one
+ map = HMalloc (sizeof (*map));
+ map->palette = AllocNativePalette ();
+ if (!map->palette)
+ {
+ HFree (map);
+ return NULL;
+ }
+ }
+ map->next = NULL;
+ map->index = -1;
+ map->refcount = 1;
+ map->version = 0;
+
+ return map;
+}
+
+static TFB_ColorMap *
+clone_colormap (TFB_ColorMap *from, int index)
+ // returns an addrefed object
+{
+ TFB_ColorMap *map;
+
+ map = alloc_colormap ();
+ if (!map)
+ {
+ log_add (log_Warning, "FATAL: clone_colormap(): "
+ "could not allocate a map");
+ exit (EXIT_FAILURE);
+ }
+ else
+ { // fresh new map
+ map->index = index;
+ if (from)
+ map->version = from->version;
+ }
+ map->version++;
+
+ return map;
+}
+
+static void
+delete_colormap (TFB_ColorMap *map)
+{
+ FreeNativePalette (map->palette);
+ HFree (map);
+}
+
+static inline void
+free_colormap (TFB_ColorMap *map)
+{
+ if (!map)
+ {
+ log_add (log_Warning, "free_colormap(): tried to free a NULL map");
+ return;
+ }
+
+ if (poolcount < SPARE_COLORMAPS)
+ { // return to the spare pool
+ map->next = poolhead;
+ poolhead = map;
+ ++poolcount;
+ }
+ else
+ { // don't need any more spares
+ delete_colormap (map);
+ }
+}
+
+static inline TFB_ColorMap *
+get_colormap (int index)
+{
+ TFB_ColorMap *map;
+
+ map = colormaps[index];
+ if (!map)
+ {
+ log_add (log_Fatal, "BUG: get_colormap(): map not present");
+ exit (EXIT_FAILURE);
+ }
+
+ map->refcount++;
+ return map;
+}
+
+static void
+release_colormap (TFB_ColorMap *map)
+{
+ if (!map)
+ return;
+
+ if (map->refcount <= 0)
+ {
+ log_add (log_Warning, "BUG: release_colormap(): refcount not >0");
+ return;
+ }
+
+ map->refcount--;
+ if (map->refcount == 0)
+ free_colormap (map);
+}
+
+void
+TFB_ReturnColorMap (TFB_ColorMap *map)
+{
+ LockMutex (maplock);
+ release_colormap (map);
+ UnlockMutex (maplock);
+}
+
+TFB_ColorMap *
+TFB_GetColorMap (int index)
+{
+ TFB_ColorMap *map;
+
+ LockMutex (maplock);
+ map = get_colormap (index);
+ UnlockMutex (maplock);
+
+ return map;
+}
+
+void
+GetColorMapColors (Color *colors, TFB_ColorMap *map)
+{
+ int i;
+
+ if (!map)
+ return;
+
+ for (i = 0; i < NUMBER_OF_PLUTVALS; ++i)
+ colors[i] = GetNativePaletteColor (map->palette, i);
+}
+
+BOOLEAN
+SetColorMap (COLORMAPPTR map)
+{
+ int start, end;
+ int total_size;
+ UBYTE *colors = (UBYTE*)map;
+ TFB_ColorMap **mpp;
+
+ if (!map)
+ return TRUE;
+
+ start = *colors++;
+ end = *colors++;
+ if (start > end)
+ {
+ log_add (log_Warning, "ERROR: SetColorMap(): "
+ "starting map (%d) not less or eq ending (%d)",
+ start, end);
+ return FALSE;
+ }
+ if (start >= MAX_COLORMAPS)
+ {
+ log_add (log_Warning, "ERROR: SetColorMap(): "
+ "starting map (%d) beyond range (0-%d)",
+ start, (int)MAX_COLORMAPS - 1);
+ return FALSE;
+ }
+ if (end >= MAX_COLORMAPS)
+ {
+ log_add (log_Warning, "SetColorMap(): "
+ "ending map (%d) beyond range (0-%d)\n",
+ end, (int)MAX_COLORMAPS - 1);
+ end = MAX_COLORMAPS - 1;
+ }
+
+ total_size = end + 1;
+
+ LockMutex (maplock);
+
+ if (total_size > mapcount)
+ mapcount = total_size;
+
+ // parse the supplied PLUTs into our colormaps
+ for (mpp = colormaps + start; start <= end; ++start, ++mpp)
+ {
+ int i;
+ TFB_ColorMap *newmap;
+ TFB_ColorMap *oldmap;
+
+ oldmap = *mpp;
+ newmap = clone_colormap (oldmap, start);
+
+ for (i = 0; i < NUMBER_OF_PLUTVALS; ++i, colors += PLUTVAL_BYTE_SIZE)
+ {
+ Color color;
+
+ color.a = 0xff;
+ color.r = colors[PLUTVAL_RED];
+ color.g = colors[PLUTVAL_GREEN];
+ color.b = colors[PLUTVAL_BLUE];
+ SetNativePaletteColor (newmap->palette, i, color);
+ }
+
+ *mpp = newmap;
+ release_colormap (oldmap);
+ }
+
+ UnlockMutex (maplock);
+
+ return TRUE;
+}
+
+/* Fade Transforms */
+
+int
+GetFadeAmount (void)
+{
+ int newAmount;
+
+ LockMutex (fadeLock);
+
+ if (fadeInterval)
+ { // have a pending fade
+ TimeCount Now = GetTimeCounter ();
+ sint32 elapsed;
+
+ elapsed = Now - fadeStartTime;
+ if (elapsed > fadeInterval)
+ elapsed = fadeInterval;
+
+ newAmount = fadeAmount + (long)fadeDelta * elapsed / fadeInterval;
+
+ if (elapsed >= fadeInterval)
+ { // fade is over
+ fadeAmount = newAmount;
+ fadeInterval = 0;
+ }
+ }
+ else
+ { // no fade pending, return the current
+ newAmount = fadeAmount;
+ }
+
+ UnlockMutex (fadeLock);
+
+ return newAmount;
+}
+
+static void
+finishPendingFade (void)
+{
+ if (fadeInterval)
+ { // end the fade immediately
+ fadeAmount += fadeDelta;
+ fadeInterval = 0;
+ }
+}
+
+static void
+FlushFadeXForms (void)
+{
+ LockMutex (fadeLock);
+ finishPendingFade ();
+ UnlockMutex (fadeLock);
+}
+
+DWORD
+FadeScreen (ScreenFadeType fadeType, SIZE TimeInterval)
+{
+ TimeCount TimeOut;
+ int FadeEnd;
+
+ switch (fadeType)
+ {
+ case FadeAllToBlack:
+ case FadeSomeToBlack:
+ FadeEnd = FADE_NO_INTENSITY;
+ break;
+ case FadeAllToColor:
+ case FadeSomeToColor:
+ FadeEnd = FADE_NORMAL_INTENSITY;
+ break;
+ case FadeAllToWhite:
+ case FadeSomeToWhite:
+ FadeEnd = FADE_FULL_INTENSITY;
+ break;
+ default:
+ return (GetTimeCounter ());
+ }
+
+ // Don't make users wait for fades
+ if (QuitPosted)
+ TimeInterval = 0;
+
+ LockMutex (fadeLock);
+
+ finishPendingFade ();
+
+ if (TimeInterval <= 0)
+ { // end the fade immediately
+ fadeAmount = FadeEnd;
+ // cancel any pending fades
+ fadeInterval = 0;
+ TimeOut = GetTimeCounter ();
+ }
+ else
+ {
+ fadeInterval = TimeInterval;
+ fadeDelta = FadeEnd - fadeAmount;
+ fadeStartTime = GetTimeCounter ();
+ TimeOut = fadeStartTime + TimeInterval + 1;
+ }
+
+ UnlockMutex (fadeLock);
+
+ return TimeOut;
+}
+
+/* Colormap Transforms */
+
+static void
+finish_colormap_xform (int which)
+{
+ SetColorMap (XFormControl.TaskControl[which].CMapPtr);
+ XFormControl.TaskControl[which].CMapIndex = -1;
+ // check Highest ptr
+ if (which == XFormControl.Highest)
+ {
+ do
+ --which;
+ while (which >= 0 && XFormControl.TaskControl[which].CMapIndex == -1);
+
+ XFormControl.Highest = which;
+ }
+}
+
+static inline BYTE
+blendChan (BYTE c1, BYTE c2, int weight, int scale)
+{
+ return c1 + ((int)c2 - c1) * weight / scale;
+}
+
+/* This gives the XFormColorMap task a timeslice to do its thing
+ * Only one thread should ever be allowed to be calling this at any time
+ */
+BOOLEAN
+XFormColorMap_step (void)
+{
+ BOOLEAN Changed = FALSE;
+ int x;
+ DWORD Now = GetTimeCounter ();
+
+ LockMutex (XFormControl.Lock);
+
+ for (x = 0; x <= XFormControl.Highest; ++x)
+ {
+ XFORM_CONTROL *control = &XFormControl.TaskControl[x];
+ int index = control->CMapIndex;
+ int TicksLeft = control->EndTime - Now;
+ TFB_ColorMap *curmap;
+
+ if (index < 0)
+ continue; // unused slot
+
+ LockMutex (maplock);
+
+ curmap = colormaps[index];
+ if (!curmap)
+ {
+ UnlockMutex (maplock);
+ log_add (log_Error, "BUG: XFormColorMap_step(): no current map");
+ finish_colormap_xform (x);
+ continue;
+ }
+
+ if (TicksLeft > 0)
+ {
+#define XFORM_SCALE 0x10000
+ TFB_ColorMap *newmap = NULL;
+ UBYTE *newClr;
+ Color *oldClr;
+ int frac;
+ int i;
+
+ newmap = clone_colormap (curmap, index);
+
+ oldClr = control->OldCMap;
+ newClr = (UBYTE*)control->CMapPtr + 2;
+
+ frac = (int)(control->Ticks - TicksLeft) * XFORM_SCALE
+ / control->Ticks;
+
+ for (i = 0; i < NUMBER_OF_PLUTVALS; ++i, ++oldClr,
+ newClr += PLUTVAL_BYTE_SIZE)
+ {
+ Color color;
+
+ color.a = 0xff;
+ color.r = blendChan (oldClr->r, newClr[PLUTVAL_RED],
+ frac, XFORM_SCALE);
+ color.g = blendChan (oldClr->g, newClr[PLUTVAL_GREEN],
+ frac, XFORM_SCALE);
+ color.b = blendChan (oldClr->b, newClr[PLUTVAL_BLUE],
+ frac, XFORM_SCALE);
+ SetNativePaletteColor (newmap->palette, i, color);
+ }
+
+ colormaps[index] = newmap;
+ release_colormap (curmap);
+ }
+
+ UnlockMutex (maplock);
+
+ if (TicksLeft <= 0)
+ { // asked for immediate xform or already done
+ finish_colormap_xform (x);
+ }
+
+ Changed = TRUE;
+ }
+
+ UnlockMutex (XFormControl.Lock);
+
+ return Changed;
+}
+
+static void
+FlushPLUTXForms (void)
+{
+ int i;
+
+ LockMutex (XFormControl.Lock);
+
+ for (i = 0; i <= XFormControl.Highest; ++i)
+ {
+ if (XFormControl.TaskControl[i].CMapIndex >= 0)
+ finish_colormap_xform (i);
+ }
+ XFormControl.Highest = -1; // all gone
+
+ UnlockMutex (XFormControl.Lock);
+}
+
+static DWORD
+XFormPLUT (COLORMAPPTR ColorMapPtr, SIZE TimeInterval)
+{
+ TFB_ColorMap *map;
+ XFORM_CONTROL *control;
+ int index;
+ int x;
+ int first_avail = -1;
+ DWORD EndTime;
+ DWORD Now;
+
+ Now = GetTimeCounter ();
+ index = *(UBYTE*)ColorMapPtr;
+
+ LockMutex (XFormControl.Lock);
+ // Find an available slot, or reuse if required
+ for (x = 0; x <= XFormControl.Highest
+ && index != XFormControl.TaskControl[x].CMapIndex;
+ ++x)
+ {
+ if (first_avail == -1 && XFormControl.TaskControl[x].CMapIndex == -1)
+ first_avail = x;
+ }
+
+ if (index == XFormControl.TaskControl[x].CMapIndex)
+ { // already xforming this colormap -- cancel and reuse slot
+ finish_colormap_xform (x);
+ }
+ else if (first_avail >= 0)
+ { // picked up a slot along the way
+ x = first_avail;
+ }
+ else if (x >= MAX_XFORMS)
+ { // flush some xforms if the queue is full
+ log_add (log_Debug, "WARNING: XFormPLUT(): no slots available");
+ x = XFormControl.Highest;
+ finish_colormap_xform (x);
+ }
+ // take next unused one
+ control = &XFormControl.TaskControl[x];
+ if (x > XFormControl.Highest)
+ XFormControl.Highest = x;
+
+ // make a copy of the current map
+ LockMutex (maplock);
+ map = colormaps[index];
+ if (!map)
+ {
+ UnlockMutex (maplock);
+ UnlockMutex (XFormControl.Lock);
+ log_add (log_Warning, "BUG: XFormPLUT(): no current map");
+ return (0);
+ }
+ GetColorMapColors (control->OldCMap, map);
+ UnlockMutex (maplock);
+
+ control->CMapIndex = index;
+ control->CMapPtr = ColorMapPtr;
+ control->Ticks = TimeInterval;
+ if (control->Ticks < 0)
+ control->Ticks = 0; /* prevent negative fade */
+ control->StartTime = Now;
+ control->EndTime = EndTime = Now + control->Ticks;
+
+ UnlockMutex (XFormControl.Lock);
+
+ return (EndTime);
+}
+
+DWORD
+XFormColorMap (COLORMAPPTR ColorMapPtr, SIZE TimeInterval)
+{
+ if (!ColorMapPtr)
+ return (0);
+
+ // Don't make users wait for transforms
+ if (QuitPosted)
+ TimeInterval = 0;
+
+ return XFormPLUT (ColorMapPtr, TimeInterval);
+}
+
+void
+FlushColorXForms (void)
+{
+ FlushFadeXForms ();
+ FlushPLUTXForms ();
+}
+
+// The type conversions are implicit and will generate errors
+// or warnings if types change imcompatibly
+COLORMAPPTR
+GetColorMapAddress (COLORMAP colormap)
+{
+ return GetStringAddress (colormap);
+}
diff --git a/src/libs/graphics/cmap.h b/src/libs/graphics/cmap.h
new file mode 100644
index 0000000..f27f789
--- /dev/null
+++ b/src/libs/graphics/cmap.h
@@ -0,0 +1,77 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CMAP_H
+#define CMAP_H
+
+#include "libs/gfxlib.h"
+
+#define MAX_COLORMAPS 250
+
+// These are pertinent to colortable file format
+// We load colormaps as binary and parse them when needed
+#define PLUTVAL_BYTE_SIZE 3
+// Channel order in colormap tables
+#define PLUTVAL_RED 0
+#define PLUTVAL_GREEN 1
+#define PLUTVAL_BLUE 2
+
+#define NUMBER_OF_PLUTVALS 256
+// Size of the colormap in a colortable file
+#define PLUT_BYTE_SIZE (PLUTVAL_BYTE_SIZE * NUMBER_OF_PLUTVALS)
+
+#define FADE_NO_INTENSITY 0
+#define FADE_NORMAL_INTENSITY 255
+#define FADE_FULL_INTENSITY 510
+
+typedef struct NativePalette NativePalette;
+
+typedef struct tfb_colormap
+{
+ int index;
+ // Colormap index as the game sees it
+ int version;
+ // Version goes up every time the colormap changes. This may
+ // be due to SetColorMap() or at every transformation step
+ // of XFormColorMap(). Paletted TFB_Images track the last
+ // colormap version they were drawn with for optimization.
+ int refcount;
+ struct tfb_colormap *next;
+ // for spares linking
+ NativePalette *palette;
+} TFB_ColorMap;
+
+extern int GetFadeAmount (void);
+
+extern void InitColorMaps (void);
+extern void UninitColorMaps (void);
+
+extern void GetColorMapColors (Color *colors, TFB_ColorMap *);
+
+extern TFB_ColorMap * TFB_GetColorMap (int index);
+extern void TFB_ReturnColorMap (TFB_ColorMap *map);
+
+extern BOOLEAN XFormColorMap_step (void);
+
+// Native
+NativePalette* AllocNativePalette (void);
+void FreeNativePalette (NativePalette *);
+void SetNativePaletteColor (NativePalette *, int index, Color);
+Color GetNativePaletteColor (NativePalette *, int index);
+
+#endif /* CMAP_H */
diff --git a/src/libs/graphics/context.c b/src/libs/graphics/context.c
new file mode 100644
index 0000000..d609ded
--- /dev/null
+++ b/src/libs/graphics/context.c
@@ -0,0 +1,404 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+GRAPHICS_STATUS _GraphicsStatusFlags;
+CONTEXT _pCurContext;
+
+#ifdef DEBUG
+// We keep track of all contexts
+CONTEXT firstContext;
+ // The first one in the list.
+CONTEXT *contextEnd = &firstContext;
+ // Where to put the next context.
+#endif
+
+PRIMITIVE _locPrim;
+
+FONT _CurFontPtr;
+
+#define DEFAULT_FORE_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+#define DEFAULT_BACK_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+
+#define DEFAULT_DRAW_MODE MAKE_DRAW_MODE (DRAW_DEFAULT, 255)
+
+CONTEXT
+SetContext (CONTEXT Context)
+{
+ CONTEXT LastContext;
+
+ LastContext = _pCurContext;
+ if (Context != LastContext)
+ {
+ if (LastContext)
+ {
+ UnsetContextFlags (
+ MAKE_WORD (0, GRAPHICS_ACTIVE | DRAWABLE_ACTIVE));
+ SetContextFlags (
+ MAKE_WORD (0, _GraphicsStatusFlags
+ & (GRAPHICS_ACTIVE | DRAWABLE_ACTIVE)));
+
+ DeactivateContext ();
+ }
+
+ _pCurContext = Context;
+ if (_pCurContext)
+ {
+ ActivateContext ();
+
+ _GraphicsStatusFlags &= ~(GRAPHICS_ACTIVE | DRAWABLE_ACTIVE);
+ _GraphicsStatusFlags |= HIBYTE (_get_context_flags ());
+
+ SetPrimColor (&_locPrim, _get_context_fg_color ());
+
+ _CurFramePtr = _get_context_fg_frame ();
+ _CurFontPtr = _get_context_font ();
+ }
+ }
+
+ return (LastContext);
+}
+
+#ifdef DEBUG
+CONTEXT
+CreateContextAux (const char *name)
+#else /* if !defined(DEBUG) */
+CONTEXT
+CreateContextAux (void)
+#endif /* !defined(DEBUG) */
+{
+ CONTEXT NewContext;
+
+ NewContext = AllocContext ();
+ if (NewContext)
+ {
+ /* initialize context */
+#ifdef DEBUG
+ NewContext->name = name;
+ NewContext->next = NULL;
+ *contextEnd = NewContext;
+ contextEnd = &NewContext->next;
+#endif /* DEBUG */
+
+ NewContext->Mode = DEFAULT_DRAW_MODE;
+ NewContext->ForeGroundColor = DEFAULT_FORE_COLOR;
+ NewContext->BackGroundColor = DEFAULT_BACK_COLOR;
+ }
+
+ return NewContext;
+}
+
+#ifdef DEBUG
+// Loop through the list of context to the pointer which points to the
+// specified context. This is either 'firstContext' or the address of
+// the 'next' field of some other context.
+static CONTEXT *
+FindContextPtr (CONTEXT context) {
+ CONTEXT *ptr;
+
+ for (ptr = &firstContext; *ptr != NULL; ptr = &(*ptr)->next) {
+ if (*ptr == context)
+ break;
+ }
+ return ptr;
+}
+#endif /* DEBUG */
+
+BOOLEAN
+DestroyContext (CONTEXT ContextRef)
+{
+ TFB_Image *img;
+
+ if (ContextRef == 0)
+ return (FALSE);
+
+ if (_pCurContext && _pCurContext == ContextRef)
+ SetContext ((CONTEXT)0);
+
+#ifdef DEBUG
+ // Unlink the context.
+ {
+ CONTEXT *contextPtr = FindContextPtr (ContextRef);
+ if (contextEnd == &ContextRef->next)
+ contextEnd = contextPtr;
+ *contextPtr = ContextRef->next;
+ }
+#endif /* DEBUG */
+
+ img = ContextRef->FontBacking;
+ if (img)
+ TFB_DrawImage_Delete (img);
+
+ FreeContext (ContextRef);
+ return TRUE;
+}
+
+Color
+SetContextForeGroundColor (Color color)
+{
+ Color oldColor;
+
+ if (!ContextActive ())
+ return DEFAULT_FORE_COLOR;
+
+ oldColor = _get_context_fg_color ();
+ if (!sameColor(oldColor, color))
+ {
+ SwitchContextForeGroundColor (color);
+
+ if (!(_get_context_fbk_flags () & FBK_IMAGE))
+ {
+ SetContextFBkFlags (FBK_DIRTY);
+ }
+ }
+ SetPrimColor (&_locPrim, color);
+
+ return (oldColor);
+}
+
+Color
+GetContextForeGroundColor (void)
+{
+ if (!ContextActive ())
+ return DEFAULT_FORE_COLOR;
+
+ return _get_context_fg_color ();
+}
+
+Color
+SetContextBackGroundColor (Color color)
+{
+ Color oldColor;
+
+ if (!ContextActive ())
+ return DEFAULT_BACK_COLOR;
+
+ oldColor = _get_context_bg_color ();
+ if (!sameColor(oldColor, color))
+ SwitchContextBackGroundColor (color);
+
+ return oldColor;
+}
+
+Color
+GetContextBackGroundColor (void)
+{
+ if (!ContextActive ())
+ return DEFAULT_BACK_COLOR;
+
+ return _get_context_bg_color ();
+}
+
+DrawMode
+SetContextDrawMode (DrawMode mode)
+{
+ DrawMode oldMode;
+
+ if (!ContextActive ())
+ return DEFAULT_DRAW_MODE;
+
+ oldMode = _get_context_draw_mode ();
+ SwitchContextDrawMode (mode);
+
+ return oldMode;
+}
+
+DrawMode
+GetContextDrawMode (void)
+{
+ if (!ContextActive ())
+ return DEFAULT_DRAW_MODE;
+
+ return _get_context_draw_mode ();
+}
+
+// Returns a rect based at 0,0 and the size of context foreground frame
+static inline RECT
+_get_context_fg_rect (void)
+{
+ RECT r = { {0, 0}, {0, 0} };
+ if (_CurFramePtr)
+ r.extent = GetFrameBounds (_CurFramePtr);
+ return r;
+}
+
+BOOLEAN
+SetContextClipRect (RECT *lpRect)
+{
+ if (!ContextActive ())
+ return (FALSE);
+
+ if (lpRect)
+ {
+ if (rectsEqual (*lpRect, _get_context_fg_rect ()))
+ { // Cliprect is undefined to mirror GetContextClipRect()
+ _pCurContext->ClipRect.extent.width = 0;
+ }
+ else
+ { // We have a cliprect
+ _pCurContext->ClipRect = *lpRect;
+ }
+ }
+ else
+ { // Set cliprect as undefined
+ _pCurContext->ClipRect.extent.width = 0;
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+GetContextClipRect (RECT *lpRect)
+{
+ if (!ContextActive ())
+ return (FALSE);
+
+ *lpRect = _pCurContext->ClipRect;
+ if (!_pCurContext->ClipRect.extent.width)
+ { // Though the cliprect is undefined, drawing will be clipped
+ // to the extent of the foreground frame
+ *lpRect = _get_context_fg_rect ();
+ }
+
+ return (_pCurContext->ClipRect.extent.width != 0);
+}
+
+POINT
+SetContextOrigin (POINT orgOffset)
+{
+ // XXX: This is a hack, kind of. But that's what the original did.
+ return SetFrameHot (_CurFramePtr, orgOffset);
+}
+
+FRAME
+SetContextFontEffect (FRAME EffectFrame)
+{
+ FRAME LastEffect;
+
+ if (!ContextActive ())
+ return (NULL);
+
+ LastEffect = _get_context_fonteff ();
+ if (EffectFrame != LastEffect)
+ {
+ SwitchContextFontEffect (EffectFrame);
+
+ if (EffectFrame != 0)
+ {
+ SetContextFBkFlags (FBK_IMAGE);
+ }
+ else
+ {
+ UnsetContextFBkFlags (FBK_IMAGE);
+ }
+ }
+
+ return LastEffect;
+}
+
+void
+FixContextFontEffect (void)
+{
+ SIZE w, h;
+ TFB_Image* img;
+
+ if (!ContextActive () || (_get_context_font_backing () != 0
+ && !(_get_context_fbk_flags () & FBK_DIRTY)))
+ return;
+
+ if (!GetContextFontLeading (&h) || !GetContextFontLeadingWidth (&w))
+ return;
+
+ img = _pCurContext->FontBacking;
+ if (img)
+ TFB_DrawScreen_DeleteImage (img);
+
+ img = TFB_DrawImage_CreateForScreen (w, h, TRUE);
+ if (_get_context_fbk_flags () & FBK_IMAGE)
+ { // image pattern backing
+ FRAME EffectFrame = _get_context_fonteff ();
+
+ TFB_DrawImage_Image (EffectFrame->image,
+ -EffectFrame->HotSpot.x, -EffectFrame->HotSpot.y,
+ 0, 0, NULL, DRAW_REPLACE_MODE, img);
+ }
+ else
+ { // solid color backing
+ RECT r = { {0, 0}, {w, h} };
+ Color color = _get_context_fg_color ();
+
+ TFB_DrawImage_Rect (&r, color, DRAW_REPLACE_MODE, img);
+ }
+
+ _pCurContext->FontBacking = img;
+ UnsetContextFBkFlags (FBK_DIRTY);
+}
+
+// 'area' may be NULL to copy the entire CONTEXT cliprect
+// 'area' is relative to the CONTEXT cliprect
+DRAWABLE
+CopyContextRect (const RECT* area)
+{
+ RECT clipRect;
+ RECT fgRect;
+ RECT r;
+
+ if (!ContextActive () || !_CurFramePtr)
+ return NULL;
+
+ fgRect = _get_context_fg_rect ();
+ GetContextClipRect (&clipRect);
+ r = clipRect;
+ if (area)
+ { // a portion of the context
+ r.corner.x += area->corner.x;
+ r.corner.y += area->corner.y;
+ r.extent = area->extent;
+ }
+ // TODO: Should this take CONTEXT origin into account too?
+ // validate the rect
+ if (!BoxIntersect (&r, &fgRect, &r))
+ return NULL;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ return LoadDisplayPixmap (&r, NULL);
+ else
+ return CopyFrameRect (_CurFramePtr, &r);
+}
+
+#ifdef DEBUG
+const char *
+GetContextName (CONTEXT context)
+{
+ return context->name;
+}
+
+CONTEXT
+GetFirstContext (void)
+{
+ return firstContext;
+}
+
+CONTEXT
+GetNextContext (CONTEXT context)
+{
+ return context->next;
+}
+#endif /* DEBUG */
+
diff --git a/src/libs/graphics/context.h b/src/libs/graphics/context.h
new file mode 100644
index 0000000..09b50cf
--- /dev/null
+++ b/src/libs/graphics/context.h
@@ -0,0 +1,147 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_CONTEXT_H_
+#define LIBS_GRAPHICS_CONTEXT_H_
+
+#include "tfb_draw.h"
+#include "libs/memlib.h"
+
+typedef UWORD FBK_FLAGS;
+#define FBK_DIRTY (1 << 0)
+#define FBK_IMAGE (1 << 1)
+
+struct context_desc
+{
+ UWORD Flags;
+ // Low nibble currently unused
+ // High nibble contains GRAPHICS_STATUS
+
+ Color ForeGroundColor, BackGroundColor;
+ DrawMode Mode;
+ FRAME ForeGroundFrame;
+ FONT Font;
+
+ RECT ClipRect;
+
+ FRAME FontEffect;
+ TFB_Image *FontBacking;
+ FBK_FLAGS BackingFlags;
+
+#ifdef DEBUG
+ const char *name;
+ CONTEXT next;
+#endif
+};
+
+#define AllocContext() HCalloc (sizeof (CONTEXT_DESC))
+#define FreeContext HFree
+
+extern CONTEXT _pCurContext;
+extern PRIMITIVE _locPrim;
+
+#define _get_context_fg_color() (_pCurContext->ForeGroundColor)
+#define _get_context_bg_color() (_pCurContext->BackGroundColor)
+#define _get_context_flags() (_pCurContext->Flags)
+#define _get_context_fg_frame() (_pCurContext->ForeGroundFrame)
+#define _get_context_font() (_pCurContext->Font)
+#define _get_context_fbk_flags() (_pCurContext->BackingFlags)
+#define _get_context_fonteff() (_pCurContext->FontEffect)
+#define _get_context_font_backing() (_pCurContext->FontBacking)
+#define _get_context_draw_mode() (_pCurContext->Mode)
+
+#define SwitchContextDrawMode(m) \
+{ \
+ _pCurContext->Mode = (m); \
+}
+#define SwitchContextForeGroundColor(c) \
+{ \
+ _pCurContext->ForeGroundColor = (c); \
+}
+#define SwitchContextBackGroundColor(c) \
+{ \
+ _pCurContext->BackGroundColor = (c); \
+}
+#define SetContextFlags(f) \
+{ \
+ _pCurContext->Flags |= (f); \
+}
+#define UnsetContextFlags(f) \
+{ \
+ _pCurContext->Flags &= ~(f); \
+}
+#define SwitchContextFGFrame(f) \
+{ \
+ _pCurContext->ForeGroundFrame = (f); \
+}
+#define SwitchContextFont(f) \
+{ \
+ _pCurContext->Font = (f); \
+ SetContextFBkFlags (FBK_DIRTY); \
+}
+#define SwitchContextBGFunc(f) \
+{ \
+ _pCurContext->BackGroundFunc = (f); \
+}
+#define SetContextFBkFlags(f) \
+{ \
+ _pCurContext->BackingFlags |= (f); \
+}
+#define UnsetContextFBkFlags(f) \
+{ \
+ _pCurContext->BackingFlags &= ~(f); \
+}
+#define SwitchContextFontEffect(f) \
+{ \
+ _pCurContext->FontEffect = (f); \
+ SetContextFBkFlags (FBK_DIRTY); \
+}
+
+typedef BYTE GRAPHICS_STATUS;
+
+extern GRAPHICS_STATUS _GraphicsStatusFlags;
+#define GRAPHICS_ACTIVE (GRAPHICS_STATUS)(1 << 0)
+#define GRAPHICS_VISIBLE (GRAPHICS_STATUS)(1 << 1)
+#define CONTEXT_ACTIVE (GRAPHICS_STATUS)(1 << 2)
+#define DRAWABLE_ACTIVE (GRAPHICS_STATUS)(1 << 3)
+#define DeactivateGraphics() (_GraphicsStatusFlags &= ~GRAPHICS_ACTIVE)
+#define ActivateGraphics() (_GraphicsStatusFlags |= GRAPHICS_ACTIVE)
+#define GraphicsActive() (_GraphicsStatusFlags & GRAPHICS_ACTIVE)
+#define DeactivateVisible() (_GraphicsStatusFlags &= ~GRAPHICS_VISIBLE)
+#define ActivateVisible() (_GraphicsStatusFlags |= GRAPHICS_VISIBLE)
+#define DeactivateContext() (_GraphicsStatusFlags &= ~CONTEXT_ACTIVE)
+#define ActivateContext() (_GraphicsStatusFlags |= CONTEXT_ACTIVE)
+#define ContextActive() (_GraphicsStatusFlags & CONTEXT_ACTIVE)
+#define DeactivateDrawable() (_GraphicsStatusFlags &= ~DRAWABLE_ACTIVE)
+#define ActivateDrawable() (_GraphicsStatusFlags |= DRAWABLE_ACTIVE)
+#define DrawableActive() (_GraphicsStatusFlags & DRAWABLE_ACTIVE)
+
+#define SYSTEM_ACTIVE (GRAPHICS_STATUS)(CONTEXT_ACTIVE | DRAWABLE_ACTIVE)
+
+#define GraphicsSystemActive() \
+ ((_GraphicsStatusFlags & SYSTEM_ACTIVE) == SYSTEM_ACTIVE)
+#define GraphicsStatus() \
+ (_GraphicsStatusFlags & (GRAPHICS_STATUS)(GRAPHICS_ACTIVE \
+ | GRAPHICS_VISIBLE))
+
+// pValidRect or origin may be NULL
+BOOLEAN GetContextValidRect (RECT *pValidRect, POINT *origin);
+extern void FixContextFontEffect (void);
+
+#endif /* LIBS_GRAPHICS_CONTEXT_H_ */
+
diff --git a/src/libs/graphics/dcqueue.c b/src/libs/graphics/dcqueue.c
new file mode 100644
index 0000000..70c0662
--- /dev/null
+++ b/src/libs/graphics/dcqueue.c
@@ -0,0 +1,670 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/threadlib.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/context.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/bbox.h"
+#include "libs/timelib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+ // for TFB_DEBUG_HALT
+
+
+static RecursiveMutex DCQ_Mutex;
+
+CondVar RenderingCond;
+
+TFB_DrawCommand DCQ[DCQ_MAX];
+
+TFB_DrawCommandQueue DrawCommandQueue;
+
+#define FPS_PERIOD (ONE_SECOND / 100)
+int RenderedFrames = 0;
+
+
+// Wait for the queue to be emptied.
+static void
+TFB_WaitForSpace (int requested_slots)
+{
+ int old_depth, i;
+ log_add (log_Debug, "DCQ overload (Size = %d, FullSize = %d, "
+ "Requested = %d). Sleeping until renderer is done.",
+ DrawCommandQueue.Size, DrawCommandQueue.FullSize,
+ requested_slots);
+ // Restore the DCQ locking level. I *think* this is
+ // always 1, but...
+ TFB_BatchReset ();
+ old_depth = GetRecursiveMutexDepth (DCQ_Mutex);
+ for (i = 0; i < old_depth; i++)
+ UnlockRecursiveMutex (DCQ_Mutex);
+ WaitCondVar (RenderingCond);
+ for (i = 0; i < old_depth; i++)
+ LockRecursiveMutex (DCQ_Mutex);
+ log_add (log_Debug, "DCQ clear (Size = %d, FullSize = %d). Continuing.",
+ DrawCommandQueue.Size, DrawCommandQueue.FullSize);
+}
+
+void
+Lock_DCQ (int slots)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ while (DrawCommandQueue.FullSize >= DCQ_MAX - slots)
+ {
+ TFB_WaitForSpace (slots);
+ }
+}
+
+void
+Unlock_DCQ (void)
+{
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+// Always have the DCQ locked when calling this.
+static void
+Synchronize_DCQ (void)
+{
+ if (!DrawCommandQueue.Batching)
+ {
+ int front = DrawCommandQueue.Front;
+ int back = DrawCommandQueue.InsertionPoint;
+ DrawCommandQueue.Back = DrawCommandQueue.InsertionPoint;
+ if (front <= back)
+ {
+ DrawCommandQueue.Size = (back - front);
+ }
+ else
+ {
+ DrawCommandQueue.Size = (back + DCQ_MAX - front);
+ }
+ DrawCommandQueue.FullSize = DrawCommandQueue.Size;
+ }
+}
+
+void
+TFB_BatchGraphics (void)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ DrawCommandQueue.Batching++;
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+void
+TFB_UnbatchGraphics (void)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ if (DrawCommandQueue.Batching)
+ {
+ DrawCommandQueue.Batching--;
+ }
+ Synchronize_DCQ ();
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+// Cancel all pending batch operations, making them unbatched. This will
+// cause a small amount of flicker when invoked, but prevents
+// batching problems from freezing the game.
+void
+TFB_BatchReset (void)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ DrawCommandQueue.Batching = 0;
+ Synchronize_DCQ ();
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+
+// Draw Command Queue Stuff
+
+void
+Init_DrawCommandQueue (void)
+{
+ DrawCommandQueue.Back = 0;
+ DrawCommandQueue.Front = 0;
+ DrawCommandQueue.InsertionPoint = 0;
+ DrawCommandQueue.Batching = 0;
+ DrawCommandQueue.FullSize = 0;
+ DrawCommandQueue.Size = 0;
+
+ TFB_BBox_Init (ScreenWidth, ScreenHeight);
+
+ DCQ_Mutex = CreateRecursiveMutex ("DCQ",
+ SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+
+ RenderingCond = CreateCondVar ("DCQ empty",
+ SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
+}
+
+void
+Uninit_DrawCommandQueue (void)
+{
+ if (RenderingCond)
+ {
+ DestroyCondVar (RenderingCond);
+ RenderingCond = 0;
+ }
+
+ if (DCQ_Mutex)
+ {
+ DestroyRecursiveMutex (DCQ_Mutex);
+ DCQ_Mutex = 0;
+ }
+}
+
+void
+TFB_DrawCommandQueue_Push (TFB_DrawCommand* Command)
+{
+ Lock_DCQ (1);
+ DCQ[DrawCommandQueue.InsertionPoint] = *Command;
+ DrawCommandQueue.InsertionPoint = (DrawCommandQueue.InsertionPoint + 1)
+ % DCQ_MAX;
+ DrawCommandQueue.FullSize++;
+ Synchronize_DCQ ();
+ Unlock_DCQ ();
+}
+
+int
+TFB_DrawCommandQueue_Pop (TFB_DrawCommand *target)
+{
+ LockRecursiveMutex (DCQ_Mutex);
+
+ if (DrawCommandQueue.Size == 0)
+ {
+ Unlock_DCQ ();
+ return (0);
+ }
+
+ if (DrawCommandQueue.Front == DrawCommandQueue.Back &&
+ DrawCommandQueue.Size != DCQ_MAX)
+ {
+ log_add (log_Debug, "Augh! Assertion failure in DCQ! "
+ "Front == Back, Size != DCQ_MAX");
+ DrawCommandQueue.Size = 0;
+ Unlock_DCQ ();
+ return (0);
+ }
+
+ *target = DCQ[DrawCommandQueue.Front];
+ DrawCommandQueue.Front = (DrawCommandQueue.Front + 1) % DCQ_MAX;
+
+ DrawCommandQueue.Size--;
+ DrawCommandQueue.FullSize--;
+ UnlockRecursiveMutex (DCQ_Mutex);
+
+ return 1;
+}
+
+void
+TFB_DrawCommandQueue_Clear ()
+{
+ LockRecursiveMutex (DCQ_Mutex);
+ DrawCommandQueue.Size = 0;
+ DrawCommandQueue.Front = 0;
+ DrawCommandQueue.Back = 0;
+ DrawCommandQueue.Batching = 0;
+ DrawCommandQueue.FullSize = 0;
+ DrawCommandQueue.InsertionPoint = 0;
+ UnlockRecursiveMutex (DCQ_Mutex);
+}
+
+static void
+checkExclusiveThread (TFB_DrawCommand* DrawCommand)
+{
+#ifdef DEBUG_DCQ_THREADS
+ static uint32 exclusiveThreadId;
+ extern uint32 SDL_ThreadID(void);
+
+ // Only one thread is currently allowed to enqueue commands
+ // This is not a technical limitation but rather a semantical one atm.
+ if (DrawCommand->Type == TFB_DRAWCOMMANDTYPE_REINITVIDEO)
+ { // TFB_DRAWCOMMANDTYPE_REINITVIDEO is an exception
+ // It is queued from the main() thread, which is safe to do
+ return;
+ }
+
+ if (!exclusiveThreadId)
+ exclusiveThreadId = SDL_ThreadID();
+ else
+ assert (SDL_ThreadID() == exclusiveThreadId);
+#else
+ (void) DrawCommand; // suppress unused warning
+#endif
+}
+
+void
+TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand)
+{
+ if (TFB_DEBUG_HALT)
+ {
+ return;
+ }
+
+ checkExclusiveThread (DrawCommand);
+
+ if (DrawCommand->Type <= TFB_DRAWCOMMANDTYPE_COPYTOIMAGE
+ && _CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ static RECT scissor_rect;
+
+ // Set the clipping region.
+ // We allow drawing with no current context set, so the whole screen
+ if ((_pCurContext && !rectsEqual (scissor_rect, _pCurContext->ClipRect))
+ || (!_pCurContext && scissor_rect.extent.width != 0))
+ {
+ // Enqueue command to set the glScissor spec
+ TFB_DrawCommand DC;
+
+ if (_pCurContext)
+ scissor_rect = _pCurContext->ClipRect;
+ else
+ scissor_rect.extent.width = 0;
+
+ if (scissor_rect.extent.width)
+ {
+ DC.Type = TFB_DRAWCOMMANDTYPE_SCISSORENABLE;
+ DC.data.scissor.rect = scissor_rect;
+ }
+ else
+ {
+ DC.Type = TFB_DRAWCOMMANDTYPE_SCISSORDISABLE;
+ }
+
+ TFB_EnqueueDrawCommand(&DC);
+ }
+ }
+
+ TFB_DrawCommandQueue_Push (DrawCommand);
+}
+
+static void
+computeFPS (void)
+{
+ static TimeCount last_time;
+ static TimePeriod fps_counter;
+ TimeCount current_time;
+ TimePeriod delta_time;
+
+ current_time = GetTimeCounter ();
+ delta_time = current_time - last_time;
+ last_time = current_time;
+
+ fps_counter += delta_time;
+ if (fps_counter > FPS_PERIOD)
+ {
+ log_add (log_User, "fps %.2f, effective %.2f",
+ (float)ONE_SECOND / delta_time,
+ (float)ONE_SECOND * RenderedFrames / fps_counter);
+
+ fps_counter = 0;
+ RenderedFrames = 0;
+ }
+}
+
+// Only call from main() thread!!
+void
+TFB_FlushGraphics (void)
+{
+ int commands_handled;
+ BOOLEAN livelock_deterrence;
+
+ // This is technically a locking violation on DrawCommandQueue.Size,
+ // but it is likely to not be very destructive.
+ if (DrawCommandQueue.Size == 0)
+ {
+ static int last_fade = 255;
+ static int last_transition = 255;
+ int current_fade = GetFadeAmount ();
+ int current_transition = TransitionAmount;
+
+ if ((current_fade != 255 && current_fade != last_fade) ||
+ (current_transition != 255 &&
+ current_transition != last_transition) ||
+ (current_fade == 255 && last_fade != 255) ||
+ (current_transition == 255 && last_transition != 255))
+ {
+ TFB_SwapBuffers (TFB_REDRAW_FADING);
+ // if fading, redraw every frame
+ }
+ else
+ {
+ TaskSwitch ();
+ }
+
+ last_fade = current_fade;
+ last_transition = current_transition;
+ BroadcastCondVar (RenderingCond);
+ return;
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SHOWFPS)
+ computeFPS ();
+
+ commands_handled = 0;
+ livelock_deterrence = FALSE;
+
+ if (DrawCommandQueue.FullSize > DCQ_FORCE_BREAK_SIZE)
+ {
+ TFB_BatchReset ();
+ }
+
+ if (DrawCommandQueue.Size > DCQ_FORCE_SLOWDOWN_SIZE)
+ {
+ Lock_DCQ (-1);
+ livelock_deterrence = TRUE;
+ }
+
+ TFB_BBox_Reset ();
+
+ for (;;)
+ {
+ TFB_DrawCommand DC;
+
+ if (!TFB_DrawCommandQueue_Pop (&DC))
+ {
+ // the Queue is now empty.
+ break;
+ }
+
+ ++commands_handled;
+ if (!livelock_deterrence && commands_handled + DrawCommandQueue.Size
+ > DCQ_LIVELOCK_MAX)
+ {
+ // log_add (log_Debug, "Initiating livelock deterrence!");
+ livelock_deterrence = TRUE;
+
+ Lock_DCQ (-1);
+ }
+
+ switch (DC.Type)
+ {
+ case TFB_DRAWCOMMANDTYPE_SETMIPMAP:
+ {
+ TFB_DrawCommand_SetMipmap *cmd = &DC.data.setmipmap;
+ TFB_DrawImage_SetMipmap (cmd->image, cmd->mipmap,
+ cmd->hotx, cmd->hoty);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_IMAGE:
+ {
+ TFB_DrawCommand_Image *cmd = &DC.data.image;
+ TFB_Image *DC_image = cmd->image;
+ const int x = cmd->x;
+ const int y = cmd->y;
+
+ TFB_DrawCanvas_Image (DC_image, x, y,
+ cmd->scale, cmd->scaleMode, cmd->colormap,
+ cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ LockMutex (DC_image->mutex);
+ if (cmd->scale)
+ TFB_BBox_RegisterCanvas (DC_image->ScaledImg,
+ x - DC_image->last_scale_hs.x,
+ y - DC_image->last_scale_hs.y);
+ else
+ TFB_BBox_RegisterCanvas (DC_image->NormalImg,
+ x - DC_image->NormalHs.x,
+ y - DC_image->NormalHs.y);
+ UnlockMutex (DC_image->mutex);
+ }
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_FILLEDIMAGE:
+ {
+ TFB_DrawCommand_FilledImage *cmd = &DC.data.filledimage;
+ TFB_Image *DC_image = cmd->image;
+ const int x = cmd->x;
+ const int y = cmd->y;
+
+ TFB_DrawCanvas_FilledImage (DC_image, x, y,
+ cmd->scale, cmd->scaleMode, cmd->color,
+ cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ LockMutex (DC_image->mutex);
+ if (cmd->scale)
+ TFB_BBox_RegisterCanvas (DC_image->ScaledImg,
+ x - DC_image->last_scale_hs.x,
+ y - DC_image->last_scale_hs.y);
+ else
+ TFB_BBox_RegisterCanvas (DC_image->NormalImg,
+ x - DC_image->NormalHs.x,
+ y - DC_image->NormalHs.y);
+ UnlockMutex (DC_image->mutex);
+ }
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_FONTCHAR:
+ {
+ TFB_DrawCommand_FontChar *cmd = &DC.data.fontchar;
+ TFB_Char *DC_char = cmd->fontchar;
+ const int x = cmd->x;
+ const int y = cmd->y;
+
+ TFB_DrawCanvas_FontChar (DC_char, cmd->backing, x, y,
+ cmd->drawMode, TFB_GetScreenCanvas (cmd->destBuffer));
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ RECT r;
+
+ r.corner.x = x - DC_char->HotSpot.x;
+ r.corner.y = y - DC_char->HotSpot.y;
+ r.extent.width = DC_char->extent.width;
+ r.extent.height = DC_char->extent.height;
+
+ TFB_BBox_RegisterRect (&r);
+ }
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_LINE:
+ {
+ TFB_DrawCommand_Line *cmd = &DC.data.line;
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ {
+ TFB_BBox_RegisterPoint (cmd->x1, cmd->y1);
+ TFB_BBox_RegisterPoint (cmd->x2, cmd->y2);
+ }
+ TFB_DrawCanvas_Line (cmd->x1, cmd->y1, cmd->x2, cmd->y2,
+ cmd->color, cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_RECTANGLE:
+ {
+ TFB_DrawCommand_Rect *cmd = &DC.data.rect;
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ TFB_BBox_RegisterRect (&cmd->rect);
+ TFB_DrawCanvas_Rect (&cmd->rect, cmd->color, cmd->drawMode,
+ TFB_GetScreenCanvas (cmd->destBuffer));
+
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_SCISSORENABLE:
+ {
+ TFB_DrawCommand_Scissor *cmd = &DC.data.scissor;
+
+ TFB_DrawCanvas_SetClipRect (
+ TFB_GetScreenCanvas (TFB_SCREEN_MAIN), &cmd->rect);
+ TFB_BBox_SetClipRect (&DC.data.scissor.rect);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_SCISSORDISABLE:
+ TFB_DrawCanvas_SetClipRect (
+ TFB_GetScreenCanvas (TFB_SCREEN_MAIN), NULL);
+ TFB_BBox_SetClipRect (NULL);
+ break;
+
+ case TFB_DRAWCOMMANDTYPE_COPYTOIMAGE:
+ {
+ TFB_DrawCommand_CopyToImage *cmd = &DC.data.copytoimage;
+ TFB_Image *DC_image = cmd->image;
+ const POINT dstPt = {0, 0};
+
+ if (DC_image == 0)
+ {
+ log_add (log_Debug, "DCQ ERROR: COPYTOIMAGE passed null "
+ "image ptr");
+ break;
+ }
+ LockMutex (DC_image->mutex);
+ TFB_DrawCanvas_CopyRect (
+ TFB_GetScreenCanvas (cmd->srcBuffer), &cmd->rect,
+ DC_image->NormalImg, dstPt);
+ UnlockMutex (DC_image->mutex);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_COPY:
+ {
+ TFB_DrawCommand_Copy *cmd = &DC.data.copy;
+ const RECT r = cmd->rect;
+
+ if (cmd->destBuffer == TFB_SCREEN_MAIN)
+ TFB_BBox_RegisterRect (&cmd->rect);
+
+ TFB_DrawCanvas_CopyRect (
+ TFB_GetScreenCanvas (cmd->srcBuffer), &r,
+ TFB_GetScreenCanvas (cmd->destBuffer), r.corner);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_DELETEIMAGE:
+ {
+ TFB_Image *DC_image = DC.data.deleteimage.image;
+ TFB_DrawImage_Delete (DC_image);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_DELETEDATA:
+ {
+ void *data = DC.data.deletedata.data;
+ HFree (data);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_SENDSIGNAL:
+ ClearSemaphore (DC.data.sendsignal.sem);
+ break;
+
+ case TFB_DRAWCOMMANDTYPE_REINITVIDEO:
+ {
+ TFB_DrawCommand_ReinitVideo *cmd = &DC.data.reinitvideo;
+ int oldDriver = GraphicsDriver;
+ int oldFlags = GfxFlags;
+ int oldWidth = ScreenWidthActual;
+ int oldHeight = ScreenHeightActual;
+ if (TFB_ReInitGraphics (cmd->driver, cmd->flags,
+ cmd->width, cmd->height))
+ {
+ log_add (log_Error, "Could not provide requested mode: "
+ "reverting to last known driver.");
+ // We don't know what exactly failed, so roll it all back
+ if (TFB_ReInitGraphics (oldDriver, oldFlags,
+ oldWidth, oldHeight))
+ {
+ log_add (log_Fatal,
+ "Couldn't reinit at that point either. "
+ "Your video has been somehow tied in knots.");
+ exit (EXIT_FAILURE);
+ }
+ }
+ TFB_SwapBuffers (TFB_REDRAW_YES);
+ break;
+ }
+
+ case TFB_DRAWCOMMANDTYPE_CALLBACK:
+ {
+ DC.data.callback.callback (DC.data.callback.arg);
+ break;
+ }
+ }
+ }
+
+ if (livelock_deterrence)
+ Unlock_DCQ ();
+
+ TFB_SwapBuffers (TFB_REDRAW_NO);
+ RenderedFrames++;
+ BroadcastCondVar (RenderingCond);
+}
+
+void
+TFB_PurgeDanglingGraphics (void)
+{
+ Lock_DCQ (-1);
+
+ for (;;)
+ {
+ TFB_DrawCommand DC;
+
+ if (!TFB_DrawCommandQueue_Pop (&DC))
+ {
+ // the Queue is now empty.
+ break;
+ }
+
+ switch (DC.Type)
+ {
+ case TFB_DRAWCOMMANDTYPE_DELETEIMAGE:
+ {
+ TFB_Image *DC_image = DC.data.deleteimage.image;
+ TFB_DrawImage_Delete (DC_image);
+ break;
+ }
+ case TFB_DRAWCOMMANDTYPE_DELETEDATA:
+ {
+ void *data = DC.data.deletedata.data;
+ HFree (data);
+ break;
+ }
+ case TFB_DRAWCOMMANDTYPE_IMAGE:
+ {
+ TFB_ColorMap *cmap = DC.data.image.colormap;
+ if (cmap)
+ TFB_ReturnColorMap (cmap);
+ break;
+ }
+ case TFB_DRAWCOMMANDTYPE_SENDSIGNAL:
+ {
+ ClearSemaphore (DC.data.sendsignal.sem);
+ break;
+ }
+ }
+ }
+ Unlock_DCQ ();
+}
diff --git a/src/libs/graphics/dcqueue.h b/src/libs/graphics/dcqueue.h
new file mode 100644
index 0000000..deed685
--- /dev/null
+++ b/src/libs/graphics/dcqueue.h
@@ -0,0 +1,55 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DCQUEUE_H
+#define DCQUEUE_H
+
+// Maximum size of the DCQ. The larger the DCQ, the larger frameskips
+// become tolerable before initiating livelock deterrence and game
+// slowdown. Other constants for controlling the frameskip/slowdown
+// balance may be found in sdl_common.c near TFB_FlushGraphics.
+
+// Livelock deterrance constants. Because the entire screen is rarely
+// refreshed, we may not drop draw commands on the floor with abandon.
+// Furthermore, if the main program is queuing commands at a speed
+// comparable to our processing of the commands, we never finish and
+// the game freezes. Thus, if the queue starts out larger than
+// DCQ_FORCE_SLOWDOWN_SIZE, or DCQ_LIVELOCK_MAX commands find
+// themselves being processed in one go, livelock deterrence is
+// enabled, and TFB_FlushGraphics locks the DCQ until it has processed
+// all entries. If batched but pending commands exceed DCQ_FORCE_BREAK_SIZE,
+// a continuity break is performed. This will effectively slow down the
+// game logic, a fate we seek to avoid - however, it seems to be unavoidable
+// on slower machines. Even there, it's seems nonexistent outside of
+// communications screens. --Michael
+
+#ifdef DCQ_OF_DOOM
+#define DCQ_MAX 512
+#define DCQ_FORCE_SLOWDOWN_SIZE 128
+#define DCQ_FORCE_BREAK_SIZE 512
+#define DCQ_LIVELOCK_MAX 256
+#else
+#define DCQ_MAX 16384
+#define DCQ_FORCE_SLOWDOWN_SIZE 4096
+#define DCQ_FORCE_BREAK_SIZE 16384
+#define DCQ_LIVELOCK_MAX 4096
+#endif
+
+extern CondVar RenderingCond;
+
+#endif
+
+
diff --git a/src/libs/graphics/drawable.c b/src/libs/graphics/drawable.c
new file mode 100644
index 0000000..9766bc7
--- /dev/null
+++ b/src/libs/graphics/drawable.c
@@ -0,0 +1,501 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/gfxlib.h"
+#include "libs/graphics/context.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/memlib.h"
+#include "tfb_draw.h"
+#include <math.h>
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+
+FRAME _CurFramePtr;
+
+FRAME
+SetContextFGFrame (FRAME Frame)
+{
+ FRAME LastFrame;
+
+ if (Frame != (LastFrame = (FRAME)_CurFramePtr))
+ {
+ if (LastFrame)
+ DeactivateDrawable ();
+
+ _CurFramePtr = Frame;
+ if (_CurFramePtr)
+ ActivateDrawable ();
+
+ if (ContextActive ())
+ {
+ SwitchContextFGFrame (Frame);
+ }
+ }
+
+ return (LastFrame);
+}
+
+FRAME
+GetContextFGFrame (void)
+{
+ return _CurFramePtr;
+}
+
+static DRAWABLE
+request_drawable (COUNT NumFrames, DRAWABLE_TYPE DrawableType,
+ CREATE_FLAGS flags, SIZE width, SIZE height)
+{
+ DRAWABLE Drawable;
+ COUNT i;
+
+ Drawable = AllocDrawable (NumFrames);
+ if (!Drawable)
+ return NULL;
+
+ Drawable->Flags = flags;
+ Drawable->MaxIndex = NumFrames - 1;
+
+ for (i = 0; i < NumFrames; ++i)
+ {
+ FRAME FramePtr = &Drawable->Frame[i];
+
+ if (DrawableType == RAM_DRAWABLE && width > 0 && height > 0)
+ {
+ FramePtr->image = TFB_DrawImage_New (TFB_DrawCanvas_New_TrueColor (
+ width, height, (flags & WANT_ALPHA) ? TRUE : FALSE));
+ }
+
+ FramePtr->Type = DrawableType;
+ FramePtr->Index = i;
+ SetFrameBounds (FramePtr, width, height);
+ }
+
+ return Drawable;
+}
+
+DRAWABLE
+CreateDisplay (CREATE_FLAGS CreateFlags, SIZE *pwidth, SIZE *pheight)
+{
+ DRAWABLE Drawable;
+
+ // TODO: ScreenWidth and ScreenHeight should be passed in
+ // instead of returned.
+ Drawable = request_drawable (1, SCREEN_DRAWABLE,
+ (CreateFlags & (WANT_PIXMAP | WANT_MASK)),
+ ScreenWidth, ScreenHeight);
+ if (Drawable)
+ {
+ FRAME F;
+
+ F = CaptureDrawable (Drawable);
+ if (F == 0)
+ DestroyDrawable (Drawable);
+ else
+ {
+ *pwidth = GetFrameWidth (F);
+ *pheight = GetFrameHeight (F);
+
+ ReleaseDrawable (F);
+ return (Drawable);
+ }
+ }
+
+ *pwidth = *pheight = 0;
+ return (0);
+}
+
+DRAWABLE
+AllocDrawable (COUNT n)
+{
+ DRAWABLE Drawable;
+ Drawable = (DRAWABLE) HCalloc(sizeof (DRAWABLE_DESC));
+ if (Drawable)
+ {
+ int i;
+ Drawable->Frame = (FRAME)HMalloc (sizeof (FRAME_DESC) * n);
+ if (Drawable->Frame == NULL)
+ {
+ HFree (Drawable);
+ return NULL;
+ }
+
+ /* Zero out the newly allocated frames, since HMalloc doesn't have
+ * MEM_ZEROINIT. */
+ for (i = 0; i < n; i++) {
+ FRAME F;
+ F = &Drawable->Frame[i];
+ F->parent = Drawable;
+ F->Type = 0;
+ F->Index = 0;
+ F->image = 0;
+ F->Bounds.width = 0;
+ F->Bounds.height = 0;
+ F->HotSpot.x = 0;
+ F->HotSpot.y = 0;
+ }
+ }
+ return Drawable;
+}
+
+DRAWABLE
+CreateDrawable (CREATE_FLAGS CreateFlags, SIZE width, SIZE height, COUNT
+ num_frames)
+{
+ DRAWABLE Drawable;
+
+ Drawable = request_drawable (num_frames, RAM_DRAWABLE,
+ (CreateFlags & (WANT_MASK | WANT_PIXMAP
+ | WANT_ALPHA | MAPPED_TO_DISPLAY)),
+ width, height);
+ if (Drawable)
+ {
+ FRAME F;
+
+ F = CaptureDrawable (Drawable);
+ if (F)
+ {
+ ReleaseDrawable (F);
+
+ return (Drawable);
+ }
+ }
+
+ return (0);
+}
+
+BOOLEAN
+DestroyDrawable (DRAWABLE Drawable)
+{
+ if (_CurFramePtr && (Drawable == _CurFramePtr->parent))
+ SetContextFGFrame ((FRAME)NULL);
+
+ if (Drawable)
+ {
+ FreeDrawable (Drawable);
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+BOOLEAN
+GetFrameRect (FRAME FramePtr, RECT *pRect)
+{
+ if (FramePtr)
+ {
+ pRect->corner.x = -FramePtr->HotSpot.x;
+ pRect->corner.y = -FramePtr->HotSpot.y;
+ pRect->extent = GetFrameBounds (FramePtr);
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+HOT_SPOT
+SetFrameHot (FRAME FramePtr, HOT_SPOT HotSpot)
+{
+ if (FramePtr)
+ {
+ HOT_SPOT OldHot;
+
+ OldHot = FramePtr->HotSpot;
+ FramePtr->HotSpot = HotSpot;
+
+ return (OldHot);
+ }
+
+ return (MAKE_HOT_SPOT (0, 0));
+}
+
+HOT_SPOT
+GetFrameHot (FRAME FramePtr)
+{
+ if (FramePtr)
+ {
+ return FramePtr->HotSpot;
+ }
+
+ return (MAKE_HOT_SPOT (0, 0));
+}
+
+DRAWABLE
+RotateFrame (FRAME Frame, int angle_deg)
+{
+ DRAWABLE Drawable;
+ FRAME RotFramePtr;
+ double dx, dy;
+ double d;
+ double angle = angle_deg * M_PI / 180;
+
+ if (!Frame)
+ return NULL;
+
+ assert (Frame->Type != SCREEN_DRAWABLE);
+
+ Drawable = request_drawable (1, RAM_DRAWABLE, WANT_PIXMAP, 0, 0);
+ if (!Drawable)
+ return 0;
+ RotFramePtr = CaptureDrawable (Drawable);
+ if (!RotFramePtr)
+ {
+ FreeDrawable (Drawable);
+ return 0;
+ }
+
+ RotFramePtr->image = TFB_DrawImage_New_Rotated (
+ Frame->image, angle_deg);
+ SetFrameBounds (RotFramePtr, RotFramePtr->image->extent.width,
+ RotFramePtr->image->extent.height);
+
+ /* now we need to rotate the hot-spot, eww */
+ dx = Frame->HotSpot.x - (GetFrameWidth (Frame) / 2);
+ dy = Frame->HotSpot.y - (GetFrameHeight (Frame) / 2);
+ d = sqrt ((double)dx*dx + (double)dy*dy);
+ if ((int)d != 0)
+ {
+ double organg = atan2 (-dy, dx);
+ dx = cos (organg + angle) * d;
+ dy = -sin (organg + angle) * d;
+ }
+ RotFramePtr->HotSpot.x = (GetFrameWidth (RotFramePtr) / 2) + (int)dx;
+ RotFramePtr->HotSpot.y = (GetFrameHeight (RotFramePtr) / 2) + (int)dy;
+
+ ReleaseDrawable (RotFramePtr);
+
+ return Drawable;
+}
+
+// color.a is ignored
+void
+SetFrameTransparentColor (FRAME frame, Color color)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ img = frame->image;
+ LockMutex (img->mutex);
+
+ // TODO: This should defer to TFB_DrawImage instead
+ TFB_DrawCanvas_SetTransparentColor (img->NormalImg, color, FALSE);
+
+ UnlockMutex (img->mutex);
+}
+
+Color
+GetFramePixel (FRAME frame, POINT pixelPt)
+{
+ TFB_Image *img;
+ Color ret;
+
+ if (!frame)
+ return BUILD_COLOR_RGBA (0, 0, 0, 0);
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ img = frame->image;
+ LockMutex (img->mutex);
+
+ // TODO: This should defer to TFB_DrawImage instead
+ ret = TFB_DrawCanvas_GetPixel (img->NormalImg, pixelPt.x, pixelPt.y);
+
+ UnlockMutex (img->mutex);
+
+ return ret;
+}
+
+static FRAME
+makeMatchingFrame (FRAME frame, int width, int height)
+{
+ DRAWABLE drawable;
+ FRAME newFrame;
+ CREATE_FLAGS flags;
+
+ flags = GetFrameParentDrawable (frame)->Flags;
+ drawable = CreateDrawable (flags, width, height, 1);
+ if (!drawable)
+ return NULL;
+ newFrame = CaptureDrawable (drawable);
+ if (!newFrame)
+ {
+ FreeDrawable (drawable);
+ return NULL;
+ }
+
+ return newFrame;
+}
+
+// Creates an new DRAWABLE containing a copy of specified FRAME's rect
+// Source FRAME must not be a SCREEN_DRAWABLE
+DRAWABLE
+CopyFrameRect (FRAME frame, const RECT *area)
+{
+ FRAME newFrame;
+ POINT nullPt = MAKE_POINT (0, 0);
+
+ if (!frame)
+ return NULL;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ newFrame = makeMatchingFrame (frame, area->extent.width,
+ area->extent.height);
+ if (!newFrame)
+ return NULL;
+
+ TFB_DrawImage_CopyRect (frame->image, area, newFrame->image, nullPt);
+
+ return ReleaseDrawable (newFrame);
+}
+
+// Creates an new DRAWABLE mostly identical to specified FRAME
+// Source FRAME must not be a SCREEN_DRAWABLE
+DRAWABLE
+CloneFrame (FRAME frame)
+{
+ FRAME newFrame;
+ RECT r;
+
+ if (!frame)
+ return NULL;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ GetFrameRect (frame, &r);
+ r.corner.x = 0;
+ r.corner.y = 0;
+
+ newFrame = CaptureDrawable (CopyFrameRect (frame, &r));
+ if (!newFrame)
+ return NULL;
+
+ // copy the hot-spot
+ newFrame->HotSpot = frame->HotSpot;
+
+ return ReleaseDrawable (newFrame);
+}
+
+// Creates a new DRAWABLE of specified size and scales the passed
+// frame onto it. The aspect ratio is not preserved.
+DRAWABLE
+RescaleFrame (FRAME frame, int width, int height)
+{
+ FRAME newFrame;
+ TFB_Image *img;
+ TFB_Canvas src, dst;
+
+ if (!frame)
+ return NULL;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ newFrame = makeMatchingFrame (frame, width, height);
+ if (!newFrame)
+ return NULL;
+
+ // scale the hot-spot
+ newFrame->HotSpot.x = frame->HotSpot.x * width / frame->Bounds.width;
+ newFrame->HotSpot.y = frame->HotSpot.y * height / frame->Bounds.height;
+
+ img = frame->image;
+ LockMutex (img->mutex);
+ // NOTE: We do not lock the target image because nothing has a
+ // reference to it yet!
+ src = img->NormalImg;
+ dst = newFrame->image->NormalImg;
+ TFB_DrawCanvas_Rescale_Nearest (src, dst, -1, NULL, NULL, NULL);
+
+ UnlockMutex (img->mutex);
+
+ return ReleaseDrawable (newFrame);
+}
+
+BOOLEAN
+ReadFramePixelColors (FRAME frame, Color *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_GetPixelColors (img->NormalImg, pixels,
+ width, height);
+}
+
+// Warning: this functions bypasses DCQ, which is why it is not a DrawXXX
+BOOLEAN
+WriteFramePixelColors (FRAME frame, const Color *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_SetPixelColors (img->NormalImg, pixels,
+ width, height);
+}
+
+BOOLEAN
+ReadFramePixelIndexes (FRAME frame, BYTE *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_GetPixelIndexes (img->NormalImg, pixels,
+ width, height);
+}
+
+// Warning: this functions bypasses DCQ, which is why it is not a DrawXXX
+BOOLEAN
+WriteFramePixelIndexes (FRAME frame, const BYTE *pixels, int width, int height)
+{
+ TFB_Image *img;
+
+ if (!frame)
+ return FALSE;
+
+ assert (frame->Type != SCREEN_DRAWABLE);
+
+ // TODO: Do we need to lock the img->mutex here?
+ img = frame->image;
+ return TFB_DrawCanvas_SetPixelIndexes (img->NormalImg, pixels,
+ width, height);
+}
diff --git a/src/libs/graphics/drawable.h b/src/libs/graphics/drawable.h
new file mode 100644
index 0000000..98f5ada
--- /dev/null
+++ b/src/libs/graphics/drawable.h
@@ -0,0 +1,88 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_DRAWABLE_H_
+#define LIBS_GRAPHICS_DRAWABLE_H_
+
+#include <stdio.h>
+#include "tfb_draw.h"
+
+#define ValidPrimType(pt) ((pt)<NUM_PRIMS)
+
+typedef struct bresenham_line
+{
+ POINT first, second;
+ SIZE abs_delta_x, abs_delta_y;
+ SIZE error_term;
+ BOOLEAN end_points_exchanged;
+ INTERSECT_CODE intersect_code;
+} BRESENHAM_LINE;
+
+typedef UWORD DRAWABLE_TYPE;
+#define ROM_DRAWABLE 0
+#define RAM_DRAWABLE 1
+#define SCREEN_DRAWABLE 2
+
+struct frame_desc
+{
+ DRAWABLE_TYPE Type;
+ UWORD Index;
+ HOT_SPOT HotSpot;
+ EXTENT Bounds;
+ TFB_Image *image;
+ struct drawable_desc *parent;
+};
+
+struct drawable_desc
+{
+ CREATE_FLAGS Flags;
+ UWORD MaxIndex;
+ FRAME_DESC *Frame;
+};
+
+#define GetFrameWidth(f) ((f)->Bounds.width)
+#define GetFrameHeight(f) ((f)->Bounds.height)
+#define GetFrameBounds(f) ((f)->Bounds)
+#define SetFrameBounds(f,w,h) \
+ ((f)->Bounds.width=(w), \
+ ((f))->Bounds.height=(h))
+
+#define DRAWABLE_PRIORITY DEFAULT_MEM_PRIORITY
+
+extern DRAWABLE AllocDrawable (COUNT num_frames);
+#define FreeDrawable(D) _ReleaseCelData (D)
+
+typedef struct
+{
+ RECT Box;
+ FRAME FramePtr;
+} IMAGE_BOX;
+
+extern INTERSECT_CODE _clip_line (const RECT *pClipRect,
+ BRESENHAM_LINE *pLine);
+
+extern void *_GetCelData (uio_Stream *fp, DWORD length);
+extern BOOLEAN _ReleaseCelData (void *handle);
+
+extern FRAME _CurFramePtr;
+
+// ClipRect is relative to ctxOrigin
+extern void _text_blt (RECT *pClipRect, TEXT *TextPtr, POINT ctxOrigin);
+
+#endif /* LIBS_GRAPHICS_DRAWABLE_H_ */
+
diff --git a/src/libs/graphics/drawcmd.h b/src/libs/graphics/drawcmd.h
new file mode 100644
index 0000000..6d44153
--- /dev/null
+++ b/src/libs/graphics/drawcmd.h
@@ -0,0 +1,202 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DRAWCMD_H
+#define DRAWCMD_H
+
+#include "libs/graphics/tfb_draw.h"
+
+enum
+{
+ TFB_DRAWCOMMANDTYPE_LINE,
+ TFB_DRAWCOMMANDTYPE_RECTANGLE,
+ TFB_DRAWCOMMANDTYPE_IMAGE,
+ TFB_DRAWCOMMANDTYPE_FILLEDIMAGE,
+ TFB_DRAWCOMMANDTYPE_FONTCHAR,
+
+ TFB_DRAWCOMMANDTYPE_COPY,
+ TFB_DRAWCOMMANDTYPE_COPYTOIMAGE,
+
+ TFB_DRAWCOMMANDTYPE_SCISSORENABLE,
+ TFB_DRAWCOMMANDTYPE_SCISSORDISABLE,
+
+ TFB_DRAWCOMMANDTYPE_SETMIPMAP,
+ TFB_DRAWCOMMANDTYPE_DELETEIMAGE,
+ TFB_DRAWCOMMANDTYPE_DELETEDATA,
+ TFB_DRAWCOMMANDTYPE_SENDSIGNAL,
+ TFB_DRAWCOMMANDTYPE_REINITVIDEO,
+ TFB_DRAWCOMMANDTYPE_CALLBACK,
+};
+
+typedef struct tfb_dc_line
+{
+ int x1, y1, x2, y2;
+ Color color;
+ DrawMode drawMode;
+ SCREEN destBuffer;
+} TFB_DrawCommand_Line;
+
+typedef struct tfb_dc_rect
+{
+ RECT rect;
+ Color color;
+ DrawMode drawMode;
+ SCREEN destBuffer;
+} TFB_DrawCommand_Rect;
+
+typedef struct tfb_dc_img
+{
+ TFB_Image *image;
+ int x, y;
+ SCREEN destBuffer;
+ TFB_ColorMap *colormap;
+ DrawMode drawMode;
+ int scale;
+ int scaleMode;
+} TFB_DrawCommand_Image;
+
+typedef struct tfb_dc_filledimg
+{
+ TFB_Image *image;
+ int x, y;
+ Color color;
+ SCREEN destBuffer;
+ DrawMode drawMode;
+ int scale;
+ int scaleMode;
+} TFB_DrawCommand_FilledImage;
+
+typedef struct tfb_dc_fontchar
+{
+ TFB_Char *fontchar;
+ TFB_Image *backing;
+ int x, y;
+ DrawMode drawMode;
+ SCREEN destBuffer;
+} TFB_DrawCommand_FontChar;
+
+typedef struct tfb_dc_copy
+{
+ RECT rect;
+ SCREEN srcBuffer, destBuffer;
+} TFB_DrawCommand_Copy;
+
+typedef struct tfb_dc_copyimg
+{
+ TFB_Image *image;
+ RECT rect;
+ SCREEN srcBuffer;
+} TFB_DrawCommand_CopyToImage;
+
+typedef struct tfb_dc_scissor
+{
+ RECT rect;
+} TFB_DrawCommand_Scissor;
+
+typedef struct tfb_dc_setmip
+{
+ TFB_Image *image;
+ TFB_Image *mipmap;
+ int hotx, hoty;
+} TFB_DrawCommand_SetMipmap;
+
+typedef struct tfb_dc_delimg
+{
+ TFB_Image *image;
+} TFB_DrawCommand_DeleteImage;
+
+typedef struct tfb_dc_deldata
+{
+ void *data;
+ // data must be a result of HXalloc() call
+} TFB_DrawCommand_DeleteData;
+
+typedef struct tfb_dc_signal
+{
+ Semaphore sem;
+} TFB_DrawCommand_SendSignal;
+
+typedef struct tfb_dc_reinit_video
+{
+ int driver, flags, width, height;
+} TFB_DrawCommand_ReinitVideo;
+
+typedef struct tfb_dc_callback
+{
+ void (*callback)(void *arg);
+ void *arg;
+} TFB_DrawCommand_Callback;
+
+typedef struct tfb_drawcommand
+{
+ int Type;
+ union {
+ TFB_DrawCommand_Line line;
+ TFB_DrawCommand_Rect rect;
+ TFB_DrawCommand_Image image;
+ TFB_DrawCommand_FilledImage filledimage;
+ TFB_DrawCommand_FontChar fontchar;
+ TFB_DrawCommand_Copy copy;
+ TFB_DrawCommand_CopyToImage copytoimage;
+ TFB_DrawCommand_Scissor scissor;
+ TFB_DrawCommand_SetMipmap setmipmap;
+ TFB_DrawCommand_DeleteImage deleteimage;
+ TFB_DrawCommand_DeleteData deletedata;
+ TFB_DrawCommand_SendSignal sendsignal;
+ TFB_DrawCommand_ReinitVideo reinitvideo;
+ TFB_DrawCommand_Callback callback;
+ } data;
+} TFB_DrawCommand;
+
+// Queue Stuff
+
+typedef struct tfb_drawcommandqueue
+{
+ int Front;
+ int Back;
+ int InsertionPoint;
+ int Batching;
+ volatile int FullSize;
+ volatile int Size;
+} TFB_DrawCommandQueue;
+
+void Init_DrawCommandQueue (void);
+
+void Uninit_DrawCommandQueue (void);
+
+void TFB_BatchGraphics (void);
+
+void TFB_UnbatchGraphics (void);
+
+void TFB_BatchReset (void);
+
+void TFB_DrawCommandQueue_Push (TFB_DrawCommand* Command);
+
+int TFB_DrawCommandQueue_Pop (TFB_DrawCommand* Command);
+
+void TFB_DrawCommandQueue_Clear (void);
+
+extern TFB_DrawCommandQueue DrawCommandQueue;
+
+void TFB_EnqueueDrawCommand (TFB_DrawCommand* DrawCommand);
+
+void Lock_DCQ (int slots);
+
+void Unlock_DCQ (void);
+
+#endif
diff --git a/src/libs/graphics/filegfx.c b/src/libs/graphics/filegfx.c
new file mode 100644
index 0000000..c6af7cd
--- /dev/null
+++ b/src/libs/graphics/filegfx.c
@@ -0,0 +1,72 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "options.h"
+#include "libs/reslib.h"
+
+
+DRAWABLE
+LoadGraphicFile (const char *pStr)
+{
+ uio_Stream *fp;
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ fp = res_OpenResFile (contentDir, pStr, "rb");
+ if (fp != NULL)
+ {
+ DRAWABLE hData;
+
+ _cur_resfile_name = pStr;
+ hData = (DRAWABLE)_GetCelData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+ res_CloseResFile (fp);
+ return hData;
+ }
+
+ return (NULL);
+}
+
+FONT
+LoadFontFile (const char *pStr)
+{
+ uio_Stream *fp;
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ fp = res_OpenResFile (contentDir, pStr, "rb");
+ if (fp != NULL)
+ {
+ FONT hData;
+
+ _cur_resfile_name = pStr;
+ hData = (FONT)_GetFontData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+ res_CloseResFile (fp);
+ return hData;
+ }
+
+ return (0);
+}
diff --git a/src/libs/graphics/font.c b/src/libs/graphics/font.c
new file mode 100644
index 0000000..638c814
--- /dev/null
+++ b/src/libs/graphics/font.c
@@ -0,0 +1,334 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "tfb_prim.h"
+#include "libs/log.h"
+
+static inline TFB_Char *getCharFrame (FONT_DESC *fontPtr, UniChar ch);
+
+
+FONT
+SetContextFont (FONT Font)
+{
+ FONT LastFont;
+
+ LastFont = _CurFontPtr;
+ _CurFontPtr = Font;
+ if (ContextActive ())
+ SwitchContextFont (Font);
+
+ return (LastFont);
+}
+
+BOOLEAN
+DestroyFont (FONT FontRef)
+{
+ if (FontRef == NULL)
+ return (FALSE);
+
+ if (_CurFontPtr && _CurFontPtr == FontRef)
+ SetContextFont ((FONT)NULL);
+
+ return (FreeFont (FontRef));
+}
+
+// XXX: Should be in frame.c (renamed to something decent?)
+void
+font_DrawText (TEXT *lpText)
+{
+ RECT ClipRect;
+ POINT origin;
+ TEXT text;
+
+ FixContextFontEffect ();
+ if (!GraphicsSystemActive () || !GetContextValidRect (NULL, &origin))
+ return;
+
+ // TextRect() clobbers TEXT.CharCount so we have to make a copy
+ text = *lpText;
+ if (!TextRect (&text, &ClipRect, NULL))
+ return;
+ // ClipRect is relative to origin
+ _text_blt (&ClipRect, &text, origin);
+}
+
+/* Draw the stroke by drawing the same text in the
+ * background color one pixel shifted to all 4 directions.
+ */
+void
+font_DrawTracedText (TEXT *pText, Color text, Color trace)
+{
+ // Preserve current foreground color for full correctness
+ Color oldfg = SetContextForeGroundColor (trace);
+ pText->baseline.x--;
+ font_DrawText (pText);
+ pText->baseline.x += 2;
+ font_DrawText (pText);
+ pText->baseline.x--;
+ pText->baseline.y--;
+ font_DrawText (pText);
+ pText->baseline.y += 2;
+ font_DrawText (pText);
+ pText->baseline.y--;
+ SetContextForeGroundColor (text);
+ font_DrawText (pText);
+ SetContextForeGroundColor (oldfg);
+}
+
+BOOLEAN
+GetContextFontLeading (SIZE *pheight)
+{
+ if (_CurFontPtr != 0)
+ {
+ *pheight = (SIZE)_CurFontPtr->Leading;
+ return (TRUE);
+ }
+
+ *pheight = 0;
+ return (FALSE);
+}
+
+BOOLEAN
+GetContextFontLeadingWidth (SIZE *pwidth)
+{
+ if (_CurFontPtr != 0)
+ {
+ *pwidth = (SIZE)_CurFontPtr->LeadingWidth;
+ return (TRUE);
+ }
+
+ *pwidth = 0;
+ return (FALSE);
+}
+
+BOOLEAN
+TextRect (TEXT *lpText, RECT *pRect, BYTE *pdelta)
+{
+ BYTE char_delta_array[MAX_DELTAS];
+ FONT FontPtr;
+
+ FontPtr = _CurFontPtr;
+ if (FontPtr != 0 && lpText->CharCount != 0)
+ {
+ COORD top_y, bot_y;
+ SIZE width;
+ UniChar next_ch = 0;
+ const char *pStr;
+ COUNT num_chars;
+
+ num_chars = lpText->CharCount;
+ /* At this point lpText->CharCount contains the *maximum* number of
+ * characters that lpText->pStr may contain.
+ * After the while loop below, it will contain the actual number.
+ */
+ if (pdelta == 0)
+ {
+ pdelta = char_delta_array;
+ if (num_chars > MAX_DELTAS)
+ {
+ num_chars = MAX_DELTAS;
+ lpText->CharCount = MAX_DELTAS;
+ }
+ }
+
+ top_y = 0;
+ bot_y = 0;
+ width = 0;
+ pStr = lpText->pStr;
+ if (num_chars > 0)
+ {
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ num_chars = 0;
+ }
+ while (num_chars--)
+ {
+ UniChar ch;
+ SIZE last_width;
+ TFB_Char *charFrame;
+
+ last_width = width;
+
+ ch = next_ch;
+ if (num_chars > 0)
+ {
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ {
+ lpText->CharCount -= num_chars;
+ num_chars = 0;
+ }
+ }
+
+ charFrame = getCharFrame (FontPtr, ch);
+ if (charFrame != NULL && charFrame->disp.width)
+ {
+ COORD y;
+
+ y = -charFrame->HotSpot.y;
+ if (y < top_y)
+ top_y = y;
+ y += charFrame->disp.height;
+ if (y > bot_y)
+ bot_y = y;
+
+ width += charFrame->disp.width;
+#if 0
+ if (num_chars && next_ch < (UNICODE) MAX_CHARS
+ && !(FontPtr->KernTab[ch]
+ & (FontPtr->KernTab[next_ch] >> 2)))
+ width -= FontPtr->KernAmount;
+#endif
+ }
+
+ *pdelta++ = (BYTE)(width - last_width);
+ }
+
+ if (width > 0 && (bot_y -= top_y) > 0)
+ {
+ /* subtract off default character spacing */
+ if (pdelta[-1] > 0)
+ {
+ --pdelta[-1];
+ --width;
+ }
+
+ if (lpText->align == ALIGN_LEFT)
+ pRect->corner.x = 0;
+ else if (lpText->align == ALIGN_CENTER)
+ pRect->corner.x = -(width >> 1);
+ else
+ pRect->corner.x = -width;
+ pRect->corner.y = top_y;
+ pRect->extent.width = width;
+ pRect->extent.height = bot_y;
+
+ pRect->corner.x += lpText->baseline.x;
+ pRect->corner.y += lpText->baseline.y;
+
+ return (TRUE);
+ }
+ }
+
+ pRect->corner = lpText->baseline;
+ pRect->extent.width = 0;
+ pRect->extent.height = 0;
+
+ return (FALSE);
+}
+
+void
+_text_blt (RECT *pClipRect, TEXT *TextPtr, POINT ctxOrigin)
+{
+ FONT FontPtr;
+ COUNT num_chars;
+ UniChar next_ch;
+ const char *pStr;
+ POINT origin;
+ TFB_Image *backing;
+ DrawMode mode = _get_context_draw_mode ();
+
+ FontPtr = _CurFontPtr;
+ if (FontPtr == NULL)
+ return;
+ backing = _get_context_font_backing ();
+ if (!backing)
+ return;
+
+ origin.x = pClipRect->corner.x;
+ origin.y = TextPtr->baseline.y;
+ num_chars = TextPtr->CharCount;
+ if (num_chars == 0)
+ return;
+
+ pStr = TextPtr->pStr;
+
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ num_chars = 0;
+ while (num_chars--)
+ {
+ UniChar ch;
+ TFB_Char* fontChar;
+
+ ch = next_ch;
+ if (num_chars > 0)
+ {
+ next_ch = getCharFromString (&pStr);
+ if (next_ch == '\0')
+ num_chars = 0;
+ }
+
+ fontChar = getCharFrame (FontPtr, ch);
+ if (fontChar != NULL && fontChar->disp.width)
+ {
+ RECT r;
+
+ r.corner.x = origin.x - fontChar->HotSpot.x;
+ r.corner.y = origin.y - fontChar->HotSpot.y;
+ r.extent.width = fontChar->disp.width;
+ r.extent.height = fontChar->disp.height;
+ if (BoxIntersect (&r, pClipRect, &r))
+ {
+ TFB_Prim_FontChar (origin, fontChar, backing, mode,
+ ctxOrigin);
+ }
+
+ origin.x += fontChar->disp.width;
+#if 0
+ if (num_chars && next_ch < (UNICODE) MAX_CHARS
+ && !(FontPtr->KernTab[ch]
+ & (FontPtr->KernTab[next_ch] >> 2)))
+ origin.x -= FontPtr->KernAmount;
+#endif
+ }
+ }
+}
+
+static inline TFB_Char *
+getCharFrame (FONT_DESC *fontPtr, UniChar ch)
+{
+ UniChar pageStart = ch & CHARACTER_PAGE_MASK;
+ size_t charIndex;
+
+ FONT_PAGE *page = fontPtr->fontPages;
+ for (;;)
+ {
+ if (page == NULL)
+ return NULL;
+
+ if (page->pageStart == pageStart)
+ break;
+
+ page = page->next;
+ }
+
+ charIndex = ch - page->firstChar;
+ if (ch >= page->firstChar && charIndex < page->numChars
+ && page->charDesc[charIndex].data)
+ {
+ return &page->charDesc[charIndex];
+ }
+ else
+ {
+ //log_add (log_Debug, "Character %u not present", (unsigned int) ch);
+ return NULL;
+ }
+}
+
diff --git a/src/libs/graphics/font.h b/src/libs/graphics/font.h
new file mode 100644
index 0000000..b6bd13b
--- /dev/null
+++ b/src/libs/graphics/font.h
@@ -0,0 +1,71 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_FONT_H_
+#define LIBS_GRAPHICS_FONT_H_
+
+#include "libs/memlib.h"
+
+#define MAX_DELTAS 100
+
+typedef struct FontPage
+{
+ struct FontPage *next;
+ UniChar pageStart;
+#define CHARACTER_PAGE_MASK 0xfffff800
+ UniChar firstChar;
+ size_t numChars;
+ TFB_Char *charDesc;
+} FONT_PAGE;
+
+static inline FONT_PAGE *
+AllocFontPage (int numChars)
+{
+ FONT_PAGE *result = HMalloc (sizeof (FONT_PAGE));
+ result->charDesc = HCalloc (numChars * sizeof *result->charDesc);
+ return result;
+}
+
+static inline void
+FreeFontPage (FONT_PAGE *page)
+{
+ HFree (page->charDesc);
+ HFree (page);
+}
+
+struct font_desc
+{
+ UWORD Leading;
+ UWORD LeadingWidth;
+ FONT_PAGE *fontPages;
+};
+
+#define CHAR_DESCPTR PCHAR_DESC
+
+#define FONT_PRIORITY DEFAULT_MEM_PRIORITY
+
+#define AllocFont(size) (FONT)HCalloc (sizeof (FONT_DESC) + (size))
+#define FreeFont _ReleaseFontData
+
+extern FONT _CurFontPtr;
+
+extern void *_GetFontData (uio_Stream *fp, DWORD length);
+extern BOOLEAN _ReleaseFontData (void *handle);
+
+#endif /* LIBS_GRAPHICS_FONT_H_ */
+
diff --git a/src/libs/graphics/frame.c b/src/libs/graphics/frame.c
new file mode 100644
index 0000000..0539746
--- /dev/null
+++ b/src/libs/graphics/frame.c
@@ -0,0 +1,266 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "gfx_common.h"
+#include "tfb_draw.h"
+#include "tfb_prim.h"
+
+HOT_SPOT
+MAKE_HOT_SPOT (COORD x, COORD y)
+{
+ HOT_SPOT hs;
+ hs.x = x;
+ hs.y = y;
+ return hs;
+}
+
+// XXX: INTERNAL_PRIMITIVE and INTERNAL_PRIM_DESC are not used
+typedef union
+{
+ POINT Point;
+ STAMP Stamp;
+ BRESENHAM_LINE Line;
+ TEXT Text;
+ RECT Rect;
+} INTERNAL_PRIM_DESC;
+
+typedef struct
+{
+ PRIM_LINKS Links;
+ GRAPHICS_PRIM Type;
+ Color Color;
+ INTERNAL_PRIM_DESC Object;
+} INTERNAL_PRIMITIVE;
+
+
+// pValidRect or origin may be NULL
+BOOLEAN
+GetContextValidRect (RECT *pValidRect, POINT *origin)
+{
+ RECT tempRect;
+ POINT tempPt;
+
+ if (!pValidRect)
+ pValidRect = &tempRect;
+ if (!origin)
+ origin = &tempPt;
+
+ // Start with a rect the size of foreground frame
+ pValidRect->corner.x = 0;
+ pValidRect->corner.y = 0;
+ pValidRect->extent = GetFrameBounds (_CurFramePtr);
+ *origin = _CurFramePtr->HotSpot;
+
+ if (_pCurContext->ClipRect.extent.width)
+ {
+ // If the cliprect is completely outside of the valid frame
+ // bounds we have nothing to draw
+ if (!BoxIntersect (&_pCurContext->ClipRect,
+ pValidRect, pValidRect))
+ return (FALSE);
+
+ // Foreground frame hotspot defines a drawing position offset
+ // WRT the context cliprect
+ origin->x += _pCurContext->ClipRect.corner.x;
+ origin->y += _pCurContext->ClipRect.corner.y;
+ }
+
+ return (TRUE);
+}
+
+static void
+ClearBackGround (RECT *pClipRect)
+{
+ RECT clearRect;
+ Color color = _get_context_bg_color ();
+ clearRect.corner.x = 0;
+ clearRect.corner.y = 0;
+ clearRect.extent = pClipRect->extent;
+ TFB_Prim_FillRect (&clearRect, color, DRAW_REPLACE_MODE,
+ pClipRect->corner);
+}
+
+void
+DrawBatch (PRIMITIVE *lpBasePrim, PRIM_LINKS PrimLinks,
+ BATCH_FLAGS BatchFlags)
+{
+ RECT ValidRect;
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (&ValidRect, &origin))
+ {
+ COUNT CurIndex;
+ PRIMITIVE *lpPrim;
+ DrawMode mode = _get_context_draw_mode ();
+
+ BatchGraphics ();
+
+ if (BatchFlags & BATCH_BUILD_PAGE)
+ {
+ ClearBackGround (&ValidRect);
+ }
+
+ CurIndex = GetPredLink (PrimLinks);
+
+ for (; CurIndex != END_OF_LIST;
+ CurIndex = GetSuccLink (GetPrimLinks (lpPrim)))
+ {
+ GRAPHICS_PRIM PrimType;
+ PRIMITIVE *lpWorkPrim;
+ RECT ClipRect;
+ Color color;
+
+ lpPrim = &lpBasePrim[CurIndex];
+ PrimType = GetPrimType (lpPrim);
+ if (!ValidPrimType (PrimType))
+ continue;
+
+ lpWorkPrim = lpPrim;
+
+ switch (PrimType)
+ {
+ case POINT_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_Point (&lpWorkPrim->Object.Point, color,
+ mode, origin);
+ break;
+ case STAMP_PRIM:
+ TFB_Prim_Stamp (&lpWorkPrim->Object.Stamp, mode, origin);
+ break;
+ case STAMPFILL_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_StampFill (&lpWorkPrim->Object.Stamp, color,
+ mode, origin);
+ break;
+ case LINE_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_Line (&lpWorkPrim->Object.Line, color,
+ mode, origin);
+ break;
+ case TEXT_PRIM:
+ if (!TextRect (&lpWorkPrim->Object.Text, &ClipRect, NULL))
+ continue;
+ // ClipRect is relative to origin
+ _text_blt (&ClipRect, &lpWorkPrim->Object.Text, origin);
+ break;
+ case RECT_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_Rect (&lpWorkPrim->Object.Rect, color,
+ mode, origin);
+ break;
+ case RECTFILL_PRIM:
+ color = GetPrimColor (lpWorkPrim);
+ TFB_Prim_FillRect (&lpWorkPrim->Object.Rect, color,
+ mode, origin);
+ break;
+ }
+ }
+
+ UnbatchGraphics ();
+ }
+}
+
+void
+ClearDrawable (void)
+{
+ RECT ValidRect;
+
+ if (GraphicsSystemActive () && GetContextValidRect (&ValidRect, NULL))
+ {
+ ClearBackGround (&ValidRect);
+ }
+}
+
+void
+DrawPoint (POINT *lpPoint)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Point (lpPoint, color, mode, origin);
+ }
+}
+
+void
+DrawRectangle (RECT *lpRect)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Rect (lpRect, color, mode, origin);
+ }
+}
+
+void
+DrawFilledRectangle (RECT *lpRect)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_FillRect (lpRect, color, mode, origin);
+ }
+}
+
+void
+DrawLine (LINE *lpLine)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Line (lpLine, color, mode, origin);
+ }
+}
+
+void
+DrawStamp (STAMP *stmp)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_Stamp (stmp, mode, origin);
+ }
+}
+
+void
+DrawFilledStamp (STAMP *stmp)
+{
+ POINT origin;
+
+ if (GraphicsSystemActive () && GetContextValidRect (NULL, &origin))
+ {
+ Color color = GetPrimColor (&_locPrim);
+ DrawMode mode = _get_context_draw_mode ();
+ TFB_Prim_StampFill (stmp, color, mode, origin);
+ }
+}
+
diff --git a/src/libs/graphics/gfx_common.c b/src/libs/graphics/gfx_common.c
new file mode 100644
index 0000000..405f614
--- /dev/null
+++ b/src/libs/graphics/gfx_common.c
@@ -0,0 +1,196 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/timelib.h"
+#include "libs/misc.h"
+ // for TFB_DEBUG_HALT
+
+
+int ScreenWidth;
+int ScreenHeight;
+int ScreenWidthActual;
+int ScreenHeightActual;
+int ScreenColorDepth;
+int GraphicsDriver;
+int TFB_DEBUG_HALT = 0;
+
+volatile int TransitionAmount = 255;
+RECT TransitionClipRect;
+
+static int gscale = GSCALE_IDENTITY;
+static int gscale_mode = TFB_SCALE_NEAREST;
+
+void
+DrawFromExtraScreen (RECT *r)
+{
+ TFB_DrawScreen_Copy(r, TFB_SCREEN_EXTRA, TFB_SCREEN_MAIN);
+}
+
+void
+LoadIntoExtraScreen (RECT *r)
+{
+ TFB_DrawScreen_Copy(r, TFB_SCREEN_MAIN, TFB_SCREEN_EXTRA);
+}
+
+int
+SetGraphicScale (int scale)
+{
+ int old_scale = gscale;
+ gscale = (scale ? scale : GSCALE_IDENTITY);
+ return old_scale;
+}
+
+int
+GetGraphicScale (void)
+{
+ return gscale;
+}
+
+int
+SetGraphicScaleMode (int mode)
+{
+ int old_mode = gscale_mode;
+ assert (mode >= TFB_SCALE_NEAREST && mode <= TFB_SCALE_TRILINEAR);
+ gscale_mode = mode;
+ return old_mode;
+}
+
+int
+GetGraphicScaleMode (void)
+{
+ return gscale_mode;
+}
+
+/* Batching and Unbatching functions. A "Batch" is a collection of
+ DrawCommands that will never be flipped to the screen half-rendered.
+ BatchGraphics and UnbatchGraphics function vaguely like a non-blocking
+ recursive lock to do this respect. */
+void
+BatchGraphics (void)
+{
+ TFB_BatchGraphics ();
+}
+
+void
+UnbatchGraphics (void)
+{
+ TFB_UnbatchGraphics ();
+}
+
+/* Sleeps this thread until all Draw Commands queued by that thread have
+ been processed. */
+
+void
+FlushGraphics (void)
+{
+ TFB_DrawScreen_WaitForSignal ();
+}
+
+static void
+ExpandRect (RECT *rect, int expansion)
+{
+ if (rect->corner.x - expansion >= 0)
+ {
+ rect->extent.width += expansion;
+ rect->corner.x -= expansion;
+ }
+ else
+ {
+ rect->extent.width += rect->corner.x;
+ rect->corner.x = 0;
+ }
+
+ if (rect->corner.y - expansion >= 0)
+ {
+ rect->extent.height += expansion;
+ rect->corner.y -= expansion;
+ }
+ else
+ {
+ rect->extent.height += rect->corner.y;
+ rect->corner.y = 0;
+ }
+
+ if (rect->corner.x + rect->extent.width + expansion <= ScreenWidth)
+ rect->extent.width += expansion;
+ else
+ rect->extent.width = ScreenWidth - rect->corner.x;
+
+ if (rect->corner.y + rect->extent.height + expansion <= ScreenHeight)
+ rect->extent.height += expansion;
+ else
+ rect->extent.height = ScreenHeight - rect->corner.y;
+}
+
+void
+SetTransitionSource (const RECT *pRect)
+{
+ RECT ActualRect;
+
+ if (pRect)
+ { /* expand the rect to accomodate scalers in OpenGL mode */
+ ActualRect = *pRect;
+ pRect = &ActualRect;
+ ExpandRect (&ActualRect, 2);
+ }
+ TFB_DrawScreen_Copy (pRect, TFB_SCREEN_MAIN, TFB_SCREEN_TRANSITION);
+}
+
+// ScreenTransition() is synchronous (does not return until transition done)
+void
+ScreenTransition (int TransType, const RECT *pRect)
+{
+ const TimePeriod DURATION = ONE_SECOND * 31 / 60;
+ TimeCount startTime;
+ (void) TransType; /* dodge compiler warning */
+
+ if (pRect)
+ {
+ TransitionClipRect = *pRect;
+ }
+ else
+ {
+ TransitionClipRect.corner.x = 0;
+ TransitionClipRect.corner.y = 0;
+ TransitionClipRect.extent.width = ScreenWidth;
+ TransitionClipRect.extent.height = ScreenHeight;
+ }
+
+ TFB_UploadTransitionScreen ();
+
+ TransitionAmount = 0;
+ FlushGraphics ();
+ startTime = GetTimeCounter ();
+ while (TransitionAmount < 255)
+ {
+ TimePeriod deltaT;
+ int newAmount;
+
+ SleepThread (ONE_SECOND / 100);
+
+ deltaT = GetTimeCounter () - startTime;
+ newAmount = deltaT * 255 / DURATION;
+ if (newAmount > 255)
+ newAmount = 255;
+
+ TransitionAmount = newAmount;
+ }
+}
diff --git a/src/libs/graphics/gfx_common.h b/src/libs/graphics/gfx_common.h
new file mode 100644
index 0000000..e9e421a
--- /dev/null
+++ b/src/libs/graphics/gfx_common.h
@@ -0,0 +1,112 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GFX_COMMON_H
+#define GFX_COMMON_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libs/gfxlib.h"
+
+// driver for TFB_InitGraphics
+enum
+{
+ TFB_GFXDRIVER_SDL_OPENGL,
+ TFB_GFXDRIVER_SDL_PURE,
+};
+
+// forced redraw
+enum
+{
+ TFB_REDRAW_NO = 0,
+ TFB_REDRAW_FADING,
+ TFB_REDRAW_EXPOSE,
+ TFB_REDRAW_YES
+};
+
+// flags for TFB_InitGraphics
+#define TFB_GFXFLAGS_FULLSCREEN (1<<0)
+#define TFB_GFXFLAGS_SHOWFPS (1<<1)
+#define TFB_GFXFLAGS_SCANLINES (1<<2)
+#define TFB_GFXFLAGS_SCALE_BILINEAR (1<<3)
+#define TFB_GFXFLAGS_SCALE_BIADAPT (1<<4)
+#define TFB_GFXFLAGS_SCALE_BIADAPTADV (1<<5)
+#define TFB_GFXFLAGS_SCALE_TRISCAN (1<<6)
+#define TFB_GFXFLAGS_SCALE_HQXX (1<<7)
+#define TFB_GFXFLAGS_SCALE_ANY \
+ ( TFB_GFXFLAGS_SCALE_BILINEAR | \
+ TFB_GFXFLAGS_SCALE_BIADAPT | \
+ TFB_GFXFLAGS_SCALE_BIADAPTADV | \
+ TFB_GFXFLAGS_SCALE_TRISCAN | \
+ TFB_GFXFLAGS_SCALE_HQXX )
+#define TFB_GFXFLAGS_SCALE_SOFT_ONLY \
+ ( TFB_GFXFLAGS_SCALE_ANY & ~TFB_GFXFLAGS_SCALE_BILINEAR )
+
+// The flag variable itself
+extern int GfxFlags;
+
+// The following functions are driver-defined
+void TFB_PreInit (void);
+int TFB_InitGraphics (int driver, int flags, const char *renderer,
+ int width, int height);
+int TFB_ReInitGraphics (int driver, int flags, int width, int height);
+void TFB_UninitGraphics (void);
+void TFB_ProcessEvents (void);
+bool TFB_SetGamma (float gamma);
+void TFB_UploadTransitionScreen (void);
+int TFB_SupportsHardwareScaling (void);
+// This function should not be called directly
+void TFB_SwapBuffers (int force_full_redraw);
+
+#define GSCALE_IDENTITY 256
+
+typedef enum {
+ TFB_SCALE_STEP, /* not really a scaler */
+ TFB_SCALE_NEAREST,
+ TFB_SCALE_BILINEAR,
+ TFB_SCALE_TRILINEAR
+} SCALE;
+
+void LoadIntoExtraScreen (RECT *r);
+void DrawFromExtraScreen (RECT *r);
+int SetGraphicScale (int scale);
+int GetGraphicScale (void);
+int SetGraphicScaleMode (int mode /* enum SCALE */);
+int GetGraphicScaleMode (void);
+void SetTransitionSource (const RECT *pRect);
+void ScreenTransition (int transition, const RECT *pRect);
+
+// TODO: there should be accessor functions for these
+extern volatile int TransitionAmount;
+extern RECT TransitionClipRect;
+
+extern float FrameRate;
+extern int FrameRateTickBase;
+
+void TFB_FlushGraphics (void); // Only call from main thread!!
+void TFB_PurgeDanglingGraphics (void); // Only call from main thread as part of shutdown.
+
+extern int ScreenWidth;
+extern int ScreenHeight;
+extern int ScreenWidthActual;
+extern int ScreenHeightActual;
+extern int ScreenColorDepth;
+extern int GraphicsDriver;
+
+#endif
diff --git a/src/libs/graphics/gfxintrn.h b/src/libs/graphics/gfxintrn.h
new file mode 100644
index 0000000..72281e6
--- /dev/null
+++ b/src/libs/graphics/gfxintrn.h
@@ -0,0 +1,32 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_GFXINTRN_H_
+#define LIBS_GRAPHICS_GFXINTRN_H_
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libs/gfxlib.h"
+#include "libs/reslib.h"
+#include "context.h"
+#include "drawable.h"
+#include "font.h"
+
+#endif /* LIBS_GRAPHICS_GFXINTRN_H_ */
+
diff --git a/src/libs/graphics/gfxload.c b/src/libs/graphics/gfxload.c
new file mode 100644
index 0000000..969a910
--- /dev/null
+++ b/src/libs/graphics/gfxload.c
@@ -0,0 +1,597 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include "options.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/reslib.h"
+ // for _cur_resfile_name
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/font.h"
+
+
+typedef struct anidata
+{
+ int transparent_color;
+ int colormap_index;
+ int hotspot_x;
+ int hotspot_y;
+} AniData;
+
+extern uio_Repository *repository;
+static uio_AutoMount *autoMount[] = { NULL };
+
+static void
+process_image (FRAME FramePtr, TFB_Canvas img[], AniData *ani, int cel_ct)
+{
+ TFB_Image *tfbimg;
+ int hx, hy;
+
+ FramePtr->Type = ROM_DRAWABLE;
+ FramePtr->Index = cel_ct;
+
+ // handle transparency cases
+ if (TFB_DrawCanvas_IsPaletted (img[cel_ct]))
+ { // indexed color image
+ if (ani[cel_ct].transparent_color >= 0)
+ {
+ TFB_DrawCanvas_SetTransparentIndex (img[cel_ct],
+ ani[cel_ct].transparent_color, FALSE);
+ }
+ }
+ else
+ { // special transparency cases for truecolor images
+ if (ani[cel_ct].transparent_color == 0)
+ { // make RGB=0,0,0 transparent
+ Color color = {0, 0, 0, 0};
+ TFB_DrawCanvas_SetTransparentColor (img[cel_ct], color, FALSE);
+ }
+ }
+ if (ani[cel_ct].transparent_color == -1)
+ { // enforce -1 to mean 'no transparency'
+ TFB_DrawCanvas_SetTransparentIndex (img[cel_ct], -1, FALSE);
+ // set transparent_color == -2 to use PNG tRNS transparency
+ }
+
+ hx = ani[cel_ct].hotspot_x;
+ hy = ani[cel_ct].hotspot_y;
+
+ FramePtr->image = TFB_DrawImage_New (img[cel_ct]);
+
+ tfbimg = FramePtr->image;
+ tfbimg->colormap_index = ani[cel_ct].colormap_index;
+ img[cel_ct] = tfbimg->NormalImg;
+
+ FramePtr->HotSpot = MAKE_HOT_SPOT (hx, hy);
+ SetFrameBounds (FramePtr, tfbimg->extent.width, tfbimg->extent.height);
+
+#ifdef CLIPDEBUG
+ {
+ /* for debugging clipping:
+ draws white (or most matching color from palette) pixels to
+ every corner of the image
+ */
+ Color color = {0xff, 0xff, 0xff, 0xff};
+ RECT r = {{0, 0}, {1, 1}};
+ if (tfbimg->extent.width > 2 && tfbimg->extent.height > 2)
+ {
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ r.corner.x = tfbimg->extent.width - 1;
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ r.corner.y = tfbimg->extent.height - 1;
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ r.corner.x = 0;
+ TFB_DrawImage_Rect (&r, color, tfbimg);
+ }
+ }
+#endif
+}
+
+static void
+processFontChar (TFB_Char* CharPtr, TFB_Canvas canvas)
+{
+ BYTE* newdata;
+ size_t dpitch;
+
+ TFB_DrawCanvas_GetExtent (canvas, &CharPtr->extent);
+
+ // Currently, each font char has its own separate data
+ // but that can change to common mem area
+ dpitch = CharPtr->extent.width;
+ newdata = HMalloc (dpitch * CharPtr->extent.height * sizeof (BYTE));
+ TFB_DrawCanvas_GetFontCharData (canvas, newdata, dpitch);
+
+ CharPtr->data = newdata;
+ CharPtr->pitch = dpitch;
+ CharPtr->disp.width = CharPtr->extent.width + 1;
+ CharPtr->disp.height = CharPtr->extent.height + 1;
+ // XXX: why the +1?
+ // I brought it into this function from the only calling
+ // function, but I don't know why it was there in the first
+ // place.
+ // XXX: the +1 appears to be for character and line spacing
+ // text_blt just adds the frame width to move to the next char
+
+ {
+ // This tunes the font positioning to be about what it should
+ // TODO: prolly needs a little tweaking still
+
+ int tune_amount = 0;
+
+ if (CharPtr->extent.height == 8)
+ tune_amount = -1;
+ else if (CharPtr->extent.height == 9)
+ tune_amount = -2;
+ else if (CharPtr->extent.height > 9)
+ tune_amount = -3;
+
+ CharPtr->HotSpot = MAKE_HOT_SPOT (0,
+ CharPtr->extent.height + tune_amount);
+ }
+}
+
+void *
+_GetCelData (uio_Stream *fp, DWORD length)
+{
+ int cel_total, cel_index, n;
+ DWORD opos;
+ char CurrentLine[1024], filename[PATH_MAX];
+ TFB_Canvas *img;
+ AniData *ani;
+ DRAWABLE Drawable;
+ uio_MountHandle *aniMount = 0;
+ uio_DirHandle *aniDir = 0;
+ uio_Stream *aniFile = 0;
+
+ opos = uio_ftell (fp);
+
+ {
+ char *s1, *s2;
+ char aniDirName[PATH_MAX];
+ const char *aniFileName;
+ uint8 buf[4] = { 0, 0, 0, 0 };
+ uint32 header;
+
+ if (_cur_resfile_name == 0
+ || (((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0)
+ && (s2 = strrchr (_cur_resfile_name, '\\')) == 0))
+ {
+ n = 0;
+ }
+ else
+ {
+ if (s2 > s1)
+ s1 = s2;
+ n = s1 - _cur_resfile_name + 1;
+ }
+
+ uio_fread(buf, 4, 1, fp);
+ header = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ if (_cur_resfile_name && header == 0x04034b50)
+ {
+ // zipped ani file
+ if (n)
+ {
+ strncpy (aniDirName, _cur_resfile_name, n - 1);
+ aniDirName[n - 1] = 0;
+ aniFileName = _cur_resfile_name + n;
+ }
+ else
+ {
+ strcpy(aniDirName, ".");
+ aniFileName = _cur_resfile_name;
+ }
+ aniDir = uio_openDir (repository, aniDirName, 0);
+ aniMount = uio_mountDir (repository, aniDirName, uio_FSTYPE_ZIP,
+ aniDir, aniFileName, "/", autoMount,
+ uio_MOUNT_RDONLY | uio_MOUNT_TOP,
+ NULL);
+ aniFile = uio_fopen (aniDir, aniFileName, "r");
+ opos = 0;
+ n = 0;
+ }
+ else
+ {
+ // unpacked ani file
+ strncpy (filename, _cur_resfile_name, n);
+ aniFile = fp;
+ aniDir = contentDir;
+ }
+ }
+
+ cel_total = 0;
+ uio_fseek (aniFile, opos, SEEK_SET);
+ while (uio_fgets (CurrentLine, sizeof (CurrentLine), aniFile))
+ {
+ ++cel_total;
+ }
+
+ img = HMalloc (sizeof (TFB_Canvas) * cel_total);
+ ani = HMalloc (sizeof (AniData) * cel_total);
+ if (!img || !ani)
+ {
+ log_add (log_Warning, "Couldn't allocate space for '%s'", _cur_resfile_name);
+ if (aniMount)
+ {
+ uio_fclose(aniFile);
+ uio_closeDir(aniDir);
+ uio_unmountDir(aniMount);
+ }
+ HFree (img);
+ HFree (ani);
+ return NULL;
+ }
+
+ cel_index = 0;
+ uio_fseek (aniFile, opos, SEEK_SET);
+ while (uio_fgets (CurrentLine, sizeof (CurrentLine), aniFile) && cel_index < cel_total)
+ {
+ sscanf (CurrentLine, "%s %d %d %d %d", &filename[n],
+ &ani[cel_index].transparent_color, &ani[cel_index].colormap_index,
+ &ani[cel_index].hotspot_x, &ani[cel_index].hotspot_y);
+
+ img[cel_index] = TFB_DrawCanvas_LoadFromFile (aniDir, filename);
+ if (img[cel_index] == NULL)
+ {
+ const char *err;
+
+ err = TFB_DrawCanvas_GetError ();
+ log_add (log_Warning, "_GetCelData: Unable to load image!");
+ if (err != NULL)
+ log_add (log_Warning, "Gfx Driver reports: %s", err);
+ }
+ else
+ {
+ ++cel_index;
+ }
+
+ if ((int)uio_ftell (aniFile) - (int)opos >= (int)length)
+ break;
+ }
+
+ Drawable = NULL;
+ if (cel_index && (Drawable = AllocDrawable (cel_index)))
+ {
+ if (!Drawable)
+ {
+ while (cel_index--)
+ TFB_DrawCanvas_Delete (img[cel_index]);
+
+ HFree (Drawable);
+ Drawable = NULL;
+ }
+ else
+ {
+ FRAME FramePtr;
+
+ Drawable->Flags = WANT_PIXMAP;
+ Drawable->MaxIndex = cel_index - 1;
+
+ FramePtr = &Drawable->Frame[cel_index];
+ while (--FramePtr, cel_index--)
+ process_image (FramePtr, img, ani, cel_index);
+ }
+ }
+
+ if (Drawable == NULL)
+ log_add (log_Warning, "Couldn't get cel data for '%s'",
+ _cur_resfile_name);
+
+ if (aniMount)
+ {
+ uio_fclose(aniFile);
+ uio_closeDir(aniDir);
+ uio_unmountDir(aniMount);
+ }
+
+ HFree (img);
+ HFree (ani);
+ return Drawable;
+}
+
+BOOLEAN
+_ReleaseCelData (void *handle)
+{
+ DRAWABLE DrawablePtr;
+ int cel_ct;
+ FRAME FramePtr = NULL;
+
+ if ((DrawablePtr = handle) == 0)
+ return (FALSE);
+
+ cel_ct = DrawablePtr->MaxIndex + 1;
+ FramePtr = DrawablePtr->Frame;
+
+ HFree (handle);
+ if (FramePtr)
+ {
+ int i;
+ for (i = 0; i < cel_ct; i++)
+ {
+ TFB_Image *img = FramePtr[i].image;
+ if (img)
+ {
+ FramePtr[i].image = NULL;
+ TFB_DrawScreen_DeleteImage (img);
+ }
+ }
+ HFree (FramePtr);
+ }
+
+ return (TRUE);
+}
+
+typedef struct BuildCharDesc
+{
+ TFB_Canvas canvas;
+ UniChar index;
+} BuildCharDesc;
+
+static int
+compareBCDIndex (const void *arg1, const void *arg2)
+{
+ const BuildCharDesc *bcd1 = (const BuildCharDesc *) arg1;
+ const BuildCharDesc *bcd2 = (const BuildCharDesc *) arg2;
+
+ return (int) bcd1->index - (int) bcd2->index;
+}
+
+void *
+_GetFontData (uio_Stream *fp, DWORD length)
+{
+ COUNT numDirEntries;
+ DIRENTRY fontDir = NULL;
+ BuildCharDesc *bcds = NULL;
+ size_t numBCDs = 0;
+ int dirEntryI;
+ uio_DirHandle *fontDirHandle = NULL;
+ uio_MountHandle *fontMount = NULL;
+ FONT fontPtr = NULL;
+
+ if (_cur_resfile_name == 0)
+ goto err;
+
+ if (fp != (uio_Stream*)~0)
+ {
+ // font is zipped instead of being in a directory
+
+ char *s1, *s2;
+ int n;
+ const char *fontZipName;
+ char fontDirName[PATH_MAX];
+
+ if ((((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0)
+ && (s2 = strrchr (_cur_resfile_name, '\\')) == 0))
+ {
+ strcpy(fontDirName, ".");
+ fontZipName = _cur_resfile_name;
+ }
+ else
+ {
+ if (s2 > s1)
+ s1 = s2;
+ n = s1 - _cur_resfile_name + 1;
+ strncpy (fontDirName, _cur_resfile_name, n - 1);
+ fontDirName[n - 1] = 0;
+ fontZipName = _cur_resfile_name + n;
+ }
+
+ fontDirHandle = uio_openDir (repository, fontDirName, 0);
+ fontMount = uio_mountDir (repository, _cur_resfile_name, uio_FSTYPE_ZIP,
+ fontDirHandle, fontZipName, "/", autoMount,
+ uio_MOUNT_RDONLY | uio_MOUNT_TOP,
+ NULL);
+ uio_closeDir (fontDirHandle);
+ }
+
+ fontDir = CaptureDirEntryTable (LoadDirEntryTable (contentDir,
+ _cur_resfile_name, ".", match_MATCH_SUBSTRING));
+ if (fontDir == 0)
+ goto err;
+ numDirEntries = GetDirEntryTableCount (fontDir);
+
+ fontDirHandle = uio_openDirRelative (contentDir, _cur_resfile_name, 0);
+ if (fontDirHandle == NULL)
+ goto err;
+
+ bcds = HMalloc (numDirEntries * sizeof (BuildCharDesc));
+ if (bcds == NULL)
+ goto err;
+
+ // Load the surfaces for all dir Entries
+ for (dirEntryI = 0; dirEntryI < numDirEntries; dirEntryI++)
+ {
+ char *char_name;
+ unsigned int charIndex;
+ TFB_Canvas canvas;
+ EXTENT size;
+
+ char_name = GetDirEntryAddress (SetAbsDirEntryTableIndex (
+ fontDir, dirEntryI));
+ if (sscanf (char_name, "%x.", &charIndex) != 1)
+ continue;
+
+ if (charIndex > 0xffff)
+ continue;
+
+ canvas = TFB_DrawCanvas_LoadFromFile (fontDirHandle, char_name);
+ if (canvas == NULL)
+ continue;
+
+ TFB_DrawCanvas_GetExtent (canvas, &size);
+ if (size.width == 0 || size.height == 0)
+ {
+ TFB_DrawCanvas_Delete (canvas);
+ continue;
+ }
+
+ bcds[numBCDs].canvas = canvas;
+ bcds[numBCDs].index = charIndex;
+ numBCDs++;
+ }
+ uio_closeDir (fontDirHandle);
+ DestroyDirEntryTable (ReleaseDirEntryTable (fontDir));
+ if (fontMount != 0)
+ uio_unmountDir(fontMount);
+
+#if 0
+ if (numBCDs == 0)
+ goto err;
+#endif
+
+ // sort on the character index
+ qsort (bcds, numBCDs, sizeof (BuildCharDesc), compareBCDIndex);
+
+ fontPtr = AllocFont (0);
+ if (fontPtr == NULL)
+ goto err;
+
+ fontPtr->Leading = 0;
+ fontPtr->LeadingWidth = 0;
+
+ {
+ size_t startBCD = 0;
+ UniChar pageStart;
+ FONT_PAGE **pageEndPtr = &fontPtr->fontPages;
+ while (startBCD < numBCDs)
+ {
+ // Process one character page.
+ size_t endBCD;
+ pageStart = bcds[startBCD].index & CHARACTER_PAGE_MASK;
+
+ endBCD = startBCD;
+ while (endBCD < numBCDs &&
+ (bcds[endBCD].index & CHARACTER_PAGE_MASK) == pageStart)
+ endBCD++;
+
+ {
+ size_t bcdI;
+ int numChars = bcds[endBCD - 1].index + 1
+ - bcds[startBCD].index;
+ FONT_PAGE *page = AllocFontPage (numChars);
+ page->pageStart = pageStart;
+ page->firstChar = bcds[startBCD].index;
+ page->numChars = numChars;
+ *pageEndPtr = page;
+ pageEndPtr = &page->next;
+
+ for (bcdI = startBCD; bcdI < endBCD; bcdI++)
+ {
+ // Process one character.
+ BuildCharDesc *bcd = &bcds[bcdI];
+ TFB_Char *destChar =
+ &page->charDesc[bcd->index - page->firstChar];
+
+ if (destChar->data != NULL)
+ {
+ // There's already an image for this character.
+ log_add (log_Debug, "Duplicate image for character %d "
+ "for font %s.", (int) bcd->index,
+ _cur_resfile_name);
+ TFB_DrawCanvas_Delete (bcd->canvas);
+ continue;
+ }
+
+ processFontChar (destChar, bcd->canvas);
+ TFB_DrawCanvas_Delete (bcd->canvas);
+
+ if (destChar->disp.height > fontPtr->Leading)
+ fontPtr->Leading = destChar->disp.height;
+ if (destChar->disp.width > fontPtr->LeadingWidth)
+ fontPtr->LeadingWidth = destChar->disp.width;
+ }
+ }
+
+ startBCD = endBCD;
+ }
+ *pageEndPtr = NULL;
+ }
+
+ fontPtr->Leading++;
+
+ HFree (bcds);
+
+ (void) fp; /* Satisfying compiler (unused parameter) */
+ (void) length; /* Satisfying compiler (unused parameter) */
+ return fontPtr;
+
+err:
+ if (fontPtr != 0)
+ HFree (fontPtr);
+
+ if (bcds != NULL)
+ {
+ size_t bcdI;
+ for (bcdI = 0; bcdI < numBCDs; bcdI++)
+ TFB_DrawCanvas_Delete (bcds[bcdI].canvas);
+ HFree (bcds);
+ }
+
+ if (fontDirHandle != NULL)
+ uio_closeDir (fontDirHandle);
+
+ if (fontDir != 0)
+ DestroyDirEntryTable (ReleaseDirEntryTable (fontDir));
+
+ if (fontMount != 0)
+ uio_unmountDir(fontMount);
+
+ return 0;
+}
+
+BOOLEAN
+_ReleaseFontData (void *handle)
+{
+ FONT font = (FONT) handle;
+ if (font == NULL)
+ return FALSE;
+
+ {
+ FONT_PAGE *page;
+ FONT_PAGE *nextPage;
+
+ for (page = font->fontPages; page != NULL; page = nextPage)
+ {
+ size_t charI;
+ for (charI = 0; charI < page->numChars; charI++)
+ {
+ TFB_Char *c = &page->charDesc[charI];
+
+ if (c->data == NULL)
+ continue;
+
+ // XXX: fix this if fonts get per-page data
+ // rather than per-char
+ TFB_DrawScreen_DeleteData (c->data);
+ }
+
+ nextPage = page->next;
+ FreeFontPage (page);
+ }
+ }
+
+ HFree (font);
+
+ return TRUE;
+}
diff --git a/src/libs/graphics/intersec.c b/src/libs/graphics/intersec.c
new file mode 100644
index 0000000..02a5226
--- /dev/null
+++ b/src/libs/graphics/intersec.c
@@ -0,0 +1,415 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/graphics/context.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/log.h"
+
+//#define DEBUG_INTERSEC
+
+static inline BOOLEAN
+images_intersect (IMAGE_BOX *box1, IMAGE_BOX *box2, const RECT *rect)
+{
+ return TFB_DrawImage_Intersect (box1->FramePtr->image, box1->Box.corner,
+ box2->FramePtr->image, box2->Box.corner, rect);
+}
+
+static TIME_VALUE
+frame_intersect (INTERSECT_CONTROL *pControl0, RECT *pr0,
+ INTERSECT_CONTROL *pControl1, RECT *pr1, TIME_VALUE t0,
+ TIME_VALUE t1)
+{
+ SIZE time_error0, time_error1;
+ SIZE cycle0, cycle1;
+ SIZE dx_0, dy_0, dx_1, dy_1;
+ SIZE xincr0, yincr0, xincr1, yincr1;
+ SIZE xerror0, xerror1, yerror0, yerror1;
+ RECT r_intersect;
+ IMAGE_BOX IB0, IB1;
+ BOOLEAN check0, check1;
+
+ IB0.FramePtr = pControl0->IntersectStamp.frame;
+ IB0.Box.corner = pr0->corner;
+ IB0.Box.extent.width = GetFrameWidth (IB0.FramePtr);
+ IB0.Box.extent.height = GetFrameHeight (IB0.FramePtr);
+ IB1.FramePtr = pControl1->IntersectStamp.frame;
+ IB1.Box.corner = pr1->corner;
+ IB1.Box.extent.width = GetFrameWidth (IB1.FramePtr);
+ IB1.Box.extent.height = GetFrameHeight (IB1.FramePtr);
+
+ dx_0 = pr0->extent.width;
+ dy_0 = pr0->extent.height;
+ if (dx_0 >= 0)
+ xincr0 = 1;
+ else
+ {
+ xincr0 = -1;
+ dx_0 = -dx_0;
+ }
+ if (dy_0 >= 0)
+ yincr0 = 1;
+ else
+ {
+ yincr0 = -1;
+ dy_0 = -dy_0;
+ }
+ if (dx_0 >= dy_0)
+ cycle0 = dx_0;
+ else
+ cycle0 = dy_0;
+ xerror0 = yerror0 = cycle0;
+
+ dx_1 = pr1->extent.width;
+ dy_1 = pr1->extent.height;
+ if (dx_1 >= 0)
+ xincr1 = 1;
+ else
+ {
+ xincr1 = -1;
+ dx_1 = -dx_1;
+ }
+ if (dy_1 >= 0)
+ yincr1 = 1;
+ else
+ {
+ yincr1 = -1;
+ dy_1 = -dy_1;
+ }
+ if (dx_1 >= dy_1)
+ cycle1 = dx_1;
+ else
+ cycle1 = dy_1;
+ xerror1 = yerror1 = cycle1;
+
+ check0 = check1 = FALSE;
+ if (t0 <= 1)
+ {
+ time_error0 = time_error1 = 0;
+ if (t0 == 0)
+ {
+ ++t0;
+ goto CheckFirstIntersection;
+ }
+ }
+ else
+ {
+ SIZE delta;
+ COUNT start;
+ long error;
+
+ start = (COUNT)cycle0 * (COUNT)(t0 - 1);
+ time_error0 = start & ((1 << TIME_SHIFT) - 1);
+ if ((start >>= (COUNT)TIME_SHIFT) > 0)
+ {
+ if ((error = (long)xerror0
+ - (long)dx_0 * (long)start) > 0)
+ xerror0 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle0) + 1;
+ IB0.Box.corner.x += xincr0 * delta;
+ xerror0 = (SIZE)(error + (long)cycle0 * (long)delta);
+ }
+ if ((error = (long)yerror0
+ - (long)dy_0 * (long)start) > 0)
+ yerror0 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle0) + 1;
+ IB0.Box.corner.y += yincr0 * delta;
+ yerror0 = (SIZE)(error + (long)cycle0 * (long)delta);
+ }
+ pr0->corner = IB0.Box.corner;
+ }
+
+ start = (COUNT)cycle1 * (COUNT)(t0 - 1);
+ time_error1 = start & ((1 << TIME_SHIFT) - 1);
+ if ((start >>= (COUNT)TIME_SHIFT) > 0)
+ {
+ if ((error = (long)xerror1
+ - (long)dx_1 * (long)start) > 0)
+ xerror1 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle1) + 1;
+ IB1.Box.corner.x += xincr1 * delta;
+ xerror1 = (SIZE)(error + (long)cycle1 * (long)delta);
+ }
+ if ((error = (long)yerror1
+ - (long)dy_1 * (long)start) > 0)
+ yerror1 = (SIZE)error;
+ else
+ {
+ delta = -(SIZE)(error / (long)cycle1) + 1;
+ IB1.Box.corner.y += yincr1 * delta;
+ yerror1 = (SIZE)(error + (long)cycle1 * (long)delta);
+ }
+ pr1->corner = IB1.Box.corner;
+ }
+ }
+
+ pControl0->last_time_val = pControl1->last_time_val = t0;
+ do
+ {
+ ++t0;
+ if ((time_error0 += cycle0) >= (1 << TIME_SHIFT))
+ {
+ if ((xerror0 -= dx_0) <= 0)
+ {
+ IB0.Box.corner.x += xincr0;
+ xerror0 += cycle0;
+ }
+ if ((yerror0 -= dy_0) <= 0)
+ {
+ IB0.Box.corner.y += yincr0;
+ yerror0 += cycle0;
+ }
+
+ check0 = TRUE;
+ time_error0 -= (1 << TIME_SHIFT);
+ }
+
+ if ((time_error1 += cycle1) >= (1 << TIME_SHIFT))
+ {
+ if ((xerror1 -= dx_1) <= 0)
+ {
+ IB1.Box.corner.x += xincr1;
+ xerror1 += cycle1;
+ }
+ if ((yerror1 -= dy_1) <= 0)
+ {
+ IB1.Box.corner.y += yincr1;
+ yerror1 += cycle1;
+ }
+
+ check1 = TRUE;
+ time_error1 -= (1 << TIME_SHIFT);
+ }
+
+ if (check0 || check1)
+ { /* if check0 && check1, this may not be quite right --
+ * if shapes had a pixel's separation to begin with
+ * and both moved toward each other, you would actually
+ * get a pixel overlap but since the last positions were
+ * separated by a pixel, the shapes wouldn't be touching
+ * each other.
+ */
+CheckFirstIntersection:
+ if (BoxIntersect (&IB0.Box, &IB1.Box, &r_intersect)
+ && images_intersect (&IB0, &IB1, &r_intersect))
+ return (t0);
+
+ if (check0)
+ {
+ pr0->corner = IB0.Box.corner;
+ pControl0->last_time_val = t0;
+ check0 = FALSE;
+ }
+ if (check1)
+ {
+ pr1->corner = IB1.Box.corner;
+ pControl1->last_time_val = t0;
+ check1 = FALSE;
+ }
+ }
+ } while (t0 <= t1);
+
+ return ((TIME_VALUE)0);
+}
+
+TIME_VALUE
+DrawablesIntersect (INTERSECT_CONTROL *pControl0,
+ INTERSECT_CONTROL *pControl1, TIME_VALUE max_time_val)
+{
+ SIZE dy;
+ SIZE time_y_0, time_y_1;
+ RECT r0, r1;
+ FRAME FramePtr0, FramePtr1;
+
+ if (!ContextActive () || max_time_val == 0)
+ return ((TIME_VALUE)0);
+ else if (max_time_val > MAX_TIME_VALUE)
+ max_time_val = MAX_TIME_VALUE;
+
+ pControl0->last_time_val = pControl1->last_time_val = 0;
+
+ r0.corner = pControl0->IntersectStamp.origin;
+ r1.corner = pControl1->IntersectStamp.origin;
+
+ r0.extent.width = pControl0->EndPoint.x - r0.corner.x;
+ r0.extent.height = pControl0->EndPoint.y - r0.corner.y;
+ r1.extent.width = pControl1->EndPoint.x - r1.corner.x;
+ r1.extent.height = pControl1->EndPoint.y - r1.corner.y;
+
+ FramePtr0 = pControl0->IntersectStamp.frame;
+ if (FramePtr0 == 0)
+ return(0);
+ r0.corner.x -= FramePtr0->HotSpot.x;
+ r0.corner.y -= FramePtr0->HotSpot.y;
+
+ FramePtr1 = pControl1->IntersectStamp.frame;
+ if (FramePtr1 == 0)
+ return(0);
+ r1.corner.x -= FramePtr1->HotSpot.x;
+ r1.corner.y -= FramePtr1->HotSpot.y;
+
+ dy = r1.corner.y - r0.corner.y;
+ time_y_0 = dy - GetFrameHeight (FramePtr0) + 1;
+ time_y_1 = dy + GetFrameHeight (FramePtr1) - 1;
+ dy = r0.extent.height - r1.extent.height;
+
+ if ((time_y_0 <= 0 && time_y_1 >= 0)
+ || (time_y_0 > 0 && dy >= time_y_0)
+ || (time_y_1 < 0 && dy <= time_y_1))
+ {
+ SIZE dx;
+ SIZE time_x_0, time_x_1;
+
+ dx = r1.corner.x - r0.corner.x;
+ time_x_0 = dx - GetFrameWidth (FramePtr0) + 1;
+ time_x_1 = dx + GetFrameWidth (FramePtr1) - 1;
+ dx = r0.extent.width - r1.extent.width;
+
+ if ((time_x_0 <= 0 && time_x_1 >= 0)
+ || (time_x_0 > 0 && dx >= time_x_0)
+ || (time_x_1 < 0 && dx <= time_x_1))
+ {
+ TIME_VALUE intersect_time;
+
+ if (dx == 0 && dy == 0)
+ time_y_0 = time_y_1 = 0;
+ else
+ {
+ SIZE t;
+ long time_beg, time_end, fract;
+
+ if (time_y_1 < 0)
+ {
+ t = time_y_0;
+ time_y_0 = -time_y_1;
+ time_y_1 = -t;
+ }
+ else if (time_y_0 <= 0)
+ {
+ if (dy < 0)
+ time_y_1 = -time_y_0;
+ time_y_0 = 0;
+ }
+ if (dy < 0)
+ dy = -dy;
+ if (dy < time_y_1)
+ time_y_1 = dy;
+ /* just to be safe, widen search area */
+ --time_y_0;
+ ++time_y_1;
+
+ if (time_x_1 < 0)
+ {
+ t = time_x_0;
+ time_x_0 = -time_x_1;
+ time_x_1 = -t;
+ }
+ else if (time_x_0 <= 0)
+ {
+ if (dx < 0)
+ time_x_1 = -time_x_0;
+ time_x_0 = 0;
+ }
+ if (dx < 0)
+ dx = -dx;
+ if (dx < time_x_1)
+ time_x_1 = dx;
+ /* just to be safe, widen search area */
+ --time_x_0;
+ ++time_x_1;
+
+#ifdef DEBUG_INTERSEC
+ log_add (log_Debug, "FramePtr0<%d, %d> --> <%d, %d>",
+ GetFrameWidth (FramePtr0), GetFrameHeight (FramePtr0),
+ r0.corner.x, r0.corner.y);
+ log_add (log_Debug, "FramePtr1<%d, %d> --> <%d, %d>",
+ GetFrameWidth (FramePtr1), GetFrameHeight (FramePtr1),
+ r1.corner.x, r1.corner.y);
+ log_add (log_Debug, "time_x(%d, %d)-%d, time_y(%d, %d)-%d",
+ time_x_0, time_x_1, dx, time_y_0, time_y_1, dy);
+#endif /* DEBUG_INTERSEC */
+ if (dx == 0)
+ {
+ time_beg = time_y_0;
+ time_end = time_y_1;
+ fract = dy;
+ }
+ else if (dy == 0)
+ {
+ time_beg = time_x_0;
+ time_end = time_x_1;
+ fract = dx;
+ }
+ else
+ {
+ long time_x, time_y;
+
+ time_x = (long)time_x_0 * (long)dy;
+ time_y = (long)time_y_0 * (long)dx;
+ time_beg = time_x < time_y ? time_y : time_x;
+
+ time_x = (long)time_x_1 * (long)dy;
+ time_y = (long)time_y_1 * (long)dx;
+ time_end = time_x > time_y ? time_y : time_x;
+
+ fract = (long)dx * (long)dy;
+ }
+
+ if ((time_beg <<= TIME_SHIFT) < fract)
+ time_y_0 = 0;
+ else
+ time_y_0 = (SIZE)(time_beg / fract);
+
+ if (time_end >= fract /* just in case of overflow */
+ || (time_end <<= TIME_SHIFT) >=
+ fract * (long)max_time_val)
+ time_y_1 = max_time_val - 1;
+ else
+ time_y_1 = (SIZE)((time_end + fract - 1) / fract) - 1;
+ }
+
+#ifdef DEBUG_INTERSEC
+ log_add (log_Debug, "start_time = %d, end_time = %d",
+ time_y_0, time_y_1);
+#endif /* DEBUG_INTERSEC */
+ if (time_y_0 <= time_y_1
+ && (intersect_time = frame_intersect (
+ pControl0, &r0, pControl1, &r1,
+ (TIME_VALUE)time_y_0, (TIME_VALUE)time_y_1)))
+ {
+ FramePtr0 = pControl0->IntersectStamp.frame;
+ pControl0->EndPoint.x = r0.corner.x + FramePtr0->HotSpot.x;
+ pControl0->EndPoint.y = r0.corner.y + FramePtr0->HotSpot.y;
+ FramePtr1 = pControl1->IntersectStamp.frame;
+ pControl1->EndPoint.x = r1.corner.x + FramePtr1->HotSpot.x;
+ pControl1->EndPoint.y = r1.corner.y + FramePtr1->HotSpot.y;
+
+ return (intersect_time);
+ }
+ }
+ }
+
+ return ((TIME_VALUE)0);
+}
+
diff --git a/src/libs/graphics/loaddisp.c b/src/libs/graphics/loaddisp.c
new file mode 100644
index 0000000..ccc7919
--- /dev/null
+++ b/src/libs/graphics/loaddisp.c
@@ -0,0 +1,65 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/gfxlib.h"
+#include "libs/graphics/drawable.h"
+#include "libs/log.h"
+
+
+// Reads a piece of screen into a passed FRAME or a newly created one
+DRAWABLE
+LoadDisplayPixmap (const RECT *area, FRAME frame)
+{
+ // TODO: This should just return a FRAME instead of DRAWABLE
+ DRAWABLE buffer = GetFrameParentDrawable (frame);
+ COUNT index;
+
+ if (!buffer)
+ { // asked to create a new DRAWABLE instead
+ buffer = CreateDrawable (WANT_PIXMAP | MAPPED_TO_DISPLAY,
+ area->extent.width, area->extent.height, 1);
+ if (!buffer)
+ return NULL;
+
+ index = 0;
+ }
+ else
+ {
+ index = GetFrameIndex (frame);
+ }
+
+ frame = SetAbsFrameIndex (CaptureDrawable (buffer), index);
+
+ if (_CurFramePtr->Type != SCREEN_DRAWABLE
+ || frame->Type == SCREEN_DRAWABLE
+ || !(GetFrameParentDrawable (frame)->Flags & MAPPED_TO_DISPLAY))
+ {
+ log_add (log_Warning, "Unimplemented function activated: "
+ "LoadDisplayPixmap()");
+ }
+ else
+ {
+ TFB_Image *img = frame->image;
+ TFB_DrawScreen_CopyToImage (img, area, TFB_SCREEN_MAIN);
+ }
+
+ ReleaseDrawable (frame);
+
+ return buffer;
+}
+
diff --git a/src/libs/graphics/pixmap.c b/src/libs/graphics/pixmap.c
new file mode 100644
index 0000000..6e68244
--- /dev/null
+++ b/src/libs/graphics/pixmap.c
@@ -0,0 +1,170 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+#include "libs/log.h"
+
+DRAWABLE
+GetFrameParentDrawable (FRAME f)
+{
+ if (f != NULL)
+ {
+ return f->parent;
+ }
+ return NULL;
+}
+
+FRAME
+CaptureDrawable (DRAWABLE DrawablePtr)
+{
+ if (DrawablePtr)
+ {
+ return &DrawablePtr->Frame[0];
+ }
+
+ return NULL;
+}
+
+DRAWABLE
+ReleaseDrawable (FRAME FramePtr)
+{
+ if (FramePtr != 0)
+ {
+ DRAWABLE Drawable;
+
+ Drawable = GetFrameParentDrawable (FramePtr);
+
+ return (Drawable);
+ }
+
+ return NULL;
+}
+
+COUNT
+GetFrameCount (FRAME FramePtr)
+{
+ DRAWABLE_DESC *DrawablePtr;
+
+ if (FramePtr == 0)
+ return (0);
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ return DrawablePtr->MaxIndex + 1;
+}
+
+COUNT
+GetFrameIndex (FRAME FramePtr)
+{
+ if (FramePtr == 0)
+ return (0);
+
+ return FramePtr->Index;
+}
+
+FRAME
+SetAbsFrameIndex (FRAME FramePtr, COUNT FrameIndex)
+{
+ if (FramePtr != 0)
+ {
+ DRAWABLE_DESC *DrawablePtr;
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+
+ FrameIndex = FrameIndex % (DrawablePtr->MaxIndex + 1);
+ FramePtr = &DrawablePtr->Frame[FrameIndex];
+ }
+
+ return FramePtr;
+}
+
+FRAME
+SetRelFrameIndex (FRAME FramePtr, SIZE FrameOffs)
+{
+ if (FramePtr != 0)
+ {
+ COUNT num_frames;
+ DRAWABLE_DESC *DrawablePtr;
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ num_frames = DrawablePtr->MaxIndex + 1;
+ if (FrameOffs < 0)
+ {
+ while ((FrameOffs += num_frames) < 0)
+ ;
+ }
+
+ FrameOffs = ((SWORD)FramePtr->Index + FrameOffs) % num_frames;
+ FramePtr = &DrawablePtr->Frame[FrameOffs];
+ }
+
+ return FramePtr;
+}
+
+FRAME
+SetEquFrameIndex (FRAME DstFramePtr, FRAME SrcFramePtr)
+{
+ COUNT Index;
+
+ if (!DstFramePtr || !SrcFramePtr)
+ return 0;
+
+ Index = GetFrameIndex (SrcFramePtr);
+#ifdef DEBUG
+ {
+ DRAWABLE_DESC *DrawablePtr = GetFrameParentDrawable (DstFramePtr);
+ if (Index > DrawablePtr->MaxIndex)
+ log_add (log_Debug, "SetEquFrameIndex: source index (%d) beyond "
+ "destination range (%d)", (int)Index,
+ (int)DrawablePtr->MaxIndex);
+ }
+#endif
+
+ return SetAbsFrameIndex (DstFramePtr, Index);
+}
+
+FRAME
+IncFrameIndex (FRAME FramePtr)
+{
+ DRAWABLE_DESC *DrawablePtr;
+
+ if (FramePtr == 0)
+ return (0);
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ if (FramePtr->Index < DrawablePtr->MaxIndex)
+ return ++FramePtr;
+ else
+ return DrawablePtr->Frame;
+}
+
+FRAME
+DecFrameIndex (FRAME FramePtr)
+{
+ if (FramePtr == 0)
+ return (0);
+
+ if (FramePtr->Index > 0)
+ return --FramePtr;
+ else
+ {
+ DRAWABLE_DESC *DrawablePtr;
+
+ DrawablePtr = GetFrameParentDrawable (FramePtr);
+ return &DrawablePtr->Frame[DrawablePtr->MaxIndex];
+ }
+}
diff --git a/src/libs/graphics/prim.h b/src/libs/graphics/prim.h
new file mode 100644
index 0000000..bce3c2a
--- /dev/null
+++ b/src/libs/graphics/prim.h
@@ -0,0 +1,80 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef LIBS_GRAPHICS_PRIM_H_
+#define LIBS_GRAPHICS_PRIM_H_
+
+enum gfx_object
+{
+ POINT_PRIM = 0,
+ STAMP_PRIM,
+ STAMPFILL_PRIM,
+ LINE_PRIM,
+ TEXT_PRIM,
+ RECT_PRIM,
+ RECTFILL_PRIM,
+
+ NUM_PRIMS
+};
+typedef BYTE GRAPHICS_PRIM;
+
+typedef union
+{
+ POINT Point;
+ STAMP Stamp;
+ LINE Line;
+ TEXT Text;
+ RECT Rect;
+} PRIM_DESC;
+
+typedef DWORD PRIM_LINKS;
+
+typedef struct
+{
+ PRIM_LINKS Links;
+ GRAPHICS_PRIM Type;
+ Color color;
+ PRIM_DESC Object;
+} PRIMITIVE;
+
+#define END_OF_LIST ((COUNT)0xFFFF)
+
+#define GetPredLink(l) LOWORD(l)
+#define GetSuccLink(l) HIWORD(l)
+#define MakeLinks MAKE_DWORD
+#define SetPrimLinks(pPrim,p,s) ((pPrim)->Links = MakeLinks (p, s))
+#define GetPrimLinks(pPrim) ((pPrim)->Links)
+#define SetPrimType(pPrim,t) ((pPrim)->Type = t)
+#define GetPrimType(pPrim) ((pPrim)->Type)
+#define SetPrimColor(pPrim,c) ((pPrim)->color = c)
+#define GetPrimColor(pPrim) ((pPrim)->color)
+
+static inline void
+SetPrimNextLink (PRIMITIVE *pPrim, COUNT Link)
+{
+ SetPrimLinks (pPrim, END_OF_LIST, Link);
+}
+
+
+static inline COUNT
+GetPrimNextLink (PRIMITIVE *pPrim)
+{
+ return GetSuccLink (GetPrimLinks (pPrim));
+}
+
+
+#endif /* PRIM_H */
+
+
diff --git a/src/libs/graphics/resgfx.c b/src/libs/graphics/resgfx.c
new file mode 100644
index 0000000..c0760dc
--- /dev/null
+++ b/src/libs/graphics/resgfx.c
@@ -0,0 +1,54 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfxintrn.h"
+
+static void
+GetCelFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetCelData);
+}
+
+static void
+GetFontFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetFontData);
+}
+
+
+BOOLEAN
+InstallGraphicResTypes (void)
+{
+ InstallResTypeVectors ("GFXRES", GetCelFileData, _ReleaseCelData, NULL);
+ InstallResTypeVectors ("FONTRES", GetFontFileData, _ReleaseFontData, NULL);
+ return (TRUE);
+}
+
+/* Needs to be void * because it could be either a DRAWABLE or a FONT. */
+void *
+LoadGraphicInstance (RESOURCE res)
+{
+ void *hData;
+
+ hData = res_GetResource (res);
+ if (hData)
+ res_DetachResource (res);
+
+ return (hData);
+}
+
diff --git a/src/libs/graphics/sdl/2xscalers.c b/src/libs/graphics/sdl/2xscalers.c
new file mode 100644
index 0000000..a612d2c
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers.c
@@ -0,0 +1,260 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_C_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_HqFilter},
+ // Default
+ {0, Scale_Nearest}
+};
+
+// See
+// nearest2x.c -- Nearest Neighboor scaling
+// bilinear2x.c -- Bilinear scaling
+// biadv2x.c -- Advanced Biadapt scaling
+// triscan2x.c -- Triscan scaling
+
+// Biadapt scaling to 2x
+void
+SCALE_(BiAdaptFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+ Uint32 pixval_tl, pixval_tr, pixval_bl, pixval_br;
+
+ // these macros are for clarity; they make the current pixel (0,0)
+ // and allow to access pixels in all directions
+ #define SRC(x, y) (src_p + (x) + ((y) * slen))
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 2, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = region->x; x < xend; ++x, ++src_p, ++dst_p)
+ {
+ pixval_tl = SCALE_GETPIX (SRC (0, 0));
+
+ SCALE_SETPIX (dst_p, pixval_tl);
+
+ if (y + 1 < h)
+ {
+ // check pixel below the current one
+ pixval_bl = SCALE_GETPIX (SRC (0, 1));
+
+ if (pixval_tl == pixval_bl)
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ else
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ pixval_tl, pixval_bl)
+ );
+ }
+ else
+ {
+ // last pixel in column - propagate
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ pixval_bl = pixval_tl;
+ }
+ ++dst_p;
+
+ if (x + 1 >= w)
+ {
+ // last pixel in row - propagate
+ SCALE_SETPIX (dst_p, pixval_tl);
+
+ if (pixval_tl == pixval_bl)
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ else
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ pixval_tl, pixval_bl)
+ );
+ continue;
+ }
+
+ // check pixel to the right from the current one
+ pixval_tr = SCALE_GETPIX (SRC (1, 0));
+
+ if (pixval_tl == pixval_tr)
+ SCALE_SETPIX (dst_p, pixval_tr);
+ else
+ SCALE_SETPIX (dst_p, Scale_Blend_11 (
+ pixval_tl, pixval_tr)
+ );
+
+ if (y + 1 >= h)
+ {
+ // last pixel in column - propagate
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ continue;
+ }
+
+ // check pixel to the bottom-right
+ pixval_br = SCALE_GETPIX (SRC (1, 1));
+
+ if (pixval_tl == pixval_br && pixval_tr == pixval_bl)
+ {
+ int cl, cr;
+ Uint32 clr;
+
+ if (pixval_tl == pixval_tr)
+ {
+ // all 4 are equal - propagate
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ continue;
+ }
+
+ // both pairs are equal, have to resolve the pixel
+ // race; we try detecting which color is
+ // the background by looking for a line or an edge
+ // examine 8 pixels surrounding the current quad
+
+ cl = cr = 1;
+
+ if (x > 0)
+ {
+ clr = SCALE_GETPIX (SRC (-1, 0));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (-1, 1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ if (y > 0)
+ {
+ clr = SCALE_GETPIX (SRC (0, -1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (1, -1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ if (x + 2 < w)
+ {
+ clr = SCALE_GETPIX (SRC (2, 0));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (2, 1));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ if (y + 2 < h)
+ {
+ clr = SCALE_GETPIX (SRC (0, 2));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+
+ clr = SCALE_GETPIX (SRC (1, 2));
+ if (clr == pixval_tl)
+ cl++;
+ else if (clr == pixval_tr)
+ cr++;
+ }
+
+ // least count wins
+ if (cl > cr)
+ SCALE_SETPIX (dst_p + dlen, pixval_tr);
+ else if (cr > cl)
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ else
+ SCALE_SETPIX (dst_p + dlen,
+ Scale_Blend_11 (pixval_tl, pixval_tr));
+ }
+ else if (pixval_tl == pixval_br)
+ {
+ // main diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, pixval_tl);
+ }
+ else if (pixval_tr == pixval_bl)
+ {
+ // 2nd diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, pixval_tr);
+ }
+ else
+ {
+ // blend all 4
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_1111 (
+ pixval_tl, pixval_bl, pixval_tr, pixval_br
+ ));
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/2xscalers.h b/src/libs/graphics/sdl/2xscalers.h
new file mode 100644
index 0000000..f004fcd
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers.h
@@ -0,0 +1,30 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_SDL_2XSCALERS_H_
+#define LIBS_GRAPHICS_SDL_2XSCALERS_H_
+
+void Scale_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_BiAdaptFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_C_Functions[];
+
+
+#endif /* LIBS_GRAPHICS_SDL_2XSCALERS_H_ */
diff --git a/src/libs/graphics/sdl/2xscalers_3dnow.c b/src/libs/graphics/sdl/2xscalers_3dnow.c
new file mode 100644
index 0000000..da34e82
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_3dnow.c
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/platform.h"
+
+#if defined(MMX_ASM)
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#include "2xscalers_mmx.h"
+
+// 3DNow! name for all functions
+#undef SCALE_
+#define SCALE_(name) Scale ## _3DNow_ ## name
+
+// Tell them which opcodes we want to support
+#undef USE_MOVNTQ
+#define USE_PREFETCH AMD_PREFETCH
+#undef USE_PSADBW
+// Bring in inline asm functions
+#include "scalemmx.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_3DNow_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_3DNow_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_MMX_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_MMX_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter},
+ // Default
+ {0, Scale_3DNow_Nearest}
+};
+
+
+void
+Scale_3DNow_PrepPlatform (const SDL_PixelFormat* fmt)
+{
+ Scale_MMX_PrepPlatform (fmt);
+}
+
+// Nearest Neighbor scaling to 2x
+// void Scale_3DNow_Nearest (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "nearest2x.c"
+
+
+// Bilinear scaling to 2x
+// void Scale_3DNow_BilinearFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "bilinear2x.c"
+
+
+#if 0 && NO_IMPROVEMENT
+
+// Advanced Biadapt scaling to 2x
+// void Scale_3DNow_BiAdaptAdvFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "biadv2x.c"
+
+
+// Triscan scaling to 2x
+// derivative of scale2x -- scale2x.sf.net
+// void Scale_3DNow_TriScanFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "triscan2x.c"
+
+// Hq2x scaling
+// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html)
+// void Scale_3DNow_HqFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "hq2x.c"
+
+#endif /* NO_IMPROVEMENT */
+
+#endif /* MMX_ASM */
+
diff --git a/src/libs/graphics/sdl/2xscalers_mmx.c b/src/libs/graphics/sdl/2xscalers_mmx.c
new file mode 100644
index 0000000..c321b16
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_mmx.c
@@ -0,0 +1,136 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/platform.h"
+
+#if defined(MMX_ASM)
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#include "2xscalers_mmx.h"
+
+// MMX name for all functions
+#undef SCALE_
+#define SCALE_(name) Scale ## _MMX_ ## name
+
+// Tell them which opcodes we want to support
+#undef USE_MOVNTQ
+#undef USE_PREFETCH
+#undef USE_PSADBW
+// And Bring in inline asm functions
+#include "scalemmx.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_MMX_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_MMX_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_MMX_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_MMX_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter},
+ // Default
+ {0, Scale_MMX_Nearest}
+};
+
+// MMX transformation multipliers
+Uint64 mmx_888to555_mult;
+Uint64 mmx_Y_mult;
+Uint64 mmx_U_mult;
+Uint64 mmx_V_mult;
+// Uint64 mmx_YUV_threshold = 0x00300706; original hq2x threshold
+//Uint64 mmx_YUV_threshold = 0x0030100e;
+Uint64 mmx_YUV_threshold = 0x0040120c;
+
+void
+Scale_MMX_PrepPlatform (const SDL_PixelFormat* fmt)
+{
+ // prepare the channel-shuffle multiplier
+ mmx_888to555_mult = ((Uint64)0x0400) << (fmt->Rshift * 2)
+ | ((Uint64)0x0020) << (fmt->Gshift * 2)
+ | ((Uint64)0x0001) << (fmt->Bshift * 2);
+
+ // prepare the RGB->YUV multipliers
+ mmx_Y_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_Y])
+ << (fmt->Rshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_Y])
+ << (fmt->Gshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_Y])
+ << (fmt->Bshift * 2);
+
+ mmx_U_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_U])
+ << (fmt->Rshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_U])
+ << (fmt->Gshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_U])
+ << (fmt->Bshift * 2);
+
+ mmx_V_mult = ((Uint64)(uint16)YUV_matrix[YUV_XFORM_R][YUV_XFORM_V])
+ << (fmt->Rshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_G][YUV_XFORM_V])
+ << (fmt->Gshift * 2)
+ | ((Uint64)(uint16)YUV_matrix[YUV_XFORM_B][YUV_XFORM_V])
+ << (fmt->Bshift * 2);
+
+ mmx_YUV_threshold = (SCALE_DIFFYUV_TY << 16) | (SCALE_DIFFYUV_TU << 8)
+ | SCALE_DIFFYUV_TV;
+}
+
+
+// Nearest Neighbor scaling to 2x
+// void Scale_MMX_Nearest (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "nearest2x.c"
+
+
+// Bilinear scaling to 2x
+// void Scale_MMX_BilinearFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "bilinear2x.c"
+
+
+// Advanced Biadapt scaling to 2x
+// void Scale_MMX_BiAdaptAdvFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "biadv2x.c"
+
+
+// Triscan scaling to 2x
+// derivative of 'scale2x' -- scale2x.sf.net
+// void Scale_MMX_TriScanFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "triscan2x.c"
+
+// Hq2x scaling
+// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html)
+// void Scale_MMX_HqFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "hq2x.c"
+
+
+#endif /* MMX_ASM */
+
diff --git a/src/libs/graphics/sdl/2xscalers_mmx.h b/src/libs/graphics/sdl/2xscalers_mmx.h
new file mode 100644
index 0000000..c8dba32
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_mmx.h
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_
+#define LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_
+
+// MMX versions
+void Scale_MMX_PrepPlatform (const SDL_PixelFormat* fmt);
+
+void Scale_MMX_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_MMX_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_MMX_Functions[];
+
+
+// SSE (Intel)/MMX Ext (Athlon) versions
+void Scale_SSE_PrepPlatform (const SDL_PixelFormat* fmt);
+
+void Scale_SSE_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_SSE_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_SSE_Functions[];
+
+
+// 3DNow (AMD K6/Athlon) versions
+void Scale_3DNow_PrepPlatform (const SDL_PixelFormat* fmt);
+
+void Scale_3DNow_Nearest (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_BilinearFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_BiAdaptAdvFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_TriScanFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+void Scale_3DNow_HqFilter (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r);
+
+extern const Scale_FuncDef_t Scale_3DNow_Functions[];
+
+
+#endif /* LIBS_GRAPHICS_SDL_2XSCALERS_MMX_H_ */
diff --git a/src/libs/graphics/sdl/2xscalers_sse.c b/src/libs/graphics/sdl/2xscalers_sse.c
new file mode 100644
index 0000000..a089744
--- /dev/null
+++ b/src/libs/graphics/sdl/2xscalers_sse.c
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "libs/platform.h"
+
+#if defined(MMX_ASM)
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#include "2xscalers_mmx.h"
+
+// SSE name for all functions
+#undef SCALE_
+#define SCALE_(name) Scale ## _SSE_ ## name
+
+// Tell them which opcodes we want to support
+#define USE_MOVNTQ
+#define USE_PREFETCH INTEL_PREFETCH
+#define USE_PSADBW
+// Bring in inline asm functions
+#include "scalemmx.h"
+
+
+// Scaler function lookup table
+//
+const Scale_FuncDef_t
+Scale_SSE_Functions[] =
+{
+ {TFB_GFXFLAGS_SCALE_BILINEAR, Scale_SSE_BilinearFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPT, Scale_BiAdaptFilter},
+ {TFB_GFXFLAGS_SCALE_BIADAPTADV, Scale_SSE_BiAdaptAdvFilter},
+ {TFB_GFXFLAGS_SCALE_TRISCAN, Scale_SSE_TriScanFilter},
+ {TFB_GFXFLAGS_SCALE_HQXX, Scale_MMX_HqFilter},
+ // Default
+ {0, Scale_SSE_Nearest}
+};
+
+
+void
+Scale_SSE_PrepPlatform (const SDL_PixelFormat* fmt)
+{
+ Scale_MMX_PrepPlatform (fmt);
+}
+
+// Nearest Neighbor scaling to 2x
+// void Scale_SSE_Nearest (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "nearest2x.c"
+
+
+// Bilinear scaling to 2x
+// void Scale_SSE_BilinearFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "bilinear2x.c"
+
+
+// Advanced Biadapt scaling to 2x
+// void Scale_SSE_BiAdaptAdvFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "biadv2x.c"
+
+
+// Triscan scaling to 2x
+// derivative of scale2x -- scale2x.sf.net
+// void Scale_SSE_TriScanFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "triscan2x.c"
+
+#if 0 && NO_IMPROVEMENT
+// Hq2x scaling
+// (adapted from 'hq2x' by Maxim Stepin -- www.hiend3d.com/hq2x.html)
+// void Scale_SSE_HqFilter (SDL_Surface *src,
+// SDL_Surface *dst, SDL_Rect *r)
+
+#include "hq2x.c"
+#endif
+
+#endif /* MMX_ASM */
+
diff --git a/src/libs/graphics/sdl/Makeinfo b/src/libs/graphics/sdl/Makeinfo
new file mode 100644
index 0000000..f940b76
--- /dev/null
+++ b/src/libs/graphics/sdl/Makeinfo
@@ -0,0 +1,9 @@
+uqm_CFILES="opengl.c palette.c primitives.c pure.c sdl2_pure.c
+ sdl_common.c sdl1_common.c sdl2_common.c
+ scalers.c 2xscalers.c
+ 2xscalers_mmx.c 2xscalers_sse.c 2xscalers_3dnow.c
+ nearest2x.c bilinear2x.c biadv2x.c triscan2x.c hq2x.c
+ canvas.c png2sdl.c sdluio.c rotozoom.c"
+uqm_HFILES="2xscalers.h 2xscalers_mmx.h opengl.h palette.h png2sdl.h
+ primitives.h pure.h rotozoom.h scaleint.h scalemmx.h
+ scalers.h sdl_common.h sdluio.h"
diff --git a/src/libs/graphics/sdl/biadv2x.c b/src/libs/graphics/sdl/biadv2x.c
new file mode 100644
index 0000000..b38cdf7
--- /dev/null
+++ b/src/libs/graphics/sdl/biadv2x.c
@@ -0,0 +1,532 @@
+/*
+ * Portions Copyright (C) 2003-2005 Alex Volkov (codepro@usa.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the Advanced BiAdaptive screen scaler
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Advanced biadapt scaling to 2x
+// The name expands to either
+// Scale_BiAdaptAdvFilter (for plain C) or
+// Scale_MMX_BiAdaptAdvFilter (for MMX)
+// [others when platforms are added]
+void
+SCALE_(BiAdaptAdvFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ // for clarity purposes, the 'pixels' array here is transposed
+ Uint32 pixels[4][4];
+ static int resolve_coord[][2] =
+ {
+ {0, -1}, {1, -1}, { 2, 0}, { 2, 1},
+ {1, 2}, {0, 2}, {-1, 1}, {-1, 0},
+ {100, 100} // term
+ };
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ // these macros are for clarity; they make the current pixel (0,0)
+ // and allow to access pixels in all directions
+ #define PIX(x, y) (pixels[1 + (x)][1 + (y)])
+ #define SRC(x, y) (src_p + (x) + ((y) * slen))
+ // commonly used operations, for clarity also
+ // others are defined at their respective bpp levels
+ #define BIADAPT_RGBHIGH 8000
+ #define BIADAPT_YUVLOW 30
+ #define BIADAPT_YUVMED 70
+ #define BIADAPT_YUVHIGH 130
+
+ // high tolerance pixel comparison
+ #define BIADAPT_CMPRGB_HIGH(p1, p2) \
+ (p1 == p2 || SCALE_CMPRGB (p1, p2) <= BIADAPT_RGBHIGH)
+
+ // low tolerance pixel comparison
+ #define BIADAPT_CMPYUV_LOW(p1, p2) \
+ (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVLOW))
+ // medium tolerance pixel comparison
+ #define BIADAPT_CMPYUV_MED(p1, p2) \
+ (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVMED))
+ // high tolerance pixel comparison
+ #define BIADAPT_CMPYUV_HIGH(p1, p2) \
+ (p1 == p2 || SCALE_CMPYUV (p1, p2, BIADAPT_YUVHIGH))
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 2, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ #define SCALE_GETPIX(p) ( *(Uint32 *)(p) )
+ #define SCALE_SETPIX(p, c) ( *(Uint32 *)(p) = (c) )
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = region->x; x < xend; ++x, ++src_p, ++dst_p)
+ {
+ // pixel equality counter
+ int cmatch;
+
+ // most pixels will fall into 'all 4 equal'
+ // pattern, so we check it first
+ cmatch = 0;
+
+ PIX (0, 0) = SCALE_GETPIX (SRC (0, 0));
+
+ SCALE_SETPIX (dst_p, PIX (0, 0));
+
+ if (y + 1 < h)
+ {
+ // check pixel below the current one
+ PIX (0, 1) = SCALE_GETPIX (SRC (0, 1));
+
+ if (PIX (0, 0) == PIX (0, 1))
+ {
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ cmatch |= 1;
+ }
+ }
+ else
+ {
+ // last pixel in column - propagate
+ PIX (0, 1) = PIX (0, 0);
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ cmatch |= 1;
+
+ }
+
+ if (x + 1 < w)
+ {
+ // check pixel to the right from the current one
+ PIX (1, 0) = SCALE_GETPIX (SRC (1, 0));
+
+ if (PIX (0, 0) == PIX (1, 0))
+ {
+ SCALE_SETPIX (dst_p + 1, PIX (0, 0));
+ cmatch |= 2;
+ }
+ }
+ else
+ {
+ // last pixel in row - propagate
+ PIX (1, 0) = PIX (0, 0);
+ SCALE_SETPIX (dst_p + 1, PIX (0, 0));
+ cmatch |= 2;
+ }
+
+ if (cmatch == 3)
+ {
+ if (y + 1 >= h || x + 1 >= w)
+ {
+ // last pixel in row/column and nearest
+ // neighboor is identical
+ dst_p++;
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ continue;
+ }
+
+ // check pixel to the bottom-right
+ PIX (1, 1) = SCALE_GETPIX (SRC (1, 1));
+
+ if (PIX (0, 0) == PIX (1, 1))
+ {
+ // all 4 are equal - propagate
+ dst_p++;
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ continue;
+ }
+ }
+
+ // some neighboors are different, lets check them
+
+ if (x > 0)
+ PIX (-1, 0) = SCALE_GETPIX (SRC (-1, 0));
+ else
+ PIX (-1, 0) = PIX (0, 0);
+
+ if (x + 2 < w)
+ PIX (2, 0) = SCALE_GETPIX (SRC (2, 0));
+ else
+ PIX (2, 0) = PIX (1, 0);
+
+ if (y + 1 < h)
+ {
+ if (x > 0)
+ PIX (-1, 1) = SCALE_GETPIX (SRC (-1, 1));
+ else
+ PIX (-1, 1) = PIX (0, 1);
+
+ if (x + 2 < w)
+ {
+ PIX (1, 1) = SCALE_GETPIX (SRC (1, 1));
+ PIX (2, 1) = SCALE_GETPIX (SRC (2, 1));
+ }
+ else if (x + 1 < w)
+ {
+ PIX (1, 1) = SCALE_GETPIX (SRC (1, 1));
+ PIX (2, 1) = PIX (1, 1);
+ }
+ else
+ {
+ PIX (1, 1) = PIX (0, 1);
+ PIX (2, 1) = PIX (0, 1);
+ }
+ }
+ else
+ {
+ // last pixel in column
+ PIX (-1, 1) = PIX (-1, 0);
+ PIX (1, 1) = PIX (1, 0);
+ PIX (2, 1) = PIX (2, 0);
+ }
+
+ if (y + 2 < h)
+ {
+ PIX (0, 2) = SCALE_GETPIX (SRC (0, 2));
+
+ if (x > 0)
+ PIX (-1, 2) = SCALE_GETPIX (SRC (-1, 2));
+ else
+ PIX (-1, 2) = PIX (0, 2);
+
+ if (x + 2 < w)
+ {
+ PIX (1, 2) = SCALE_GETPIX (SRC (1, 2));
+ PIX (2, 2) = SCALE_GETPIX (SRC (2, 2));
+ }
+ else if (x + 1 < w)
+ {
+ PIX (1, 2) = SCALE_GETPIX (SRC (1, 2));
+ PIX (2, 2) = PIX (1, 2);
+ }
+ else
+ {
+ PIX (1, 2) = PIX (0, 2);
+ PIX (2, 2) = PIX (0, 2);
+ }
+ }
+ else
+ {
+ // last pixel in column
+ PIX (-1, 2) = PIX (-1, 1);
+ PIX (0, 2) = PIX (0, 1);
+ PIX (1, 2) = PIX (1, 1);
+ PIX (2, 2) = PIX (2, 1);
+ }
+
+ if (y > 0)
+ {
+ PIX (0, -1) = SCALE_GETPIX (SRC (0, -1));
+
+ if (x > 0)
+ PIX (-1, -1) = SCALE_GETPIX (SRC (-1, -1));
+ else
+ PIX (-1, -1) = PIX (0, -1);
+
+ if (x + 2 < w)
+ {
+ PIX (1, -1) = SCALE_GETPIX (SRC (1, -1));
+ PIX (2, -1) = SCALE_GETPIX (SRC (2, -1));
+ }
+ else if (x + 1 < w)
+ {
+ PIX (1, -1) = SCALE_GETPIX (SRC (1, -1));
+ PIX (2, -1) = PIX (1, -1);
+ }
+ else
+ {
+ PIX (1, -1) = PIX (0, -1);
+ PIX (2, -1) = PIX (0, -1);
+ }
+ }
+ else
+ {
+ PIX (-1, -1) = PIX (-1, 0);
+ PIX (0, -1) = PIX (0, 0);
+ PIX (1, -1) = PIX (1, 0);
+ PIX (2, -1) = PIX (2, 0);
+ }
+
+ // check pixel below the current one
+ if (!(cmatch & 1))
+ {
+ if (SCALE_CMPYUV (PIX (0, 0), PIX (0, 1), BIADAPT_YUVLOW))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (0, 1))
+ );
+ cmatch |= 1;
+ }
+ // detect a 2:1 line going across the current pixel
+ else if ( (PIX (0, 0) == PIX (-1, 0)
+ && PIX (0, 0) == PIX (1, 1)
+ && PIX (0, 0) == PIX (2, 1) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 0))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 2))))) ||
+
+ (PIX (0, 0) == PIX (1, 0)
+ && PIX (0, 0) == PIX (-1, 1)
+ && PIX (0, 0) == PIX (2, -1) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, -1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 0))))) )
+ {
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 0));
+ }
+ // detect a 2:1 line going across the pixel below current
+ else if ( (PIX (0, 1) == PIX (-1, 0)
+ && PIX (0, 1) == PIX (1, 1)
+ && PIX (0, 1) == PIX (2, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, 1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (0, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 2))))) ||
+
+ (PIX (0, 1) == PIX (1, 0)
+ && PIX (0, 1) == PIX (-1, 1)
+ && PIX (0, 1) == PIX (2, 0) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, -1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (-1, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (0, 2))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 1), PIX (2, 1))))) )
+ {
+ SCALE_SETPIX (dst_p + dlen, PIX (0, 1));
+ }
+ else
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (0, 1))
+ );
+ }
+
+ dst_p++;
+
+ // check pixel to the right from the current one
+ if (!(cmatch & 2))
+ {
+ if (SCALE_CMPYUV (PIX (0, 0), PIX (1, 0), BIADAPT_YUVLOW))
+ {
+ SCALE_SETPIX (dst_p, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 0))
+ );
+ cmatch |= 2;
+ }
+ // detect a 1:2 line going across the current pixel
+ else if ( (PIX (0, 0) == PIX (1, -1)
+ && PIX (0, 0) == PIX (0, 1)
+ && PIX (0, 0) == PIX (-1, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 1))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 2))))) ||
+
+ (PIX (0, 0) == PIX (0, -1)
+ && PIX (0, 0) == PIX (1, 1)
+ && PIX (0, 0) == PIX (1, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (-1, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (0, 2))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (0, 0), PIX (2, 2))))) )
+
+ {
+ SCALE_SETPIX (dst_p, PIX (0, 0));
+ }
+ // detect a 1:2 line going across the pixel to the right
+ else if ( (PIX (1, 0) == PIX (1, -1)
+ && PIX (1, 0) == PIX (0, 1)
+ && PIX (1, 0) == PIX (0, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (0, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, 2))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 2))))) ||
+
+ (PIX (1, 0) == PIX (0, -1)
+ && PIX (1, 0) == PIX (1, 1)
+ && PIX (1, 0) == PIX (2, 2) &&
+
+ ((!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (-1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (0, 1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, 2))) ||
+ (!BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (1, -1))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 0))
+ && !BIADAPT_CMPRGB_HIGH (PIX (1, 0), PIX (2, 1))))) )
+ {
+ SCALE_SETPIX (dst_p, PIX (1, 0));
+ }
+ else
+ SCALE_SETPIX (dst_p, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 0))
+ );
+ }
+
+ if (PIX (0, 0) == PIX (1, 1) && PIX (1, 0) == PIX (0, 1))
+ {
+ // diagonals are equal
+ int *coord;
+ int cl, cr;
+ Uint32 clr;
+
+ // both pairs are equal, have to resolve the pixel
+ // race; we try detecting which color is
+ // the background by looking for a line or an edge
+ // examine 8 pixels surrounding the current quad
+
+ cl = cr = 2;
+ for (coord = resolve_coord[0]; *coord < 100; coord += 2)
+ {
+ clr = PIX (coord[0], coord[1]);
+
+ if (BIADAPT_CMPYUV_MED (clr, PIX (0, 0)))
+ cl++;
+ else if (BIADAPT_CMPYUV_MED (clr, PIX (1, 0)))
+ cr++;
+ }
+
+ // least count wins
+ if (cl > cr)
+ clr = PIX (1, 0);
+ else if (cr > cl)
+ clr = PIX (0, 0);
+ else
+ clr = Scale_Blend_11 (PIX (0, 0), PIX (1, 0));
+
+ SCALE_SETPIX (dst_p + dlen, clr);
+ continue;
+ }
+
+ if (cmatch == 3
+ || (BIADAPT_CMPYUV_LOW (PIX (1, 0), PIX (0, 1))
+ && BIADAPT_CMPYUV_LOW (PIX (1, 0), PIX (1, 1))))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 1), PIX (1, 0))
+ );
+ continue;
+ }
+ else if (cmatch && BIADAPT_CMPYUV_LOW (PIX (0, 0), PIX (1, 1)))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 1))
+ );
+ continue;
+ }
+
+ // check pixel to the bottom-right
+ if (BIADAPT_CMPYUV_HIGH (PIX (0, 0), PIX (1, 1))
+ && BIADAPT_CMPYUV_HIGH (PIX (1, 0), PIX (0, 1)))
+ {
+ if (SCALE_GETY (PIX (0, 0)) > SCALE_GETY (PIX (1, 0)))
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 1))
+ );
+ }
+ else
+ {
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (1, 0), PIX (0, 1))
+ );
+ }
+ }
+ else if (BIADAPT_CMPYUV_HIGH (PIX (0, 0), PIX (1, 1)))
+ {
+ // main diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (0, 0), PIX (1, 1))
+ );
+ }
+ else if (BIADAPT_CMPYUV_HIGH (PIX (1, 0), PIX (0, 1)))
+ {
+ // 2nd diagonal is same color
+ // use its value
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_11 (
+ PIX (1, 0), PIX (0, 1))
+ );
+ }
+ else
+ {
+ // blend all 4
+ SCALE_SETPIX (dst_p + dlen, Scale_Blend_1111 (
+ PIX (0, 0), PIX (0, 1),
+ PIX (1, 0), PIX (1, 1)
+ ));
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/bilinear2x.c b/src/libs/graphics/sdl/bilinear2x.c
new file mode 100644
index 0000000..a12eb93
--- /dev/null
+++ b/src/libs/graphics/sdl/bilinear2x.c
@@ -0,0 +1,112 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the BiLinear screen scaler
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Bilinear scaling to 2x
+// The name expands to either
+// Scale_BilinearFilter (for plain C) or
+// Scale_MMX_BilinearFilter (for MMX)
+// Scale_SSE_BilinearFilter (for SSE)
+// [others when platforms are added]
+void
+SCALE_(BilinearFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int pitch = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int len = pitch / bpp, dlen = dp / bpp;
+ Uint32 p[4]; // influential pixels array
+ Uint32 *srow0 = (Uint32 *) src->pixels;
+ Uint32 *dst_p = (Uint32 *) dst->pixels;
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = w;
+ limits.h = h;
+ Scale_ExpandRect (region, 1, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = len - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ srow0 += len * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, srow0 += dsrc)
+ {
+ Uint32 *srow1;
+
+ SCALE_(Prefetch) (srow0 + 16);
+ SCALE_(Prefetch) (srow0 + 32);
+
+ if (y < h - 1)
+ srow1 = srow0 + len;
+ else
+ srow1 = srow0;
+
+ SCALE_(Prefetch) (srow1 + 16);
+ SCALE_(Prefetch) (srow1 + 32);
+
+ for (x = region->x; x < xend; ++x, ++srow0, ++srow1, dst_p += 2)
+ {
+ if (x < w - 1)
+ { // can blend directly from pixels
+ SCALE_BILINEAR_BLEND4 (srow0, srow1, dst_p, dlen);
+ }
+ else
+ { // need to make temp pixel rows
+ p[0] = srow0[0];
+ p[1] = p[0];
+ p[2] = srow1[0];
+ p[3] = p[2];
+
+ SCALE_BILINEAR_BLEND4 (&p[0], &p[2], dst_p, dlen);
+ }
+ }
+
+ SCALE_(Prefetch) (srow0 + dsrc);
+ SCALE_(Prefetch) (srow0 + dsrc + 16);
+ SCALE_(Prefetch) (srow1 + dsrc);
+ SCALE_(Prefetch) (srow1 + dsrc + 16);
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/canvas.c b/src/libs/graphics/sdl/canvas.c
new file mode 100644
index 0000000..ad7024d
--- /dev/null
+++ b/src/libs/graphics/sdl/canvas.c
@@ -0,0 +1,2176 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include <string.h>
+ // for memcpy()
+
+#include SDL_INCLUDE(SDL.h)
+#include "sdl_common.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/graphics/cmap.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "primitives.h"
+#include "palette.h"
+#include "sdluio.h"
+#include "rotozoom.h"
+#include "options.h"
+#include "types.h"
+
+typedef SDL_Surface *NativeCanvas;
+
+// BYTE x BYTE weight (mult >> 8) table
+static Uint8 btable[256][256];
+
+void
+TFB_DrawCanvas_Initialize (void)
+{
+ int i, j;
+ for (i = 0; i < 256; ++i)
+ for (j = 0; j < 256; ++j)
+ btable[j][i] = (j * i + 0x80) >> 8;
+ // need error correction here
+}
+
+const char *
+TFB_DrawCanvas_GetError (void)
+{
+ const char *err = SDL_GetError ();
+ // TODO: Should we call SDL_ClearError() here so that it is not
+ // returned again later?
+ return err;
+}
+
+static void
+checkPrimitiveMode (SDL_Surface *surf, Color *color, DrawMode *mode)
+{
+ const SDL_PixelFormat *fmt = surf->format;
+ // Special case: We support DRAW_ALPHA mode to non-alpha surfaces
+ // for primitives via Color.a
+ if (mode->kind == DRAW_REPLACE && fmt->Amask == 0 && color->a != 0xff)
+ {
+ mode->kind = DRAW_ALPHA;
+ mode->factor = color->a;
+ color->a = 0xff;
+ }
+}
+
+void
+TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_PixelFormat *fmt = dst->format;
+ Uint32 sdlColor;
+ RenderPixelFn plotFn;
+
+ checkPrimitiveMode (dst, &color, &mode);
+ sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a);
+
+ plotFn = renderpixel_for (target, mode.kind);
+ if (!plotFn)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_Line "
+ "unsupported draw mode (%d)", (int)mode.kind);
+ return;
+ }
+
+ SDL_LockSurface (dst);
+ line_prim (x1, y1, x2, y2, sdlColor, plotFn, mode.factor, dst);
+ SDL_UnlockSurface (dst);
+}
+
+void
+TFB_DrawCanvas_Rect (RECT *rect, Color color, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_PixelFormat *fmt = dst->format;
+ Uint32 sdlColor;
+ SDL_Rect sr;
+ sr.x = rect->corner.x;
+ sr.y = rect->corner.y;
+ sr.w = rect->extent.width;
+ sr.h = rect->extent.height;
+
+ checkPrimitiveMode (dst, &color, &mode);
+ sdlColor = SDL_MapRGBA (fmt, color.r, color.g, color.b, color.a);
+
+ if (mode.kind == DRAW_REPLACE)
+ { // Standard SDL fillrect rendering
+ Uint32 colorkey;
+ if (fmt->Amask && (TFB_GetColorKey (dst, &colorkey) == 0))
+ { // special case -- alpha surface with colorkey
+ // colorkey rects are transparent
+ if ((sdlColor & ~fmt->Amask) == (colorkey & ~fmt->Amask))
+ sdlColor &= ~fmt->Amask; // make transparent
+ }
+ SDL_FillRect (dst, &sr, sdlColor);
+ }
+ else
+ { // Custom fillrect rendering
+ RenderPixelFn plotFn = renderpixel_for (target, mode.kind);
+ if (!plotFn)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_Rect "
+ "unsupported draw mode (%d)", (int)mode.kind);
+ return;
+ }
+
+ SDL_LockSurface (dst);
+ fillrect_prim (sr, sdlColor, plotFn, mode.factor, dst);
+ SDL_UnlockSurface (dst);
+ }
+}
+
+static void
+TFB_DrawCanvas_Blit (SDL_Surface *src, SDL_Rect *src_r,
+ SDL_Surface *dst, SDL_Rect *dst_r, DrawMode mode)
+{
+ SDL_PixelFormat *srcfmt = src->format;
+
+ if (mode.kind == DRAW_REPLACE)
+ { // Standard SDL simple blit
+ SDL_BlitSurface (src, src_r, dst, dst_r);
+ }
+ else if (mode.kind == DRAW_ALPHA && srcfmt->Amask == 0)
+ { // Standard SDL surface-alpha blit
+ // Note that surface alpha and per-pixel alpha cannot work
+ // at the same time, which is why the Amask test
+ int hasAlpha = TFB_HasSurfaceAlphaMod (src);
+ assert (!hasAlpha);
+ // Set surface alpha temporarily
+ TFB_SetSurfaceAlphaMod (src, mode.factor);
+ SDL_BlitSurface (src, src_r, dst, dst_r);
+ TFB_DisableSurfaceAlphaMod (src);
+ }
+ else
+ { // Custom blit
+ SDL_Rect loc_src_r, loc_dst_r;
+ RenderPixelFn plotFn = renderpixel_for (dst, mode.kind);
+ if (!plotFn)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_Blit "
+ "unsupported draw mode (%d)", (int)mode.kind);
+ return;
+ }
+
+ if (!src_r)
+ { // blit whole image; generate rect
+ loc_src_r.x = 0;
+ loc_src_r.y = 0;
+ loc_src_r.w = src->w;
+ loc_src_r.h = src->h;
+ src_r = &loc_src_r;
+ }
+
+ if (!dst_r)
+ { // blit to 0,0; generate rect
+ loc_dst_r.x = 0;
+ loc_dst_r.y = 0;
+ loc_dst_r.w = dst->w;
+ loc_dst_r.h = dst->h;
+ dst_r = &loc_dst_r;
+ }
+
+ SDL_LockSurface (dst);
+ blt_prim (src, *src_r, plotFn, mode.factor, dst, *dst_r);
+ SDL_UnlockSurface (dst);
+ }
+}
+
+// XXX: If a colormap is passed in, it has to have been acquired via
+// TFB_GetColorMap(). We release the colormap at the end.
+void
+TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Rect srcRect, targetRect, *pSrcRect;
+ SDL_Surface *surf;
+ SDL_Palette *NormalPal;
+
+ if (img == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_Image passed null image ptr");
+ return;
+ }
+
+ LockMutex (img->mutex);
+
+ NormalPal = ((SDL_Surface *)img->NormalImg)->format->palette;
+ // only set the new palette if it changed
+ if (NormalPal && cmap && img->colormap_version != cmap->version)
+ TFB_SetColors (img->NormalImg, cmap->palette->colors, 0, 256);
+
+ if (scale != 0 && scale != GSCALE_IDENTITY)
+ {
+ if (scaleMode == TFB_SCALE_TRILINEAR && img->MipmapImg)
+ {
+ // only set the new palette if it changed
+ if (TFB_DrawCanvas_IsPaletted (img->MipmapImg)
+ && cmap && img->colormap_version != cmap->version)
+ TFB_SetColors (img->MipmapImg, cmap->palette->colors, 0, 256);
+ }
+ else if (scaleMode == TFB_SCALE_TRILINEAR && !img->MipmapImg)
+ { // Do bilinear scaling instead when mipmap is unavailable
+ scaleMode = TFB_SCALE_BILINEAR;
+ }
+
+ TFB_DrawImage_FixScaling (img, scale, scaleMode);
+ surf = img->ScaledImg;
+ if (TFB_DrawCanvas_IsPaletted (surf))
+ {
+ // We may only get a paletted scaled image if the source is
+ // paletted. Currently, all scaling targets are truecolor.
+ assert (NormalPal && NormalPal->colors);
+ TFB_SetColors (surf, NormalPal->colors, 0, NormalPal->ncolors);
+ }
+
+ srcRect.x = 0;
+ srcRect.y = 0;
+ srcRect.w = img->extent.width;
+ srcRect.h = img->extent.height;
+ pSrcRect = &srcRect;
+
+ targetRect.x = x - img->last_scale_hs.x;
+ targetRect.y = y - img->last_scale_hs.y;
+ }
+ else
+ {
+ surf = img->NormalImg;
+ pSrcRect = NULL;
+
+ targetRect.x = x - img->NormalHs.x;
+ targetRect.y = y - img->NormalHs.y;
+ }
+
+ if (cmap)
+ {
+ img->colormap_version = cmap->version;
+ // TODO: Technically, this is not a proper place to release a
+ // colormap. As it stands now, the colormap must have been
+ // addrefed when passed to us.
+ TFB_ReturnColorMap (cmap);
+ }
+
+ TFB_DrawCanvas_Blit (surf, pSrcRect, target, &targetRect, mode);
+ UnlockMutex (img->mutex);
+}
+
+// Assumes the source and destination surfaces are in the same format
+static void
+TFB_DrawCanvas_Fill (SDL_Surface *src, Uint32 fillcolor, SDL_Surface *dst)
+{
+ const SDL_PixelFormat *srcfmt = src->format;
+ SDL_PixelFormat *dstfmt = dst->format;
+ const int width = src->w;
+ const int height = src->h;
+ const int bpp = dstfmt->BytesPerPixel;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ const int dsrc = slen - width, ddst = dlen - width;
+ Uint32 *src_p;
+ Uint32 *dst_p;
+ int x, y;
+ Uint32 srckey = 0, dstkey = 0; // 0 means alpha=0 too
+ Uint32 amask = srcfmt->Amask;
+ int alpha = (fillcolor & amask) >> srcfmt->Ashift;
+
+ if (srcfmt->BytesPerPixel != 4 || dstfmt->BytesPerPixel != 4)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported surface "
+ "formats: %d bytes/pixel source, %d bytes/pixel destination",
+ (int)srcfmt->BytesPerPixel, (int)dstfmt->BytesPerPixel);
+ return;
+ }
+
+ // Strip the alpha channel from fillcolor because we process the
+ // alpha separately
+ fillcolor &= ~amask;
+
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+
+ src_p = (Uint32 *)src->pixels;
+ dst_p = (Uint32 *)dst->pixels;
+
+ if (dstkey == fillcolor)
+ { // color collision, must switch colorkey
+ // new colorkey is grey (1/2,1/2,1/2)
+ dstkey = SDL_MapRGBA (dstfmt, 127, 127, 127, 0);
+ }
+
+ if (srcfmt->Amask)
+ { // alpha-based fill
+ for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = 0; x < width; ++x, ++src_p, ++dst_p)
+ {
+ Uint32 p = *src_p & amask;
+
+ if (p == 0)
+ { // fully transparent pixel
+ *dst_p = dstkey;
+ }
+ else if (alpha == 0xff)
+ { // not for DRAW_ALPHA; use alpha chan directly
+ *dst_p = p | fillcolor;
+ }
+ else
+ { // for DRAW_ALPHA; modulate the alpha channel
+ p >>= srcfmt->Ashift;
+ p = (p * alpha) >> 8;
+ p <<= srcfmt->Ashift;
+ *dst_p = p | fillcolor;
+ }
+ }
+ }
+ }
+ else if (TFB_GetColorKey (src, &srckey) == 0)
+ { // colorkey-based fill
+
+ for (y = 0; y < height; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ for (x = 0; x < width; ++x, ++src_p, ++dst_p)
+ {
+ Uint32 p = *src_p;
+
+ *dst_p = (p == srckey) ? dstkey : fillcolor;
+ }
+ }
+ }
+ else
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Fill: Unsupported source"
+ "surface format\n");
+ }
+
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+
+ // save the colorkey (dynamic image -- not using RLE coding here)
+ TFB_SetColorKey (dst, dstkey, 0);
+ // if the filled surface is RGBA, colorkey will only be used
+ // when SDL_SRCALPHA flag is cleared. this allows us to blit
+ // the surface in different ways to diff targets
+}
+
+void
+TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color color, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_Rect srcRect, targetRect, *pSrcRect;
+ SDL_Surface *surf;
+ SDL_Palette *palette;
+ int i;
+ bool force_fill = false;
+
+ if (img == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_FilledImage passed null image ptr");
+ return;
+ }
+
+ checkPrimitiveMode (dst, &color, &mode);
+
+ LockMutex (img->mutex);
+
+ if (scale != 0 && scale != GSCALE_IDENTITY)
+ {
+ if (scaleMode == TFB_SCALE_TRILINEAR)
+ scaleMode = TFB_SCALE_BILINEAR;
+ // no point in trilinear for filled images
+
+ if (scale != img->last_scale || scaleMode != img->last_scale_type)
+ force_fill = true;
+
+ TFB_DrawImage_FixScaling (img, scale, scaleMode);
+ surf = img->ScaledImg;
+ srcRect.x = 0;
+ srcRect.y = 0;
+ srcRect.w = img->extent.width;
+ srcRect.h = img->extent.height;
+ pSrcRect = &srcRect;
+
+ targetRect.x = x - img->last_scale_hs.x;
+ targetRect.y = y - img->last_scale_hs.y;
+ }
+ else
+ {
+ if (img->last_scale != 0)
+ {
+ // Make sure we remember that the last fill was from
+ // an unscaled image
+ force_fill = true;
+ img->last_scale = 0;
+ }
+
+ surf = img->NormalImg;
+ pSrcRect = NULL;
+
+ targetRect.x = x - img->NormalHs.x;
+ targetRect.y = y - img->NormalHs.y;
+ }
+
+ palette = surf->format->palette;
+ if (palette)
+ { // set palette for fill-stamp
+ // Calling TFB_SetColors() results in an expensive src -> dst
+ // color-mapping operation for an SDL blit, following the call.
+ // We want to avoid that as much as possible.
+
+ // TODO: generate a 32bpp filled image?
+
+ SDL_Color colors[256];
+
+ colors[0] = ColorToNative (color);
+ for (i = 1; i < palette->ncolors; i++)
+ colors[i] = colors[0];
+
+ TFB_SetColors (surf, colors, 0, palette->ncolors);
+ // reflect the change in *actual* image palette
+ img->colormap_version--;
+ }
+ else
+ { // fill the non-transparent parts of the image with fillcolor
+ SDL_Surface *newfill = img->FilledImg;
+ SDL_PixelFormat *fillfmt;
+
+ if (newfill && (newfill->w < surf->w || newfill->h < surf->h))
+ {
+ TFB_DrawCanvas_Delete (newfill);
+ newfill = NULL;
+ }
+
+ // prepare the filled image
+ if (!newfill)
+ {
+ newfill = SDL_CreateRGBSurface (SDL_SWSURFACE,
+ surf->w, surf->h,
+ surf->format->BitsPerPixel,
+ surf->format->Rmask,
+ surf->format->Gmask,
+ surf->format->Bmask,
+ surf->format->Amask);
+ force_fill = true;
+ }
+ fillfmt = newfill->format;
+
+ if (force_fill || !sameColor (img->last_fill, color))
+ { // image or fillcolor changed - regenerate
+ Uint32 fillColor;
+
+ if (mode.kind == DRAW_ALPHA && fillfmt->Amask)
+ { // Per-pixel alpha and surface alpha will not work together
+ // We have to handle DRAW_ALPHA differently by modulating
+ // the surface alpha channel ourselves.
+ color.a = mode.factor;
+ mode.kind = DRAW_REPLACE;
+ }
+ else
+ { // Make sure we do not modulate the alpha channel
+ color.a = 0xff;
+ }
+ fillColor = SDL_MapRGBA (newfill->format, color.r, color.g,
+ color.b, color.a);
+ TFB_DrawCanvas_Fill (surf, fillColor, newfill);
+ // cache filled image if possible
+ img->last_fill = color;
+ }
+
+ img->FilledImg = newfill;
+ surf = newfill;
+ }
+
+ TFB_DrawCanvas_Blit (surf, pSrcRect, dst, &targetRect, mode);
+ UnlockMutex (img->mutex);
+}
+
+void
+TFB_DrawCanvas_FontChar (TFB_Char *fontChar, TFB_Image *backing,
+ int x, int y, DrawMode mode, TFB_Canvas target)
+{
+ SDL_Surface *dst = target;
+ SDL_Rect srcRect, targetRect;
+ SDL_Surface *surf;
+ int w, h;
+
+ if (fontChar == 0)
+ {
+ log_add (log_Warning, "ERROR: "
+ "TFB_DrawCanvas_FontChar passed null char ptr");
+ return;
+ }
+ if (backing == 0)
+ {
+ log_add (log_Warning, "ERROR: "
+ "TFB_DrawCanvas_FontChar passed null backing ptr");
+ return;
+ }
+
+ w = fontChar->extent.width;
+ h = fontChar->extent.height;
+
+ LockMutex (backing->mutex);
+
+ surf = backing->NormalImg;
+ if (surf->format->BytesPerPixel != 4
+ || surf->w < w || surf->h < h)
+ {
+ log_add (log_Warning, "ERROR: "
+ "TFB_DrawCanvas_FontChar bad backing surface: %dx%dx%d; "
+ "char: %dx%d",
+ surf->w, surf->h, (int)surf->format->BytesPerPixel, w, h);
+ UnlockMutex (backing->mutex);
+ return;
+ }
+
+ srcRect.x = 0;
+ srcRect.y = 0;
+ srcRect.w = fontChar->extent.width;
+ srcRect.h = fontChar->extent.height;
+
+ targetRect.x = x - fontChar->HotSpot.x;
+ targetRect.y = y - fontChar->HotSpot.y;
+
+ // transfer the alpha channel to the backing surface
+ SDL_LockSurface (surf);
+ {
+ int x, y;
+ const int sskip = fontChar->pitch - w;
+ const int dskip = (surf->pitch / 4) - w;
+ const Uint32 dmask = ~surf->format->Amask;
+ const int ashift = surf->format->Ashift;
+ Uint8 *src_p = fontChar->data;
+ Uint32 *dst_p = (Uint32 *)surf->pixels;
+
+ if (mode.kind == DRAW_ALPHA)
+ { // Per-pixel alpha and surface alpha will not work together
+ // We have to handle DRAW_ALPHA differently by modulating
+ // the backing surface alpha channel ourselves.
+ // The existing backing surface alpha channel is ignored.
+ int alpha = mode.factor;
+ mode.kind = DRAW_REPLACE;
+
+ for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip)
+ {
+ for (x = 0; x < w; ++x, ++src_p, ++dst_p)
+ {
+ Uint32 p = *dst_p & dmask;
+ Uint32 a = *src_p;
+
+ // we use >> 8 instead of / 255, and it does not handle
+ // alpha == 255 correctly
+ if (alpha != 0xff)
+ { // modulate the alpha channel
+ a = (a * alpha) >> 8;
+ }
+ *dst_p = p | (a << ashift);
+ }
+ }
+ }
+ else /* if (mode.kind != DRAW_ALPHA) */
+ { // Transfer the alpha channel to the backing surface
+ // DRAW_REPLACE + Color.a is NOT supported right now
+ for (y = 0; y < h; ++y, src_p += sskip, dst_p += dskip)
+ {
+ for (x = 0; x < w; ++x, ++src_p, ++dst_p)
+ {
+ *dst_p = (*dst_p & dmask) | ((Uint32)*src_p << ashift);
+ }
+ }
+ }
+ }
+ SDL_UnlockSurface (surf);
+
+ TFB_DrawCanvas_Blit (surf, &srcRect, dst, &targetRect, mode);
+ UnlockMutex (backing->mutex);
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha)
+{
+ SDL_Surface *new_surf;
+ SDL_PixelFormat* fmt = format_conv_surf->format;
+
+ new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h,
+ fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask,
+ hasalpha ? fmt->Amask : 0);
+ if (!new_surf)
+ {
+ log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s",
+ SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ return new_surf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_ForScreen (int w, int h, BOOLEAN withalpha)
+{
+ SDL_Surface *new_surf;
+ SDL_PixelFormat* fmt = SDL_Screen->format;
+
+ if (fmt->palette)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_New_ForScreen() WARNING:"
+ "Paletted display format will be slow");
+
+ new_surf = TFB_DrawCanvas_New_TrueColor (w, h, withalpha);
+ }
+ else
+ {
+ if (withalpha && fmt->Amask == 0)
+ fmt = format_conv_surf->format; // Screen has no alpha and we need it
+
+ new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h,
+ fmt->BitsPerPixel, fmt->Rmask, fmt->Gmask, fmt->Bmask,
+ withalpha ? fmt->Amask : 0);
+ }
+
+ if (!new_surf)
+ {
+ log_add (log_Fatal, "TFB_DrawCanvas_New_ForScreen() INTERNAL PANIC:"
+ "Failed to create TFB_Canvas: %s", SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ return new_surf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_Paletted (int w, int h, Color palette[256],
+ int transparent_index)
+{
+ SDL_Surface *new_surf;
+ new_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, w, h, 8, 0, 0, 0, 0);
+ if (!new_surf)
+ {
+ log_add (log_Fatal, "INTERNAL PANIC: Failed to create TFB_Canvas: %s",
+ SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ if (palette != NULL)
+ {
+ TFB_DrawCanvas_SetPalette (new_surf, palette);
+ }
+ if (transparent_index >= 0)
+ {
+ TFB_SetColorKey (new_surf, transparent_index, 0);
+ }
+ else
+ {
+ TFB_DisableColorKey (new_surf);
+ }
+ return new_surf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas, TFB_Canvas oldcanvas, int type, int last_type)
+{
+ SDL_Surface *src = canvas;
+ SDL_Surface *old = oldcanvas;
+ SDL_Surface *newsurf = NULL;
+
+ // For the purposes of this function, bilinear == trilinear
+ if (type == TFB_SCALE_TRILINEAR)
+ type = TFB_SCALE_BILINEAR;
+ if (last_type == TFB_SCALE_TRILINEAR)
+ last_type = TFB_SCALE_BILINEAR;
+
+ if (old && type != last_type)
+ {
+ TFB_DrawCanvas_Delete (old);
+ old = NULL;
+ }
+ if (old)
+ return old; /* can just reuse the old one */
+
+ if (type == TFB_SCALE_NEAREST)
+ {
+ newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE, src->w,
+ src->h,
+ src->format->BitsPerPixel,
+ src->format->Rmask,
+ src->format->Gmask,
+ src->format->Bmask,
+ src->format->Amask);
+ TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
+ }
+ else
+ {
+ // The scaled image may in fact be larger by 1 pixel than the source
+ // because of hotspot alignment and fractional edge pixels
+ if (SDL_Screen->format->BitsPerPixel == 32)
+ newsurf = TFB_DrawCanvas_New_ForScreen (src->w + 1, src->h + 1, TRUE);
+ else
+ newsurf = TFB_DrawCanvas_New_TrueColor (src->w + 1, src->h + 1, TRUE);
+ }
+
+ return newsurf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src_canvas, int angle)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *newsurf;
+ EXTENT size;
+
+ TFB_DrawCanvas_GetRotatedExtent (src_canvas, angle, &size);
+
+ newsurf = SDL_CreateRGBSurface (SDL_SWSURFACE,
+ size.width, size.height,
+ src->format->BitsPerPixel,
+ src->format->Rmask,
+ src->format->Gmask,
+ src->format->Bmask,
+ src->format->Amask);
+ if (!newsurf)
+ {
+ log_add (log_Fatal, "TFB_DrawCanvas_New_RotationTarget()"
+ " INTERNAL PANIC: Failed to create TFB_Canvas: %s",
+ SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+ TFB_DrawCanvas_CopyTransparencyInfo (src, newsurf);
+
+ return newsurf;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_LoadFromFile (void *dir, const char *fileName)
+{
+ SDL_Surface *surf = sdluio_loadImage (dir, fileName);
+ if (!surf)
+ return NULL;
+
+ if (surf->format->BitsPerPixel < 8)
+ {
+ SDL_SetError ("unsupported image format (min 8bpp)");
+ SDL_FreeSurface (surf);
+ surf = NULL;
+ }
+
+ return surf;
+}
+
+void
+TFB_DrawCanvas_Delete (TFB_Canvas canvas)
+{
+ if (!canvas)
+ {
+ log_add (log_Warning, "INTERNAL PANIC: Attempted"
+ " to delete a NULL canvas!");
+ /* Should we actually die here? */
+ }
+ else
+ {
+ SDL_FreeSurface (canvas);
+ }
+}
+
+BOOLEAN
+TFB_DrawCanvas_GetFontCharData (TFB_Canvas canvas, BYTE *outData,
+ unsigned dataPitch)
+{
+ SDL_Surface *surf = canvas;
+ int x, y;
+ Uint8 r, g, b, a;
+ Uint32 p;
+ SDL_PixelFormat *fmt = surf->format;
+ GetPixelFn getpix;
+
+ if (!surf || !outData)
+ return FALSE;
+
+ SDL_LockSurface (surf);
+
+ getpix = getpixel_for (surf);
+
+ // produce an alpha-only image in internal BYTE[] format
+ // from the SDL surface
+ for (y = 0; y < surf->h; ++y)
+ {
+ BYTE *dst = outData + dataPitch * y;
+
+ for (x = 0; x < surf->w; ++x, ++dst)
+ {
+ p = getpix (surf, x, y);
+ SDL_GetRGBA (p, fmt, &r, &g, &b, &a);
+
+ if (!fmt->Amask)
+ { // produce alpha from intensity (Y component)
+ // using a fast approximation
+ // contributions to Y are: R=2, G=4, B=1
+ a = ((r * 2) + (g * 4) + b) / 7;
+ }
+
+ *dst = a;
+ }
+ }
+
+ SDL_UnlockSurface (surf);
+
+ return TRUE;
+}
+
+Color *
+TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas)
+{
+ int i;
+ Color *result;
+ SDL_Surface *surf = canvas;
+ SDL_Palette *palette = surf->format->palette;
+
+ if (!palette)
+ return NULL;
+
+ // There may be less colors in the surface than 256. Init to 0 first.
+ result = HCalloc (sizeof (Color) * 256);
+ assert (palette->ncolors <= 256);
+ for (i = 0; i < palette->ncolors; ++i)
+ result[i] = NativeToColor (palette->colors[i]);
+
+ return result;
+}
+
+TFB_Canvas
+TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas)
+{
+ SDL_Surface *result = TFB_DisplayFormatAlpha (canvas);
+ if (result == NULL)
+ {
+ log_add (log_Debug, "WARNING: Could not convert"
+ " sprite-canvas to display format.");
+ return canvas;
+ }
+ else if (result == canvas)
+ { // no conversion was necessary
+ return canvas;
+ }
+ else
+ { // converted
+ TFB_DrawCanvas_Delete (canvas);
+ return result;
+ }
+
+ return canvas;
+}
+
+BOOLEAN
+TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas)
+{
+ return ((SDL_Surface *)canvas)->format->palette != NULL;
+}
+
+void
+TFB_DrawCanvas_SetPalette (TFB_Canvas target, Color palette[256])
+{
+ SDL_Color colors[256];
+ int i;
+
+ for (i = 0; i < 256; ++i)
+ colors[i] = ColorToNative (palette[i]);
+
+ TFB_SetColors (target, colors, 0, 256);
+}
+
+int
+TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas)
+{
+ Uint32 colorkey;
+ if (TFB_GetColorKey (canvas, &colorkey))
+ {
+ return colorkey;
+ }
+ return -1;
+}
+
+void
+TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int index, BOOLEAN rleaccel)
+{
+ if (index >= 0)
+ {
+ TFB_SetColorKey (canvas, index, rleaccel);
+
+ if (!TFB_DrawCanvas_IsPaletted (canvas))
+ {
+ // disables surface alpha so color key transparency actually works
+ TFB_DisableSurfaceAlphaMod (canvas);
+ }
+ }
+ else
+ {
+ TFB_DisableColorKey (canvas);
+ }
+}
+
+void
+TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src_canvas,
+ TFB_Canvas dst_canvas)
+{
+ SDL_Surface* src = src_canvas;
+
+ if (src->format->palette)
+ {
+ int index;
+ index = TFB_DrawCanvas_GetTransparentIndex (src_canvas);
+ TFB_DrawCanvas_SetTransparentIndex (dst_canvas, index, FALSE);
+ }
+ else
+ {
+ Color color;
+ if (TFB_DrawCanvas_GetTransparentColor (src_canvas, &color))
+ TFB_DrawCanvas_SetTransparentColor (dst_canvas, color, FALSE);
+ }
+}
+
+BOOLEAN
+TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas, Color *color)
+{
+ Uint32 colorkey;
+ if (!TFB_DrawCanvas_IsPaletted (canvas)
+ && (TFB_GetColorKey ((SDL_Surface *)canvas, &colorkey) == 0))
+ {
+ Uint8 ur, ug, ub;
+ SDL_GetRGB (colorkey, ((SDL_Surface *)canvas)->format, &ur, &ug, &ub);
+ color->r = ur;
+ color->g = ug;
+ color->b = ub;
+ color->a = 0xff;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas, Color color,
+ BOOLEAN rleaccel)
+{
+ Uint32 sdlColor;
+ sdlColor = SDL_MapRGBA (((SDL_Surface *)canvas)->format,
+ color.r, color.g, color.b, 0);
+ TFB_SetColorKey (canvas, sdlColor, rleaccel);
+
+ if (!TFB_DrawCanvas_IsPaletted (canvas))
+ {
+ // disables surface alpha so color key transparency actually works
+ TFB_DisableSurfaceAlphaMod (canvas);
+ }
+}
+
+void
+TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs,
+ TFB_Canvas src_mipmap, HOT_SPOT* mm_hs,
+ int scale, int type, EXTENT *size, HOT_SPOT *hs)
+{
+ SDL_Surface *src = src_canvas;
+ sint32 x, y, w, h;
+ int frac;
+
+ if (!src_mipmap)
+ {
+ w = src->w * scale;
+ h = src->h * scale;
+ x = src_hs->x * scale;
+ y = src_hs->y * scale;
+ }
+ else
+ {
+ // interpolates extents between src and mipmap to get smoother
+ // transition when surface changes
+ SDL_Surface *mipmap = src_mipmap;
+ int ratio = scale * 2 - GSCALE_IDENTITY;
+
+ assert (scale >= GSCALE_IDENTITY / 2);
+
+ w = mipmap->w * GSCALE_IDENTITY + (src->w - mipmap->w) * ratio;
+ h = mipmap->h * GSCALE_IDENTITY + (src->h - mipmap->h) * ratio;
+
+ // Seems it is better to use mipmap hotspot because some
+ // source and mipmap images have the same dimensions!
+ x = mm_hs->x * GSCALE_IDENTITY + (src_hs->x - mm_hs->x) * ratio;
+ y = mm_hs->y * GSCALE_IDENTITY + (src_hs->y - mm_hs->y) * ratio;
+ }
+
+ if (type != TFB_SCALE_NEAREST)
+ {
+ // align hotspot on an whole pixel
+ if (x & (GSCALE_IDENTITY - 1))
+ {
+ frac = GSCALE_IDENTITY - (x & (GSCALE_IDENTITY - 1));
+ x += frac;
+ w += frac;
+ }
+ if (y & (GSCALE_IDENTITY - 1))
+ {
+ frac = GSCALE_IDENTITY - (y & (GSCALE_IDENTITY - 1));
+ y += frac;
+ h += frac;
+ }
+ // pad the extent to accomodate fractional edge pixels
+ w += (GSCALE_IDENTITY - 1);
+ h += (GSCALE_IDENTITY - 1);
+ }
+
+ size->width = w / GSCALE_IDENTITY;
+ size->height = h / GSCALE_IDENTITY;
+ hs->x = x / GSCALE_IDENTITY;
+ hs->y = y / GSCALE_IDENTITY;
+
+ // Scaled image can be larger than the source by 1 pixel
+ // because of hotspot alignment and fractional edge pixels
+ assert (size->width <= src->w + 1 && size->height <= src->h + 1);
+
+ if (!size->width && src->w)
+ size->width = 1;
+ if (!size->height && src->h)
+ size->height = 1;
+}
+
+void
+TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size)
+{
+ SDL_Surface *src = canvas;
+
+ size->width = src->w;
+ size->height = src->h;
+}
+
+void
+TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ int x, y;
+ int fsx = 0, fsy = 0; // source fractional dx and dy increments
+ int ssx = 0, ssy = 0; // source fractional x and y starting points
+ int w, h;
+
+ if (scale > 0)
+ {
+ TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale,
+ TFB_SCALE_NEAREST, size, dst_hs);
+
+ w = size->width;
+ h = size->height;
+ }
+ else
+ {
+ // Just go with the dst surface dimensions
+ w = dst->w;
+ h = dst->h;
+ }
+
+ if (w > dst->w || h > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Nearest: Tried to scale"
+ " image to size %d %d when dest_canvas has only"
+ " dimensions of %d %d! Failing.",
+ w, h, dst->w, dst->h);
+ return;
+ }
+
+ if (w > 1)
+ fsx = ((src->w - 1) << 16) / (w - 1);
+ if (h > 1)
+ fsy = ((src->h - 1) << 16) / (h - 1);
+ // We start with a value in 0..0.5 range to shift the bigger
+ // jumps towards the center of the image
+ ssx = 0x6000;
+ ssy = 0x6000;
+
+ SDL_LockSurface (src);
+ SDL_LockSurface (dst);
+
+ if (src->format->BytesPerPixel == 1 && dst->format->BytesPerPixel == 1)
+ {
+ Uint8 *sp, *csp, *dp, *cdp;
+ int sx, sy; // source fractional x and y positions
+
+ sp = csp = (Uint8 *) src->pixels;
+ dp = cdp = (Uint8 *) dst->pixels;
+
+ for (y = 0, sy = ssy; y < h; ++y)
+ {
+ csp += (sy >> 16) * src->pitch;
+ sp = csp;
+ dp = cdp;
+ for (x = 0, sx = ssx; x < w; ++x)
+ {
+ sp += (sx >> 16);
+ *dp = *sp;
+ sx &= 0xffff;
+ sx += fsx;
+ ++dp;
+ }
+ sy &= 0xffff;
+ sy += fsy;
+ cdp += dst->pitch;
+ }
+ }
+ else if (src->format->BytesPerPixel == 4 && dst->format->BytesPerPixel == 4)
+ {
+ Uint32 *sp, *csp, *dp, *cdp;
+ int sx, sy; // source fractional x and y positions
+ int sgap, dgap;
+
+ sgap = src->pitch >> 2;
+ dgap = dst->pitch >> 2;
+
+ sp = csp = (Uint32 *) src->pixels;
+ dp = cdp = (Uint32 *) dst->pixels;
+
+ for (y = 0, sy = ssy; y < h; ++y)
+ {
+ csp += (sy >> 16) * sgap;
+ sp = csp;
+ dp = cdp;
+ for (x = 0, sx = ssx; x < w; ++x)
+ {
+ sp += (sx >> 16);
+ *dp = *sp;
+ sx &= 0xffff;
+ sx += fsx;
+ ++dp;
+ }
+ sy &= 0xffff;
+ sy += fsy;
+ cdp += dgap;
+ }
+ }
+ else
+ {
+ log_add (log_Warning, "Tried to deal with unknown BPP: %d -> %d",
+ src->format->BitsPerPixel, dst->format->BitsPerPixel);
+ }
+ SDL_UnlockSurface (dst);
+ SDL_UnlockSurface (src);
+}
+
+typedef union
+{
+ Uint32 value;
+ Uint8 chan[4];
+ struct
+ {
+ Uint8 r, g, b, a;
+ } c;
+} pixel_t;
+
+static inline Uint8
+dot_product_8_4 (pixel_t* p, int c, Uint8* v)
+{ // math expanded for speed
+#if 0
+ return (
+ (Uint32)p[0].chan[c] * v[0] + (Uint32)p[1].chan[c] * v[1] +
+ (Uint32)p[2].chan[c] * v[2] + (Uint32)p[3].chan[c] * v[3]
+ ) >> 8;
+#else
+ // mult-table driven version
+ return
+ btable[p[0].chan[c]][v[0]] + btable[p[1].chan[c]][v[1]] +
+ btable[p[2].chan[c]][v[2]] + btable[p[3].chan[c]][v[3]];
+#endif
+}
+
+static inline Uint8
+weight_product_8_4 (pixel_t* p, int c, Uint8* w)
+{ // math expanded for speed
+ return (
+ (Uint32)p[0].chan[c] * w[0] + (Uint32)p[1].chan[c] * w[1] +
+ (Uint32)p[2].chan[c] * w[2] + (Uint32)p[3].chan[c] * w[3]
+ ) / (w[0] + w[1] + w[2] + w[3]);
+}
+
+static inline Uint8
+blend_ratio_2 (Uint8 c1, Uint8 c2, int ratio)
+{ // blend 2 color values according to ratio (0..256)
+ // identical to proper alpha blending
+ return (((c1 - c2) * ratio) >> 8) + c2;
+}
+
+static inline Uint32
+scale_read_pixel (void* ppix, SDL_PixelFormat *fmt, SDL_Color *pal,
+ Uint32 mask, Uint32 key)
+{
+ pixel_t p;
+
+ // start off with non-present pixel
+ p.value = 0;
+
+ if (pal)
+ { // paletted pixel; mask not used
+ Uint32 c = *(Uint8 *)ppix;
+
+ if (c != key)
+ {
+ p.c.r = pal[c].r;
+ p.c.g = pal[c].g;
+ p.c.b = pal[c].b;
+ p.c.a = SDL_ALPHA_OPAQUE;
+ }
+ }
+ else
+ { // RGB(A) pixel; (pix & mask) != key
+ Uint32 c = *(Uint32 *)ppix;
+
+ if ((c & mask) != key)
+ {
+#if 0
+ SDL_GetRGBA (c, fmt, &p.c.r, &p.c.g, &p.c.b, &p.c.a);
+#else
+ // Assume 8 bits/channel; a safe assumption with 32bpp surfaces
+ p.c.r = (c >> fmt->Rshift) & 0xff;
+ p.c.g = (c >> fmt->Gshift) & 0xff;
+ p.c.b = (c >> fmt->Bshift) & 0xff;
+ if (fmt->Amask)
+ p.c.a = (c >> fmt->Ashift) & 0xff;
+ else
+ p.c.a = SDL_ALPHA_OPAQUE;
+ }
+#endif
+ }
+
+ return p.value;
+}
+
+static inline Uint32
+scale_get_pixel (SDL_Surface *src, Uint32 mask, Uint32 key, int x, int y)
+{
+ SDL_Color *pal = src->format->palette? src->format->palette->colors : 0;
+
+ if (x < 0 || x >= src->w || y < 0 || y >= src->h)
+ return 0;
+
+ return scale_read_pixel ((Uint8*)src->pixels + y * src->pitch +
+ x * src->format->BytesPerPixel, src->format, pal, mask, key);
+}
+
+void
+TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src_canvas, TFB_Canvas src_mipmap,
+ TFB_Canvas dst_canvas, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs,
+ EXTENT* size, HOT_SPOT* dst_hs)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ SDL_Surface *mm = src_mipmap;
+ SDL_PixelFormat *srcfmt = src->format;
+ SDL_PixelFormat *mmfmt = mm->format;
+ SDL_PixelFormat *dstfmt = dst->format;
+ SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0;
+ const int sbpp = srcfmt->BytesPerPixel;
+ const int mmbpp = mmfmt->BytesPerPixel;
+ const int slen = src->pitch;
+ const int mmlen = mm->pitch;
+ const int dst_has_alpha = (dstfmt->Amask != 0);
+ Uint32 transparent = 0;
+ const int alpha_threshold = dst_has_alpha ? 0 : 127;
+ // src v. mipmap importance factor
+ int ratio = scale * 2 - GSCALE_IDENTITY;
+ // source masks and keys
+ Uint32 mk0 = 0, ck0 = ~0, mk1 = 0, ck1 = ~0;
+ // source fractional x and y positions
+ int sx0, sy0, sx1, sy1;
+ // source fractional dx and dy increments
+ int fsx0 = 0, fsy0 = 0, fsx1 = 0, fsy1 = 0;
+ // source fractional x and y starting points
+ int ssx0 = 0, ssy0 = 0, ssx1 = 0, ssy1 = 0;
+ int x, y, w, h;
+
+ TFB_GetColorKey (dst, &transparent);
+
+ if (mmfmt->palette && !srcpal)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
+ "Mipmap is paletted, but source is not! Failing.");
+ return;
+ }
+
+ if (scale > 0)
+ {
+ int fw, fh;
+
+ // Use (scale / GSCALE_IDENTITY) sizing factor
+ TFB_DrawCanvas_GetScaledExtent (src, src_hs, mm, mm_hs, scale,
+ TFB_SCALE_TRILINEAR, size, dst_hs);
+
+ w = size->width;
+ h = size->height;
+
+ fw = mm->w * GSCALE_IDENTITY + (src->w - mm->w) * ratio;
+ fh = mm->h * GSCALE_IDENTITY + (src->h - mm->h) * ratio;
+
+ // This limits the effective source dimensions to 2048x2048,
+ // and we also lose 4 bits of precision out of 16 (no problem)
+ fsx0 = (src->w << 20) / fw;
+ fsx0 <<= 4;
+ fsy0 = (src->h << 20) / fh;
+ fsy0 <<= 4;
+
+ fsx1 = (mm->w << 20) / fw;
+ fsx1 <<= 4;
+ fsy1 = (mm->h << 20) / fh;
+ fsy1 <<= 4;
+
+ // position the hotspots directly over each other
+ ssx0 = (src_hs->x << 16) - fsx0 * dst_hs->x;
+ ssy0 = (src_hs->y << 16) - fsy0 * dst_hs->y;
+
+ ssx1 = (mm_hs->x << 16) - fsx1 * dst_hs->x;
+ ssy1 = (mm_hs->y << 16) - fsy1 * dst_hs->y;
+ }
+ else
+ {
+ // Just go with the dst surface dimensions
+ w = dst->w;
+ h = dst->h;
+
+ fsx0 = (src->w << 16) / w;
+ fsy0 = (src->h << 16) / h;
+
+ fsx1 = (mm->w << 16) / w;
+ fsy1 = (mm->h << 16) / h;
+
+ // give equal importance to both edges
+ ssx0 = (((src->w - 1) << 16) - fsx0 * (w - 1)) >> 1;
+ ssy0 = (((src->h - 1) << 16) - fsy0 * (h - 1)) >> 1;
+
+ ssx1 = (((mm->w - 1) << 16) - fsx1 * (w - 1)) >> 1;
+ ssy1 = (((mm->h - 1) << 16) - fsy1 * (h - 1)) >> 1;
+ }
+
+ if (w > dst->w || h > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
+ "Tried to scale image to size %d %d when dest_canvas"
+ " has only dimensions of %d %d! Failing.",
+ w, h, dst->w, dst->h);
+ return;
+ }
+
+ if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) ||
+ (mmfmt->BytesPerPixel != 1 && mmfmt->BytesPerPixel != 4) ||
+ (dst->format->BytesPerPixel != 4))
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Trilinear: "
+ "Tried to deal with unknown BPP: %d -> %d, mipmap %d",
+ srcfmt->BitsPerPixel, dst->format->BitsPerPixel,
+ mmfmt->BitsPerPixel);
+ return;
+ }
+
+ // use colorkeys where appropriate
+ if (srcfmt->Amask)
+ { // alpha transparency
+ mk0 = srcfmt->Amask;
+ ck0 = 0;
+ }
+ else if (TFB_GetColorKey (src, &ck0) == 0)
+ { // colorkey transparency
+ mk0 = ~srcfmt->Amask;
+ ck0 &= mk0;
+ }
+
+ if (mmfmt->Amask)
+ { // alpha transparency
+ mk1 = mmfmt->Amask;
+ ck1 = 0;
+ }
+ else if (TFB_GetColorKey (mm, &ck1) == 0)
+ { // colorkey transparency
+ mk1 = ~mmfmt->Amask;
+ ck1 &= mk1;
+ }
+
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+ SDL_LockSurface(mm);
+
+ for (y = 0, sy0 = ssy0, sy1 = ssy1;
+ y < h;
+ ++y, sy0 += fsy0, sy1 += fsy1)
+ {
+ Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch);
+ const int py0 = (sy0 >> 16);
+ const int py1 = (sy1 >> 16);
+ Uint8 *src_a0 = (Uint8*)src->pixels + py0 * slen;
+ Uint8 *src_a1 = (Uint8*)mm->pixels + py1 * mmlen;
+ // retrieve the fractional portions of y
+ const Uint8 v0 = (sy0 >> 8) & 0xff;
+ const Uint8 v1 = (sy1 >> 8) & 0xff;
+ Uint8 w0[4], w1[4]; // pixel weight vectors
+
+ for (x = 0, sx0 = ssx0, sx1 = ssx1;
+ x < w;
+ ++x, ++dst_p, sx0 += fsx0, sx1 += fsx1)
+ {
+ const int px0 = (sx0 >> 16);
+ const int px1 = (sx1 >> 16);
+ // retrieve the fractional portions of x
+ const Uint8 u0 = (sx0 >> 8) & 0xff;
+ const Uint8 u1 = (sx1 >> 8) & 0xff;
+ // pixels are examined and numbered in pattern
+ // 0 1
+ // 2 3
+ // the ideal pixel (4) is somewhere between these four
+ // and is calculated from these using weight vector (w)
+ // with a dot product
+ pixel_t p0[5], p1[5];
+ Uint8 res_a;
+
+ w0[0] = btable[255 - u0][255 - v0];
+ w0[1] = btable[u0][255 - v0];
+ w0[2] = btable[255 - u0][v0];
+ w0[3] = btable[u0][v0];
+
+ w1[0] = btable[255 - u1][255 - v1];
+ w1[1] = btable[u1][255 - v1];
+ w1[2] = btable[255 - u1][v1];
+ w1[3] = btable[u1][v1];
+
+ // Collect interesting pixels from src image
+ // Optimization: speed is criticial on larger images;
+ // most pixel reads fall completely inside the image
+ if (px0 >= 0 && px0 + 1 < src->w && py0 >= 0 && py0 + 1 < src->h)
+ {
+ Uint8 *src_p = src_a0 + px0 * sbpp;
+
+ p0[0].value = scale_read_pixel (src_p, srcfmt,
+ srcpal, mk0, ck0);
+ p0[1].value = scale_read_pixel (src_p + sbpp, srcfmt,
+ srcpal, mk0, ck0);
+ p0[2].value = scale_read_pixel (src_p + slen, srcfmt,
+ srcpal, mk0, ck0);
+ p0[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt,
+ srcpal, mk0, ck0);
+ }
+ else
+ {
+ p0[0].value = scale_get_pixel (src, mk0, ck0, px0, py0);
+ p0[1].value = scale_get_pixel (src, mk0, ck0, px0 + 1, py0);
+ p0[2].value = scale_get_pixel (src, mk0, ck0, px0, py0 + 1);
+ p0[3].value = scale_get_pixel (src, mk0, ck0,
+ px0 + 1, py0 + 1);
+ }
+
+ // Collect interesting pixels from mipmap image
+ if (px1 >= 0 && px1 + 1 < mm->w && py1 >= 0 && py1 + 1 < mm->h)
+ {
+ Uint8 *mm_p = src_a1 + px1 * mmbpp;
+
+ p1[0].value = scale_read_pixel (mm_p, mmfmt,
+ srcpal, mk1, ck1);
+ p1[1].value = scale_read_pixel (mm_p + mmbpp, mmfmt,
+ srcpal, mk1, ck1);
+ p1[2].value = scale_read_pixel (mm_p + mmlen, mmfmt,
+ srcpal, mk1, ck1);
+ p1[3].value = scale_read_pixel (mm_p + mmbpp + mmlen, mmfmt,
+ srcpal, mk1, ck1);
+ }
+ else
+ {
+ p1[0].value = scale_get_pixel (mm, mk1, ck1, px1, py1);
+ p1[1].value = scale_get_pixel (mm, mk1, ck1, px1 + 1, py1);
+ p1[2].value = scale_get_pixel (mm, mk1, ck1, px1, py1 + 1);
+ p1[3].value = scale_get_pixel (mm, mk1, ck1,
+ px1 + 1, py1 + 1);
+ }
+
+ p0[4].c.a = dot_product_8_4 (p0, 3, w0);
+ p1[4].c.a = dot_product_8_4 (p1, 3, w1);
+
+ res_a = blend_ratio_2 (p0[4].c.a, p1[4].c.a, ratio);
+
+ if (res_a <= alpha_threshold)
+ {
+ *dst_p = transparent;
+ }
+ else if (!dst_has_alpha)
+ { // RGB surface handling
+ p0[4].c.r = dot_product_8_4 (p0, 0, w0);
+ p0[4].c.g = dot_product_8_4 (p0, 1, w0);
+ p0[4].c.b = dot_product_8_4 (p0, 2, w0);
+
+ p1[4].c.r = dot_product_8_4 (p1, 0, w1);
+ p1[4].c.g = dot_product_8_4 (p1, 1, w1);
+ p1[4].c.b = dot_product_8_4 (p1, 2, w1);
+
+ p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio);
+ p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio);
+ p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio);
+
+ // TODO: we should handle alpha-blending here, but we do
+ // not know the destination color for blending!
+
+ *dst_p =
+ (p0[4].c.r << dstfmt->Rshift) |
+ (p0[4].c.g << dstfmt->Gshift) |
+ (p0[4].c.b << dstfmt->Bshift);
+ }
+ else
+ { // RGBA surface handling
+
+ // we do not want to blend with non-present pixels
+ // (pixels that have alpha == 0) as these will
+ // skew the result and make resulting alpha useless
+ if (p0[4].c.a != 0)
+ {
+ int i;
+ for (i = 0; i < 4; ++i)
+ if (p0[i].c.a == 0)
+ w0[i] = 0;
+
+ p0[4].c.r = weight_product_8_4 (p0, 0, w0);
+ p0[4].c.g = weight_product_8_4 (p0, 1, w0);
+ p0[4].c.b = weight_product_8_4 (p0, 2, w0);
+ }
+ if (p1[4].c.a != 0)
+ {
+ int i;
+ for (i = 0; i < 4; ++i)
+ if (p1[i].c.a == 0)
+ w1[i] = 0;
+
+ p1[4].c.r = weight_product_8_4 (p1, 0, w1);
+ p1[4].c.g = weight_product_8_4 (p1, 1, w1);
+ p1[4].c.b = weight_product_8_4 (p1, 2, w1);
+ }
+
+ if (p0[4].c.a != 0 && p1[4].c.a != 0)
+ { // blend if both present
+ p0[4].c.r = blend_ratio_2 (p0[4].c.r, p1[4].c.r, ratio);
+ p0[4].c.g = blend_ratio_2 (p0[4].c.g, p1[4].c.g, ratio);
+ p0[4].c.b = blend_ratio_2 (p0[4].c.b, p1[4].c.b, ratio);
+ }
+ else if (p1[4].c.a != 0)
+ { // other pixel is present
+ p0[4].value = p1[4].value;
+ }
+
+ // error-correct alpha to fully opaque to remove
+ // the often unwanted and unnecessary blending
+ if (res_a > 0xf8)
+ res_a = 0xff;
+
+ *dst_p =
+ (p0[4].c.r << dstfmt->Rshift) |
+ (p0[4].c.g << dstfmt->Gshift) |
+ (p0[4].c.b << dstfmt->Bshift) |
+ (res_a << dstfmt->Ashift);
+ }
+ }
+ }
+
+ SDL_UnlockSurface(mm);
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+}
+
+void
+TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ SDL_PixelFormat *srcfmt = src->format;
+ SDL_PixelFormat *dstfmt = dst->format;
+ SDL_Color *srcpal = srcfmt->palette? srcfmt->palette->colors : 0;
+ const int sbpp = srcfmt->BytesPerPixel;
+ const int slen = src->pitch;
+ const int dst_has_alpha = (dstfmt->Amask != 0);
+ Uint32 srckey = 0, transparent = 0;
+ const int alpha_threshold = dst_has_alpha ? 0 : 127;
+ // source masks and keys
+ Uint32 mk = 0, ck = ~0;
+ // source fractional x and y positions
+ int sx, sy;
+ // source fractional dx and dy increments
+ int fsx = 0, fsy = 0;
+ // source fractional x and y starting points
+ int ssx = 0, ssy = 0;
+ int x, y, w, h;
+
+ // Get destination transparent color if it exists
+ TFB_GetColorKey (dst, &transparent);
+
+ if (scale > 0)
+ {
+ // Use (scale / GSCALE_IDENTITY) sizing factor
+ TFB_DrawCanvas_GetScaledExtent (src, src_hs, NULL, NULL, scale,
+ TFB_SCALE_BILINEAR, size, dst_hs);
+
+ w = size->width;
+ h = size->height;
+ fsx = (GSCALE_IDENTITY << 16) / scale;
+ fsy = (GSCALE_IDENTITY << 16) / scale;
+
+ // position the hotspots directly over each other
+ ssx = (src_hs->x << 16) - fsx * dst_hs->x;
+ ssy = (src_hs->y << 16) - fsy * dst_hs->y;
+ }
+ else
+ {
+ // Just go with the dst surface dimensions
+ w = dst->w;
+ h = dst->h;
+ fsx = (src->w << 16) / w;
+ fsy = (src->h << 16) / h;
+
+ // give equal importance to both edges
+ ssx = (((src->w - 1) << 16) - fsx * (w - 1)) >> 1;
+ ssy = (((src->h - 1) << 16) - fsy * (h - 1)) >> 1;
+ }
+
+ if (w > dst->w || h > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: "
+ "Tried to scale image to size %d %d when dest_canvas"
+ " has only dimensions of %d %d! Failing.",
+ w, h, dst->w, dst->h);
+ return;
+ }
+
+ if ((srcfmt->BytesPerPixel != 1 && srcfmt->BytesPerPixel != 4) ||
+ (dst->format->BytesPerPixel != 4))
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rescale_Bilinear: "
+ "Tried to deal with unknown BPP: %d -> %d",
+ srcfmt->BitsPerPixel, dst->format->BitsPerPixel);
+ return;
+ }
+
+ // use colorkeys where appropriate
+ if (srcfmt->Amask)
+ { // alpha transparency
+ mk = srcfmt->Amask;
+ ck = 0;
+ }
+ else if (TFB_GetColorKey (src, &srckey) == 0)
+ { // colorkey transparency
+ mk = ~srcfmt->Amask;
+ ck = srckey & mk;
+ }
+
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+
+ for (y = 0, sy = ssy; y < h; ++y, sy += fsy)
+ {
+ Uint32 *dst_p = (Uint32 *) ((Uint8*)dst->pixels + y * dst->pitch);
+ const int py = (sy >> 16);
+ Uint8 *src_a = (Uint8*)src->pixels + py * slen;
+ // retrieve the fractional portions of y
+ const Uint8 v = (sy >> 8) & 0xff;
+ Uint8 weight[4]; // pixel weight vectors
+
+ for (x = 0, sx = ssx; x < w; ++x, ++dst_p, sx += fsx)
+ {
+ const int px = (sx >> 16);
+ // retrieve the fractional portions of x
+ const Uint8 u = (sx >> 8) & 0xff;
+ // pixels are examined and numbered in pattern
+ // 0 1
+ // 2 3
+ // the ideal pixel (4) is somewhere between these four
+ // and is calculated from these using weight vector (weight)
+ // with a dot product
+ pixel_t p[5];
+
+ weight[0] = btable[255 - u][255 - v];
+ weight[1] = btable[u][255 - v];
+ weight[2] = btable[255 - u][v];
+ weight[3] = btable[u][v];
+
+ // Collect interesting pixels from src image
+ // Optimization: speed is criticial on larger images;
+ // most pixel reads fall completely inside the image
+ if (px >= 0 && px + 1 < src->w && py >= 0 && py + 1 < src->h)
+ {
+ Uint8 *src_p = src_a + px * sbpp;
+
+ p[0].value = scale_read_pixel (src_p, srcfmt, srcpal, mk, ck);
+ p[1].value = scale_read_pixel (src_p + sbpp, srcfmt,
+ srcpal, mk, ck);
+ p[2].value = scale_read_pixel (src_p + slen, srcfmt,
+ srcpal, mk, ck);
+ p[3].value = scale_read_pixel (src_p + sbpp + slen, srcfmt,
+ srcpal, mk, ck);
+ }
+ else
+ {
+ p[0].value = scale_get_pixel (src, mk, ck, px, py);
+ p[1].value = scale_get_pixel (src, mk, ck, px + 1, py);
+ p[2].value = scale_get_pixel (src, mk, ck, px, py + 1);
+ p[3].value = scale_get_pixel (src, mk, ck, px + 1, py + 1);
+ }
+
+ p[4].c.a = dot_product_8_4 (p, 3, weight);
+
+ if (p[4].c.a <= alpha_threshold)
+ {
+ *dst_p = transparent;
+ }
+ else if (!dst_has_alpha)
+ { // RGB surface handling
+ p[4].c.r = dot_product_8_4 (p, 0, weight);
+ p[4].c.g = dot_product_8_4 (p, 1, weight);
+ p[4].c.b = dot_product_8_4 (p, 2, weight);
+
+ // TODO: we should handle alpha-blending here, but we do
+ // not know the destination color for blending!
+
+ *dst_p =
+ (p[4].c.r << dstfmt->Rshift) |
+ (p[4].c.g << dstfmt->Gshift) |
+ (p[4].c.b << dstfmt->Bshift);
+ }
+ else
+ { // RGBA surface handling
+
+ // we do not want to blend with non-present pixels
+ // (pixels that have alpha == 0) as these will
+ // skew the result and make resulting alpha useless
+ int i;
+ for (i = 0; i < 4; ++i)
+ if (p[i].c.a == 0)
+ weight[i] = 0;
+
+ p[4].c.r = weight_product_8_4 (p, 0, weight);
+ p[4].c.g = weight_product_8_4 (p, 1, weight);
+ p[4].c.b = weight_product_8_4 (p, 2, weight);
+
+ // error-correct alpha to fully opaque to remove
+ // the often unwanted and unnecessary blending
+ if (p[4].c.a > 0xf8)
+ p[4].c.a = 0xff;
+
+ *dst_p =
+ (p[4].c.r << dstfmt->Rshift) |
+ (p[4].c.g << dstfmt->Gshift) |
+ (p[4].c.b << dstfmt->Bshift) |
+ (p[4].c.a << dstfmt->Ashift);
+ }
+ }
+ }
+
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+}
+
+void
+TFB_DrawCanvas_Lock (TFB_Canvas canvas)
+{
+ SDL_Surface *surf = canvas;
+ SDL_LockSurface (surf);
+}
+
+void
+TFB_DrawCanvas_Unlock (TFB_Canvas canvas)
+{
+ SDL_Surface *surf = canvas;
+ SDL_UnlockSurface (surf);
+}
+
+void
+TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt)
+{
+ SDL_PixelFormat *sdl = SDL_Screen->format;
+
+ if (sdl->palette)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_GetScreenFormat() WARNING:"
+ "Paletted display format will be slow");
+
+ fmt->BitsPerPixel = 32;
+ fmt->Rmask = 0x000000ff;
+ fmt->Gmask = 0x0000ff00;
+ fmt->Bmask = 0x00ff0000;
+ fmt->Amask = 0xff000000;
+ }
+ else
+ {
+ fmt->BitsPerPixel = sdl->BitsPerPixel;
+ fmt->Rmask = sdl->Rmask;
+ fmt->Gmask = sdl->Gmask;
+ fmt->Bmask = sdl->Bmask;
+ fmt->Amask = sdl->Amask;
+ }
+}
+
+int
+TFB_DrawCanvas_GetStride (TFB_Canvas canvas)
+{
+ SDL_Surface *surf = canvas;
+ return surf->pitch;
+}
+
+void*
+TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line)
+{
+ SDL_Surface *surf = canvas;
+ return (uint8 *)surf->pixels + surf->pitch * line;
+}
+
+Color
+TFB_DrawCanvas_GetPixel (TFB_Canvas canvas, int x, int y)
+{
+ SDL_Surface* surf = canvas;
+ Uint32 pixel;
+ GetPixelFn getpixel;
+ Color c = {0, 0, 0, 0};
+
+ if (x < 0 || x >= surf->w || y < 0 || y >= surf->h)
+ { // outside bounds, return 0
+ return c;
+ }
+
+ SDL_LockSurface (surf);
+
+ getpixel = getpixel_for(surf);
+ pixel = (*getpixel)(surf, x, y);
+ SDL_GetRGBA (pixel, surf->format, &c.r, &c.g, &c.b, &c.a);
+
+ SDL_UnlockSurface (surf);
+
+ return c;
+}
+
+void
+TFB_DrawCanvas_Rotate (TFB_Canvas src_canvas, TFB_Canvas dst_canvas,
+ int angle, EXTENT size)
+{
+ SDL_Surface *src = src_canvas;
+ SDL_Surface *dst = dst_canvas;
+ int ret;
+ Color color;
+
+ if (size.width > dst->w || size.height > dst->h)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rotate: Tried to rotate"
+ " image to size %d %d when dst_canvas has only dimensions"
+ " of %d %d! Failing.",
+ size.width, size.height, dst->w, dst->h);
+ return;
+ }
+
+ if (TFB_DrawCanvas_GetTransparentColor (src, &color))
+ {
+ TFB_DrawCanvas_SetTransparentColor (dst, color, FALSE);
+ /* fill destination with transparent color before rotating */
+ SDL_FillRect(dst, NULL, SDL_MapRGBA (dst->format,
+ color.r, color.g, color.b, 0));
+ }
+
+ ret = rotateSurface (src, dst, angle, 0);
+ if (ret != 0)
+ {
+ log_add (log_Warning, "TFB_DrawCanvas_Rotate: WARNING:"
+ " actual rotation func returned failure\n");
+ }
+}
+
+void
+TFB_DrawCanvas_GetRotatedExtent (TFB_Canvas src_canvas, int angle, EXTENT *size)
+{
+ int dstw, dsth;
+ SDL_Surface *src = src_canvas;
+
+ rotozoomSurfaceSize (src->w, src->h, angle, 1, &dstw, &dsth);
+ size->height = dsth;
+ size->width = dstw;
+}
+
+void
+TFB_DrawCanvas_CopyRect (TFB_Canvas source, const RECT *srcRect,
+ TFB_Canvas target, POINT dstPt)
+{
+ SDL_Rect sourceRect, targetRect;
+
+ if (source == 0 || target == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_CopyRect passed null canvas ptr");
+ return;
+ }
+
+ sourceRect.x = srcRect->corner.x;
+ sourceRect.y = srcRect->corner.y;
+ sourceRect.w = srcRect->extent.width;
+ sourceRect.h = srcRect->extent.height;
+
+ targetRect.x = dstPt.x;
+ targetRect.y = dstPt.y;
+ // According to SDL docs, width and height are ignored, but
+ // we'll set them anyway, just in case.
+ targetRect.w = srcRect->extent.width;
+ targetRect.h = srcRect->extent.height;
+
+ SDL_BlitSurface (source, &sourceRect, target, &targetRect);
+}
+
+void
+TFB_DrawCanvas_SetClipRect (TFB_Canvas canvas, const RECT *clipRect)
+{
+ if (canvas == 0)
+ {
+ log_add (log_Warning,
+ "ERROR: TFB_DrawCanvas_SetClipRect passed null canvas ptr");
+ return;
+ }
+
+ if (!clipRect)
+ { // clipping disabled
+ SDL_SetClipRect (canvas, NULL);
+ }
+ else
+ {
+ SDL_Rect r;
+ r.x = clipRect->corner.x;
+ r.y = clipRect->corner.y;
+ r.w = clipRect->extent.width;
+ r.h = clipRect->extent.height;
+ SDL_SetClipRect (canvas, &r);
+ }
+}
+
+BOOLEAN
+TFB_DrawCanvas_Intersect (TFB_Canvas canvas1, POINT c1org,
+ TFB_Canvas canvas2, POINT c2org, const RECT *interRect)
+{
+ BOOLEAN ret = FALSE;
+ SDL_Surface *surf1 = canvas1;
+ SDL_Surface *surf2 = canvas2;
+ int x, y;
+ Uint32 s1key, s2key;
+ Uint32 s1mask, s2mask;
+ GetPixelFn getpixel1, getpixel2;
+
+ SDL_LockSurface (surf1);
+ SDL_LockSurface (surf2);
+
+ getpixel1 = getpixel_for (surf1);
+ getpixel2 = getpixel_for (surf2);
+
+ if (surf1->format->Amask)
+ { // use alpha transparency info
+ s1mask = surf1->format->Amask;
+ // consider any not fully transparent pixel collidable
+ s1key = 0;
+ }
+ else
+ { // colorkey transparency
+ Uint32 colorkey = 0;
+ TFB_GetColorKey(surf1, &colorkey);
+ s1mask = ~surf1->format->Amask;
+ s1key = colorkey & s1mask;
+ }
+
+ if (surf2->format->Amask)
+ { // use alpha transparency info
+ s2mask = surf2->format->Amask;
+ // consider any not fully transparent pixel collidable
+ s2key = 0;
+ }
+ else
+ { // colorkey transparency
+ Uint32 colorkey = 0;
+ TFB_GetColorKey(surf2, &colorkey);
+ s2mask = ~surf2->format->Amask;
+ s2key = colorkey & s2mask;
+ }
+
+ // convert surface origins to pixel offsets within
+ c1org.x = interRect->corner.x - c1org.x;
+ c1org.y = interRect->corner.y - c1org.y;
+ c2org.x = interRect->corner.x - c2org.x;
+ c2org.y = interRect->corner.y - c2org.y;
+
+ for (y = 0; y < interRect->extent.height; ++y)
+ {
+ for (x = 0; x < interRect->extent.width; ++x)
+ {
+ Uint32 p1 = getpixel1 (surf1, x + c1org.x, y + c1org.y) & s1mask;
+ Uint32 p2 = getpixel2 (surf2, x + c2org.x, y + c2org.y) & s2mask;
+
+ if (p1 != s1key && p2 != s2key)
+ { // pixel collision
+ ret = TRUE;
+ break;
+ }
+ }
+ }
+
+ SDL_UnlockSurface (surf2);
+ SDL_UnlockSurface (surf1);
+
+ return ret;
+}
+
+// Read/write the canvas pixels in a Color format understood by the core.
+// The pixels array is assumed to be at least width * height large.
+// The pixels array can be wider/narrower or taller/shorter than the canvas,
+// and in that case, only the relevant pixels will be transfered.
+static BOOLEAN
+TFB_DrawCanvas_TransferColors (TFB_Canvas canvas, BOOLEAN write,
+ Color *pixels, int width, int height)
+{
+ SDL_Surface *surf = canvas;
+ SDL_PixelFormat *fmt;
+ GetPixelFn getpix;
+ PutPixelFn putpix;
+ int x, y, w, h;
+
+ if (canvas == 0)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferColors "
+ "passed null canvas");
+ return FALSE;
+ }
+
+ fmt = surf->format;
+ getpix = getpixel_for (surf);
+ putpix = putpixel_for (surf);
+
+ w = width < surf->w ? width : surf->w;
+ h = height < surf->h ? height : surf->h;
+
+ SDL_LockSurface (surf);
+
+ // This could be done faster if we assumed 32bpp surfaces
+ for (y = 0; y < h; ++y)
+ {
+ // pixels array pitch is width so as not to violate the interface
+ Color *c = pixels + y * width;
+
+ for (x = 0; x < w; ++x, ++c)
+ {
+ if (write)
+ { // writing from data to surface
+ Uint32 p = SDL_MapRGBA (fmt, c->r, c->g, c->b, c->a);
+ putpix (surf, x, y, p);
+ }
+ else
+ { // reading from surface to data
+ Uint32 p = getpix (surf, x, y);
+ SDL_GetRGBA (p, fmt, &c->r, &c->g, &c->b, &c->a);
+ }
+ }
+ }
+
+ SDL_UnlockSurface (surf);
+
+ return TRUE;
+}
+
+// Read the canvas pixels in a Color format understood by the core.
+// See TFB_DrawCanvas_TransferColors() for pixels array info
+BOOLEAN
+TFB_DrawCanvas_GetPixelColors (TFB_Canvas canvas, Color *pixels,
+ int width, int height)
+{
+ return TFB_DrawCanvas_TransferColors (canvas, FALSE, pixels,
+ width, height);
+}
+
+// Write the canvas pixels from a Color format understood by the core.
+// See TFB_DrawCanvas_TransferColors() for pixels array info
+BOOLEAN
+TFB_DrawCanvas_SetPixelColors (TFB_Canvas canvas, const Color *pixels,
+ int width, int height)
+{
+ // unconst pixels, but it is safe -- it will not be written to
+ return TFB_DrawCanvas_TransferColors (canvas, TRUE, (Color *)pixels,
+ width, height);
+}
+
+// Read/write the indexed canvas pixels as palette indexes.
+// The data array is assumed to be at least width * height large.
+// The data array can be wider/narrower or taller/shorter than the canvas,
+// and in that case, only the relevant pixels will be transfered.
+static BOOLEAN
+TFB_DrawCanvas_TransferIndexes (TFB_Canvas canvas, BOOLEAN write,
+ BYTE *data, int width, int height)
+{
+ SDL_Surface *surf = canvas;
+ const SDL_PixelFormat *fmt;
+ int y, w, h;
+
+ if (canvas == 0)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes "
+ "passed null canvas");
+ return FALSE;
+ }
+ fmt = surf->format;
+ if (!TFB_DrawCanvas_IsPaletted (canvas) || fmt->BitsPerPixel != 8)
+ {
+ log_add (log_Warning, "ERROR: TFB_DrawCanvas_TransferIndexes "
+ "unimplemeted function: not an 8bpp indexed canvas");
+ return FALSE;
+ }
+
+ w = width < surf->w ? width : surf->w;
+ h = height < surf->h ? height : surf->h;
+
+ SDL_LockSurface (surf);
+
+ for (y = 0; y < h; ++y)
+ {
+ Uint8 *surf_p = (Uint8 *)surf->pixels + y * surf->pitch;
+ // pixels array pitch is width so as not to violate the interface
+ BYTE *data_p = data + y * width;
+
+ if (write)
+ { // writing from data to surface
+ memcpy (surf_p, data_p, w * sizeof (BYTE));
+ }
+ else
+ { // reading from surface to data
+ memcpy (data_p, surf_p, w * sizeof (BYTE));
+ }
+ }
+
+ SDL_UnlockSurface (surf);
+
+ return TRUE;
+}
+
+// Read the indexed canvas pixels as palette indexes.
+// See TFB_DrawCanvas_TransferIndexes() for data array info.
+BOOLEAN
+TFB_DrawCanvas_GetPixelIndexes (TFB_Canvas canvas, BYTE *data,
+ int width, int height)
+{
+ return TFB_DrawCanvas_TransferIndexes (canvas, FALSE, data,
+ width, height);
+}
+
+// Write the indexed canvas pixels as palette indexes.
+// See TFB_DrawCanvas_TransferIndexes() for data array info.
+BOOLEAN
+TFB_DrawCanvas_SetPixelIndexes (TFB_Canvas canvas, const BYTE *data,
+ int width, int height)
+{
+ // unconst data, but it is safe -- it will not be written to
+ return TFB_DrawCanvas_TransferIndexes (canvas, TRUE, (BYTE *)data,
+ width, height);
+}
diff --git a/src/libs/graphics/sdl/hq2x.c b/src/libs/graphics/sdl/hq2x.c
new file mode 100644
index 0000000..02dc806
--- /dev/null
+++ b/src/libs/graphics/sdl/hq2x.c
@@ -0,0 +1,2888 @@
+//hq2x filter
+//--------------------------------------------------------------------------
+//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) - Original version
+//
+//Portions Copyright (C) 2005 Alex Volkov ( codepro@usa.net )
+// Modified Oct-2-2005
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the HQ screen scaler
+// adapted from hq2x -- www.hiend3d.com/hq2x.html
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Pixel blending/manipulation instructions
+#define PIXEL00_0 dst_p[0] = pix[5];
+#define PIXEL00_10 dst_p[0] = Scale_Blend_31(pix[5], pix[1]);
+#define PIXEL00_11 dst_p[0] = Scale_Blend_31(pix[5], pix[4]);
+#define PIXEL00_12 dst_p[0] = Scale_Blend_31(pix[5], pix[2]);
+#define PIXEL00_20 dst_p[0] = Scale_Blend_211(pix[5], pix[4], pix[2]);
+#define PIXEL00_21 dst_p[0] = Scale_Blend_211(pix[5], pix[1], pix[2]);
+#define PIXEL00_22 dst_p[0] = Scale_Blend_211(pix[5], pix[1], pix[4]);
+#define PIXEL00_60 dst_p[0] = Scale_Blend_521(pix[5], pix[2], pix[4]);
+#define PIXEL00_61 dst_p[0] = Scale_Blend_521(pix[5], pix[4], pix[2]);
+#define PIXEL00_70 dst_p[0] = Scale_Blend_611(pix[5], pix[4], pix[2]);
+#define PIXEL00_90 dst_p[0] = Scale_Blend_233(pix[5], pix[4], pix[2]);
+#define PIXEL00_100 dst_p[0] = Scale_Blend_e11(pix[5], pix[4], pix[2]);
+#define PIXEL01_0 dst_p[1] = pix[5];
+#define PIXEL01_10 dst_p[1] = Scale_Blend_31(pix[5], pix[3]);
+#define PIXEL01_11 dst_p[1] = Scale_Blend_31(pix[5], pix[2]);
+#define PIXEL01_12 dst_p[1] = Scale_Blend_31(pix[5], pix[6]);
+#define PIXEL01_20 dst_p[1] = Scale_Blend_211(pix[5], pix[2], pix[6]);
+#define PIXEL01_21 dst_p[1] = Scale_Blend_211(pix[5], pix[3], pix[6]);
+#define PIXEL01_22 dst_p[1] = Scale_Blend_211(pix[5], pix[3], pix[2]);
+#define PIXEL01_60 dst_p[1] = Scale_Blend_521(pix[5], pix[6], pix[2]);
+#define PIXEL01_61 dst_p[1] = Scale_Blend_521(pix[5], pix[2], pix[6]);
+#define PIXEL01_70 dst_p[1] = Scale_Blend_611(pix[5], pix[2], pix[6]);
+#define PIXEL01_90 dst_p[1] = Scale_Blend_233(pix[5], pix[2], pix[6]);
+#define PIXEL01_100 dst_p[1] = Scale_Blend_e11(pix[5], pix[2], pix[6]);
+#define PIXEL10_0 dst_p[dlen] = pix[5];
+#define PIXEL10_10 dst_p[dlen] = Scale_Blend_31(pix[5], pix[7]);
+#define PIXEL10_11 dst_p[dlen] = Scale_Blend_31(pix[5], pix[8]);
+#define PIXEL10_12 dst_p[dlen] = Scale_Blend_31(pix[5], pix[4]);
+#define PIXEL10_20 dst_p[dlen] = Scale_Blend_211(pix[5], pix[8], pix[4]);
+#define PIXEL10_21 dst_p[dlen] = Scale_Blend_211(pix[5], pix[7], pix[4]);
+#define PIXEL10_22 dst_p[dlen] = Scale_Blend_211(pix[5], pix[7], pix[8]);
+#define PIXEL10_60 dst_p[dlen] = Scale_Blend_521(pix[5], pix[4], pix[8]);
+#define PIXEL10_61 dst_p[dlen] = Scale_Blend_521(pix[5], pix[8], pix[4]);
+#define PIXEL10_70 dst_p[dlen] = Scale_Blend_611(pix[5], pix[8], pix[4]);
+#define PIXEL10_90 dst_p[dlen] = Scale_Blend_233(pix[5], pix[8], pix[4]);
+#define PIXEL10_100 dst_p[dlen] = Scale_Blend_e11(pix[5], pix[8], pix[4]);
+#define PIXEL11_0 dst_p[dlen + 1] = pix[5];
+#define PIXEL11_10 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[9]);
+#define PIXEL11_11 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[6]);
+#define PIXEL11_12 dst_p[dlen + 1] = Scale_Blend_31(pix[5], pix[8]);
+#define PIXEL11_20 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[6], pix[8]);
+#define PIXEL11_21 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[9], pix[8]);
+#define PIXEL11_22 dst_p[dlen + 1] = Scale_Blend_211(pix[5], pix[9], pix[6]);
+#define PIXEL11_60 dst_p[dlen + 1] = Scale_Blend_521(pix[5], pix[8], pix[6]);
+#define PIXEL11_61 dst_p[dlen + 1] = Scale_Blend_521(pix[5], pix[6], pix[8]);
+#define PIXEL11_70 dst_p[dlen + 1] = Scale_Blend_611(pix[5], pix[6], pix[8]);
+#define PIXEL11_90 dst_p[dlen + 1] = Scale_Blend_233(pix[5], pix[6], pix[8]);
+#define PIXEL11_100 dst_p[dlen + 1] = Scale_Blend_e11(pix[5], pix[6], pix[8]);
+
+
+// HQ scaling to 2x
+// The name expands to
+// Scale_HqFilter (for plain C)
+// Scale_MMX_HqFilter (for MMX)
+// [others when platforms are added]
+void
+SCALE_(HqFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ int prevline, nextline;
+ Uint32 pix[10];
+ Uint32 yuv[10];
+// +----+----+----+
+// | | | |
+// | p1 | p2 | p3 |
+// +----+----+----+
+// | | | |
+// | p4 | p5 | p6 |
+// +----+----+----+
+// | | | |
+// | p7 | p8 | p9 |
+// +----+----+----+
+
+ // MMX code runs faster w/o branching
+ #define HQXX_DIFFYUV(p1, p2) \
+ SCALE_DIFFYUV (p1, p2)
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 1, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ if (y > 0)
+ prevline = -slen;
+ else
+ prevline = 0;
+
+ if (y < h - 1)
+ nextline = slen;
+ else
+ nextline = 0;
+
+ // prime the (tiny) sliding-window pixel arrays
+ pix[3] = src_p[prevline];
+ pix[6] = src_p[0];
+ pix[9] = src_p[nextline];
+
+ yuv[3] = SCALE_TOYUV (pix[3]);
+ yuv[6] = SCALE_TOYUV (pix[6]);
+ yuv[9] = SCALE_TOYUV (pix[9]);
+
+ if (region->x > 0)
+ {
+ pix[2] = src_p[prevline - 1];
+ pix[5] = src_p[-1];
+ pix[8] = src_p[nextline - 1];
+
+ yuv[2] = SCALE_TOYUV (pix[2]);
+ yuv[5] = SCALE_TOYUV (pix[5]);
+ yuv[8] = SCALE_TOYUV (pix[8]);
+ }
+ else
+ {
+ pix[2] = pix[3];
+ pix[5] = pix[6];
+ pix[8] = pix[9];
+
+ yuv[2] = yuv[3];
+ yuv[5] = yuv[6];
+ yuv[8] = yuv[9];
+ }
+
+ for (x = region->x; x < xend; ++x, ++src_p, dst_p += 2)
+ {
+ int pattern = 0;
+
+ // slide the window
+ pix[1] = pix[2];
+ pix[4] = pix[5];
+ pix[7] = pix[8];
+
+ yuv[1] = yuv[2];
+ yuv[4] = yuv[5];
+ yuv[7] = yuv[8];
+
+ pix[2] = pix[3];
+ pix[5] = pix[6];
+ pix[8] = pix[9];
+
+ yuv[2] = yuv[3];
+ yuv[5] = yuv[6];
+ yuv[8] = yuv[9];
+
+ if (x < w - 1)
+ {
+ pix[3] = src_p[prevline + 1];
+ pix[6] = src_p[1];
+ pix[9] = src_p[nextline + 1];
+
+ yuv[3] = SCALE_TOYUV (pix[3]);
+ yuv[6] = SCALE_TOYUV (pix[6]);
+ yuv[9] = SCALE_TOYUV (pix[9]);
+ }
+ else
+ {
+ pix[3] = pix[2];
+ pix[6] = pix[5];
+ pix[9] = pix[8];
+
+ yuv[3] = yuv[2];
+ yuv[6] = yuv[5];
+ yuv[9] = yuv[8];
+ }
+
+ // this runs much faster with branching removed
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[1]) & 0x0001;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[2]) & 0x0002;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[3]) & 0x0004;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[4]) & 0x0008;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[6]) & 0x0010;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[7]) & 0x0020;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[8]) & 0x0040;
+ pattern |= HQXX_DIFFYUV (yuv[5], yuv[9]) & 0x0080;
+
+ switch (pattern)
+ {
+ case 0:
+ case 1:
+ case 4:
+ case 32:
+ case 128:
+ case 5:
+ case 132:
+ case 160:
+ case 33:
+ case 129:
+ case 36:
+ case 133:
+ case 164:
+ case 161:
+ case 37:
+ case 165:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 2:
+ case 34:
+ case 130:
+ case 162:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 16:
+ case 17:
+ case 48:
+ case 49:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 64:
+ case 65:
+ case 68:
+ case 69:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 8:
+ case 12:
+ case 136:
+ case 140:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 3:
+ case 35:
+ case 131:
+ case 163:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 6:
+ case 38:
+ case 134:
+ case 166:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 20:
+ case 21:
+ case 52:
+ case 53:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 144:
+ case 145:
+ case 176:
+ case 177:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 192:
+ case 193:
+ case 196:
+ case 197:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 96:
+ case 97:
+ case 100:
+ case 101:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 40:
+ case 44:
+ case 168:
+ case 172:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 9:
+ case 13:
+ case 137:
+ case 141:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 18:
+ case 50:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 80:
+ case 81:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 72:
+ case 76:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 10:
+ case 138:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 66:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 24:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 7:
+ case 39:
+ case 135:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 148:
+ case 149:
+ case 180:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 224:
+ case 228:
+ case 225:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 41:
+ case 169:
+ case 45:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 22:
+ case 54:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 208:
+ case 209:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 104:
+ case 108:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 11:
+ case 139:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 19:
+ case 51:
+ {
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL00_11
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 146:
+ case 178:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ PIXEL11_12
+ }
+ else
+ {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_20
+ break;
+ }
+ case 84:
+ case 85:
+ {
+ PIXEL00_20
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL01_11
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_21
+ break;
+ }
+ case 112:
+ case 113:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL10_12
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ }
+ case 200:
+ case 204:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ PIXEL11_11
+ }
+ else
+ {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ }
+ case 73:
+ case 77:
+ {
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL00_12
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_20
+ PIXEL11_22
+ break;
+ }
+ case 42:
+ case 170:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ PIXEL10_11
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_21
+ PIXEL11_20
+ break;
+ }
+ case 14:
+ case 142:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ PIXEL01_12
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 67:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 70:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 28:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 152:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 194:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 98:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 56:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 25:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 26:
+ case 31:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 82:
+ case 214:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 88:
+ case 248:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 74:
+ case 107:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 27:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 86:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ PIXEL11_10
+ break;
+ }
+ case 216:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 106:
+ {
+ PIXEL00_10
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 30:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 210:
+ {
+ PIXEL00_22
+ PIXEL01_10
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 120:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 75:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_10
+ PIXEL11_22
+ break;
+ }
+ case 29:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_21
+ break;
+ }
+ case 198:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 184:
+ {
+ PIXEL00_21
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 99:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 57:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 71:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_22
+ break;
+ }
+ case 156:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 226:
+ {
+ PIXEL00_22
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 60:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 195:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 102:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 153:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 58:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 83:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 92:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 202:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ }
+ case 78:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_22
+ break;
+ }
+ case 154:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 114:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 89:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 90:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 55:
+ case 23:
+ {
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL00_11
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_20
+ PIXEL11_21
+ break;
+ }
+ case 182:
+ case 150:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ PIXEL11_12
+ }
+ else
+ {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_20
+ break;
+ }
+ case 213:
+ case 212:
+ {
+ PIXEL00_20
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL01_11
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_21
+ break;
+ }
+ case 241:
+ case 240:
+ {
+ PIXEL00_20
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL10_12
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ }
+ case 236:
+ case 232:
+ {
+ PIXEL00_21
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ PIXEL11_11
+ }
+ else
+ {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ }
+ case 109:
+ case 105:
+ {
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL00_12
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_20
+ PIXEL11_22
+ break;
+ }
+ case 171:
+ case 43:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL10_11
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_21
+ PIXEL11_20
+ break;
+ }
+ case 143:
+ case 15:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL01_12
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_22
+ PIXEL11_20
+ break;
+ }
+ case 124:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 203:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ PIXEL10_10
+ PIXEL11_11
+ break;
+ }
+ case 62:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 211:
+ {
+ PIXEL00_11
+ PIXEL01_10
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 118:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_12
+ PIXEL11_10
+ break;
+ }
+ case 217:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 110:
+ {
+ PIXEL00_10
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 155:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 188:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 185:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 61:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 157:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 103:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_22
+ break;
+ }
+ case 227:
+ {
+ PIXEL00_11
+ PIXEL01_21
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 230:
+ {
+ PIXEL00_22
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 199:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_21
+ PIXEL11_11
+ break;
+ }
+ case 220:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 158:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 234:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_11
+ break;
+ }
+ case 242:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 59:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 121:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 87:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 79:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_22
+ break;
+ }
+ case 122:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 94:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 218:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 91:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 229:
+ {
+ PIXEL00_20
+ PIXEL01_20
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 167:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_20
+ PIXEL11_20
+ break;
+ }
+ case 173:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 181:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 186:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 115:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 93:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 206:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ }
+ case 205:
+ case 201:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_10
+ }
+ else
+ {
+ PIXEL10_70
+ }
+ PIXEL11_11
+ break;
+ }
+ case 174:
+ case 46:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_10
+ }
+ else
+ {
+ PIXEL00_70
+ }
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 179:
+ case 147:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_10
+ }
+ else
+ {
+ PIXEL01_70
+ }
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 117:
+ case 116:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_10
+ }
+ else
+ {
+ PIXEL11_70
+ }
+ break;
+ }
+ case 189:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 231:
+ {
+ PIXEL00_11
+ PIXEL01_12
+ PIXEL10_12
+ PIXEL11_11
+ break;
+ }
+ case 126:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 219:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 125:
+ {
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL00_12
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL00_61
+ PIXEL10_90
+ }
+ PIXEL01_11
+ PIXEL11_10
+ break;
+ }
+ case 221:
+ {
+ PIXEL00_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL01_11
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL01_60
+ PIXEL11_90
+ }
+ PIXEL10_10
+ break;
+ }
+ case 207:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL01_12
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL01_61
+ }
+ PIXEL10_10
+ PIXEL11_11
+ break;
+ }
+ case 238:
+ {
+ PIXEL00_10
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ PIXEL11_11
+ }
+ else
+ {
+ PIXEL10_90
+ PIXEL11_60
+ }
+ break;
+ }
+ case 190:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ PIXEL11_12
+ }
+ else
+ {
+ PIXEL01_90
+ PIXEL11_61
+ }
+ PIXEL10_11
+ break;
+ }
+ case 187:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ PIXEL10_11
+ }
+ else
+ {
+ PIXEL00_90
+ PIXEL10_60
+ }
+ PIXEL01_10
+ PIXEL11_12
+ break;
+ }
+ case 243:
+ {
+ PIXEL00_11
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL10_12
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL10_61
+ PIXEL11_90
+ }
+ break;
+ }
+ case 119:
+ {
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL00_11
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL00_60
+ PIXEL01_90
+ }
+ PIXEL10_12
+ PIXEL11_10
+ break;
+ }
+ case 237:
+ case 233:
+ {
+ PIXEL00_12
+ PIXEL01_20
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ }
+ case 175:
+ case 47:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ PIXEL10_11
+ PIXEL11_20
+ break;
+ }
+ case 183:
+ case 151:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_20
+ PIXEL11_12
+ break;
+ }
+ case 245:
+ case 244:
+ {
+ PIXEL00_20
+ PIXEL01_11
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 250:
+ {
+ PIXEL00_10
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 123:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 95:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_10
+ PIXEL11_10
+ break;
+ }
+ case 222:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 252:
+ {
+ PIXEL00_21
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 249:
+ {
+ PIXEL00_12
+ PIXEL01_22
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 235:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_21
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ }
+ case 111:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_22
+ break;
+ }
+ case 63:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_11
+ PIXEL11_21
+ break;
+ }
+ case 159:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_22
+ PIXEL11_12
+ break;
+ }
+ case 215:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_21
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 246:
+ {
+ PIXEL00_22
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 254:
+ {
+ PIXEL00_10
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 253:
+ {
+ PIXEL00_12
+ PIXEL01_11
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 251:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ PIXEL01_10
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 239:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ PIXEL01_12
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ PIXEL11_11
+ break;
+ }
+ case 127:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_20
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_20
+ }
+ PIXEL11_10
+ break;
+ }
+ case 191:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_11
+ PIXEL11_12
+ break;
+ }
+ case 223:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_20
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_10
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_20
+ }
+ break;
+ }
+ case 247:
+ {
+ PIXEL00_11
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ PIXEL10_12
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ case 255:
+ {
+ if (HQXX_DIFFYUV (yuv[4], yuv[2]))
+ {
+ PIXEL00_0
+ }
+ else
+ {
+ PIXEL00_100
+ }
+ if (HQXX_DIFFYUV (yuv[2], yuv[6]))
+ {
+ PIXEL01_0
+ }
+ else
+ {
+ PIXEL01_100
+ }
+ if (HQXX_DIFFYUV (yuv[8], yuv[4]))
+ {
+ PIXEL10_0
+ }
+ else
+ {
+ PIXEL10_100
+ }
+ if (HQXX_DIFFYUV (yuv[6], yuv[8]))
+ {
+ PIXEL11_0
+ }
+ else
+ {
+ PIXEL11_100
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/nearest2x.c b/src/libs/graphics/sdl/nearest2x.c
new file mode 100644
index 0000000..42e6813
--- /dev/null
+++ b/src/libs/graphics/sdl/nearest2x.c
@@ -0,0 +1,207 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the BiLinear screen scaler
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+// Nearest Neighbor scaling to 2x
+// The name expands to
+// Scale_Nearest (for plain C)
+// Scale_MMX_Nearest (for MMX)
+// Scale_SSE_Nearest (for SSE)
+// [others when platforms are added]
+void
+SCALE_(Nearest) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int y;
+ const int rw = r->w, rh = r->h;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = dst->format->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ const int dsrc = slen-rw, ddst = (dlen-rw) * 2;
+
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ // guard asm code against such atrocities
+ if (rw == 0 || rh == 0)
+ return;
+
+ SCALE_(PlatInit) ();
+
+ // move ptrs to the first updated pixel
+ src_p += slen * r->y + r->x;
+ dst_p += (dlen * r->y + r->x) * 2;
+
+#if defined(MMX_ASM) && defined(MSVC_ASM)
+ // Just about everything has to be done in asm for MSVC
+ // to actually take advantage of asm here
+ // MSVC does not support beautiful GCC-like asm templates
+
+ y = rh;
+ __asm
+ {
+ // setup vars
+ mov esi, src_p
+ mov edi, dst_p
+
+ PREFETCH (esi + 0x40)
+ PREFETCH (esi + 0x80)
+ PREFETCH (esi + 0xc0)
+
+ mov edx, dlen
+ lea edx, [edx * 4]
+ mov eax, dsrc
+ lea eax, [eax * 4]
+ mov ebx, ddst
+ lea ebx, [ebx * 4]
+
+ mov ecx, rw
+ loop_y:
+ test ecx, 1
+ jz even_x
+
+ // one-pixel transfer
+ movd mm1, [esi]
+ punpckldq mm1, mm1 // pix1 | pix1 -> mm1
+ add esi, 4
+ MOVNTQ (edi, mm1)
+ add edi, 8
+ MOVNTQ (edi - 8 + edx, mm1)
+
+ even_x:
+ shr ecx, 1 // x = rw / 2
+ jz end_x // rw was 1
+
+ loop_x:
+ // two-pixel transfer
+ movq mm1, [esi]
+ movq mm2, mm1
+ PREFETCH (esi + 0x100)
+ punpckldq mm1, mm1 // pix1 | pix1 -> mm1
+ add esi, 8
+ MOVNTQ (edi, mm1)
+ punpckhdq mm2, mm2 // pix2 | pix2 -> mm2
+ MOVNTQ (edi + edx, mm1)
+ add edi, 16
+ MOVNTQ (edi - 8, mm2)
+ MOVNTQ (edi - 8 + edx, mm2)
+
+ dec ecx
+ jnz loop_x
+
+ end_x:
+ // try to prefetch as early as possible to have it on time
+ PREFETCH (esi + eax)
+
+ mov ecx, rw
+ add esi, eax
+
+ PREFETCH (esi + 0x40)
+ PREFETCH (esi + 0x80)
+ PREFETCH (esi + 0xc0)
+
+ add edi, ebx
+
+ dec y
+ jnz loop_y
+ }
+
+#elif defined(MMX_ASM) && defined(GCC_ASM)
+
+ SCALE_(Prefetch) (src_p + 16);
+ SCALE_(Prefetch) (src_p + 32);
+ SCALE_(Prefetch) (src_p + 48);
+
+ for (y = rh; y; --y)
+ {
+ int x = rw;
+
+ if (x & 1)
+ { // one-pixel transfer
+ __asm__ (
+ "movd (%0), %%mm1 \n\t"
+ "punpckldq %%mm1, %%mm1 \n\t"
+ MOVNTQ (%%mm1, (%1)) "\n\t"
+ MOVNTQ (%%mm1, (%1,%2)) "\n\t"
+
+ : /* nothing */
+ : /*0*/"r" (src_p), /*1*/"r" (dst_p), /*2*/"r" (dlen*sizeof(Uint32))
+ );
+
+ ++src_p;
+ dst_p += 2;
+ --x;
+ }
+
+ for (x >>= 1; x; --x, src_p += 2, dst_p += 4)
+ { // two-pixel transfer
+ __asm__ (
+ "movq (%0), %%mm1 \n\t"
+ "movq %%mm1, %%mm2 \n\t"
+ PREFETCH (0x100(%0)) "\n\t"
+ "punpckldq %%mm1, %%mm1 \n\t"
+ MOVNTQ (%%mm1, (%1)) "\n\t"
+ MOVNTQ (%%mm1, (%1,%2)) "\n\t"
+ "punpckhdq %%mm2, %%mm2 \n\t"
+ MOVNTQ (%%mm2, 8(%1)) "\n\t"
+ MOVNTQ (%%mm2, 8(%1,%2)) "\n\t"
+
+ : /* nothing */
+ : /*0*/"r" (src_p), /*1*/"r" (dst_p), /*2*/"r" (dlen*sizeof(Uint32))
+ );
+ }
+
+ src_p += dsrc;
+ // try to prefetch as early as possible to have it on time
+ SCALE_(Prefetch) (src_p);
+
+ dst_p += ddst;
+
+ SCALE_(Prefetch) (src_p + 16);
+ SCALE_(Prefetch) (src_p + 32);
+ SCALE_(Prefetch) (src_p + 48);
+ }
+
+#else
+ // Plain C version
+ for (y = 0; y < rh; ++y)
+ {
+ int x;
+ for (x = 0; x < rw; ++x, ++src_p, dst_p += 2)
+ {
+ Uint32 pix = *src_p;
+ dst_p[0] = pix;
+ dst_p[1] = pix;
+ dst_p[dlen] = pix;
+ dst_p[dlen + 1] = pix;
+ }
+ dst_p += ddst;
+ src_p += dsrc;
+ }
+#endif
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/sdl/opengl.c b/src/libs/graphics/sdl/opengl.c
new file mode 100644
index 0000000..d8fe33f
--- /dev/null
+++ b/src/libs/graphics/sdl/opengl.c
@@ -0,0 +1,575 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_OPENGL
+
+#include "libs/graphics/sdl/opengl.h"
+#include "libs/graphics/bbox.h"
+#include "scalers.h"
+#include "options.h"
+#include "libs/log.h"
+
+#if SDL_MAJOR_VERSION == 1
+
+typedef struct _gl_screeninfo {
+ SDL_Surface *scaled;
+ GLuint texture;
+ BOOLEAN dirty, active;
+ SDL_Rect updated;
+} TFB_GL_SCREENINFO;
+
+static TFB_GL_SCREENINFO GL_Screens[TFB_GFX_NUMSCREENS];
+
+static int ScreenFilterMode;
+
+static TFB_ScaleFunc scaler = NULL;
+static BOOLEAN first_init = TRUE;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#define R_MASK 0xff000000
+#define G_MASK 0x00ff0000
+#define B_MASK 0x0000ff00
+#define A_MASK 0x000000ff
+#else
+#define R_MASK 0x000000ff
+#define G_MASK 0x0000ff00
+#define B_MASK 0x00ff0000
+#define A_MASK 0xff000000
+#endif
+
+static void TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_GL_Postprocess (void);
+static void TFB_GL_UploadTransitionScreen (void);
+static void TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+
+static TFB_GRAPHICS_BACKEND opengl_scaled_backend = {
+ TFB_GL_Preprocess,
+ TFB_GL_Postprocess,
+ TFB_GL_UploadTransitionScreen,
+ TFB_GL_Scaled_ScreenLayer,
+ TFB_GL_ColorLayer };
+
+static TFB_GRAPHICS_BACKEND opengl_unscaled_backend = {
+ TFB_GL_Preprocess,
+ TFB_GL_Postprocess,
+ TFB_GL_UploadTransitionScreen,
+ TFB_GL_Unscaled_ScreenLayer,
+ TFB_GL_ColorLayer };
+
+
+static int
+AttemptColorDepth (int flags, int width, int height, int bpp)
+{
+ SDL_Surface *SDL_Video;
+ int videomode_flags;
+ ScreenColorDepth = bpp;
+ ScreenWidthActual = width;
+ ScreenHeightActual = height;
+
+ switch (bpp) {
+ case 15:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
+ break;
+
+ case 16:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 6);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5);
+ break;
+
+ case 24:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
+ break;
+
+ case 32:
+ SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 8);
+ SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 8);
+ break;
+ default:
+ break;
+ }
+
+ SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 0);
+ SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1);
+
+ videomode_flags = SDL_OPENGL;
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ videomode_flags |= SDL_FULLSCREEN;
+ videomode_flags |= SDL_ANYFORMAT;
+
+ SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual,
+ bpp, videomode_flags);
+ if (SDL_Video == NULL)
+ {
+ log_add (log_Error, "Couldn't set OpenGL %ix%ix%i video mode: %s",
+ ScreenWidthActual, ScreenHeightActual, bpp,
+ SDL_GetError ());
+ return -1;
+ }
+ else
+ {
+ log_add (log_Info, "Set the resolution to: %ix%ix%i"
+ " (surface reports %ix%ix%i)",
+ width, height, bpp,
+ SDL_GetVideoSurface()->w, SDL_GetVideoSurface()->h,
+ SDL_GetVideoSurface()->format->BitsPerPixel);
+
+ log_add (log_Info, "OpenGL renderer: %s version: %s",
+ glGetString (GL_RENDERER), glGetString (GL_VERSION));
+ }
+ return 0;
+}
+
+int
+TFB_GL_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen)
+{
+ int i, texture_width, texture_height;
+ GraphicsDriver = driver;
+
+ if (AttemptColorDepth (flags, width, height, 32) &&
+ AttemptColorDepth (flags, width, height, 24) &&
+ AttemptColorDepth (flags, width, height, 16))
+ {
+ log_add (log_Error, "Couldn't set any OpenGL %ix%i video mode!",
+ width, height);
+ return -1;
+ }
+
+ if (!togglefullscreen)
+ {
+ if (format_conv_surf)
+ SDL_FreeSurface (format_conv_surf);
+ format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0, 32,
+ R_MASK, G_MASK, B_MASK, A_MASK);
+ if (format_conv_surf == NULL)
+ {
+ log_add (log_Error, "Couldn't create format_conv_surf: %s",
+ SDL_GetError());
+ return -1;
+ }
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+ }
+
+ SDL_Screen = SDL_Screens[0];
+ TransitionScreen = SDL_Screens[2];
+
+ if (first_init)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ GL_Screens[i].scaled = NULL;
+ GL_Screens[i].dirty = TRUE;
+ GL_Screens[i].active = TRUE;
+ }
+ GL_Screens[1].active = FALSE;
+ first_init = FALSE;
+ }
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_SOFT_ONLY)
+ {
+ if (!togglefullscreen)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!GL_Screens[i].active)
+ continue;
+ if (0 != SDL1_ReInit_Screen (&GL_Screens[i].scaled, format_conv_surf,
+ ScreenWidth * 2, ScreenHeight * 2))
+ return -1;
+ }
+ scaler = Scale_PrepPlatform (flags, SDL_Screen->format);
+ }
+
+ texture_width = 1024;
+ texture_height = 512;
+
+ graphics_backend = &opengl_scaled_backend;
+ }
+ else
+ {
+ texture_width = 512;
+ texture_height = 256;
+
+ scaler = NULL;
+ graphics_backend = &opengl_unscaled_backend;
+ }
+
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_ANY)
+ ScreenFilterMode = GL_LINEAR;
+ else
+ ScreenFilterMode = GL_NEAREST;
+
+ glViewport (0, 0, ScreenWidthActual, ScreenHeightActual);
+ glClearColor (0,0,0,0);
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ SDL_GL_SwapBuffers ();
+ glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ glDisable (GL_DITHER);
+ glDepthMask(GL_FALSE);
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!GL_Screens[i].active)
+ continue;
+ glGenTextures (1, &GL_Screens[i].texture);
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[i].texture);
+ glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, texture_width, texture_height,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+ }
+
+ return 0;
+}
+
+int
+TFB_GL_InitGraphics (int driver, int flags, int width, int height)
+{
+ char VideoName[256];
+
+ log_add (log_Info, "Initializing SDL with OpenGL support.");
+
+ SDL_VideoDriverName (VideoName, sizeof (VideoName));
+ log_add (log_Info, "SDL driver used: %s", VideoName);
+ log_add (log_Info, "SDL initialized.");
+ log_add (log_Info, "Initializing Screen.");
+
+ ScreenWidth = 320;
+ ScreenHeight = 240;
+
+ if (TFB_GL_ConfigureVideo (driver, flags, width, height, 0))
+ {
+ log_add (log_Fatal, "Could not initialize video: "
+ "no fallback at start of program!");
+ exit (EXIT_FAILURE);
+ }
+
+ // Initialize scalers (let them precompute whatever)
+ Scale_Init ();
+
+ return 0;
+}
+
+void
+TFB_GL_UninitGraphics (void)
+{
+ int i;
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++) {
+ UnInit_Screen (&GL_Screens[i].scaled);
+ }
+}
+
+static void
+TFB_GL_UploadTransitionScreen (void)
+{
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.x = 0;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.y = 0;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.w = ScreenWidth;
+ GL_Screens[TFB_SCREEN_TRANSITION].updated.h = ScreenHeight;
+ GL_Screens[TFB_SCREEN_TRANSITION].dirty = TRUE;
+}
+
+static void
+TFB_GL_ScanLines (void)
+{
+ int y;
+
+ glDisable (GL_TEXTURE_2D);
+ glEnable (GL_BLEND);
+ glBlendFunc (GL_DST_COLOR, GL_ZERO);
+ glColor3f (0.85f, 0.85f, 0.85f);
+ for (y = 0; y < ScreenHeightActual; y += 2)
+ {
+ glBegin (GL_LINES);
+ glVertex2i (0, y);
+ glVertex2i (ScreenWidthActual, y);
+ glEnd ();
+ }
+
+ glBlendFunc (GL_DST_COLOR, GL_ONE);
+ glColor3f (0.2f, 0.2f, 0.2f);
+ for (y = 1; y < ScreenHeightActual; y += 2)
+ {
+ glBegin (GL_LINES);
+ glVertex2i (0, y);
+ glVertex2i (ScreenWidthActual, y);
+ glEnd ();
+ }
+}
+
+static void
+TFB_GL_DrawQuad (SDL_Rect *r)
+{
+ BOOLEAN keep_aspect_ratio = optKeepAspectRatio;
+ int x1 = 0, y1 = 0, x2 = ScreenWidthActual, y2 = ScreenHeightActual;
+ int sx = 0, sy = 0;
+ int sw, sh;
+ float sx_multiplier = 1;
+ float sy_multiplier = 1;
+
+ if (keep_aspect_ratio)
+ {
+ float threshold = 0.75f;
+ float ratio = ScreenHeightActual / (float)ScreenWidthActual;
+
+ if (ratio > threshold)
+ {
+ // screen is narrower than 4:3
+ int height = (int)(ScreenWidthActual * threshold);
+ y1 = (ScreenHeightActual - height) / 2;
+ y2 = ScreenHeightActual - y1;
+
+ if (r != NULL)
+ {
+ sx_multiplier = ScreenWidthActual / (float)ScreenWidth;
+ sy_multiplier = height / (float)ScreenHeight;
+ sx = (int)(r->x * sx_multiplier);
+ sy = (int)(((ScreenHeight - (r->y + r->h)) * sy_multiplier) + y1);
+ }
+ }
+ else if (ratio < threshold)
+ {
+ // screen is wider than 4:3
+ int width = (int)(ScreenHeightActual / threshold);
+ x1 = (ScreenWidthActual - width) / 2;
+ x2 = ScreenWidthActual - x1;
+
+ if (r != NULL)
+ {
+ sx_multiplier = width / (float)ScreenWidth;
+ sy_multiplier = ScreenHeightActual / (float)ScreenHeight;
+ sx = (int)((r->x * sx_multiplier) + x1);
+ sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier);
+ }
+ }
+ else
+ {
+ // screen is 4:3
+ keep_aspect_ratio = 0;
+ }
+ }
+
+ if (r != NULL)
+ {
+ if (!keep_aspect_ratio)
+ {
+ sx_multiplier = ScreenWidthActual / (float)ScreenWidth;
+ sy_multiplier = ScreenHeightActual / (float)ScreenHeight;
+ sx = (int)(r->x * sx_multiplier);
+ sy = (int)((ScreenHeight - (r->y + r->h)) * sy_multiplier);
+ }
+ sw = (int)(r->w * sx_multiplier);
+ sh = (int)(r->h * sy_multiplier);
+ glScissor (sx, sy, sw, sh);
+ glEnable (GL_SCISSOR_TEST);
+ }
+
+ glBegin (GL_TRIANGLE_FAN);
+ glTexCoord2f (0, 0);
+ glVertex2i (x1, y1);
+ glTexCoord2f (ScreenWidth / 512.0f, 0);
+ glVertex2i (x2, y1);
+ glTexCoord2f (ScreenWidth / 512.0f, ScreenHeight / 256.0f);
+ glVertex2i (x2, y2);
+ glTexCoord2f (0, ScreenHeight / 256.0f);
+ glVertex2i (x1, y2);
+ glEnd ();
+ if (r != NULL)
+ {
+ glDisable (GL_SCISSOR_TEST);
+ }
+}
+
+static void
+TFB_GL_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ glMatrixMode (GL_PROJECTION);
+ glLoadIdentity ();
+ glOrtho (0,ScreenWidthActual,ScreenHeightActual, 0, -1, 1);
+ glMatrixMode (GL_MODELVIEW);
+ glLoadIdentity ();
+ if (optKeepAspectRatio)
+ glClear (GL_COLOR_BUFFER_BIT);
+
+ (void) transition_amount;
+ (void) fade_amount;
+
+ if (force_full_redraw == TFB_REDRAW_YES)
+ {
+ GL_Screens[TFB_SCREEN_MAIN].updated.x = 0;
+ GL_Screens[TFB_SCREEN_MAIN].updated.y = 0;
+ GL_Screens[TFB_SCREEN_MAIN].updated.w = ScreenWidth;
+ GL_Screens[TFB_SCREEN_MAIN].updated.h = ScreenHeight;
+ GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+ else if (TFB_BBox.valid)
+ {
+ GL_Screens[TFB_SCREEN_MAIN].updated.x = TFB_BBox.region.corner.x;
+ GL_Screens[TFB_SCREEN_MAIN].updated.y = TFB_BBox.region.corner.y;
+ GL_Screens[TFB_SCREEN_MAIN].updated.w = TFB_BBox.region.extent.width;
+ GL_Screens[TFB_SCREEN_MAIN].updated.h = TFB_BBox.region.extent.height;
+ GL_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+}
+
+static void
+TFB_GL_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (GL_Screens[screen].dirty)
+ {
+ int PitchWords = SDL_Screens[screen]->pitch / 4;
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords);
+ /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_*
+ correctly */
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+ SDL_LockSurface (SDL_Screens[screen]);
+ glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x,
+ GL_Screens[screen].updated.y,
+ GL_Screens[screen].updated.w,
+ GL_Screens[screen].updated.h,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (Uint32 *)SDL_Screens[screen]->pixels +
+ (GL_Screens[screen].updated.y * PitchWords +
+ GL_Screens[screen].updated.x));
+ SDL_UnlockSurface (SDL_Screens[screen]);
+ GL_Screens[screen].dirty = FALSE;
+ }
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode);
+ glEnable (GL_TEXTURE_2D);
+
+ if (a == 255)
+ {
+ glDisable (GL_BLEND);
+ glColor4f (1, 1, 1, 1);
+ }
+ else
+ {
+ float a_f = a / 255.0f;
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ glColor4f (1, 1, 1, a_f);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ glBindTexture (GL_TEXTURE_2D, GL_Screens[screen].texture);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (GL_Screens[screen].dirty)
+ {
+ int PitchWords = GL_Screens[screen].scaled->pitch / 4;
+ scaler (SDL_Screens[screen], GL_Screens[screen].scaled, &GL_Screens[screen].updated);
+ glPixelStorei (GL_UNPACK_ROW_LENGTH, PitchWords);
+
+ /* Matrox OpenGL drivers do not handle GL_UNPACK_SKIP_*
+ correctly */
+ glPixelStorei (GL_UNPACK_SKIP_ROWS, 0);
+ glPixelStorei (GL_UNPACK_SKIP_PIXELS, 0);
+ SDL_LockSurface (GL_Screens[screen].scaled);
+ glTexSubImage2D (GL_TEXTURE_2D, 0, GL_Screens[screen].updated.x * 2,
+ GL_Screens[screen].updated.y * 2,
+ GL_Screens[screen].updated.w * 2,
+ GL_Screens[screen].updated.h * 2,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ (Uint32 *)GL_Screens[screen].scaled->pixels +
+ (GL_Screens[screen].updated.y * 2 * PitchWords +
+ GL_Screens[screen].updated.x * 2));
+ SDL_UnlockSurface (GL_Screens[screen].scaled);
+ GL_Screens[screen].dirty = FALSE;
+ }
+
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScreenFilterMode);
+ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScreenFilterMode);
+ glEnable (GL_TEXTURE_2D);
+
+ if (a == 255)
+ {
+ glDisable (GL_BLEND);
+ glColor4f (1, 1, 1, 1);
+ }
+ else
+ {
+ float a_f = a / 255.0f;
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ glColor4f (1, 1, 1, a_f);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect)
+{
+ float r_f = r / 255.0f;
+ float g_f = g / 255.0f;
+ float b_f = b / 255.0f;
+ float a_f = a / 255.0f;
+ glColor4f(r_f, g_f, b_f, a_f);
+
+ glDisable (GL_TEXTURE_2D);
+ if (a != 255)
+ {
+ glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glEnable (GL_BLEND);
+ }
+ else
+ {
+ glDisable (GL_BLEND);
+ }
+
+ TFB_GL_DrawQuad (rect);
+}
+
+static void
+TFB_GL_Postprocess (void)
+{
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ TFB_GL_ScanLines ();
+
+ SDL_GL_SwapBuffers ();
+}
+
+#endif
+#endif
diff --git a/src/libs/graphics/sdl/opengl.h b/src/libs/graphics/sdl/opengl.h
new file mode 100644
index 0000000..6550bca
--- /dev/null
+++ b/src/libs/graphics/sdl/opengl.h
@@ -0,0 +1,89 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef OPENGL_H
+#define OPENGL_H
+
+#include "libs/graphics/sdl/sdl_common.h"
+#if SDL_MAJOR_VERSION == 1
+
+int TFB_GL_InitGraphics (int driver, int flags, int width, int height);
+void TFB_GL_UninitGraphics (void);
+int TFB_GL_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen);
+
+#ifdef HAVE_OPENGL
+#ifdef WIN32
+
+#ifdef _MSC_VER
+#pragma comment (lib, "opengl32.lib")
+#pragma comment (lib, "glu32.lib")
+#endif
+
+/* To avoid including windows.h,
+ Win32's <GL/gl.h> needs APIENTRY and WINGDIAPI defined properly. */
+
+#ifndef APIENTRY
+#define GLUT_APIENTRY_DEFINED
+#if __MINGW32__ || (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED)
+#define APIENTRY __stdcall
+#else
+#define APIENTRY
+#endif
+#endif
+
+#ifndef WINAPI
+#define GLUT_WINAPI_DEFINED
+#if __MINGW32__ || (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED)
+#define WINAPI __stdcall
+#else
+#define WINAPI
+#endif
+#endif
+
+/* This is from Win32's <winnt.h> */
+#ifndef CALLBACK
+#if (defined(_M_MRX000) || defined(_M_IX86) || defined(_M_ALPHA) || defined(_M_PPC)) && !defined(MIDL_PASS)
+#define CALLBACK __stdcall
+#else
+#define CALLBACK
+#endif
+#endif
+
+/* This is from Win32's <wingdi.h> and <winnt.h> */
+#ifndef WINGDIAPI
+#define GLUT_WINGDIAPI_DEFINED
+#define WINGDIAPI __declspec(dllimport)
+#endif
+
+/* This is from Win32's <ctype.h> */
+#ifndef LIBS_GRAPHICS_SDL_OPENGL_H_
+typedef unsigned short wchar_t;
+#define LIBS_GRAPHICS_SDL_OPENGL_H_
+#endif
+
+#include "GL/glu.h"
+
+#else /* !defined(WIN32) */
+
+#include "port.h"
+#include SDL_INCLUDE(SDL_opengl.h)
+
+#endif /* WIN32 */
+#endif /* SDL_MAJOR_VERSION == 1 */
+#endif /* HAVE_OPENGL */
+#endif
diff --git a/src/libs/graphics/sdl/palette.c b/src/libs/graphics/sdl/palette.c
new file mode 100644
index 0000000..18f4418
--- /dev/null
+++ b/src/libs/graphics/sdl/palette.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2009 Alex Volkov <codepro@usa.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "palette.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+
+NativePalette *
+AllocNativePalette (void)
+{
+ return HCalloc (sizeof (NativePalette));
+}
+
+void
+FreeNativePalette (NativePalette *palette)
+{
+ HFree (palette);
+}
+
+void
+SetNativePaletteColor (NativePalette *palette, int index, Color color)
+{
+ assert (index < NUMBER_OF_PLUTVALS);
+ palette->colors[index] = ColorToNative (color);
+}
+
+Color
+GetNativePaletteColor (NativePalette *palette, int index)
+{
+ assert (index < NUMBER_OF_PLUTVALS);
+ return NativeToColor (palette->colors[index]);
+}
diff --git a/src/libs/graphics/sdl/palette.h b/src/libs/graphics/sdl/palette.h
new file mode 100644
index 0000000..2cefe7c
--- /dev/null
+++ b/src/libs/graphics/sdl/palette.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2009 Alex Volkov <codepro@usa.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PALETTE_H_INCL__
+#define PALETTE_H_INCL__
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+#include "libs/graphics/cmap.h"
+
+struct NativePalette
+{
+ SDL_Color colors[NUMBER_OF_PLUTVALS];
+};
+
+static inline Color
+NativeToColor (SDL_Color native)
+{
+ Color color;
+ color.r = native.r;
+ color.g = native.g;
+ color.b = native.b;
+ color.a = 0xff; // fully opaque
+ return color;
+}
+
+static inline SDL_Color
+ColorToNative (Color color)
+{
+ SDL_Color native;
+ native.r = color.r;
+ native.g = color.g;
+ native.b = color.b;
+#if SDL_MAJOR_VERSION == 1
+ native.unused = 0;
+#else
+ native.a = color.a;
+#endif
+ return native;
+}
+
+#endif /* PALETTE_H_INCL__ */
diff --git a/src/libs/graphics/sdl/png2sdl.c b/src/libs/graphics/sdl/png2sdl.c
new file mode 100644
index 0000000..1717886
--- /dev/null
+++ b/src/libs/graphics/sdl/png2sdl.c
@@ -0,0 +1,300 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This code is adapted from IMG_png.c in the SDL2_image library, which is
+ * available under the zlib license and is (c) 1997-2019 Sam Lantinga. The
+ * code itself is credited to Philippe Lavoie as the original author. It
+ * also shares some heritage with libpng's own example.c file, and
+ * ultimately inherits the GPL2+ license from the rest of UQM.
+ *
+ * Differences from SDL2_image are:
+ * - It is PNG-only
+ * - It directly links the relevant version of libpng at compile time
+ * - It always uses libpng and will never forward to alternative
+ * libraries such as ImageIO.framework. This means that palette
+ * information will always be preserved, as UQM requires.
+ * - It locks the surface as the API demands rather than using
+ * volatility markers
+ * - Palette assignment is done through the API rather than by
+ * directly editing the format contents
+ */
+
+#include "png2sdl.h"
+#include <png.h>
+
+/* Link function between SDL_RWops and PNG's data source */
+static void
+png_read_data(png_structp ctx, png_bytep area, png_size_t size)
+{
+ SDL_RWops *src = (SDL_RWops *)png_get_io_ptr (ctx);
+ SDL_RWread (src, area, size, 1);
+}
+
+SDL_Surface *
+TFB_png_to_sdl (SDL_RWops *src)
+{
+ Sint64 start;
+ const char *error;
+ SDL_Surface *surface;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height;
+ int bit_depth, color_type, interlace_type, num_channels;
+ Uint32 Rmask, Gmask, Bmask, Amask;
+ png_bytep *row_pointers;
+ int row, i;
+ int ckey = -1;
+ png_color_16 *transv;
+
+ if (!src)
+ {
+ /* The error message has been set in SDL_RWFromFile */
+ return NULL;
+ }
+ start = SDL_RWtell (src);
+
+ /* Initialize the data we will clean up when we're done */
+ error = NULL;
+ png_ptr = NULL;
+ info_ptr = NULL;
+ row_pointers = NULL;
+ surface = NULL;
+
+ /* Create the PNG loading context structure */
+ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
+ NULL, NULL, NULL);
+ if (png_ptr == NULL)
+ {
+ error = "Couldn't allocate memory for PNG file";
+ goto done;
+ }
+
+ /* Allocate/initialize the memory for image information */
+ info_ptr = png_create_info_struct (png_ptr);
+ if (info_ptr == NULL)
+ {
+ error = "Couldn't create image information for PNG file";
+ goto done;
+ }
+
+ /* Set error handling */
+ if (setjmp (png_jmpbuf (png_ptr)))
+ {
+ error = "Error reading the PNG file.";
+ goto done;
+ }
+
+ /* Set up the input control */
+ png_set_read_fn (png_ptr, src, png_read_data);
+
+ /* Read PNG header info */
+ png_read_info (png_ptr, info_ptr);
+ png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth,
+ &color_type, &interlace_type, NULL, NULL);
+
+
+ /* Configure the decode based on what we know of the image
+ * already: strip 16 bit color down to 8 bit, automatically
+ * deinterlace, expand grayscale images or those with more
+ * than one transparent color or any translucent colors into
+ * full RGB or RGBA, and expand 1, 2, or 4-bpp paletted
+ * images to 8bpp. */
+ png_set_strip_16 (png_ptr);
+ png_set_interlace_handling (png_ptr);
+ png_set_packing (png_ptr);
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ png_set_expand (png_ptr);
+ }
+ if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
+ {
+ int num_trans;
+ Uint8 *trans;
+ png_get_tRNS (png_ptr, info_ptr, &trans, &num_trans, &transv);
+ if (color_type == PNG_COLOR_TYPE_PALETTE)
+ {
+ /* Check if all tRNS entries are opaque except one */
+ int j, t = -1;
+ for (j = 0; j < num_trans; j++)
+ {
+ if (trans[j] == 0)
+ {
+ if (t >= 0)
+ {
+ break;
+ }
+ t = j;
+ }
+ else if (trans[j] != 255)
+ {
+ break;
+ }
+ }
+ if (j == num_trans)
+ {
+ /* exactly one transparent index */
+ ckey = t;
+ }
+ else
+ {
+ /* more than one transparent index, or translucency */
+ png_set_expand (png_ptr);
+ }
+ }
+ else
+ {
+ ckey = 0; /* actual value will be set later */
+ }
+ }
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ {
+ png_set_gray_to_rgb (png_ptr);
+ }
+
+ /* Register our changes with the reading machinery and refresh
+ * our ancillary data about the image */
+ png_read_update_info (png_ptr, info_ptr);
+ png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth,
+ &color_type, &interlace_type, NULL, NULL);
+
+ /* Allocate the SDL surface to hold the image */
+ Rmask = Gmask = Bmask = Amask = 0;
+ num_channels = png_get_channels (png_ptr, info_ptr);
+ if (num_channels >= 3)
+ {
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ Rmask = 0x000000FF;
+ Gmask = 0x0000FF00;
+ Bmask = 0x00FF0000;
+ Amask = (num_channels == 4) ? 0xFF000000 : 0;
+#else
+ int s = (num_channels == 4) ? 0 : 8;
+ Rmask = 0xFF000000 >> s;
+ Gmask = 0x00FF0000 >> s;
+ Bmask = 0x0000FF00 >> s;
+ Amask = 0x000000FF >> s;
+#endif
+ }
+ surface = SDL_CreateRGBSurface (SDL_SWSURFACE, width, height,
+ bit_depth * num_channels,
+ Rmask, Gmask, Bmask, Amask);
+ if (surface == NULL)
+ {
+ error = SDL_GetError ();
+ goto done;
+ }
+
+ if (ckey != -1)
+ {
+ if (color_type != PNG_COLOR_TYPE_PALETTE)
+ {
+ /* FIXME: Should these be truncated or shifted down? */
+ ckey = SDL_MapRGB(surface->format,
+ (Uint8)transv->red,
+ (Uint8)transv->green,
+ (Uint8)transv->blue);
+ }
+#if SDL_MAJOR_VERSION >= 2
+ SDL_SetColorKey (surface, SDL_TRUE, ckey);
+#else
+ SDL_SetColorKey (surface, SDL_SRCCOLORKEY, ckey);
+#endif
+ }
+
+ SDL_LockSurface (surface);
+ /* Create the array of pointers to image data */
+ row_pointers = (png_bytep *)SDL_malloc (sizeof (png_bytep) * height);
+ if (!row_pointers)
+ {
+ error = "Out of memory";
+ goto done;
+ }
+ for (row = 0; row < (int)height; row++)
+ {
+ row_pointers[row] = (png_bytep)
+ (Uint8 *)surface->pixels + row*surface->pitch;
+ }
+
+ /* Read the entire image in one go */
+ png_read_image (png_ptr, row_pointers);
+ SDL_UnlockSurface (surface);
+
+ /* and we're done! (png_read_end() can be omitted if no
+ * processing of post-IDAT text/time/etc. is desired)
+ * In some cases it can't read PNGs created by some popular
+ * programs (ACDSEE), we do not want to process comments, so
+ * we omit png_read_end */
+
+ /* Load the palette, if any */
+ if (surface->format->palette)
+ {
+ SDL_Color palette[256];
+ int png_num_palette;
+ png_colorp png_palette;
+ png_get_PLTE (png_ptr, info_ptr, &png_palette, &png_num_palette);
+ if (color_type == PNG_COLOR_TYPE_GRAY)
+ {
+ png_num_palette = 256;
+ for (i = 0; i < 256; i++)
+ {
+ palette[i].r = (Uint8)i;
+ palette[i].g = (Uint8)i;
+ palette[i].b = (Uint8)i;
+ }
+ }
+ else if (png_num_palette > 0)
+ {
+ for (i = 0; i < png_num_palette; ++i)
+ {
+ palette[i].b = png_palette[i].blue;
+ palette[i].g = png_palette[i].green;
+ palette[i].r = png_palette[i].red;
+ }
+ }
+#if SDL_MAJOR_VERSION >= 2
+ SDL_SetPaletteColors (surface->format->palette, palette,
+ 0, png_num_palette);
+#else
+ SDL_SetPalette (surface, SDL_LOGPAL, palette,
+ 0, png_num_palette);
+#endif
+ }
+
+done: /* Clean up and return */
+ if (png_ptr)
+ {
+ png_destroy_read_struct (&png_ptr,
+ info_ptr ? &info_ptr : (png_infopp)0,
+ (png_infopp)0);
+ }
+ if (row_pointers)
+ {
+ SDL_free (row_pointers);
+ }
+ if (error)
+ {
+ SDL_RWseek(src, start, RW_SEEK_SET);
+ if (surface)
+ {
+ SDL_FreeSurface (surface);
+ surface = NULL;
+ }
+ fprintf (stderr, "%s", error);
+ }
+ return surface;
+}
diff --git a/src/libs/graphics/sdl/png2sdl.h b/src/libs/graphics/sdl/png2sdl.h
new file mode 100644
index 0000000..c0a9ec3
--- /dev/null
+++ b/src/libs/graphics/sdl/png2sdl.h
@@ -0,0 +1,24 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef PNG2SDL_H_
+#define PNG2SDL_H_
+
+#include <SDL.h>
+
+SDL_Surface *TFB_png_to_sdl (SDL_RWops *src);
+
+#endif
diff --git a/src/libs/graphics/sdl/primitives.c b/src/libs/graphics/sdl/primitives.c
new file mode 100644
index 0000000..6dd539a
--- /dev/null
+++ b/src/libs/graphics/sdl/primitives.c
@@ -0,0 +1,633 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "sdl_common.h"
+#include "primitives.h"
+
+
+// Pixel drawing routines
+
+static Uint32
+getpixel_8(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x;
+ return *p;
+}
+
+static void
+putpixel_8(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 1;
+ *p = pixel;
+}
+
+static Uint32
+getpixel_16(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2;
+ return *(Uint16 *)p;
+}
+
+static void
+putpixel_16(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 2;
+ *(Uint16 *)p = pixel;
+}
+
+static Uint32
+getpixel_24_be(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ return p[0] << 16 | p[1] << 8 | p[2];
+}
+
+static void
+putpixel_24_be(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ p[0] = (pixel >> 16) & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = pixel & 0xff;
+}
+
+static Uint32
+getpixel_24_le(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ return p[0] | p[1] << 8 | p[2] << 16;
+}
+
+static void
+putpixel_24_le(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 3;
+ p[0] = pixel & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = (pixel >> 16) & 0xff;
+}
+
+static Uint32
+getpixel_32(SDL_Surface *surface, int x, int y)
+{
+ /* Here p is the address to the pixel we want to retrieve */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
+ return *(Uint32 *)p;
+}
+
+static void
+putpixel_32(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ /* Here p is the address to the pixel we want to set */
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
+ *(Uint32 *)p = pixel;
+}
+
+GetPixelFn
+getpixel_for(SDL_Surface *surface)
+{
+ int bpp = surface->format->BytesPerPixel;
+ switch (bpp) {
+ case 1:
+ return &getpixel_8;
+ case 2:
+ return &getpixel_16;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+ return &getpixel_24_be;
+ } else {
+ return &getpixel_24_le;
+ }
+ case 4:
+ return &getpixel_32;
+ }
+ return NULL;
+}
+
+PutPixelFn
+putpixel_for(SDL_Surface *surface)
+{
+ int bpp = surface->format->BytesPerPixel;
+ switch (bpp) {
+ case 1:
+ return &putpixel_8;
+ case 2:
+ return &putpixel_16;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+ return &putpixel_24_be;
+ } else {
+ return &putpixel_24_le;
+ }
+ case 4:
+ return &putpixel_32;
+ }
+ return NULL;
+}
+
+static void
+renderpixel_replace(SDL_Surface *surface, int x, int y, Uint32 pixel,
+ int factor)
+{
+ (void) factor; // ignored
+ putpixel_32(surface, x, y, pixel);
+}
+
+static inline Uint8
+clip_channel(int c)
+{
+ if (c < 0)
+ c = 0;
+ else if (c > 255)
+ c = 255;
+ return c;
+}
+
+static inline Uint8
+modulated_sum(Uint8 dc, Uint8 sc, int factor)
+{
+ // We use >> 8 instead of / 255 because it is faster, but it does
+ // not work 100% correctly. It should be safe because this should
+ // not be called for factor==255
+ int b = dc + ((sc * factor) >> 8);
+ return clip_channel(b);
+}
+
+static inline Uint8
+alpha_blend(Uint8 dc, Uint8 sc, int alpha)
+{
+ // We use >> 8 instead of / 255 because it is faster, but it does
+ // not work 100% correctly. It should be safe because this should
+ // not be called for alpha==255
+ // No need to clip since we should never get values outside of 0..255
+ // range, unless alpha is over 255, which is not supported.
+ return (((sc - dc) * alpha) >> 8) + dc;
+}
+
+// Assumes 8 bits/channel, a safe assumption for 32bpp surfaces
+#define UNPACK_PIXEL_32(p, fmt, r, g, b) \
+ do { \
+ (r) = ((p) >> (fmt)->Rshift) & 0xff; \
+ (g) = ((p) >> (fmt)->Gshift) & 0xff; \
+ (b) = ((p) >> (fmt)->Bshift) & 0xff; \
+ } while (0)
+
+// Assumes the channels already clipped to 8 bits
+static inline Uint32
+PACK_PIXEL_32(const SDL_PixelFormat *fmt,
+ Uint8 r, Uint8 g, Uint8 b)
+{
+ return ((Uint32)r << fmt->Rshift) | ((Uint32)g << fmt->Gshift)
+ | ((Uint32)b << fmt->Bshift);
+}
+
+static void
+renderpixel_additive(SDL_Surface *surface, int x, int y, Uint32 pixel,
+ int factor)
+{
+ const SDL_PixelFormat *fmt = surface->format;
+ Uint32 *p;
+ Uint32 sp;
+ Uint8 sr, sg, sb;
+ int r, g, b;
+
+ p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4);
+ sp = *p;
+ UNPACK_PIXEL_32(sp, fmt, sr, sg, sb);
+ UNPACK_PIXEL_32(pixel, fmt, r, g, b);
+
+ // TODO: We may need a special case for factor == -ADDITIVE_FACTOR_1 too,
+ // but it is not important enough right now to care ;)
+ if (factor == ADDITIVE_FACTOR_1)
+ { // no need to modulate the 'pixel', and modulation does not
+ // work correctly with factor==255 anyway
+ sr = clip_channel(sr + r);
+ sg = clip_channel(sg + g);
+ sb = clip_channel(sb + b);
+ }
+ else
+ {
+ sr = modulated_sum(sr, r, factor);
+ sg = modulated_sum(sg, g, factor);
+ sb = modulated_sum(sb, b, factor);
+ }
+
+ *p = PACK_PIXEL_32(fmt, sr, sg, sb);
+}
+
+static void
+renderpixel_alpha(SDL_Surface *surface, int x, int y, Uint32 pixel,
+ int factor)
+{
+ const SDL_PixelFormat *fmt = surface->format;
+ Uint32 *p;
+ Uint32 sp;
+ Uint8 sr, sg, sb;
+ int r, g, b;
+
+ if (factor == FULLY_OPAQUE_ALPHA)
+ { // alpha == 255 is equivalent to 'replace' and blending does not
+ // work correctly anyway because we use >> 8 instead of / 255
+ putpixel_32(surface, x, y, pixel);
+ return;
+ }
+
+ p = (Uint32 *) ((Uint8 *)surface->pixels + y * surface->pitch + x * 4);
+ sp = *p;
+ UNPACK_PIXEL_32(sp, fmt, sr, sg, sb);
+ UNPACK_PIXEL_32(pixel, fmt, r, g, b);
+ sr = alpha_blend(sr, r, factor);
+ sg = alpha_blend(sg, g, factor);
+ sb = alpha_blend(sb, b, factor);
+ *p = PACK_PIXEL_32(fmt, sr, sg, sb);
+}
+
+RenderPixelFn
+renderpixel_for(SDL_Surface *surface, RenderKind kind)
+{
+ const SDL_PixelFormat *fmt = surface->format;
+
+ // The only supported rendering is to 32bpp surfaces
+ if (fmt->BytesPerPixel != 4)
+ return NULL;
+
+ // Rendering other than REPLACE is not supported on RGBA surfaces
+ if (fmt->Amask != 0 && kind != renderReplace)
+ return NULL;
+
+ switch (kind)
+ {
+ case renderReplace:
+ return &renderpixel_replace;
+ case renderAdditive:
+ return &renderpixel_additive;
+ case renderAlpha:
+ return &renderpixel_alpha;
+ }
+ // should not ever get here
+ return NULL;
+}
+
+/* Line drawing routine
+ * Adapted from Paul Heckbert's implementation of Bresenham's algorithm,
+ * 3 Sep 85; taken from Graphics Gems I */
+
+void
+line_prim(int x1, int y1, int x2, int y2, Uint32 color, RenderPixelFn plot,
+ int factor, SDL_Surface *dst)
+{
+ int d, x, y, ax, ay, sx, sy, dx, dy;
+ SDL_Rect clip_r;
+
+ SDL_GetClipRect (dst, &clip_r);
+ if (!clip_line (&x1, &y1, &x2, &y2, &clip_r))
+ return; // line is completely outside clipping rectangle
+
+ dx = x2-x1;
+ ax = ((dx < 0) ? -dx : dx) << 1;
+ sx = (dx < 0) ? -1 : 1;
+ dy = y2-y1;
+ ay = ((dy < 0) ? -dy : dy) << 1;
+ sy = (dy < 0) ? -1 : 1;
+
+ x = x1;
+ y = y1;
+ if (ax > ay) {
+ d = ay - (ax >> 1);
+ for (;;) {
+ (*plot)(dst, x, y, color, factor);
+ if (x == x2)
+ return;
+ if (d >= 0) {
+ y += sy;
+ d -= ax;
+ }
+ x += sx;
+ d += ay;
+ }
+ } else {
+ d = ax - (ay >> 1);
+ for (;;) {
+ (*plot)(dst, x, y, color, factor);
+ if (y == y2)
+ return;
+ if (d >= 0) {
+ x += sx;
+ d -= ay;
+ }
+ y += sy;
+ d += ax;
+ }
+ }
+}
+
+
+// Clips line against rectangle using Cohen-Sutherland algorithm
+
+enum {C_TOP = 0x1, C_BOTTOM = 0x2, C_RIGHT = 0x4, C_LEFT = 0x8};
+
+static int
+compute_code (float x, float y, float xmin, float ymin, float xmax, float ymax)
+{
+ int c = 0;
+ if (y > ymax)
+ c |= C_TOP;
+ else if (y < ymin)
+ c |= C_BOTTOM;
+ if (x > xmax)
+ c |= C_RIGHT;
+ else if (x < xmin)
+ c |= C_LEFT;
+ return c;
+}
+
+int
+clip_line (int *lx1, int *ly1, int *lx2, int *ly2, const SDL_Rect *r)
+{
+ int C0, C1, C;
+ float x, y, x0, y0, x1, y1, xmin, ymin, xmax, ymax;
+
+ x0 = (float)*lx1;
+ y0 = (float)*ly1;
+ x1 = (float)*lx2;
+ y1 = (float)*ly2;
+
+ xmin = (float)r->x;
+ ymin = (float)r->y;
+ xmax = (float)r->x + r->w - 1;
+ ymax = (float)r->y + r->h - 1;
+
+ C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax);
+ C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax);
+
+ for (;;) {
+ /* trivial accept: both ends in rectangle */
+ if ((C0 | C1) == 0)
+ {
+ *lx1 = (int)x0;
+ *ly1 = (int)y0;
+ *lx2 = (int)x1;
+ *ly2 = (int)y1;
+ return 1;
+ }
+
+ /* trivial reject: both ends on the external side of the rectangle */
+ if ((C0 & C1) != 0)
+ return 0;
+
+ /* normal case: clip end outside rectangle */
+ C = C0 ? C0 : C1;
+ if (C & C_TOP)
+ {
+ x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
+ y = ymax;
+ }
+ else if (C & C_BOTTOM)
+ {
+ x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
+ y = ymin;
+ }
+ else if (C & C_RIGHT)
+ {
+ x = xmax;
+ y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
+ }
+ else
+ {
+ x = xmin;
+ y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
+ }
+
+ /* set new end point and iterate */
+ if (C == C0)
+ {
+ x0 = x; y0 = y;
+ C0 = compute_code (x0, y0, xmin, ymin, xmax, ymax);
+ }
+ else
+ {
+ x1 = x; y1 = y;
+ C1 = compute_code (x1, y1, xmin, ymin, xmax, ymax);
+ }
+ }
+}
+
+void
+fillrect_prim(SDL_Rect r, Uint32 color, RenderPixelFn plot, int factor,
+ SDL_Surface *dst)
+{
+ int x, y;
+ int x1, y1;
+ SDL_Rect clip_r;
+
+ SDL_GetClipRect (dst, &clip_r);
+ if (!clip_rect (&r, &clip_r))
+ return; // rect is completely outside clipping rectangle
+
+ // TODO: calculate destination pointer directly instead of
+ // using the plot(x,y) version
+ x1 = r.x + r.w;
+ y1 = r.y + r.h;
+ for (y = r.y; y < y1; ++y)
+ {
+ for (x = r.x; x < x1; ++x)
+ plot(dst, x, y, color, factor);
+ }
+}
+
+// clip the rectangle against the clip rectangle
+int
+clip_rect(SDL_Rect *r, const SDL_Rect *clip_r)
+{
+ // NOTE: the following clipping code is copied in part
+ // from SDL-1.2.4 sources
+ int dx, dy;
+ int w = r->w;
+ int h = r->h;
+ // SDL_Rect.w and .h are unsigned, we need signed
+
+ dx = clip_r->x - r->x;
+ if (dx > 0)
+ {
+ w -= dx;
+ r->x += dx;
+ }
+ dx = r->x + w - clip_r->x - clip_r->w;
+ if (dx > 0)
+ w -= dx;
+
+ dy = clip_r->y - r->y;
+ if (dy > 0)
+ {
+ h -= dy;
+ r->y += dy;
+ }
+ dy = r->y + h - clip_r->y - clip_r->h;
+ if (dy > 0)
+ h -= dy;
+
+ if (w <= 0 || h <= 0)
+ {
+ r->w = 0;
+ r->h = 0;
+ return 0;
+ }
+
+ r->w = w;
+ r->h = h;
+ return 1;
+}
+
+void
+blt_prim(SDL_Surface *src, SDL_Rect src_r, RenderPixelFn plot, int factor,
+ SDL_Surface *dst, SDL_Rect dst_r)
+{
+ SDL_PixelFormat *srcfmt = src->format;
+ SDL_Palette *srcpal = srcfmt->palette;
+ SDL_PixelFormat *dstfmt = dst->format;
+ Uint32 mask = 0;
+ Uint32 key = ~0;
+ GetPixelFn getpix = getpixel_for(src);
+ SDL_Rect clip_r;
+ int x, y;
+
+ SDL_GetClipRect (dst, &clip_r);
+ if (!clip_blt_rects (&src_r, &dst_r, &clip_r))
+ return; // rect is completely outside clipping rectangle
+
+ if (src_r.x >= src->w || src_r.y >= src->h)
+ return; // rect is completely outside source bounds
+
+ if (src_r.x + src_r.w > src->w)
+ src_r.w = src->w - src_r.x;
+ if (src_r.y + src_r.h > src->h)
+ src_r.h = src->h - src_r.y;
+
+ // use colorkeys where appropriate
+ if (srcfmt->Amask)
+ { // alpha transparency
+ mask = srcfmt->Amask;
+ key = 0;
+ }
+ else if (TFB_GetColorKey (src, &key) == 0)
+ {
+ mask = ~0;
+ }
+ // TODO: calculate the source and destination pointers directly
+ // instead of using the plot(x,y) version
+ for (y = 0; y < src_r.h; ++y)
+ {
+ for (x = 0; x < src_r.w; ++x)
+ {
+ Uint8 r, g, b, a;
+ Uint32 p;
+
+ p = getpix(src, src_r.x + x, src_r.y + y);
+ if (srcpal)
+ { // source is paletted, colorkey does not use mask
+ if (p == key)
+ continue; // transparent pixel
+ }
+ else
+ { // source is RGB(A), colorkey uses mask
+ if ((p & mask) == key)
+ continue; // transparent pixel
+ }
+
+ // convert pixel format to destination
+ SDL_GetRGBA(p, srcfmt, &r, &g, &b, &a);
+ // TODO: handle source pixel alpha; plot() should probably
+ // get a source alpha parameter
+ p = SDL_MapRGBA(dstfmt, r, g, b, a);
+
+ plot(dst, dst_r.x + x, dst_r.y + y, p, factor);
+ }
+ }
+}
+
+// clip the source and destination rectangles against the clip rectangle
+int
+clip_blt_rects(SDL_Rect *src_r, SDL_Rect *dst_r, const SDL_Rect *clip_r)
+{
+ // NOTE: the following clipping code is copied in part
+ // from SDL-1.2.4 sources
+ int w, h;
+ int dx, dy;
+
+ // clip the source rectangle to the source surface
+ w = src_r->w;
+ if (src_r->x < 0)
+ {
+ w += src_r->x;
+ dst_r->x -= src_r->x;
+ src_r->x = 0;
+ }
+
+ h = src_r->h;
+ if (src_r->y < 0)
+ {
+ h += src_r->y;
+ dst_r->y -= src_r->y;
+ src_r->y = 0;
+ }
+
+ // clip the destination rectangle against the clip rectangle,
+ // minding the source rectangle in the process
+ dx = clip_r->x - dst_r->x;
+ if (dx > 0)
+ {
+ w -= dx;
+ dst_r->x += dx;
+ src_r->x += dx;
+ }
+ dx = dst_r->x + w - clip_r->x - clip_r->w;
+ if (dx > 0)
+ w -= dx;
+
+ dy = clip_r->y - dst_r->y;
+ if (dy > 0)
+ {
+ h -= dy;
+ dst_r->y += dy;
+ src_r->y += dy;
+ }
+ dy = dst_r->y + h - clip_r->y - clip_r->h;
+ if (dy > 0)
+ h -= dy;
+
+ if (w <= 0 || h <= 0)
+ {
+ src_r->w = 0;
+ src_r->h = 0;
+ return 0;
+ }
+
+ src_r->w = w;
+ src_r->h = h;
+ return 1;
+}
+
diff --git a/src/libs/graphics/sdl/primitives.h b/src/libs/graphics/sdl/primitives.h
new file mode 100644
index 0000000..9d8aff2
--- /dev/null
+++ b/src/libs/graphics/sdl/primitives.h
@@ -0,0 +1,62 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PRIMITIVES_H
+#define PRIMITIVES_H
+
+/* Function types for the pixel functions */
+
+typedef Uint32 (*GetPixelFn)(SDL_Surface *, int x, int y);
+// 'pixel' is in destination surface format
+typedef void (*PutPixelFn)(SDL_Surface *, int x, int y, Uint32 pixel);
+
+GetPixelFn getpixel_for(SDL_Surface *surface);
+PutPixelFn putpixel_for(SDL_Surface *surface);
+
+// This currently matches gfxlib.h:DrawKind for simplicity
+typedef enum
+{
+ renderReplace = 0,
+ renderAdditive,
+ renderAlpha,
+} RenderKind;
+
+#define FULLY_OPAQUE_ALPHA 255
+#define ADDITIVE_FACTOR_1 255
+
+// 'pixel' is in destination surface format
+// See gfxlib.h:DrawKind for 'factor' spec
+typedef void (*RenderPixelFn)(SDL_Surface *, int x, int y, Uint32 pixel,
+ int factor);
+
+RenderPixelFn renderpixel_for(SDL_Surface *surface, RenderKind);
+
+void line_prim(int x1, int y1, int x2, int y2, Uint32 color,
+ RenderPixelFn plot, int factor, SDL_Surface *dst);
+void fillrect_prim(SDL_Rect r, Uint32 color,
+ RenderPixelFn plot, int factor, SDL_Surface *dst);
+void blt_prim(SDL_Surface *src, SDL_Rect src_r,
+ RenderPixelFn plot, int factor,
+ SDL_Surface *dst, SDL_Rect dst_r);
+
+int clip_line(int *lx1, int *ly1, int *lx2, int *ly2, const SDL_Rect *clip_r);
+int clip_rect(SDL_Rect *r, const SDL_Rect *clip_r);
+int clip_blt_rects(SDL_Rect *src_r, SDL_Rect *dst_r, const SDL_Rect *clip_r);
+
+
+#endif /* PRIMITIVES_H */
diff --git a/src/libs/graphics/sdl/pure.c b/src/libs/graphics/sdl/pure.c
new file mode 100644
index 0000000..df4a329
--- /dev/null
+++ b/src/libs/graphics/sdl/pure.c
@@ -0,0 +1,474 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "pure.h"
+#include "libs/graphics/bbox.h"
+#include "scalers.h"
+#include "libs/log.h"
+
+#if SDL_MAJOR_VERSION == 1
+
+static SDL_Surface *SDL_Video = NULL;
+static SDL_Surface *fade_color_surface = NULL;
+static SDL_Surface *fade_temp = NULL;
+static SDL_Surface *scaled_display = NULL;
+
+static TFB_ScaleFunc scaler = NULL;
+
+static Uint32 fade_color;
+
+static void TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_Pure_Scaled_Postprocess (void);
+static void TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_Pure_Unscaled_Postprocess (void);
+static void TFB_Pure_UploadTransitionScreen (void);
+static void TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+
+static TFB_GRAPHICS_BACKEND pure_scaled_backend = {
+ TFB_Pure_Scaled_Preprocess,
+ TFB_Pure_Scaled_Postprocess,
+ TFB_Pure_UploadTransitionScreen,
+ TFB_Pure_ScreenLayer,
+ TFB_Pure_ColorLayer };
+
+static TFB_GRAPHICS_BACKEND pure_unscaled_backend = {
+ TFB_Pure_Unscaled_Preprocess,
+ TFB_Pure_Unscaled_Postprocess,
+ TFB_Pure_UploadTransitionScreen,
+ TFB_Pure_ScreenLayer,
+ TFB_Pure_ColorLayer };
+
+// We cannot rely on SDL_DisplayFormatAlpha() anymore. It can return
+// formats that we do not expect (SDL v1.2.14 on Mac OSX). Mac likes
+// ARGB surfaces, but SDL_DisplayFormatAlpha thinks that only RGBA are fast.
+// This is a generic replacement that gives what we want.
+static void
+CalcAlphaFormat (const SDL_PixelFormat* video, SDL_PixelFormat* ours)
+{
+ int valid = 0;
+
+ // We use 32-bit surfaces internally
+ ours->BitsPerPixel = 32;
+
+ // Try to get as close to the video format as possible
+ if (video->BitsPerPixel == 15 || video->BitsPerPixel == 16)
+ { // At least match the channel order
+ ours->Rshift = video->Rshift / 5 * 8;
+ ours->Gshift = video->Gshift / 5 * 8;
+ ours->Bshift = video->Bshift / 5 * 8;
+ valid = 1;
+ }
+ else if (video->BitsPerPixel == 24 || video->BitsPerPixel == 32)
+ {
+ // We can only use channels aligned on byte boundary
+ if (video->Rshift % 8 == 0 && video->Gshift % 8 == 0
+ && video->Bshift % 8 == 0)
+ { // Match RGB in video
+ ours->Rshift = video->Rshift;
+ ours->Gshift = video->Gshift;
+ ours->Bshift = video->Bshift;
+ valid = 1;
+ }
+ }
+
+ if (valid)
+ { // For alpha, use the unoccupied byte
+ ours->Ashift = 48 - (ours->Rshift + ours->Gshift + ours->Bshift);
+ // Set channels according to byte positions
+ ours->Rmask = 0xff << ours->Rshift;
+ ours->Gmask = 0xff << ours->Gshift;
+ ours->Bmask = 0xff << ours->Bshift;
+ ours->Amask = 0xff << ours->Ashift;
+ return;
+ }
+
+ // Fallback case. It does not matter what we set, but SDL likes
+ // Alpha to be the highest.
+ ours->Rmask = 0x000000ff;
+ ours->Gmask = 0x0000ff00;
+ ours->Bmask = 0x00ff0000;
+ ours->Amask = 0xff000000;
+}
+
+int
+TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen)
+{
+ int i, videomode_flags;
+ SDL_PixelFormat conv_fmt;
+
+ GraphicsDriver = driver;
+
+ // must use SDL_SWSURFACE, HWSURFACE doesn't work properly
+ // with fades/scaling
+ if (width == 320 && height == 240)
+ {
+ videomode_flags = SDL_SWSURFACE;
+ ScreenWidthActual = 320;
+ ScreenHeightActual = 240;
+ graphics_backend = &pure_unscaled_backend;
+ }
+ else
+ {
+ videomode_flags = SDL_SWSURFACE;
+ ScreenWidthActual = 640;
+ ScreenHeightActual = 480;
+ graphics_backend = &pure_scaled_backend;
+
+ if (width != 640 || height != 480)
+ log_add (log_Error, "Screen resolution of %dx%d not supported "
+ "under pure SDL, using 640x480", width, height);
+ }
+
+ videomode_flags |= SDL_ANYFORMAT;
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ videomode_flags |= SDL_FULLSCREEN;
+
+ /* We'll ask for a 32bpp frame, but it doesn't really matter, because we've set
+ SDL_ANYFORMAT */
+ SDL_Video = SDL_SetVideoMode (ScreenWidthActual, ScreenHeightActual,
+ 32, videomode_flags);
+
+ if (SDL_Video == NULL)
+ {
+ log_add (log_Error, "Couldn't set %ix%i video mode: %s",
+ ScreenWidthActual, ScreenHeightActual,
+ SDL_GetError ());
+ return -1;
+ }
+ else
+ {
+ const SDL_Surface *video = SDL_GetVideoSurface ();
+ const SDL_PixelFormat* fmt = video->format;
+
+ ScreenColorDepth = fmt->BitsPerPixel;
+ log_add (log_Info, "Set the resolution to: %ix%ix%i",
+ video->w, video->h, ScreenColorDepth);
+ log_add (log_Info, " Video: R %08x, G %08x, B %08x, A %08x",
+ fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
+
+ if (togglefullscreen)
+ {
+ // NOTE: We cannot change the format_conv_surf now because we
+ // have already loaded lots of graphics and changing it now
+ // will only lead to chaos.
+ // Just check if channel order has changed significantly
+ CalcAlphaFormat (fmt, &conv_fmt);
+ fmt = format_conv_surf->format;
+ if (conv_fmt.Rmask != fmt->Rmask || conv_fmt.Bmask != fmt->Bmask)
+ log_add (log_Warning, "Warning: pixel format has changed "
+ "significantly. Rendering will be slow.");
+ return 0;
+ }
+ }
+
+ // Create a 32bpp surface in a compatible format which will supply
+ // the format information to all other surfaces used in the game
+ if (format_conv_surf)
+ {
+ SDL_FreeSurface (format_conv_surf);
+ format_conv_surf = NULL;
+ }
+ CalcAlphaFormat (SDL_Video->format, &conv_fmt);
+ format_conv_surf = SDL_CreateRGBSurface (SDL_SWSURFACE, 0, 0,
+ conv_fmt.BitsPerPixel, conv_fmt.Rmask, conv_fmt.Gmask,
+ conv_fmt.Bmask, conv_fmt.Amask);
+ if (!format_conv_surf)
+ {
+ log_add (log_Error, "Couldn't create format_conv_surf: %s",
+ SDL_GetError());
+ return -1;
+ }
+ else
+ {
+ const SDL_PixelFormat* fmt = format_conv_surf->format;
+ log_add (log_Info, " Internal: R %08x, G %08x, B %08x, A %08x",
+ fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
+ }
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (0 != SDL1_ReInit_Screen (&SDL_Screens[i], format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+ }
+
+ SDL_Screen = SDL_Screens[0];
+ TransitionScreen = SDL_Screens[2];
+
+ if (0 != SDL1_ReInit_Screen (&fade_color_surface, format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+ fade_color = SDL_MapRGB (fade_color_surface->format, 0, 0, 0);
+ SDL_FillRect (fade_color_surface, NULL, fade_color);
+
+ if (0 != SDL1_ReInit_Screen (&fade_temp, format_conv_surf,
+ ScreenWidth, ScreenHeight))
+ return -1;
+
+ if (ScreenWidthActual > ScreenWidth || ScreenHeightActual > ScreenHeight)
+ {
+ if (0 != SDL1_ReInit_Screen (&scaled_display, format_conv_surf,
+ ScreenWidthActual, ScreenHeightActual))
+ return -1;
+
+ scaler = Scale_PrepPlatform (flags, SDL_Screen->format);
+ }
+ else
+ { // no need to scale
+ scaler = NULL;
+ }
+
+ return 0;
+}
+
+int
+TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height)
+{
+ char VideoName[256];
+
+ log_add (log_Info, "Initializing Pure-SDL graphics.");
+
+ SDL_VideoDriverName (VideoName, sizeof (VideoName));
+ log_add (log_Info, "SDL driver used: %s", VideoName);
+ (void) renderer;
+ // The "renderer" argument is ignored by SDL1. To control how SDL1
+ // gets its pixmap, set the environment variable SDL_VIDEODRIVER.
+ // For Linux: x11 (default), dga, fbcon, directfb, svgalib,
+ // ggi, aalib
+ // For Windows: directx (default), windib
+
+ log_add (log_Info, "SDL initialized.");
+ log_add (log_Info, "Initializing Screen.");
+
+ ScreenWidth = 320;
+ ScreenHeight = 240;
+
+ if (TFB_Pure_ConfigureVideo (driver, flags, width, height, 0))
+ {
+ log_add (log_Fatal, "Could not initialize video: "
+ "no fallback at start of program!");
+ exit (EXIT_FAILURE);
+ }
+
+ // Initialize scalers (let them precompute whatever)
+ Scale_Init ();
+
+ return 0;
+}
+
+void
+TFB_Pure_UninitGraphics (void)
+{
+ UnInit_Screen (&scaled_display);
+ UnInit_Screen (&fade_color_surface);
+ UnInit_Screen (&fade_temp);
+}
+
+static void
+ScanLines (SDL_Surface *dst, SDL_Rect *r)
+{
+ const int rw = r->w * 2;
+ const int rh = r->h * 2;
+ SDL_PixelFormat *fmt = dst->format;
+ const int pitch = dst->pitch;
+ const int len = pitch / fmt->BytesPerPixel;
+ int ddst;
+ Uint32 *p = (Uint32 *) dst->pixels;
+ int x, y;
+
+ p += len * (r->y * 2) + (r->x * 2);
+ ddst = len + len - rw;
+
+ for (y = rh; y; y -= 2, p += ddst)
+ {
+ for (x = rw; x; --x, ++p)
+ {
+ // we ignore the lower bits as the difference
+ // of 1 in 255 is negligible
+ *p = ((*p >> 1) & 0x7f7f7f7f) + ((*p >> 2) & 0x3f3f3f3f);
+ }
+ }
+}
+
+static SDL_Surface *backbuffer = NULL, *scalebuffer = NULL;
+static SDL_Rect updated;
+
+static void
+TFB_Pure_Scaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ if (force_full_redraw != TFB_REDRAW_NO)
+ {
+ updated.x = updated.y = 0;
+ updated.w = ScreenWidth;
+ updated.h = ScreenHeight;
+ }
+ else
+ {
+ updated.x = TFB_BBox.region.corner.x;
+ updated.y = TFB_BBox.region.corner.y;
+ updated.w = TFB_BBox.region.extent.width;
+ updated.h = TFB_BBox.region.extent.height;
+ }
+
+ if (transition_amount == 255 && fade_amount == 255)
+ backbuffer = SDL_Screens[TFB_SCREEN_MAIN];
+ else
+ backbuffer = fade_temp;
+
+ // we can scale directly onto SDL_Video if video is compatible
+ if (SDL_Video->format->BitsPerPixel == SDL_Screen->format->BitsPerPixel
+ && SDL_Video->format->Rmask == SDL_Screen->format->Rmask
+ && SDL_Video->format->Bmask == SDL_Screen->format->Bmask)
+ scalebuffer = SDL_Video;
+ else
+ scalebuffer = scaled_display;
+
+}
+
+static void
+TFB_Pure_Unscaled_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ if (force_full_redraw != TFB_REDRAW_NO)
+ {
+ updated.x = updated.y = 0;
+ updated.w = ScreenWidth;
+ updated.h = ScreenHeight;
+ }
+ else
+ {
+ updated.x = TFB_BBox.region.corner.x;
+ updated.y = TFB_BBox.region.corner.y;
+ updated.w = TFB_BBox.region.extent.width;
+ updated.h = TFB_BBox.region.extent.height;
+ }
+
+ backbuffer = SDL_Video;
+ (void)transition_amount;
+ (void)fade_amount;
+}
+
+static void
+TFB_Pure_Scaled_Postprocess (void)
+{
+ SDL_LockSurface (scalebuffer);
+ SDL_LockSurface (backbuffer);
+
+ if (scaler)
+ scaler (backbuffer, scalebuffer, &updated);
+
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ ScanLines (scalebuffer, &updated);
+
+ SDL_UnlockSurface (backbuffer);
+ SDL_UnlockSurface (scalebuffer);
+
+ updated.x *= 2;
+ updated.y *= 2;
+ updated.w *= 2;
+ updated.h *= 2;
+ if (scalebuffer != SDL_Video)
+ SDL_BlitSurface (scalebuffer, &updated, SDL_Video, &updated);
+
+ SDL_UpdateRects (SDL_Video, 1, &updated);
+}
+
+static void
+TFB_Pure_Unscaled_Postprocess (void)
+{
+ SDL_UpdateRect (SDL_Video, updated.x, updated.y,
+ updated.w, updated.h);
+}
+
+static void
+TFB_Pure_UploadTransitionScreen (void)
+{
+ /* This is a no-op in SDL1 Pure mode */
+}
+
+static void
+TFB_Pure_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ if (SDL_Screens[screen] == backbuffer)
+ return;
+ SDL_SetAlpha (SDL_Screens[screen], SDL_SRCALPHA, a);
+ SDL_BlitSurface (SDL_Screens[screen], rect, backbuffer, rect);
+}
+
+static void
+TFB_Pure_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect)
+{
+ Uint32 col = SDL_MapRGB (fade_color_surface->format, r, g, b);
+ if (col != fade_color)
+ {
+ fade_color = col;
+ SDL_FillRect (fade_color_surface, NULL, fade_color);
+ }
+ SDL_SetAlpha (fade_color_surface, SDL_SRCALPHA, a);
+ SDL_BlitSurface (fade_color_surface, rect, backbuffer, rect);
+}
+
+void
+Scale_PerfTest (void)
+{
+ TimeCount TimeStart, TimeIn;
+ TimeCount Now = 0;
+ SDL_Rect updated = {0, 0, ScreenWidth, ScreenHeight};
+ int i;
+
+ if (!scaler)
+ {
+ log_add (log_Error, "No scaler configured! "
+ "Run with larger resolution, please");
+ return;
+ }
+ if (!scaled_display)
+ {
+ log_add (log_Error, "Run scaler performance tests "
+ "in Pure mode, please");
+ return;
+ }
+
+ SDL_LockSurface (SDL_Screen);
+ SDL_LockSurface (scaled_display);
+
+ TimeStart = TimeIn = SDL_GetTicks ();
+
+ for (i = 1; i < 1001; ++i) // run for 1000 frames
+ {
+ scaler (SDL_Screen, scaled_display, &updated);
+
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ ScanLines (scaled_display, &updated);
+
+ if (i % 100 == 0)
+ {
+ Now = SDL_GetTicks ();
+ log_add (log_Debug, "%03d(%04u) ", 100*1000 / (Now - TimeIn),
+ Now - TimeIn);
+ TimeIn = Now;
+ }
+ }
+
+ log_add (log_Debug, "Full frames scaled: %d; over %u ms; %d fps\n",
+ (i - 1), Now - TimeStart, i * 1000 / (Now - TimeStart));
+
+ SDL_UnlockSurface (scaled_display);
+ SDL_UnlockSurface (SDL_Screen);
+}
+
+#endif
diff --git a/src/libs/graphics/sdl/pure.h b/src/libs/graphics/sdl/pure.h
new file mode 100644
index 0000000..50cf06f
--- /dev/null
+++ b/src/libs/graphics/sdl/pure.h
@@ -0,0 +1,29 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PURE_H
+#define PURE_H
+
+#include "libs/graphics/sdl/sdl_common.h"
+
+int TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height);
+void TFB_Pure_UninitGraphics (void);
+int TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen);
+void Scale_PerfTest (void);
+
+#endif
diff --git a/src/libs/graphics/sdl/rotozoom.c b/src/libs/graphics/sdl/rotozoom.c
new file mode 100644
index 0000000..9f22769
--- /dev/null
+++ b/src/libs/graphics/sdl/rotozoom.c
@@ -0,0 +1,1038 @@
+/*
+
+ rotozoom.c - rotozoomer for 32bit or 8bit surfaces
+ LGPL (c) A. Schiffler
+
+ Note by sc2 developers:
+ Taken from SDL_gfx library and modified, original code can be downloaded
+ from http://www.ferzkopp.net/Software/SDL_gfx-2.0/
+
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include "sdl_common.h"
+#include "libs/memlib.h"
+#include "port.h"
+#include "rotozoom.h"
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+
+
+/*
+
+ 32bit Zoomer with optional anti-aliasing by bilinear interpolation.
+
+ Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
+
+*/
+
+int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth)
+{
+ int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep;
+ tColorRGBA *c00, *c01, *c10, *c11;
+ tColorRGBA *sp, *csp, *dp;
+ int sgap, dgap;
+
+ /*
+ * Variable setup
+ */
+ if (smooth) {
+ /*
+ * For interpolation: assume source dimension is one pixel
+ */
+ /*
+ * smaller to avoid overflow on right and bottom edge.
+ */
+ sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
+ sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
+ } else {
+ sx = (int) (65536.0 * (float) src->w / (float) dst->w);
+ sy = (int) (65536.0 * (float) src->h / (float) dst->h);
+ }
+
+ /*
+ * Allocate memory for row increments
+ */
+
+#ifndef __SYMBIAN32__
+ if ((sax = (int *) alloca((dst->w + 1) * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (int *) alloca((dst->h + 1) * sizeof(Uint32))) == NULL)
+ return (-1);
+#else
+ if ((sax = (int *) HMalloc((dst->w + 1) * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (int *) HMalloc((dst->h + 1) * sizeof(Uint32))) == NULL)
+ {
+ HFree(sax);
+ return (-1);
+ }
+#endif
+
+
+ /*
+ * Precalculate row increments
+ */
+ csx = 0;
+ csax = sax;
+ for (x = 0; x <= dst->w; x++) {
+ *csax = csx;
+ csax++;
+ csx &= 0xffff;
+ csx += sx;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y <= dst->h; y++) {
+ *csay = csy;
+ csay++;
+ csy &= 0xffff;
+ csy += sy;
+ }
+
+ /*
+ * Pointer setup
+ */
+ sp = csp = (tColorRGBA *) src->pixels;
+ dp = (tColorRGBA *) dst->pixels;
+ sgap = src->pitch - src->w * 4;
+ dgap = dst->pitch - dst->w * 4;
+
+ /*
+ * Switch between interpolating and non-interpolating code
+ */
+ if (smooth) {
+
+ /*
+ * Interpolating Zoom
+ */
+
+ /*
+ * Scan destination
+ */
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ /*
+ * Setup color source pointers
+ */
+ c00 = csp;
+ c01 = csp;
+ c01++;
+ c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch);
+ c11 = c10;
+ c11++;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+
+ /*
+ * Interpolate colors
+ */
+ ex = (*csax & 0xffff);
+ ey = (*csay & 0xffff);
+ t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
+ t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
+ dp->r = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
+ t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
+ dp->g = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
+ t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
+ dp->b = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
+ t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
+ dp->a = (((t2 - t1) * ey) >> 16) + t1;
+
+ /*
+ * Advance source pointers
+ */
+ csax++;
+ sstep = (*csax >> 16);
+ c00 += sstep;
+ c01 += sstep;
+ c10 += sstep;
+ c11 += sstep;
+ /*
+ * Advance destination pointer
+ */
+ dp++;
+ }
+ /*
+ * Advance source pointer
+ */
+ csay++;
+ csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
+ /*
+ * Advance destination pointers
+ */
+ dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
+ }
+
+ } else {
+
+ /*
+ * Non-Interpolating Zoom
+ */
+
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ sp = csp;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+ /*
+ * Draw
+ */
+ *dp = *sp;
+ /*
+ * Advance source pointers
+ */
+ csax++;
+ sp += (*csax >> 16);
+ /*
+ * Advance destination pointer
+ */
+ dp++;
+ }
+ /*
+ * Advance source pointer
+ */
+ csay++;
+ csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
+ /*
+ * Advance destination pointers
+ */
+ dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
+ }
+
+ }
+
+#ifdef __SYMBIAN32__
+ HFree(sax);
+ HFree(say);
+#endif
+
+ return (0);
+}
+
+/*
+
+ 8bit Zoomer without smoothing.
+
+ Zoomes 8bit palette/Y 'src' surface to 'dst' surface.
+
+*/
+
+static
+int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst)
+{
+ Uint32 sx, sy, *sax, *say, *csax, *csay, csx, csy;
+ int x, y;
+ Uint8 *sp, *dp, *csp;
+ int dgap;
+
+ /*
+ * Variable setup
+ */
+ sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
+ sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
+
+ /*
+ * Allocate memory for row increments
+ */
+#ifndef __SYMBIAN32__
+ if ((sax = (Uint32 *) alloca(dst->w * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (Uint32 *) alloca(dst->h * sizeof(Uint32))) == NULL)
+ return (-1);
+#else
+ if ((sax = (Uint32 *) HMalloc(dst->w * sizeof(Uint32))) == NULL)
+ return (-1);
+ if ((say = (Uint32 *) HMalloc(dst->h * sizeof(Uint32))) == NULL)
+ {
+ HFree(sax);
+ return (-1);
+ }
+#endif
+
+ /*
+ * Precalculate row increments
+ */
+ csx = 0;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+ csx += sx;
+ *csax = (csx >> 16);
+ csx &= 0xffff;
+ csax++;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ csy += sy;
+ *csay = (csy >> 16);
+ csy &= 0xffff;
+ csay++;
+ }
+
+ csx = 0;
+ csax = sax;
+ for (x = 0; x < dst->w; x++) {
+ csx += (*csax);
+ csax++;
+ }
+ csy = 0;
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ csy += (*csay);
+ csay++;
+ }
+
+ /*
+ * Pointer setup
+ */
+ sp = csp = (Uint8 *) src->pixels;
+ dp = (Uint8 *) dst->pixels;
+ dgap = dst->pitch - dst->w;
+
+ /*
+ * Draw
+ */
+ csay = say;
+ for (y = 0; y < dst->h; y++) {
+ csax = sax;
+ sp = csp;
+ for (x = 0; x < dst->w; x++) {
+ /*
+ * Draw
+ */
+ *dp = *sp;
+ /*
+ * Advance source pointers
+ */
+ sp += (*csax);
+ csax++;
+ /*
+ * Advance destination pointer
+ */
+ dp++;
+ }
+ /*
+ * Advance source pointer (for row)
+ */
+ csp += ((*csay) * src->pitch);
+ csay++;
+ /*
+ * Advance destination pointers
+ */
+ dp += dgap;
+ }
+
+#ifdef __SYMBIAN32__
+ HFree(sax);
+ HFree(say);
+#endif
+
+ return (0);
+}
+
+/*
+
+ 32bit Rotozoomer with optional anti-aliasing by bilinear interpolation.
+
+ Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
+
+*/
+
+static
+void transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int smooth)
+{
+ int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
+ tColorRGBA c00, c01, c10, c11;
+ tColorRGBA *pc, *sp;
+ int gap;
+
+ /*
+ * Variable setup
+ */
+ xd = ((src->w - dst->w) << 15);
+ yd = ((src->h - dst->h) << 15);
+ ax = (cx << 16) - (icos * cx);
+ ay = (cy << 16) - (isin * cx);
+ sw = src->w - 1;
+ sh = src->h - 1;
+ pc = dst->pixels;
+ gap = dst->pitch - dst->w * 4;
+
+ /*
+ * Switch between interpolating and non-interpolating code
+ */
+ if (smooth) {
+ for (y = 0; y < dst->h; y++) {
+ dy = cy - y;
+ sdx = (ax + (isin * dy)) + xd;
+ sdy = (ay - (icos * dy)) + yd;
+ for (x = 0; x < dst->w; x++) {
+ dx = (sdx >> 16);
+ dy = (sdy >> 16);
+ if ((dx >= -1) && (dy >= -1) && (dx < src->w) && (dy < src->h)) {
+ if ((dx >= 0) && (dy >= 0) && (dx < sw) && (dy < sh)) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ sp += 1;
+ c01 = *sp;
+ sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
+ sp -= 1;
+ c10 = *sp;
+ sp += 1;
+ c11 = *sp;
+ } else if ((dx == sw) && (dy == sh)) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if ((dx == -1) && (dy == -1)) {
+ sp = (tColorRGBA *) (src->pixels);
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if ((dx == -1) && (dy == sh)) {
+ sp = (tColorRGBA *) (src->pixels);
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if ((dx == sw) && (dy == -1)) {
+ sp = (tColorRGBA *) (src->pixels);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ } else if (dx == -1) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
+ c11 = *sp;
+ } else if (dy == -1) {
+ sp = (tColorRGBA *) (src->pixels);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ c10 = *sp;
+ sp += 1;
+ c11 = *sp;
+ } else if (dx == sw) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ c01 = *sp;
+ sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
+ c10 = *sp;
+ c11 = *sp;
+ } else if (dy == sh) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ c00 = *sp;
+ sp += 1;
+ c01 = *sp;
+ c10 = *sp;
+ c11 = *sp;
+ }
+ /*
+ * Interpolate colors
+ */
+ ex = (sdx & 0xffff);
+ ey = (sdy & 0xffff);
+ t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
+ t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
+ pc->r = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
+ t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
+ pc->g = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
+ t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
+ pc->b = (((t2 - t1) * ey) >> 16) + t1;
+ t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
+ t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
+ pc->a = (((t2 - t1) * ey) >> 16) + t1;
+ }
+ sdx += icos;
+ sdy += isin;
+ pc++;
+ }
+ pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+ }
+ } else {
+ for (y = 0; y < dst->h; y++) {
+ dy = cy - y;
+ sdx = (ax + (isin * dy)) + xd;
+ sdy = (ay - (icos * dy)) + yd;
+ for (x = 0; x < dst->w; x++) {
+ dx = (short) (sdx >> 16);
+ dy = (short) (sdy >> 16);
+ if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
+ sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+ sp += dx;
+ *pc = *sp;
+ }
+ sdx += icos;
+ sdy += isin;
+ pc++;
+ }
+ pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+ }
+ }
+}
+
+/*
+
+ 8bit Rotozoomer without smoothing
+
+ Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface.
+
+*/
+
+static
+void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos)
+{
+ int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
+ tColorY *pc, *sp;
+ int gap;
+ Uint32 colorkey = 0;
+
+ /*
+ * Variable setup
+ */
+ xd = ((src->w - dst->w) << 15);
+ yd = ((src->h - dst->h) << 15);
+ ax = (cx << 16) - (icos * cx);
+ ay = (cy << 16) - (isin * cx);
+ sw = src->w - 1;
+ sh = src->h - 1;
+ pc = dst->pixels;
+ gap = dst->pitch - dst->w;
+ /*
+ * Clear surface to colorkey
+ */
+ TFB_GetColorKey (src, &colorkey);
+ memset(pc, (unsigned char) (colorkey & 0xff), dst->pitch * dst->h);
+ /*
+ * Iterate through destination surface
+ */
+ for (y = 0; y < dst->h; y++) {
+ dy = cy - y;
+ sdx = (ax + (isin * dy)) + xd;
+ sdy = (ay - (icos * dy)) + yd;
+ for (x = 0; x < dst->w; x++) {
+ dx = (short) (sdx >> 16);
+ dy = (short) (sdy >> 16);
+ if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
+ sp = (tColorY *) (src->pixels);
+ sp += (src->pitch * dy + dx);
+ *pc = *sp;
+ }
+ sdx += icos;
+ sdy += isin;
+ pc++;
+ }
+ pc += gap;
+ }
+}
+
+/*
+
+ rotozoomSurface()
+
+ Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+#define VALUE_LIMIT 0.001
+
+
+/* Local rotozoom-size function with trig result return */
+
+static
+void rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight,
+ double *canglezoom, double *sanglezoom)
+{
+ double x, y, cx, cy, sx, sy;
+ double radangle;
+ int dstwidthhalf, dstheighthalf;
+
+ /*
+ * Determine destination width and height by rotating a centered source box
+ */
+ radangle = angle * (M_PI / 180.0);
+ *sanglezoom = sin(radangle);
+ *canglezoom = cos(radangle);
+ *sanglezoom *= zoom;
+ *canglezoom *= zoom;
+ x = width / 2;
+ y = height / 2;
+ cx = *canglezoom * x;
+ cy = *canglezoom * y;
+ sx = *sanglezoom * x;
+ sy = *sanglezoom * y;
+ dstwidthhalf = MAX(ceil(fabs(cx) + fabs(sy)), 1);
+ dstheighthalf = MAX(ceil(fabs(sx) + fabs(cy)), 1);
+ *dstwidth = 2 * dstwidthhalf;
+ *dstheight = 2 * dstheighthalf;
+}
+
+
+/* Publically available rotozoom-size function */
+
+void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight)
+{
+ double dummy_sanglezoom, dummy_canglezoom;
+
+ rotozoomSurfaceSizeTrig(width, height, angle, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
+}
+
+
+/* Publically available rotozoom function */
+
+SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth)
+{
+ SDL_Surface *rz_src;
+ SDL_Surface *rz_dst;
+ double zoominv;
+ double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
+ int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
+ int is32bit;
+ int i, src_converted;
+
+ /*
+ * Sanity check
+ */
+ if (src == NULL)
+ return (NULL);
+
+ /*
+ * Determine if source surface is 32bit or 8bit
+ */
+ is32bit = (src->format->BitsPerPixel == 32);
+ if ((is32bit) || (src->format->BitsPerPixel == 8)) {
+ /*
+ * Use source surface 'as is'
+ */
+ rz_src = src;
+ src_converted = 0;
+ } else {
+ /*
+ * New source surface is 32bit with a defined RGBA ordering
+ */
+ rz_src =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+ SDL_BlitSurface(src, NULL, rz_src, NULL);
+ src_converted = 1;
+ is32bit = 1;
+ }
+
+ /*
+ * Sanity check zoom factor
+ */
+ if (zoom < VALUE_LIMIT) {
+ zoom = VALUE_LIMIT;
+ }
+ zoominv = 65536.0 / (zoom * zoom);
+
+ /*
+ * Check if we have a rotozoom or just a zoom
+ */
+ if (fabs(angle) > VALUE_LIMIT) {
+
+ /*
+ * Angle!=0: full rotozoom
+ */
+ /*
+ * -----------------------
+ */
+
+ /* Determine target size */
+ rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoom, &dstwidth, &dstheight, &canglezoom, &sanglezoom);
+
+ /*
+ * Calculate target factors from sin/cos and zoom
+ */
+ sanglezoominv = sanglezoom;
+ canglezoominv = canglezoom;
+ sanglezoominv *= zoominv;
+ canglezoominv *= zoominv;
+
+ /* Calculate half size */
+ dstwidthhalf = dstwidth / 2;
+ dstheighthalf = dstheight / 2;
+
+ /*
+ * Alloc space to completely contain the rotated surface
+ */
+ rz_dst = NULL;
+ if (is32bit) {
+ /*
+ * Target surface is 32bit with source RGBA/ABGR ordering
+ */
+ rz_dst =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
+ rz_src->format->Rmask, rz_src->format->Gmask,
+ rz_src->format->Bmask, rz_src->format->Amask);
+ } else {
+ /*
+ * Target surface is 8bit
+ */
+ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
+ }
+
+ /*
+ * Lock source surface
+ */
+ SDL_LockSurface(rz_src);
+ /*
+ * Check which kind of surface we have
+ */
+ if (is32bit) {
+ /*
+ * Call the 32bit transformation routine to do the rotation (using alpha)
+ */
+ transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv), smooth);
+ /*
+ * Turn on source-alpha support
+ */
+ TFB_SetSurfaceAlphaMod (rz_dst, 255);
+ } else {
+ /*
+ * Copy palette and colorkey info
+ */
+ Uint32 srckey = 0;
+ TFB_GetColorKey (rz_src, &srckey);
+ for (i = 0; i < rz_src->format->palette->ncolors; i++) {
+ rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
+ }
+ rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
+ /*
+ * Call the 8bit transformation routine to do the rotation
+ */
+ transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv));
+ TFB_SetColorKey(rz_dst, srckey, 1);
+ }
+ /*
+ * Unlock source surface
+ */
+ SDL_UnlockSurface(rz_src);
+
+ } else {
+
+ /*
+ * Angle=0: Just a zoom
+ */
+ /*
+ * --------------------
+ */
+
+ /*
+ * Calculate target size
+ */
+ zoomSurfaceSize(rz_src->w, rz_src->h, zoom, zoom, &dstwidth, &dstheight);
+
+ /*
+ * Alloc space to completely contain the zoomed surface
+ */
+ rz_dst = NULL;
+ if (is32bit) {
+ /*
+ * Target surface is 32bit with source RGBA/ABGR ordering
+ */
+ rz_dst =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
+ rz_src->format->Rmask, rz_src->format->Gmask,
+ rz_src->format->Bmask, rz_src->format->Amask);
+ } else {
+ /*
+ * Target surface is 8bit
+ */
+ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
+ }
+
+ /*
+ * Lock source surface
+ */
+ SDL_LockSurface(rz_src);
+ /*
+ * Check which kind of surface we have
+ */
+ if (is32bit) {
+ /*
+ * Call the 32bit transformation routine to do the zooming (using alpha)
+ */
+ zoomSurfaceRGBA(rz_src, rz_dst, smooth);
+ /*
+ * Turn on source-alpha support
+ */
+ TFB_SetSurfaceAlphaMod (rz_dst, 255);
+ } else {
+ /*
+ * Copy palette and colorkey info
+ */
+ Uint32 srckey = 0;
+ TFB_GetColorKey (rz_src, &srckey);
+ for (i = 0; i < rz_src->format->palette->ncolors; i++) {
+ rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
+ }
+ rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
+ /*
+ * Call the 8bit transformation routine to do the zooming
+ */
+ zoomSurfaceY(rz_src, rz_dst);
+ TFB_SetColorKey(rz_dst, srckey, 1);
+ }
+ /*
+ * Unlock source surface
+ */
+ SDL_UnlockSurface(rz_src);
+ }
+
+ /*
+ * Cleanup temp surface
+ */
+ if (src_converted) {
+ SDL_FreeSurface(rz_src);
+ }
+
+ /*
+ * Return destination surface
+ */
+ return (rz_dst);
+}
+
+/* Publically available rotate function */
+
+int rotateSurface(SDL_Surface *src, SDL_Surface *dst, double angle, int smooth)
+{
+ double zoominv;
+ double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
+ int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
+ int is32bit;
+ int i;
+
+ /* Sanity check */
+ if (!src || !dst)
+ return -1;
+ if (src->format->BitsPerPixel != dst->format->BitsPerPixel)
+ return -1;
+
+ /* Determine if source surface is 32bit or 8bit */
+ is32bit = (src->format->BitsPerPixel == 32);
+
+ zoominv = 65536.0;
+
+ /* Check if we have to rotate anything */
+ if (fabs(angle) <= VALUE_LIMIT)
+ {
+ SDL_BlitSurface(src, NULL, dst, NULL);
+ return 0;
+ }
+
+ /* Determine target size */
+ rotozoomSurfaceSizeTrig(src->w, src->h, angle, 1, &dstwidth, &dstheight, &canglezoom, &sanglezoom);
+
+ /*
+ * Calculate target factors from sin/cos and zoom
+ */
+ sanglezoominv = sanglezoom;
+ canglezoominv = canglezoom;
+ sanglezoominv *= zoominv;
+ canglezoominv *= zoominv;
+
+ /* Calculate half size */
+ dstwidthhalf = dstwidth / 2;
+ dstheighthalf = dstheight / 2;
+
+ /* Check if the rotated surface will fit destination */
+ if (dst->w < dstwidth || dst->h < dstheight)
+ return -1;
+
+ /* Lock source surface */
+ SDL_LockSurface(src);
+ SDL_LockSurface(dst);
+ /* Check which kind of surface we have */
+ if (is32bit)
+ { /* Call the 32bit transformation routine to do the rotation (using alpha) */
+ transformSurfaceRGBA(src, dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv), smooth);
+ }
+ else
+ {
+ /* Copy palette info */
+ for (i = 0; i < src->format->palette->ncolors; i++)
+ dst->format->palette->colors[i] = src->format->palette->colors[i];
+ dst->format->palette->ncolors = src->format->palette->ncolors;
+ /* Call the 8bit transformation routine to do the rotation */
+ transformSurfaceY(src, dst, dstwidthhalf, dstheighthalf,
+ (int) (sanglezoominv), (int) (canglezoominv));
+ }
+ /* Unlock source surface */
+ SDL_UnlockSurface(dst);
+ SDL_UnlockSurface(src);
+
+ return 0;
+}
+
+/*
+
+ zoomSurface()
+
+ Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+#define VALUE_LIMIT 0.001
+
+void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight)
+{
+ /*
+ * Sanity check zoom factors
+ */
+ if (zoomx < VALUE_LIMIT) {
+ zoomx = VALUE_LIMIT;
+ }
+ if (zoomy < VALUE_LIMIT) {
+ zoomy = VALUE_LIMIT;
+ }
+
+ /*
+ * Calculate target size
+ */
+ *dstwidth = (int) ((double) width * zoomx);
+ *dstheight = (int) ((double) height * zoomy);
+ if (*dstwidth < 1) {
+ *dstwidth = 1;
+ }
+ if (*dstheight < 1) {
+ *dstheight = 1;
+ }
+}
+
+SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth)
+{
+ SDL_Surface *rz_src;
+ SDL_Surface *rz_dst;
+ int dstwidth, dstheight;
+ int is32bit;
+ int i, src_converted;
+
+ /*
+ * Sanity check
+ */
+ if (src == NULL)
+ return (NULL);
+
+ /*
+ * Determine if source surface is 32bit or 8bit
+ */
+ is32bit = (src->format->BitsPerPixel == 32);
+ if ((is32bit) || (src->format->BitsPerPixel == 8)) {
+ /*
+ * Use source surface 'as is'
+ */
+ rz_src = src;
+ src_converted = 0;
+ } else {
+ /*
+ * New source surface is 32bit with a defined RGBA ordering
+ */
+ rz_src =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+ SDL_BlitSurface(src, NULL, rz_src, NULL);
+ src_converted = 1;
+ is32bit = 1;
+ }
+
+ /* Get size if target */
+ zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
+
+ /*
+ * Alloc space to completely contain the zoomed surface
+ */
+ rz_dst = NULL;
+ if (is32bit) {
+ /*
+ * Target surface is 32bit with source RGBA/ABGR ordering
+ */
+ rz_dst =
+ SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
+ rz_src->format->Rmask, rz_src->format->Gmask,
+ rz_src->format->Bmask, rz_src->format->Amask);
+ } else {
+ /*
+ * Target surface is 8bit
+ */
+ rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
+ }
+
+ /*
+ * Lock source surface
+ */
+ SDL_LockSurface(rz_src);
+ /*
+ * Check which kind of surface we have
+ */
+ if (is32bit) {
+ /*
+ * Call the 32bit transformation routine to do the zooming (using alpha)
+ */
+ zoomSurfaceRGBA(rz_src, rz_dst, smooth);
+ /*
+ * Turn on source-alpha support
+ */
+ TFB_SetSurfaceAlphaMod (rz_dst, 255);
+ } else {
+ /*
+ * Copy palette and colorkey info
+ */
+ Uint32 srckey = 0;
+ TFB_GetColorKey (rz_src, &srckey);
+ for (i = 0; i < rz_src->format->palette->ncolors; i++) {
+ rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
+ }
+ rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
+ /*
+ * Call the 8bit transformation routine to do the zooming
+ */
+ zoomSurfaceY(rz_src, rz_dst);
+ TFB_SetColorKey(rz_dst, srckey, 0);
+ }
+ /*
+ * Unlock source surface
+ */
+ SDL_UnlockSurface(rz_src);
+
+ /*
+ * Cleanup temp surface
+ */
+ if (src_converted) {
+ SDL_FreeSurface(rz_src);
+ }
+
+ /*
+ * Return destination surface
+ */
+ return (rz_dst);
+}
+
diff --git a/src/libs/graphics/sdl/rotozoom.h b/src/libs/graphics/sdl/rotozoom.h
new file mode 100644
index 0000000..728fd42
--- /dev/null
+++ b/src/libs/graphics/sdl/rotozoom.h
@@ -0,0 +1,96 @@
+/*
+
+ rotozoom.h - rotozoomer for 32bit or 8bit surfaces
+ LGPL (c) A. Schiffler
+
+ Note by sc2 developers:
+ Taken from SDL_gfx library and modified, original code can be downloaded
+ from http://www.ferzkopp.net/Software/SDL_gfx-2.0/
+
+*/
+
+
+#ifndef ROTOZOOM_H
+#define ROTOZOOM_H
+
+#include <math.h>
+#ifndef M_PI
+#define M_PI 3.141592654
+#endif
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+
+
+/* ---- Defines */
+
+#define SMOOTHING_OFF 0
+#define SMOOTHING_ON 1
+
+/* ---- Structures */
+
+typedef struct tColorRGBA {
+ Uint8 r;
+ Uint8 g;
+ Uint8 b;
+ Uint8 a;
+} tColorRGBA;
+
+typedef struct tColorY {
+ Uint8 y;
+} tColorY;
+
+
+/* ---- Prototypes */
+
+/*
+ zoomSurfaceRGBA()
+
+ Zoom the src surface into dst. The zoom amount is determined
+ by the dimensions of src and dst
+
+*/
+int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int smooth);
+
+/*
+
+ rotozoomSurface()
+
+ Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom,
+ int smooth);
+
+int rotateSurface(SDL_Surface * src, SDL_Surface * dst, double angle,
+ int smooth);
+
+/* Returns the size of the target surface for a rotozoomSurface() call */
+
+void rotozoomSurfaceSize(int width, int height, double angle, double zoom,
+ int *dstwidth, int *dstheight);
+
+/*
+
+ zoomSurface()
+
+ Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+ 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1
+ then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+ or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+*/
+
+SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy,
+ int smooth);
+
+/* Returns the size of the target surface for a zoomSurface() call */
+
+void zoomSurfaceSize(int width, int height, double zoomx, double zoomy,
+ int *dstwidth, int *dstheight);
+
+#endif
diff --git a/src/libs/graphics/sdl/scaleint.h b/src/libs/graphics/sdl/scaleint.h
new file mode 100644
index 0000000..e54de80
--- /dev/null
+++ b/src/libs/graphics/sdl/scaleint.h
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2005 Alex Volkov (codepro@usa.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Scalers Internals
+
+#ifndef SCALEINT_H_
+#define SCALEINT_H_
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+
+
+// Plain C names
+#define SCALE_(name) Scale ## _ ## name
+
+// These are defaults
+#define SCALE_GETPIX(p) ( *(Uint32 *)(p) )
+#define SCALE_SETPIX(p, c) ( *(Uint32 *)(p) = (c) )
+
+// Plain C defaults
+#define SCALE_CMPRGB(p1, p2) \
+ SCALE_(GetRGBDelta) (fmt, p1, p2)
+
+#define SCALE_TOYUV(p) \
+ SCALE_(RGBtoYUV) (fmt, p)
+
+#define SCALE_CMPYUV(p1, p2, toler) \
+ SCALE_(CmpYUV) (fmt, p1, p2, toler)
+
+#define SCALE_DIFFYUV(p1, p2) \
+ SCALE_(DiffYUV) (p1, p2)
+#define SCALE_DIFFYUV_TY 0x40
+#define SCALE_DIFFYUV_TU 0x12
+#define SCALE_DIFFYUV_TV 0x0c
+
+#define SCALE_GETY(p) \
+ SCALE_(GetPixY) (fmt, p)
+
+#define SCALE_BILINEAR_BLEND4(r0, r1, dst, dlen) \
+ SCALE_(Blend_bilinear) (r0, r1, dst, dlen)
+
+#define NO_PREFETCH 0
+#define INTEL_PREFETCH 1
+#define AMD_PREFETCH 2
+
+typedef enum
+{
+ YUV_XFORM_R = 0,
+ YUV_XFORM_G = 1,
+ YUV_XFORM_B = 2,
+ YUV_XFORM_Y = 0,
+ YUV_XFORM_U = 1,
+ YUV_XFORM_V = 2
+} RGB_YUV_INDEX;
+
+extern const int YUV_matrix[3][3];
+
+// pre-computed transformations for 8 bits per channel
+extern int RGB_to_YUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 256];
+extern sint16 dRGB_to_dYUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 512];
+
+typedef Uint32 YUV_VECTOR;
+// pre-computed transformations for RGB555
+extern YUV_VECTOR RGB15_to_YUV[0x8000];
+
+
+// Platform+Scaler function lookups
+//
+typedef struct
+{
+ int flag;
+ TFB_ScaleFunc func;
+} Scale_FuncDef_t;
+
+
+// expands the given rectangle in all directions by 'expansion'
+// guarded by 'limits'
+extern void Scale_ExpandRect (SDL_Rect* rect, int expansion,
+ const SDL_Rect* limits);
+
+
+// Standard plain C versions of support functions
+
+// Initialize various platform-specific features
+static inline void
+SCALE_(PlatInit) (void)
+{
+}
+
+// Finish with various platform-specific features
+static inline void
+SCALE_(PlatDone) (void)
+{
+}
+
+#if 0
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ /* no-op in pure C */
+ (void)p;
+}
+#else
+# define Scale_Prefetch(p)
+#endif
+
+// compute the RGB distance squared between 2 pixels
+// Plain C version
+static inline int
+SCALE_(GetRGBDelta) (const SDL_PixelFormat* fmt, Uint32 pix1, Uint32 pix2)
+{
+ int c;
+ int delta;
+
+ c = ((pix1 >> fmt->Rshift) & 0xff) - ((pix2 >> fmt->Rshift) & 0xff);
+ delta = c * c;
+
+ c = ((pix1 >> fmt->Gshift) & 0xff) - ((pix2 >> fmt->Gshift) & 0xff);
+ delta += c * c;
+
+ c = ((pix1 >> fmt->Bshift) & 0xff) - ((pix2 >> fmt->Bshift) & 0xff);
+ delta += c * c;
+
+ return delta;
+}
+
+// retrieve the Y (intensity) component of pixel's YUV
+// Plain C version
+static inline int
+SCALE_(GetPixY) (const SDL_PixelFormat* fmt, Uint32 pix)
+{
+ Uint32 r, g, b;
+
+ r = (pix >> fmt->Rshift) & 0xff;
+ g = (pix >> fmt->Gshift) & 0xff;
+ b = (pix >> fmt->Bshift) & 0xff;
+
+ return RGB_to_YUV [YUV_XFORM_R][YUV_XFORM_Y][r]
+ + RGB_to_YUV [YUV_XFORM_G][YUV_XFORM_Y][g]
+ + RGB_to_YUV [YUV_XFORM_B][YUV_XFORM_Y][b];
+}
+
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (const SDL_PixelFormat* fmt, Uint32 pix)
+{
+ return RGB15_to_YUV[
+ (((pix >> (fmt->Rshift + 3)) & 0x1f) << 10) |
+ (((pix >> (fmt->Gshift + 3)) & 0x1f) << 5) |
+ (((pix >> (fmt->Bshift + 3)) & 0x1f) )
+ ];
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+// Plain C version
+static inline bool
+SCALE_(CmpYUV) (const SDL_PixelFormat* fmt, Uint32 pix1, Uint32 pix2, int toler)
+#if 1
+{
+ int dr, dg, db;
+ int delta;
+
+ dr = ((pix1 >> fmt->Rshift) & 0xff) - ((pix2 >> fmt->Rshift) & 0xff) + 255;
+ dg = ((pix1 >> fmt->Gshift) & 0xff) - ((pix2 >> fmt->Gshift) & 0xff) + 255;
+ db = ((pix1 >> fmt->Bshift) & 0xff) - ((pix2 >> fmt->Bshift) & 0xff) + 255;
+
+ // compute Y delta
+ delta = abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_Y][dr]
+ + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_Y][dg]
+ + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_Y][db]);
+ if (delta > toler)
+ return false;
+
+ // compute U delta
+ delta += abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_U][dr]
+ + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_U][dg]
+ + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_U][db]);
+ if (delta > toler)
+ return false;
+
+ // compute V delta
+ delta += abs (dRGB_to_dYUV [YUV_XFORM_R][YUV_XFORM_V][dr]
+ + dRGB_to_dYUV [YUV_XFORM_G][YUV_XFORM_V][dg]
+ + dRGB_to_dYUV [YUV_XFORM_B][YUV_XFORM_V][db]);
+
+ return delta <= toler;
+}
+#else
+{
+ int delta;
+ Uint32 yuv1, yuv2;
+
+ yuv1 = RGB15_to_YUV[
+ (((pix1 >> (fmt->Rshift + 3)) & 0x1f) << 10) |
+ (((pix1 >> (fmt->Gshift + 3)) & 0x1f) << 5) |
+ (((pix1 >> (fmt->Bshift + 3)) & 0x1f) )
+ ];
+
+ yuv2 = RGB15_to_YUV[
+ (((pix2 >> (fmt->Rshift + 3)) & 0x1f) << 10) |
+ (((pix2 >> (fmt->Gshift + 3)) & 0x1f) << 5) |
+ (((pix2 >> (fmt->Bshift + 3)) & 0x1f) )
+ ];
+
+ // compute Y delta
+ delta = abs ((yuv1 & 0xff0000) - (yuv2 & 0xff0000)) >> 16;
+ if (delta > toler)
+ return false;
+
+ // compute U delta
+ delta += abs ((yuv1 & 0x00ff00) - (yuv2 & 0x00ff00)) >> 8;
+ if (delta > toler)
+ return false;
+
+ // compute V delta
+ delta += abs ((yuv1 & 0x0000ff) - (yuv2 & 0x0000ff));
+
+ return delta <= toler;
+}
+#endif
+
+// Check if 2 pixels are different with respect to their
+// YUV representations
+// returns 0: close; ~0: distant
+static inline int
+SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2)
+{
+ // non-branching version -- assumes 2's complement integers
+ // delta math only needs 25 bits and we have 32 available;
+ // only interested in the sign bits after subtraction
+ sint32 delta, ret;
+
+ if (yuv1 == yuv2)
+ return 0;
+
+ // compute Y delta
+ delta = abs ((yuv1 & 0xff0000) - (yuv2 & 0xff0000));
+ ret = (SCALE_DIFFYUV_TY << 16) - delta; // save sign bit
+
+ // compute U delta
+ delta = abs ((yuv1 & 0x00ff00) - (yuv2 & 0x00ff00));
+ ret |= (SCALE_DIFFYUV_TU << 8) - delta; // save sign bit
+
+ // compute V delta
+ delta = abs ((yuv1 & 0x0000ff) - (yuv2 & 0x0000ff));
+ ret |= SCALE_DIFFYUV_TV - delta; // save sign bit
+
+ return (ret >> 31);
+}
+
+// blends two pixels with 1:1 ratio
+static inline Uint32
+SCALE_(Blend_11) (Uint32 pix1, Uint32 pix2)
+{
+ /* (pix1 + pix2) >> 1 */
+ return
+ /* lower bits can be safely ignored - the error is minimal
+ expression that calcs them is left for posterity
+ (pix1 & pix2 & low_mask) +
+ */
+ ((pix1 & 0xfefefefe) >> 1) + ((pix2 & 0xfefefefe) >> 1);
+}
+
+// blends four pixels with 1:1:1:1 ratio
+static inline Uint32
+SCALE_(Blend_1111) (Uint32 pix1, Uint32 pix2,
+ Uint32 pix3, Uint32 pix4)
+{
+ /* (pix1 + pix2 + pix3 + pix4) >> 2 */
+ return
+ /* lower bits can be safely ignored - the error is minimal
+ expression that calcs them is left for posterity
+ ((((pix1 & low_mask) + (pix2 & low_mask) +
+ (pix3 & low_mask) + (pix4 & low_mask)
+ ) >> 2) & low_mask) +
+ */
+ ((pix1 & 0xfcfcfcfc) >> 2) + ((pix2 & 0xfcfcfcfc) >> 2) +
+ ((pix3 & 0xfcfcfcfc) >> 2) + ((pix4 & 0xfcfcfcfc) >> 2);
+}
+
+// blends pixels with 3:1 ratio
+static inline Uint32
+Scale_Blend_31 (Uint32 pix1, Uint32 pix2)
+{
+ /* (pix1 * 3 + pix2) / 4 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix2 & 0xfcfcfcfc) >> 2);
+}
+
+// blends pixels with 2:1:1 ratio
+static inline Uint32
+Scale_Blend_211 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 2 + pix2 + pix3) / 4 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) +
+ ((pix2 & 0xfcfcfcfc) >> 2) +
+ ((pix3 & 0xfcfcfcfc) >> 2);
+}
+
+// blends pixels with 5:2:1 ratio
+static inline Uint32
+Scale_Blend_521 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 5 + pix2 * 2 + pix3) / 8 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xf8f8f8f8) >> 3) +
+ ((pix2 & 0xfcfcfcfc) >> 2) +
+ ((pix3 & 0xf8f8f8f8) >> 3) +
+ 0x02020202 /* half-error */;
+}
+
+// blends pixels with 6:1:1 ratio
+static inline Uint32
+Scale_Blend_611 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 6 + pix2 + pix3) / 8 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix2 & 0xf8f8f8f8) >> 3) +
+ ((pix3 & 0xf8f8f8f8) >> 3) +
+ 0x02020202 /* half-error */;
+}
+
+// blends pixels with 2:3:3 ratio
+static inline Uint32
+Scale_Blend_233 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 2 + pix2 * 3 + pix3 * 3) / 8 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix2 & 0xfcfcfcfc) >> 2) + ((pix2 & 0xf8f8f8f8) >> 3) +
+ ((pix3 & 0xfcfcfcfc) >> 2) + ((pix3 & 0xf8f8f8f8) >> 3) +
+ 0x02020202 /* half-error */;
+}
+
+// blends pixels with 14:1:1 ratio
+static inline Uint32
+Scale_Blend_e11 (Uint32 pix1, Uint32 pix2, Uint32 pix3)
+{
+ /* (pix1 * 14 + pix2 + pix3) >> 4 */
+ /* lower bits can be safely ignored - the error is minimal */
+ return ((pix1 & 0xfefefefe) >> 1) + ((pix1 & 0xfcfcfcfc) >> 2) +
+ ((pix1 & 0xf8f8f8f8) >> 3) +
+ ((pix2 & 0xf0f0f0f0) >> 4) +
+ ((pix3 & 0xf0f0f0f0) >> 4) +
+ 0x03030303 /* half-error */;
+}
+
+// Halfs the pixel's intensity
+static inline Uint32
+SCALE_(HalfPixel) (Uint32 pix)
+{
+ return ((pix & 0xfefefefe) >> 1);
+}
+
+
+// Bilinear weighted blend of four pixels
+// Function produces 4 blended pixels and writes them
+// out to the surface (in 2x2 matrix)
+// Pixels are computed using expanded weight matrix like so:
+// ('sp' - source pixel, 'dp' - destination pixel)
+// dp[0] = (9*sp[0] + 3*sp[1] + 3*sp[2] + 1*sp[3]) / 16
+// dp[1] = (3*sp[0] + 9*sp[1] + 1*sp[2] + 3*sp[3]) / 16
+// dp[2] = (3*sp[0] + 1*sp[1] + 9*sp[2] + 3*sp[3]) / 16
+// dp[3] = (1*sp[0] + 3*sp[1] + 3*sp[2] + 9*sp[3]) / 16
+static inline void
+SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1,
+ Uint32* dst_p, Uint32 dlen)
+{
+ // We loose some lower bits here and try to compensate for
+ // that by adding half-error values.
+ // In general, the error is minimal (+-7)
+ // The >>4 reduction is achieved gradually
+# define BL_PACKED_HALF(p) \
+ (((p) & 0xfefefefe) >> 1)
+# define BL_SUM(p1, p2) \
+ (BL_PACKED_HALF(p1) + BL_PACKED_HALF(p2))
+# define BL_HALF_ERR 0x01010101
+# define BL_SUM_WERR(p1, p2) \
+ (BL_PACKED_HALF(p1) + BL_PACKED_HALF(p2) + BL_HALF_ERR)
+
+ Uint32 sum1111, sum1331, sum3113;
+
+ // cache p[0] + 3*(p[1] + p[2]) + p[3] in sum1331
+ // cache p[1] + 3*(p[0] + p[3]) + p[2] in sum3113
+ sum1331 = BL_SUM (row0[1], row1[0]);
+ sum3113 = BL_SUM (row0[0], row1[1]);
+
+ // cache p[0] + p[1] + p[2] + p[3] in sum1111
+ sum1111 = BL_SUM_WERR (sum1331, sum3113);
+
+ sum1331 = BL_SUM_WERR (sum1331, sum1111);
+ sum1331 = BL_PACKED_HALF (sum1331);
+ sum3113 = BL_SUM_WERR (sum3113, sum1111);
+ sum3113 = BL_PACKED_HALF (sum3113);
+
+ // pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16
+ dst_p[0] = BL_PACKED_HALF (row0[0]) + sum1331;
+
+ // pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16
+ dst_p[1] = BL_PACKED_HALF (row0[1]) + sum3113;
+
+ // pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16
+ dst_p[dlen] = BL_PACKED_HALF (row1[0]) + sum3113;
+
+ // pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16
+ dst_p[dlen + 1] = BL_PACKED_HALF (row1[1]) + sum1331;
+
+# undef BL_PACKED_HALF
+# undef BL_SUM
+# undef BL_HALF_ERR
+# undef BL_SUM_WERR
+}
+
+#endif /* SCALEINT_H_ */
diff --git a/src/libs/graphics/sdl/scalemmx.h b/src/libs/graphics/sdl/scalemmx.h
new file mode 100644
index 0000000..69c83fe
--- /dev/null
+++ b/src/libs/graphics/sdl/scalemmx.h
@@ -0,0 +1,793 @@
+/*
+ * Copyright (C) 2005 Alex Volkov (codepro@usa.net)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SCALEMMX_H_
+#define SCALEMMX_H_
+
+#if !defined(SCALE_)
+# error Please define SCALE_(name) before including scalemmx.h
+#endif
+
+#if !defined(MSVC_ASM) && !defined(GCC_ASM)
+# error Please define target assembler (MSVC_ASM, GCC_ASM) before including scalemmx.h
+#endif
+
+// MMX defaults (no Format param)
+#undef SCALE_CMPRGB
+#define SCALE_CMPRGB(p1, p2) \
+ SCALE_(GetRGBDelta) (p1, p2)
+
+#undef SCALE_TOYUV
+#define SCALE_TOYUV(p) \
+ SCALE_(RGBtoYUV) (p)
+
+#undef SCALE_CMPYUV
+#define SCALE_CMPYUV(p1, p2, toler) \
+ SCALE_(CmpYUV) (p1, p2, toler)
+
+#undef SCALE_GETY
+#define SCALE_GETY(p) \
+ SCALE_(GetPixY) (p)
+
+// MMX transformation multipliers
+extern Uint64 mmx_888to555_mult;
+extern Uint64 mmx_Y_mult;
+extern Uint64 mmx_U_mult;
+extern Uint64 mmx_V_mult;
+extern Uint64 mmx_YUV_threshold;
+
+#define USE_YUV_LOOKUP
+
+#if defined(MSVC_ASM)
+// MSVC inline assembly versions
+
+#if defined(USE_MOVNTQ)
+# define MOVNTQ(addr, val) movntq [addr], val
+#else
+# define MOVNTQ(addr, val) movq [addr], val
+#endif
+
+#if USE_PREFETCH == INTEL_PREFETCH
+// using Intel SSE non-temporal prefetch
+# define PREFETCH(addr) prefetchnta [addr]
+# define HAVE_PREFETCH
+#elif USE_PREFETCH == AMD_PREFETCH
+// using AMD 3DNOW! prefetch
+# define PREFETCH(addr) prefetch [addr]
+# define HAVE_PREFETCH
+#else
+// no prefetch -- too bad for poor MMX-only souls
+# define PREFETCH(addr)
+# undef HAVE_PREFETCH
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300)
+# pragma warning( disable : 4799 )
+#endif
+
+static inline void
+SCALE_(PlatInit) (void)
+{
+ __asm
+ {
+ // mm0 will be kept == 0 throughout
+ // 0 is needed for bytes->words unpack instructions
+ pxor mm0, mm0
+ }
+}
+
+static inline void
+SCALE_(PlatDone) (void)
+{
+ // finish with MMX registers and yield them to FPU
+ __asm
+ {
+ emms
+ }
+}
+
+#if defined(HAVE_PREFETCH)
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ __asm
+ {
+ mov eax, p
+ PREFETCH (eax)
+ }
+}
+
+#else /* Not HAVE_PREFETCH */
+
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ (void)p; // silence compiler
+ /* no-op */
+}
+
+#endif /* HAVE_PREFETCH */
+
+// compute the RGB distance squared between 2 pixels
+static inline int
+SCALE_(GetRGBDelta) (Uint32 pix1, Uint32 pix2)
+{
+ __asm
+ {
+ // load pixels
+ movd mm1, pix1
+ punpcklbw mm1, mm0
+ movd mm2, pix2
+ punpcklbw mm2, mm0
+ // get the difference between RGBA components
+ psubw mm1, mm2
+ // squared and sumed
+ pmaddwd mm1, mm1
+ // finish suming the squares
+ movq mm2, mm1
+ punpckhdq mm2, mm0
+ paddd mm1, mm2
+ // store result
+ movd eax, mm1
+ }
+}
+
+// retrieve the Y (intensity) component of pixel's YUV
+static inline int
+SCALE_(GetPixY) (Uint32 pix)
+{
+ __asm
+ {
+ // load pixel
+ movd mm1, pix
+ punpcklbw mm1, mm0
+ // process
+ pmaddwd mm1, mmx_Y_mult // RGB * Yvec
+ movq mm2, mm1 // finish suming
+ punpckhdq mm2, mm0 // ditto
+ paddd mm1, mm2 // ditto
+ // store result
+ movd eax, mm1
+ shr eax, 14
+ }
+}
+
+#ifdef USE_YUV_LOOKUP
+
+// convert pixel RGB vector into YUV representation vector
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (Uint32 pix)
+{
+ __asm
+ {
+ // convert RGB888 to 555
+ movd mm1, pix
+ punpcklbw mm1, mm0
+ psrlw mm1, 3 // 8->5 bit
+ pmaddwd mm1, mmx_888to555_mult // shuffle into the right channel order
+ movq mm2, mm1 // finish shuffling
+ punpckhdq mm2, mm0 // ditto
+ por mm1, mm2 // ditto
+
+ // lookup the YUV vector
+ movd eax, mm1
+ mov eax, [RGB15_to_YUV + eax * 4]
+ }
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+static inline bool
+SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler)
+{
+ __asm
+ {
+ // convert RGB888 to 555
+ movd mm1, pix1
+ punpcklbw mm1, mm0
+ psrlw mm1, 3 // 8->5 bit
+ movd mm3, pix2
+ punpcklbw mm3, mm0
+ psrlw mm3, 3 // 8->5 bit
+ pmaddwd mm1, mmx_888to555_mult // shuffle into the right channel order
+ movq mm2, mm1 // finish shuffling
+ pmaddwd mm3, mmx_888to555_mult // shuffle into the right channel order
+ movq mm4, mm3 // finish shuffling
+ punpckhdq mm2, mm0 // ditto
+ por mm1, mm2 // ditto
+ punpckhdq mm4, mm0 // ditto
+ por mm3, mm4 // ditto
+
+ // lookup the YUV vector
+ movd eax, mm1
+ movd edx, mm3
+ movd mm1, [RGB15_to_YUV + eax * 4]
+ movq mm4, mm1
+ movd mm2, [RGB15_to_YUV + edx * 4]
+
+ // get abs difference between YUV components
+#ifdef USE_PSADBW
+ // we can use PSADBW and save us some grief
+ psadbw mm1, mm2
+ movd edx, mm1
+#else
+ // no PSADBW -- have to do it the hard way
+ psubusb mm1, mm2
+ psubusb mm2, mm4
+ por mm1, mm2
+
+ // sum the differences
+ // XXX: technically, this produces a MAX diff of 510
+ // but we do not need anything bigger, currently
+ movq mm2, mm1
+ psrlq mm2, 8
+ paddusb mm1, mm2
+ psrlq mm2, 8
+ paddusb mm1, mm2
+ movd edx, mm1
+ and edx, 0xff
+#endif /* USE_PSADBW */
+ xor eax, eax
+ shl edx, 1
+ cmp edx, toler
+ // store result
+ setle al
+ }
+}
+
+#else /* Not USE_YUV_LOOKUP */
+
+// convert pixel RGB vector into YUV representation vector
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (Uint32 pix)
+{
+ __asm
+ {
+ movd mm1, pix
+ punpcklbw mm1, mm0
+
+ movq mm2, mm1
+
+ // Y vector multiply
+ pmaddwd mm1, mmx_Y_mult
+ movq mm4, mm1
+ punpckhdq mm4, mm0
+ punpckldq mm1, mm0 // clear out the high dword
+ paddd mm1, mm4
+ psrad mm1, 15
+
+ movq mm3, mm2
+
+ // U vector multiply
+ pmaddwd mm2, mmx_U_mult
+ psrad mm2, 10
+
+ // V vector multiply
+ pmaddwd mm3, mmx_V_mult
+ psrad mm3, 10
+
+ // load (1|1|1|1) into mm4
+ pcmpeqw mm4, mm4
+ psrlw mm4, 15
+
+ packssdw mm3, mm2
+ pmaddwd mm3, mm4
+ psrad mm3, 5
+
+ // load (64|64) into mm4
+ punpcklwd mm4, mm0
+ pslld mm4, 6
+ paddd mm3, mm4
+
+ packssdw mm3, mm1
+ packuswb mm3, mm0
+
+ movd eax, mm3
+ }
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+static inline bool
+SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler)
+{
+ __asm
+ {
+ movd mm1, pix1
+ punpcklbw mm1, mm0
+ movd mm2, pix2
+ punpcklbw mm2, mm0
+
+ psubw mm1, mm2
+ movq mm2, mm1
+
+ // Y vector multiply
+ pmaddwd mm1, mmx_Y_mult
+ movq mm4, mm1
+ punpckhdq mm4, mm0
+ paddd mm1, mm4
+ // abs()
+ movq mm4, mm1
+ psrad mm4, 31
+ pxor mm4, mm1
+ psubd mm1, mm4
+
+ movq mm3, mm2
+
+ // U vector multiply
+ pmaddwd mm2, mmx_U_mult
+ movq mm4, mm2
+ punpckhdq mm4, mm0
+ paddd mm2, mm4
+ // abs()
+ movq mm4, mm2
+ psrad mm4, 31
+ pxor mm4, mm2
+ psubd mm2, mm4
+
+ paddd mm1, mm2
+
+ // V vector multiply
+ pmaddwd mm3, mmx_V_mult
+ movq mm4, mm3
+ punpckhdq mm3, mm0
+ paddd mm3, mm4
+ // abs()
+ movq mm4, mm3
+ psrad mm4, 31
+ pxor mm4, mm3
+ psubd mm3, mm4
+
+ paddd mm1, mm3
+
+ movd edx, mm1
+ xor eax, eax
+ shr edx, 14
+ cmp edx, toler
+ // store result
+ setle al
+ }
+}
+
+#endif /* USE_YUV_LOOKUP */
+
+// Check if 2 pixels are different with respect to their
+// YUV representations
+// returns 0: close; ~0: distant
+static inline int
+SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2)
+{
+ __asm
+ {
+ // load YUV pixels
+ movd mm1, yuv1
+ movq mm4, mm1
+ movd mm2, yuv2
+ // abs difference between channels
+ psubusb mm1, mm2
+ psubusb mm2, mm4
+ por mm1, mm2
+ // compare to threshold
+ psubusb mm1, mmx_YUV_threshold
+
+ movd edx, mm1
+ // transform eax to 0 or ~0
+ xor eax, eax
+ or edx, edx
+ setz al
+ dec eax
+ }
+}
+
+// bilinear weighted blend of four pixels
+// MSVC asm version
+static inline void
+SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1,
+ Uint32* dst_p, Uint32 dlen)
+{
+ __asm
+ {
+ // EL0: setup vars
+ mov ebx, row0 // EL0
+
+ // EL0: load pixels
+ movq mm1, [ebx] // EL0
+ movq mm2, mm1 // EL0: p[1] -> mm2
+ PREFETCH (ebx + 0x80)
+ punpckhbw mm2, mm0 // EL0: p[1] -> mm2
+ mov ebx, row1
+ punpcklbw mm1, mm0 // EL0: p[0] -> mm1
+ movq mm3, [ebx]
+ movq mm4, mm3 // EL0: p[3] -> mm4
+ movq mm6, mm2 // EL1.1: p[1] -> mm6
+ PREFETCH (ebx + 0x80)
+ punpcklbw mm3, mm0 // EL0: p[2] -> mm3
+ movq mm5, mm1 // EL1.1: p[0] -> mm5
+ punpckhbw mm4, mm0 // EL0: p[3] -> mm4
+
+ mov edi, dst_p // EL0
+
+ // EL1: cache p[0] + 3*(p[1] + p[2]) + p[3] in mm6
+ paddw mm6, mm3 // EL1.2: p[1] + p[2] -> mm6
+ // EL1: cache p[0] + p[1] + p[2] + p[3] in mm7
+ movq mm7, mm6 // EL1.3: p[1] + p[2] -> mm7
+ // EL1: cache p[1] + 3*(p[0] + p[3]) + p[2] in mm5
+ paddw mm5, mm4 // EL1.2: p[0] + p[3] -> mm5
+ psllw mm6, 1 // EL1.4: 2*(p[1] + p[2]) -> mm6
+ paddw mm7, mm5 // EL1.4: sum(p[]) -> mm7
+ psllw mm5, 1 // EL1.5: 2*(p[0] + p[3]) -> mm5
+ paddw mm6, mm7 // EL1.5: p[0] + 3*(p[1] + p[2]) + p[3] -> mm6
+ paddw mm5, mm7 // EL1.6: p[1] + 3*(p[0] + p[3]) + p[2] -> mm5
+
+ // EL2: pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16
+ psllw mm1, 3 // EL2.1: 8*p[0] -> mm1
+ paddw mm1, mm6 // EL2.2: 9*p[0] + 3*(p[1] + p[2]) + p[3] -> mm1
+ psrlw mm1, 4 // EL2.3: sum[0]/16 -> mm1
+
+ mov edx, dlen // EL0
+
+ // EL3: pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16
+ psllw mm2, 3 // EL3.1: 8*p[1] -> mm2
+ paddw mm2, mm5 // EL3.2: 9*p[1] + 3*(p[0] + p[3]) + p[2] -> mm2
+ psrlw mm2, 4 // EL3.3: sum[1]/16 -> mm5
+
+ // EL2/3: store pixels 0 & 1
+ packuswb mm1, mm2 // EL2/3: pack into bytes
+ MOVNTQ (edi, mm1) // EL2/3: store 2 pixels
+
+ // EL4: pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16
+ psllw mm3, 3 // EL4.1: 8*p[2] -> mm3
+ paddw mm3, mm5 // EL4.2: 9*p[2] + 3*(p[0] + p[3]) + p[1] -> mm3
+ psrlw mm3, 4 // EL4.3: sum[2]/16 -> mm3
+
+ // EL5: pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16
+ psllw mm4, 3 // EL5.1: 8*p[3] -> mm4
+ paddw mm4, mm6 // EL5.2: 9*p[3] + 3*(p[1] + p[2]) + p[0] -> mm4
+ psrlw mm4, 4 // EL5.3: sum[3]/16 -> mm4
+
+ // EL4/5: store pixels 2 & 3
+ packuswb mm3, mm4 // EL4/5: pack into bytes
+ MOVNTQ (edi + edx*4, mm3) // EL4/5: store 2 pixels
+ }
+}
+// End MSVC_ASM
+
+#elif defined(GCC_ASM)
+// GCC inline assembly versions
+
+#if defined(USE_MOVNTQ)
+# define MOVNTQ(val, addr) "movntq " #val "," #addr
+#else
+# define MOVNTQ(val, addr) "movq " #val "," #addr
+#endif
+
+#if USE_PREFETCH == INTEL_PREFETCH
+// using Intel SSE non-temporal prefetch
+# define PREFETCH(addr) "prefetchnta " #addr
+#elif USE_PREFETCH == AMD_PREFETCH
+// using AMD 3DNOW! prefetch
+# define PREFETCH(addr) "prefetch " #addr
+#else
+// no prefetch -- too bad for poor MMX-only souls
+# define PREFETCH(addr)
+#endif
+
+#if defined(__x86_64__)
+# define A_REG "rax"
+# define D_REG "rdx"
+# define CLR_UPPER32(r) "xor " "%%" r "," "%%" r
+#else
+# define A_REG "eax"
+# define D_REG "edx"
+# define CLR_UPPER32(r)
+#endif
+
+static inline void
+SCALE_(PlatInit) (void)
+{
+ __asm__ (
+ // mm0 will be kept == 0 throughout
+ // 0 is needed for bytes->words unpack instructions
+ "pxor %%mm0, %%mm0 \n\t"
+
+ : /* nothing */
+ : /* nothing */
+ );
+}
+
+static inline void
+SCALE_(PlatDone) (void)
+{
+ // finish with MMX registers and yield them to FPU
+ __asm__ (
+ "emms \n\t"
+ : /* nothing */ : /* nothing */
+ );
+}
+
+static inline void
+SCALE_(Prefetch) (const void* p)
+{
+ __asm__ __volatile__ ("" PREFETCH (%0) : /*nothing*/ : "m" (p) );
+}
+
+// compute the RGB distance squared between 2 pixels
+static inline int
+SCALE_(GetRGBDelta) (Uint32 pix1, Uint32 pix2)
+{
+ int res;
+
+ __asm__ (
+ // load pixels
+ "movd %1, %%mm1 \n\t"
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ "movd %2, %%mm2 \n\t"
+ "punpcklbw %%mm0, %%mm2 \n\t"
+ // get the difference between RGBA components
+ "psubw %%mm2, %%mm1 \n\t"
+ // squared and sumed
+ "pmaddwd %%mm1, %%mm1 \n\t"
+ // finish suming the squares
+ "movq %%mm1, %%mm2 \n\t"
+ "punpckhdq %%mm0, %%mm2 \n\t"
+ "paddd %%mm2, %%mm1 \n\t"
+ // store result
+ "movd %%mm1, %0 \n\t"
+
+ : /*0*/"=rm" (res)
+ : /*1*/"rm" (pix1), /*2*/"rm" (pix2)
+ );
+
+ return res;
+}
+
+// retrieve the Y (intensity) component of pixel's YUV
+static inline int
+SCALE_(GetPixY) (Uint32 pix)
+{
+ int ret;
+
+ __asm__ (
+ // load pixel
+ "movd %1, %%mm1 \n\t"
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ // process
+ "pmaddwd %2, %%mm1 \n\t" // R,G,B * Yvec
+ "movq %%mm1, %%mm2 \n\t" // finish suming
+ "punpckhdq %%mm0, %%mm2 \n\t" // ditto
+ "paddd %%mm2, %%mm1 \n\t" // ditto
+ // store index
+ "movd %%mm1, %0 \n\t"
+
+ : /*0*/"=r" (ret)
+ : /*1*/"rm" (pix), /*2*/"m" (mmx_Y_mult)
+ );
+ return ret >> 14;
+}
+
+#ifdef USE_YUV_LOOKUP
+
+// convert pixel RGB vector into YUV representation vector
+static inline YUV_VECTOR
+SCALE_(RGBtoYUV) (Uint32 pix)
+{
+ int i;
+
+ __asm__ (
+ // convert RGB888 to 555
+ "movd %1, %%mm1 \n\t"
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ "psrlw $3, %%mm1 \n\t" // 8->5 bit
+ "pmaddwd %2, %%mm1 \n\t" // shuffle into the right channel order
+ "movq %%mm1, %%mm2 \n\t" // finish shuffling
+ "punpckhdq %%mm0, %%mm2 \n\t" // ditto
+ "por %%mm2, %%mm1 \n\t" // ditto
+ "movd %%mm1, %0 \n\t"
+
+ : /*0*/"=rm" (i)
+ : /*1*/"rm" (pix), /*2*/"m" (mmx_888to555_mult)
+ );
+ return RGB15_to_YUV[i];
+}
+
+// compare 2 pixels with respect to their YUV representations
+// tolerance set by toler arg
+// returns true: close; false: distant (-gt toler)
+static inline bool
+SCALE_(CmpYUV) (Uint32 pix1, Uint32 pix2, int toler)
+{
+ int delta;
+
+ __asm__ (
+ "movd %1, %%mm1 \n\t"
+ "movd %2, %%mm3 \n\t"
+
+ // convert RGB888 to 555
+ // this is somewhat parallelized
+ "punpcklbw %%mm0, %%mm1 \n\t"
+ CLR_UPPER32 (A_REG) "\n\t"
+ "psrlw $3, %%mm1 \n\t" // 8->5 bit
+ "punpcklbw %%mm0, %%mm3 \n\t"
+ "psrlw $3, %%mm3 \n\t" // 8->5 bit
+ "pmaddwd %4, %%mm1 \n\t" // shuffle into the right channel order
+ "movq %%mm1, %%mm2 \n\t" // finish shuffling
+ "pmaddwd %4, %%mm3 \n\t" // shuffle into the right channel order
+ CLR_UPPER32 (D_REG) "\n\t"
+ "movq %%mm3, %%mm4 \n\t" // finish shuffling
+ "punpckhdq %%mm0, %%mm2 \n\t" // ditto
+ "por %%mm2, %%mm1 \n\t" // ditto
+ "punpckhdq %%mm0, %%mm4 \n\t" // ditto
+ "por %%mm4, %%mm3 \n\t" // ditto
+
+ // lookup the YUV vector
+ "movd %%mm1, %%eax \n\t"
+ "movd %%mm3, %%edx \n\t"
+ "movd (%3, %%" A_REG ", 4), %%mm1 \n\t"
+ "movq %%mm1, %%mm4 \n\t"
+ "movd (%3, %%" D_REG ", 4), %%mm2 \n\t"
+
+ // get abs difference between YUV components
+#ifdef USE_PSADBW
+ // we can use PSADBW and save us some grief
+ "psadbw %%mm2, %%mm1 \n\t"
+ "movd %%mm1, %0 \n\t"
+#else
+ // no PSADBW -- have to do it the hard way
+ "psubusb %%mm2, %%mm1 \n\t"
+ "psubusb %%mm4, %%mm2 \n\t"
+ "por %%mm2, %%mm1 \n\t"
+
+ // sum the differences
+ // technically, this produces a MAX diff of 510
+ // but we do not need anything bigger, currently
+ "movq %%mm1, %%mm2 \n\t"
+ "psrlq $8, %%mm2 \n\t"
+ "paddusb %%mm2, %%mm1 \n\t"
+ "psrlq $8, %%mm2 \n\t"
+ "paddusb %%mm2, %%mm1 \n\t"
+ // store intermediate delta
+ "movd %%mm1, %0 \n\t"
+ "andl $0xff, %0 \n\t"
+#endif /* USE_PSADBW */
+ : /*0*/"=rm" (delta)
+ : /*1*/"rm" (pix1), /*2*/"rm" (pix2),
+ /*3*/ "r" (RGB15_to_YUV),
+ /*4*/"m" (mmx_888to555_mult)
+ : "%" A_REG, "%" D_REG, "cc"
+ );
+
+ return (delta << 1) <= toler;
+}
+
+#endif /* USE_YUV_LOOKUP */
+
+// Check if 2 pixels are different with respect to their
+// YUV representations
+// returns 0: close; ~0: distant
+static inline int
+SCALE_(DiffYUV) (Uint32 yuv1, Uint32 yuv2)
+{
+ sint32 ret;
+
+ __asm__ (
+ // load YUV pixels
+ "movd %1, %%mm1 \n\t"
+ "movq %%mm1, %%mm4 \n\t"
+ "movd %2, %%mm2 \n\t"
+ // abs difference between channels
+ "psubusb %%mm2, %%mm1 \n\t"
+ "psubusb %%mm4, %%mm2 \n\t"
+ CLR_UPPER32(D_REG) "\n\t"
+ "por %%mm2, %%mm1 \n\t"
+ // compare to threshold
+ "psubusb %3, %%mm1 \n\t"
+
+ "movd %%mm1, %%edx \n\t"
+ // transform eax to 0 or ~0
+ "xor %%" A_REG ", %%" A_REG "\n\t"
+ "or %%" D_REG ", %%" D_REG "\n\t"
+ "setz %%al \n\t"
+ "dec %%" A_REG " \n\t"
+
+ : /*0*/"=a" (ret)
+ : /*1*/"rm" (yuv1), /*2*/"rm" (yuv2),
+ /*3*/"m" (mmx_YUV_threshold)
+ : "%" D_REG, "cc"
+ );
+ return ret;
+}
+
+// Bilinear weighted blend of four pixels
+// Function produces 4 blended pixels (in 2x2 matrix) and writes them
+// out to the surface
+// Last version
+static inline void
+SCALE_(Blend_bilinear) (const Uint32* row0, const Uint32* row1,
+ Uint32* dst_p, Uint32 dlen)
+{
+ __asm__ (
+ // EL0: load pixels
+ "movq %0, %%mm1 \n\t" // EL0
+ "movq %%mm1, %%mm2 \n\t" // EL0: p[1] -> mm2
+ PREFETCH (0x80%0) "\n\t"
+ "punpckhbw %%mm0, %%mm2 \n\t" // EL0: p[1] -> mm2
+ "punpcklbw %%mm0, %%mm1 \n\t" // EL0: p[0] -> mm1
+ "movq %1, %%mm3 \n\t"
+ "movq %%mm3, %%mm4 \n\t" // EL0: p[3] -> mm4
+ "movq %%mm2, %%mm6 \n\t" // EL1.1: p[1] -> mm6
+ PREFETCH (0x80%1) "\n\t"
+ "punpcklbw %%mm0, %%mm3 \n\t" // EL0: p[2] -> mm3
+ "movq %%mm1, %%mm5 \n\t" // EL1.1: p[0] -> mm5
+ "punpckhbw %%mm0, %%mm4 \n\t" // EL0: p[3] -> mm4
+
+ // EL1: cache p[0] + 3*(p[1] + p[2]) + p[3] in mm6
+ "paddw %%mm3, %%mm6 \n\t" // EL1.2: p[1] + p[2] -> mm6
+ // EL1: cache p[0] + p[1] + p[2] + p[3] in mm7
+ "movq %%mm6, %%mm7 \n\t" // EL1.3: p[1] + p[2] -> mm7
+ // EL1: cache p[1] + 3*(p[0] + p[3]) + p[2] in mm5
+ "paddw %%mm4, %%mm5 \n\t" // EL1.2: p[0] + p[3] -> mm5
+ "psllw $1, %%mm6 \n\t" // EL1.4: 2*(p[1] + p[2]) -> mm6
+ "paddw %%mm5, %%mm7 \n\t" // EL1.4: sum(p[]) -> mm7
+ "psllw $1, %%mm5 \n\t" // EL1.5: 2*(p[0] + p[3]) -> mm5
+ "paddw %%mm7, %%mm6 \n\t" // EL1.5: p[0] + 3*(p[1] + p[2]) + p[3] -> mm6
+ "paddw %%mm7, %%mm5 \n\t" // EL1.6: p[1] + 3*(p[0] + p[3]) + p[2] -> mm5
+
+ // EL2: pixel 0 math -- (9*p[0] + 3*(p[1] + p[2]) + p[3]) / 16
+ "psllw $3, %%mm1 \n\t" // EL2.1: 8*p[0] -> mm1
+ "paddw %%mm6, %%mm1 \n\t" // EL2.2: 9*p[0] + 3*(p[1] + p[2]) + p[3] -> mm1
+ "psrlw $4, %%mm1 \n\t" // EL2.3: sum[0]/16 -> mm1
+
+ // EL3: pixel 1 math -- (9*p[1] + 3*(p[0] + p[3]) + p[2]) / 16
+ "psllw $3, %%mm2 \n\t" // EL3.1: 8*p[1] -> mm2
+ "paddw %%mm5, %%mm2 \n\t" // EL3.2: 9*p[1] + 3*(p[0] + p[3]) + p[2] -> mm5
+ "psrlw $4, %%mm2 \n\t" // EL3.3: sum[1]/16 -> mm5
+
+ // EL2/4: store pixels 0 & 1
+ "packuswb %%mm2, %%mm1 \n\t" // EL2/4: pack into bytes
+ MOVNTQ (%%mm1, (%2)) "\n\t" // EL2/4: store 2 pixels
+
+ // EL4: pixel 2 math -- (9*p[2] + 3*(p[0] + p[3]) + p[1]) / 16
+ "psllw $3, %%mm3 \n\t" // EL4.1: 8*p[2] -> mm3
+ "paddw %%mm5, %%mm3 \n\t" // EL4.2: 9*p[2] + 3*(p[0] + p[3]) + p[1] -> mm3
+ "psrlw $4, %%mm3 \n\t" // EL4.3: sum[2]/16 -> mm3
+
+ // EL5: pixel 3 math -- (9*p[3] + 3*(p[1] + p[2]) + p[0]) / 16
+ "psllw $3, %%mm4 \n\t" // EL5.1: 8*p[3] -> mm4
+ "paddw %%mm6, %%mm4 \n\t" // EL5.2: 9*p[3] + 3*(p[1] + p[2]) + p[0] -> mm4
+ "psrlw $4, %%mm4 \n\t" // EL5.3: sum[3]/16 -> mm4
+
+ // EL4/5: store pixels 2 & 3
+ "packuswb %%mm4, %%mm3 \n\t" // EL4/5: pack into bytes
+ MOVNTQ (%%mm3, (%2,%3,4)) "\n\t" // EL4/5: store 2 pixels
+
+ : /* nothing */
+ : /*0*/"m" (*row0), /*1*/"m" (*row1), /*2*/"r" (dst_p),
+ /*3*/"r" ((unsigned long)dlen) /* 'long' is for proper reg alloc on amd64 */
+ : "memory"
+ );
+}
+
+#undef A_REG
+#undef D_REG
+#undef CLR_UPPER32
+
+#endif // GCC_ASM
+
+#endif /* SCALEMMX_H_ */
diff --git a/src/libs/graphics/sdl/scalers.c b/src/libs/graphics/sdl/scalers.c
new file mode 100644
index 0000000..751dae3
--- /dev/null
+++ b/src/libs/graphics/sdl/scalers.c
@@ -0,0 +1,289 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "types.h"
+#include "libs/graphics/sdl/sdl_common.h"
+#include "libs/platform.h"
+#include "libs/log.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+#ifdef USE_PLATFORM_ACCEL
+# ifndef __APPLE__
+ // MacOS X framework has no SDL_cpuinfo.h for some reason
+# include SDL_INCLUDE(SDL_cpuinfo.h)
+# endif
+# ifdef MMX_ASM
+# include "2xscalers_mmx.h"
+# endif /* MMX_ASM */
+#endif /* USE_PLATFORM_ACCEL */
+
+#if SDL_MAJOR_VERSION == 1
+#define SDL_HasMMX SDL_HasMMXExt
+#endif
+
+typedef enum
+{
+ SCALEPLAT_NULL = PLATFORM_NULL,
+ SCALEPLAT_C = PLATFORM_C,
+ SCALEPLAT_MMX = PLATFORM_MMX,
+ SCALEPLAT_SSE = PLATFORM_SSE,
+ SCALEPLAT_3DNOW = PLATFORM_3DNOW,
+ SCALEPLAT_ALTIVEC = PLATFORM_ALTIVEC,
+
+ SCALEPLAT_C_RGBA,
+ SCALEPLAT_C_BGRA,
+ SCALEPLAT_C_ARGB,
+ SCALEPLAT_C_ABGR,
+
+} Scale_PlatType_t;
+
+
+// RGB -> YUV transformation
+// the RGB vector is multiplied by the transformation matrix
+// to get the YUV vector
+#if 0
+// original table -- not used
+const int YUV_matrix[3][3] =
+{
+ /* Y U V */
+ /* R */ {0.2989, -0.1687, 0.5000},
+ /* G */ {0.5867, -0.3312, -0.4183},
+ /* B */ {0.1144, 0.5000, -0.0816}
+};
+#else
+// scaled up by a 2^14 factor, with Y doubled
+const int YUV_matrix[3][3] =
+{
+ /* Y U V */
+ /* R */ { 9794, -2764, 8192},
+ /* G */ {19224, -5428, -6853},
+ /* B */ { 3749, 8192, -1339}
+};
+#endif
+
+// pre-computed transformations for 8 bits per channel
+int RGB_to_YUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 256];
+sint16 dRGB_to_dYUV[/*RGB*/ 3][/*YUV*/ 3][ /*mult-res*/ 512];
+
+// pre-computed transformations for RGB555
+YUV_VECTOR RGB15_to_YUV[0x8000];
+
+PLATFORM_TYPE force_platform = PLATFORM_NULL;
+Scale_PlatType_t Scale_Platform = SCALEPLAT_NULL;
+
+
+// pre-compute the RGB->YUV transformations
+void
+Scale_Init (void)
+{
+ int i1, i2, i3;
+
+ for (i1 = 0; i1 < 3; i1++) // enum R,G,B
+ for (i2 = 0; i2 < 3; i2++) // enum Y,U,V
+ for (i3 = 0; i3 < 256; i3++) // enum possible channel vals
+ {
+ RGB_to_YUV[i1][i2][i3] =
+ (YUV_matrix[i1][i2] * i3) >> 14;
+ }
+
+ for (i1 = 0; i1 < 3; i1++) // enum R,G,B
+ for (i2 = 0; i2 < 3; i2++) // enum Y,U,V
+ for (i3 = -255; i3 < 256; i3++) // enum possible channel delta vals
+ {
+ dRGB_to_dYUV[i1][i2][i3 + 255] =
+ (YUV_matrix[i1][i2] * i3) >> 14;
+ }
+
+ for (i1 = 0; i1 < 32; ++i1)
+ for (i2 = 0; i2 < 32; ++i2)
+ for (i3 = 0; i3 < 32; ++i3)
+ {
+ int y, u, v;
+ // adding upper bits halved for error correction
+ int r = (i1 << 3) | (i1 >> 3);
+ int g = (i2 << 3) | (i2 >> 3);
+ int b = (i3 << 3) | (i3 >> 3);
+
+ y = ( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_Y]
+ + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_Y]
+ + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_Y]
+ ) >> 15; // we dont need Y doubled, need Y to fit 8 bits
+
+ // U and V are half the importance of Y
+ u = 64+(( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_U]
+ + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_U]
+ + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_U]
+ ) >> 15); // halved
+
+ v = 64+(( r * YUV_matrix[YUV_XFORM_R][YUV_XFORM_V]
+ + g * YUV_matrix[YUV_XFORM_G][YUV_XFORM_V]
+ + b * YUV_matrix[YUV_XFORM_B][YUV_XFORM_V]
+ ) >> 15); // halved
+
+ RGB15_to_YUV[(i1 << 10) | (i2 << 5) | i3] = (y << 16) | (u << 8) | v;
+ }
+}
+
+
+// expands the given rectangle in all directions by 'expansion'
+// guarded by 'limits'
+void
+Scale_ExpandRect (SDL_Rect* rect, int expansion, const SDL_Rect* limits)
+{
+ if (rect->x - expansion >= limits->x)
+ {
+ rect->w += expansion;
+ rect->x -= expansion;
+ }
+ else
+ {
+ rect->w += rect->x - limits->x;
+ rect->x = limits->x;
+ }
+
+ if (rect->y - expansion >= limits->y)
+ {
+ rect->h += expansion;
+ rect->y -= expansion;
+ }
+ else
+ {
+ rect->h += rect->y - limits->y;
+ rect->y = limits->y;
+ }
+
+ if (rect->x + rect->w + expansion <= limits->w)
+ rect->w += expansion;
+ else
+ rect->w = limits->w - rect->x;
+
+ if (rect->y + rect->h + expansion <= limits->h)
+ rect->h += expansion;
+ else
+ rect->h = limits->h - rect->y;
+}
+
+
+// Platform+Scaler function lookups
+
+typedef struct
+{
+ Scale_PlatType_t platform;
+ const Scale_FuncDef_t* funcdefs;
+} Scale_PlatDef_t;
+
+
+static const Scale_PlatDef_t
+Scale_PlatDefs[] =
+{
+#if defined(MMX_ASM)
+ {SCALEPLAT_SSE, Scale_SSE_Functions},
+ {SCALEPLAT_3DNOW, Scale_3DNow_Functions},
+ {SCALEPLAT_MMX, Scale_MMX_Functions},
+#endif /* MMX_ASM */
+ // Default
+ {SCALEPLAT_NULL, Scale_C_Functions}
+};
+
+
+TFB_ScaleFunc
+Scale_PrepPlatform (int flags, const SDL_PixelFormat* fmt)
+{
+ const Scale_PlatDef_t* pdef;
+ const Scale_FuncDef_t* fdef;
+
+ (void)flags;
+
+ Scale_Platform = SCALEPLAT_NULL;
+
+ // first match wins
+ // add better platform techs to the top
+#ifdef MMX_ASM
+ if ( (!force_platform && (SDL_HasSSE () || SDL_HasMMX ()))
+ || force_platform == PLATFORM_SSE)
+ {
+ log_add (log_Info, "Screen scalers are using SSE/MMX-Ext/MMX code");
+ Scale_Platform = SCALEPLAT_SSE;
+
+ Scale_SSE_PrepPlatform (fmt);
+ }
+ else
+ if ( (!force_platform && SDL_HasAltiVec ())
+ || force_platform == PLATFORM_ALTIVEC)
+ {
+ log_add (log_Info, "Screen scalers would use AltiVec code "
+ "if someone actually wrote it");
+ //Scale_Platform = SCALEPLAT_ALTIVEC;
+ }
+ else
+ if ( (!force_platform && SDL_Has3DNow ())
+ || force_platform == PLATFORM_3DNOW)
+ {
+ log_add (log_Info, "Screen scalers are using 3DNow/MMX code");
+ Scale_Platform = SCALEPLAT_3DNOW;
+
+ Scale_3DNow_PrepPlatform (fmt);
+ }
+ else
+ if ( (!force_platform && SDL_HasMMX ())
+ || force_platform == PLATFORM_MMX)
+ {
+ log_add (log_Info, "Screen scalers are using MMX code");
+ Scale_Platform = SCALEPLAT_MMX;
+
+ Scale_MMX_PrepPlatform (fmt);
+ }
+#endif
+
+ if (Scale_Platform == SCALEPLAT_NULL)
+ { // Plain C versions
+ if (fmt->Rmask == 0xff000000 && fmt->Bmask == 0x0000ff00)
+ Scale_Platform = SCALEPLAT_C_RGBA;
+ else if (fmt->Rmask == 0x00ff0000 && fmt->Bmask == 0x000000ff)
+ Scale_Platform = SCALEPLAT_C_ARGB;
+ else if (fmt->Rmask == 0x0000ff00 && fmt->Bmask == 0xff000000)
+ Scale_Platform = SCALEPLAT_C_BGRA;
+ else if (fmt->Rmask == 0x000000ff && fmt->Bmask == 0x00ff0000)
+ Scale_Platform = SCALEPLAT_C_ABGR;
+ else
+ { // use slowest default
+ log_add (log_Warning, "Scale_PrepPlatform(): unknown masks "
+ "(Red %08x, Blue %08x)", fmt->Rmask, fmt->Bmask);
+ Scale_Platform = SCALEPLAT_C;
+ }
+
+ if (Scale_Platform == SCALEPLAT_C)
+ log_add (log_Info, "Screen scalers are using slow generic C code");
+ else
+ log_add (log_Info, "Screen scalers are using optimized C code");
+ }
+
+ // Lookup the scaling function
+ // First find the right platform
+ for (pdef = Scale_PlatDefs;
+ pdef->platform != Scale_Platform && pdef->platform != SCALEPLAT_NULL;
+ ++pdef)
+ ;
+ // Next find the right function
+ for (fdef = pdef->funcdefs;
+ (flags & fdef->flag) != fdef->flag;
+ ++fdef)
+ ;
+
+ return fdef->func;
+}
+
diff --git a/src/libs/graphics/sdl/scalers.h b/src/libs/graphics/sdl/scalers.h
new file mode 100644
index 0000000..cd36fe5
--- /dev/null
+++ b/src/libs/graphics/sdl/scalers.h
@@ -0,0 +1,27 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SCALERS_H_
+#define SCALERS_H_
+
+void Scale_Init (void);
+
+typedef void (* TFB_ScaleFunc) (SDL_Surface *src, SDL_Surface *dst,
+ SDL_Rect *r);
+
+TFB_ScaleFunc Scale_PrepPlatform (int flags, const SDL_PixelFormat* fmt);
+
+#endif /* SCALERS_H_ */
diff --git a/src/libs/graphics/sdl/sdl1_common.c b/src/libs/graphics/sdl/sdl1_common.c
new file mode 100644
index 0000000..5c675e1
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl1_common.c
@@ -0,0 +1,247 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdl_common.h"
+#include "opengl.h"
+#include "pure.h"
+#include "primitives.h"
+#include "options.h"
+#include "uqmversion.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/cmap.h"
+#include "libs/input/sdl/input.h"
+ // for ProcessInputEvent()
+#include "libs/graphics/bbox.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/vidlib.h"
+
+#if SDL_MAJOR_VERSION == 1
+
+static void TFB_PreQuit (void);
+
+void
+TFB_PreInit (void)
+{
+ log_add (log_Info, "Initializing base SDL functionality.");
+ log_add (log_Info, "Using SDL version %d.%d.%d (compiled with "
+ "%d.%d.%d)", SDL_Linked_Version ()->major,
+ SDL_Linked_Version ()->minor, SDL_Linked_Version ()->patch,
+ SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL);
+#if 0
+ if (SDL_Linked_Version ()->major != SDL_MAJOR_VERSION ||
+ SDL_Linked_Version ()->minor != SDL_MINOR_VERSION ||
+ SDL_Linked_Version ()->patch != SDL_PATCHLEVEL) {
+ log_add (log_Warning, "The used SDL library is not the same version "
+ "as the one used to compile The Ur-Quan Masters with! "
+ "If you experience any crashes, this would be an excellent "
+ "suspect.");
+ }
+#endif
+
+ if ((SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE) == -1))
+ {
+ log_add (log_Fatal, "Could not initialize SDL: %s.", SDL_GetError ());
+ exit (EXIT_FAILURE);
+ }
+
+ atexit (TFB_PreQuit);
+}
+
+static void
+TFB_PreQuit (void)
+{
+ SDL_Quit ();
+}
+
+int
+TFB_ReInitGraphics (int driver, int flags, int width, int height)
+{
+ int result;
+ int togglefullscreen = 0;
+ char caption[200];
+
+ if (GfxFlags == (flags ^ TFB_GFXFLAGS_FULLSCREEN) &&
+ driver == GraphicsDriver &&
+ width == ScreenWidthActual && height == ScreenHeightActual)
+ {
+ togglefullscreen = 1;
+ }
+
+ GfxFlags = flags;
+
+ if (driver == TFB_GFXDRIVER_SDL_OPENGL)
+ {
+#ifdef HAVE_OPENGL
+ result = TFB_GL_ConfigureVideo (driver, flags, width, height,
+ togglefullscreen);
+#else
+ driver = TFB_GFXDRIVER_SDL_PURE;
+ log_add (log_Warning, "OpenGL support not compiled in,"
+ " so using pure SDL driver");
+ result = TFB_Pure_ConfigureVideo (driver, flags, width, height,
+ togglefullscreen);
+#endif
+ }
+ else
+ {
+ result = TFB_Pure_ConfigureVideo (driver, flags, width, height,
+ togglefullscreen);
+ }
+
+ sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s",
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ SDL_WM_SetCaption (caption, NULL);
+
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ SDL_ShowCursor (SDL_DISABLE);
+ else
+ SDL_ShowCursor (SDL_ENABLE);
+
+ return result;
+}
+
+bool
+TFB_SetGamma (float gamma)
+{
+ return (SDL_SetGamma (gamma, gamma, gamma) == 0);
+}
+
+int
+TFB_HasSurfaceAlphaMod (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return 0;
+ }
+ return (surface->flags & SDL_SRCALPHA) ? 1 : 0;
+}
+
+int
+TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha)
+{
+ if (!surface || !surface->format || !alpha)
+ {
+ return -1;
+ }
+ if (surface->flags & SDL_SRCALPHA)
+ {
+ *alpha = surface->format->alpha;
+ }
+ else
+ {
+ *alpha = 255;
+ }
+ return 0;
+}
+
+int
+TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetAlpha (surface, SDL_SRCALPHA, alpha);
+}
+
+int
+TFB_DisableSurfaceAlphaMod (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetAlpha (surface, 0, 255);
+}
+
+int
+TFB_GetColorKey (SDL_Surface *surface, Uint32 *key)
+{
+ if (surface && surface->format && key &&
+ (surface->flags & SDL_SRCCOLORKEY))
+ {
+ *key = surface->format->colorkey;
+ return 0;
+ }
+ return -1;
+}
+
+int
+TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetColorKey (surface, SDL_SRCCOLORKEY | (rleaccel ? SDL_RLEACCEL : 0), key);
+}
+
+int
+TFB_DisableColorKey (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetColorKey (surface, 0, 0);
+}
+
+int
+TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors)
+{
+ return SDL_SetColors (surface, colors, firstcolor, ncolors);
+}
+
+int
+TFB_SupportsHardwareScaling (void)
+{
+#ifdef HAVE_OPENGL
+ return 1;
+#else
+ return 0;
+#endif
+}
+
+static SDL_Surface *
+Create_Screen (SDL_Surface *templat, int w, int h)
+{
+ SDL_Surface *newsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
+ templat->format->BitsPerPixel,
+ templat->format->Rmask, templat->format->Gmask,
+ templat->format->Bmask, 0);
+ if (newsurf == 0) {
+ log_add (log_Error, "Couldn't create screen buffes: %s",
+ SDL_GetError());
+ }
+ return newsurf;
+}
+
+int
+SDL1_ReInit_Screen (SDL_Surface **screen, SDL_Surface *templat, int w, int h)
+{
+ UnInit_Screen (screen);
+ *screen = Create_Screen (templat, w, h);
+
+ return *screen == 0 ? -1 : 0;
+}
+#endif
diff --git a/src/libs/graphics/sdl/sdl2_common.c b/src/libs/graphics/sdl/sdl2_common.c
new file mode 100644
index 0000000..3eaf7af
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl2_common.c
@@ -0,0 +1,222 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdl_common.h"
+#include "opengl.h"
+#include "pure.h"
+#include "primitives.h"
+#include "options.h"
+#include "uqmversion.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/cmap.h"
+#include "libs/input/sdl/input.h"
+ // for ProcessInputEvent()
+#include "libs/graphics/bbox.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/vidlib.h"
+
+#if SDL_MAJOR_VERSION > 1
+
+static void TFB_PreQuit (void);
+
+void
+TFB_PreInit (void)
+{
+ SDL_version compiled, linked;
+ SDL_VERSION(&compiled);
+ SDL_GetVersion(&linked);
+ log_add (log_Info, "Initializing base SDL functionality.");
+ log_add (log_Info, "Using SDL version %d.%d.%d (compiled with "
+ "%d.%d.%d)", linked.major, linked.minor, linked.patch,
+ compiled.major, compiled.minor, compiled.patch);
+#if 0
+ if (compiled.major != linked.major || compiled.minor != linked.minor ||
+ compiled.patch != linked.patch)
+ {
+ log_add (log_Warning, "The used SDL library is not the same version "
+ "as the one used to compile The Ur-Quan Masters with! "
+ "If you experience any crashes, this would be an excellent "
+ "suspect.");
+ }
+#endif
+
+ if ((SDL_Init (SDL_INIT_VIDEO) == -1))
+ {
+ log_add (log_Fatal, "Could not initialize SDL: %s.", SDL_GetError ());
+ exit (EXIT_FAILURE);
+ }
+
+ atexit (TFB_PreQuit);
+}
+
+static void
+TFB_PreQuit (void)
+{
+ SDL_Quit ();
+}
+
+int
+TFB_ReInitGraphics (int driver, int flags, int width, int height)
+{
+ int result;
+ int togglefullscreen = 0;
+
+ if (GfxFlags == (flags ^ TFB_GFXFLAGS_FULLSCREEN) &&
+ driver == GraphicsDriver &&
+ width == ScreenWidthActual && height == ScreenHeightActual)
+ {
+ togglefullscreen = 1;
+ }
+
+ GfxFlags = flags;
+
+ result = TFB_Pure_ConfigureVideo (TFB_GFXDRIVER_SDL_PURE, flags,
+ width, height, togglefullscreen);
+
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ SDL_ShowCursor (SDL_DISABLE);
+ else
+ SDL_ShowCursor (SDL_ENABLE);
+
+ return result;
+}
+
+bool
+TFB_SetGamma (float gamma)
+{
+ log_add (log_Warning, "Custom gamma correction is not available in the SDL2 engine.");
+ return 0;
+}
+
+int
+TFB_HasSurfaceAlphaMod (SDL_Surface *surface)
+{
+ SDL_BlendMode blend_mode;
+ if (!surface)
+ {
+ return 0;
+ }
+ if (SDL_GetSurfaceBlendMode (surface, &blend_mode) != 0)
+ {
+ return 0;
+ }
+ return blend_mode == SDL_BLENDMODE_BLEND;
+}
+
+int
+TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha)
+{
+ SDL_BlendMode blend_mode;
+ if (!surface || !alpha)
+ {
+ return -1;
+ }
+ if (SDL_GetSurfaceBlendMode (surface, &blend_mode) == 0)
+ {
+ if (blend_mode == SDL_BLENDMODE_BLEND)
+ {
+ return SDL_GetSurfaceAlphaMod (surface, alpha);
+ }
+ }
+ *alpha = 255;
+ return 0;
+}
+
+int
+TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha)
+{
+ int result;
+ if (!surface)
+ {
+ return -1;
+ }
+ result = SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_BLEND);
+ if (result == 0)
+ {
+ result = SDL_SetSurfaceAlphaMod (surface, alpha);
+ }
+ return result;
+}
+
+int
+TFB_DisableSurfaceAlphaMod (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ SDL_SetSurfaceAlphaMod (surface, 255);
+ return SDL_SetSurfaceBlendMode (surface, SDL_BLENDMODE_NONE);
+}
+
+int
+TFB_GetColorKey (SDL_Surface *surface, Uint32 *key)
+{
+ if (!surface || !key)
+ {
+ return -1;
+ }
+ return SDL_GetColorKey (surface, key);
+}
+
+int
+TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ SDL_SetSurfaceRLE (surface, rleaccel);
+ return SDL_SetColorKey (surface, SDL_TRUE, key);
+}
+
+int
+TFB_DisableColorKey (SDL_Surface *surface)
+{
+ if (!surface)
+ {
+ return -1;
+ }
+ return SDL_SetColorKey (surface, SDL_FALSE, 0);
+}
+
+int
+TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors)
+{
+ if (!surface || !colors || !surface->format || !surface->format->palette)
+ {
+ return 0;
+ }
+ if (SDL_SetPaletteColors (surface->format->palette, colors, firstcolor, ncolors) == 0)
+ {
+ // SDL2's success code is opposite from SDL1's SDL_SetColors
+ return 1;
+ }
+ return 0;
+}
+
+int
+TFB_SupportsHardwareScaling (void)
+{
+ return 1;
+}
+#endif
diff --git a/src/libs/graphics/sdl/sdl2_pure.c b/src/libs/graphics/sdl/sdl2_pure.c
new file mode 100644
index 0000000..c6503db
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl2_pure.c
@@ -0,0 +1,465 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "pure.h"
+#include "libs/graphics/bbox.h"
+#include "libs/log.h"
+#include "scalers.h"
+#include "uqmversion.h"
+
+#if SDL_MAJOR_VERSION > 1
+
+typedef struct tfb_sdl2_screeninfo_s {
+ SDL_Surface *scaled;
+ SDL_Texture *texture;
+ BOOLEAN dirty, active;
+ SDL_Rect updated;
+} TFB_SDL2_SCREENINFO;
+
+static TFB_SDL2_SCREENINFO SDL2_Screens[TFB_GFX_NUMSCREENS];
+
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+static const char *rendererBackend = NULL;
+
+static int ScreenFilterMode;
+
+static TFB_ScaleFunc scaler = NULL;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#define A_MASK 0xff000000
+#define B_MASK 0x00ff0000
+#define G_MASK 0x0000ff00
+#define R_MASK 0x000000ff
+#else
+#define A_MASK 0x000000ff
+#define B_MASK 0x0000ff00
+#define G_MASK 0x00ff0000
+#define R_MASK 0xff000000
+#endif
+
+static void TFB_SDL2_Preprocess (int force_full_redraw, int transition_amount, int fade_amount);
+static void TFB_SDL2_Postprocess (void);
+static void TFB_SDL2_UploadTransitionScreen (void);
+static void TFB_SDL2_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_SDL2_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect);
+static void TFB_SDL2_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+
+static TFB_GRAPHICS_BACKEND sdl2_scaled_backend = {
+ TFB_SDL2_Preprocess,
+ TFB_SDL2_Postprocess,
+ TFB_SDL2_UploadTransitionScreen,
+ TFB_SDL2_Scaled_ScreenLayer,
+ TFB_SDL2_ColorLayer };
+
+static TFB_GRAPHICS_BACKEND sdl2_unscaled_backend = {
+ TFB_SDL2_Preprocess,
+ TFB_SDL2_Postprocess,
+ TFB_SDL2_UploadTransitionScreen,
+ TFB_SDL2_Unscaled_ScreenLayer,
+ TFB_SDL2_ColorLayer };
+
+static SDL_Surface *
+Create_Screen (int w, int h)
+{
+ SDL_Surface *newsurf = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
+ 32, R_MASK, G_MASK, B_MASK, 0);
+ if (newsurf == 0)
+ {
+ log_add (log_Error, "Couldn't create screen buffers: %s",
+ SDL_GetError());
+ }
+ return newsurf;
+}
+
+static int
+ReInit_Screen (SDL_Surface **screen, int w, int h)
+{
+ if (*screen)
+ SDL_FreeSurface (*screen);
+ *screen = Create_Screen (w, h);
+
+ return *screen == 0 ? -1 : 0;
+}
+
+static int
+FindBestRenderDriver (void)
+{
+ int i, n;
+ if (!rendererBackend) {
+ /* If the user has no preference, just let SDL2 choose */
+ return -1;
+ }
+ n = SDL_GetNumRenderDrivers ();
+ log_add (log_Info, "Searching for render driver \"%s\".", rendererBackend);
+
+ for (i = 0; i < n; i++) {
+ SDL_RendererInfo info;
+ if (SDL_GetRenderDriverInfo (i, &info) < 0) {
+ continue;
+ }
+ if (!strcmp(info.name, rendererBackend)) {
+ return i;
+ }
+ log_add (log_Info, "Skipping render driver \"%s\"", info.name);
+ }
+ /* We did not find any accelerated drivers that weren't D3D9.
+ * Return -1 to ask SDL2 to do its best. */
+ log_add (log_Info, "Render driver \"%s\" not available, using system default", rendererBackend);
+ return -1;
+}
+
+int
+TFB_Pure_ConfigureVideo (int driver, int flags, int width, int height, int togglefullscreen)
+{
+ int i;
+ GraphicsDriver = driver;
+ (void) togglefullscreen;
+ if (window == NULL)
+ {
+ SDL_RendererInfo info;
+ char caption[200];
+
+ sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s",
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ window = SDL_CreateWindow (caption,
+ SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ width, height, 0);
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ {
+ /* If we create the window fullscreen, it will have
+ * no icon if and when it becomes windowed. */
+ SDL_SetWindowFullscreen (window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+ }
+ if (!window)
+ {
+ return -1;
+ }
+ renderer = SDL_CreateRenderer (window, FindBestRenderDriver (), 0);
+ if (!renderer)
+ {
+ return -1;
+ }
+ if (SDL_GetRendererInfo (renderer, &info) == 0)
+ {
+ log_add (log_Info, "SDL2 renderer '%s' selected.\n", info.name);
+ }
+ else
+ {
+ log_add (log_Info, "SDL2 renderer had no name.");
+ }
+ SDL_RenderSetLogicalSize (renderer, ScreenWidth, ScreenHeight);
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ SDL2_Screens[i].scaled = NULL;
+ SDL2_Screens[i].texture = NULL;
+ SDL2_Screens[i].dirty = TRUE;
+ SDL2_Screens[i].active = TRUE;
+ if (0 != ReInit_Screen (&SDL_Screens[i], ScreenWidth, ScreenHeight))
+ {
+ return -1;
+ }
+ }
+ SDL2_Screens[1].active = FALSE;
+ SDL_Screen = SDL_Screens[0];
+ TransitionScreen = SDL_Screens[2];
+ format_conv_surf = SDL_CreateRGBSurface(SDL_SWSURFACE, 0, 0,
+ 32, R_MASK, G_MASK, B_MASK, A_MASK);
+ if (!format_conv_surf)
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ {
+ SDL_SetWindowFullscreen (window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+ }
+ else
+ {
+ SDL_SetWindowFullscreen (window, 0);
+ SDL_SetWindowSize (window, width, height);
+ }
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_ANY)
+ {
+ /* Linear scaling */
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
+ }
+ else
+ {
+ /* Nearest-neighbor scaling */
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
+ }
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_SOFT_ONLY)
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (!SDL2_Screens[i].active)
+ {
+ continue;
+ }
+ if (0 != ReInit_Screen(&SDL2_Screens[i].scaled,
+ ScreenWidth * 2, ScreenHeight * 2))
+ {
+ return -1;
+ }
+ if (SDL2_Screens[i].texture)
+ {
+ SDL_DestroyTexture (SDL2_Screens[i].texture);
+ SDL2_Screens[i].texture = NULL;
+ }
+ SDL2_Screens[i].texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, ScreenWidth * 2, ScreenHeight * 2);
+ SDL_LockSurface (SDL2_Screens[i].scaled);
+ SDL_UpdateTexture (SDL2_Screens[i].texture, NULL, SDL2_Screens[i].scaled->pixels, SDL2_Screens[i].scaled->pitch);
+ SDL_UnlockSurface (SDL2_Screens[i].scaled);
+ }
+ scaler = Scale_PrepPlatform (flags, SDL2_Screens[0].scaled->format);
+ graphics_backend = &sdl2_scaled_backend;
+ }
+ else
+ {
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ if (SDL2_Screens[i].scaled)
+ {
+ SDL_FreeSurface (SDL2_Screens[i].scaled);
+ SDL2_Screens[i].scaled = NULL;
+ }
+ if (SDL2_Screens[i].texture)
+ {
+ SDL_DestroyTexture (SDL2_Screens[i].texture);
+ SDL2_Screens[i].texture = NULL;
+ }
+ SDL2_Screens[i].texture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_RGBX8888, SDL_TEXTUREACCESS_STREAMING, ScreenWidth, ScreenHeight);
+ SDL_LockSurface (SDL_Screens[i]);
+ SDL_UpdateTexture (SDL2_Screens[i].texture, NULL, SDL_Screens[i]->pixels, SDL_Screens[i]->pitch);
+ SDL_UnlockSurface (SDL_Screens[i]);
+ }
+ scaler = NULL;
+ graphics_backend = &sdl2_unscaled_backend;
+ }
+
+ /* We succeeded, so alter the screen size to our new sizes */
+ ScreenWidthActual = width;
+ ScreenHeightActual = height;
+
+ return 0;
+}
+
+int
+TFB_Pure_InitGraphics (int driver, int flags, const char *renderer, int width, int height)
+{
+ log_add (log_Info, "Initializing SDL.");
+ log_add (log_Info, "SDL initialized.");
+ log_add (log_Info, "Initializing Screen.");
+
+ ScreenWidth = 320;
+ ScreenHeight = 240;
+ rendererBackend = renderer;
+
+ if (TFB_Pure_ConfigureVideo (driver, flags, width, height, 0))
+ {
+ log_add (log_Fatal, "Could not initialize video: %s",
+ SDL_GetError ());
+ exit (EXIT_FAILURE);
+ }
+
+ /* Initialize scalers (let them precompute whatever) */
+ Scale_Init ();
+
+ return 0;
+}
+
+void
+TFB_Pure_UninitGraphics (void)
+{
+ if (renderer) {
+ SDL_DestroyRenderer (renderer);
+ }
+ if (window) {
+ SDL_DestroyWindow (window);
+ }
+}
+
+static void
+TFB_SDL2_UploadTransitionScreen (void)
+{
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.x = 0;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.y = 0;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.w = ScreenWidth;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].updated.h = ScreenHeight;
+ SDL2_Screens[TFB_SCREEN_TRANSITION].dirty = TRUE;
+}
+
+static void
+TFB_SDL2_UpdateTexture (SDL_Texture *dest, SDL_Surface *src, SDL_Rect *rect)
+{
+ char *srcBytes;
+ SDL_LockSurface (src);
+ srcBytes = src->pixels;
+ if (rect)
+ {
+ /* SDL2 screen surfaces are always 32bpp */
+ srcBytes += (src->pitch * rect->y) + (rect->x * 4);
+ }
+ /* 2020-08-02: At time of writing, the documentation for
+ * SDL_UpdateTexture states this: "If the texture is intended to be
+ * updated often, it is preferred to create the texture as streaming
+ * and use [SDL_LockTexture and SDL_UnlockTexture]." Unfortunately,
+ * SDL_LockTexture will corrupt driver-space memory in the 32-bit
+ * Direct3D 9 driver on Intel Integrated graphics chips, resulting
+ * in an immediate crash with no detectable errors from the API up
+ * to that point.
+ *
+ * We also cannot simply forbid the Direct3D driver outright, because
+ * pre-Windows 10 machines appear to fail to initialize D3D11 even
+ * while claiming to support it.
+ *
+ * These bugs may be fixed in the future, but in the meantime we
+ * rely on this allegedly slower but definitely more reliable
+ * function. */
+ SDL_UpdateTexture (dest, rect, srcBytes, src->pitch);
+ SDL_UnlockSurface (src);
+}
+
+static void
+TFB_SDL2_ScanLines (void)
+{
+ int y;
+ SDL_SetRenderDrawColor (renderer, 0, 0, 0, 64);
+ SDL_SetRenderDrawBlendMode (renderer, SDL_BLENDMODE_BLEND);
+ SDL_RenderSetLogicalSize (renderer, ScreenWidth * 2, ScreenHeight * 2);
+ for (y = 0; y < ScreenHeight * 2; y += 2)
+ {
+ SDL_RenderDrawLine (renderer, 0, y, ScreenWidth * 2 - 1, y);
+ }
+ SDL_RenderSetLogicalSize (renderer, ScreenWidth, ScreenHeight);
+}
+
+static void
+TFB_SDL2_Preprocess (int force_full_redraw, int transition_amount, int fade_amount)
+{
+ (void) transition_amount;
+ (void) fade_amount;
+
+ if (force_full_redraw == TFB_REDRAW_YES)
+ {
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.x = 0;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.y = 0;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.w = ScreenWidth;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.h = ScreenHeight;
+ SDL2_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+ else if (TFB_BBox.valid)
+ {
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.x = TFB_BBox.region.corner.x;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.y = TFB_BBox.region.corner.y;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.w = TFB_BBox.region.extent.width;
+ SDL2_Screens[TFB_SCREEN_MAIN].updated.h = TFB_BBox.region.extent.height;
+ SDL2_Screens[TFB_SCREEN_MAIN].dirty = TRUE;
+ }
+
+ SDL_SetRenderDrawBlendMode (renderer, SDL_BLENDMODE_NONE);
+ SDL_SetRenderDrawColor (renderer, 0, 0, 0, 255);
+ SDL_RenderClear (renderer);
+}
+
+static void
+TFB_SDL2_Unscaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ SDL_Texture *texture = SDL2_Screens[screen].texture;
+ if (SDL2_Screens[screen].dirty)
+ {
+ TFB_SDL2_UpdateTexture (texture, SDL_Screens[screen], &SDL2_Screens[screen].updated);
+ }
+ if (a == 255)
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_NONE);
+ }
+ else
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod (texture, a);
+ }
+ SDL_RenderCopy (renderer, texture, rect, rect);
+}
+
+static void
+TFB_SDL2_Scaled_ScreenLayer (SCREEN screen, Uint8 a, SDL_Rect *rect)
+{
+ SDL_Texture *texture = SDL2_Screens[screen].texture;
+ SDL_Rect srcRect, *pSrcRect = NULL;
+ if (SDL2_Screens[screen].dirty)
+ {
+ SDL_Surface *src = SDL2_Screens[screen].scaled;
+ SDL_Rect scaled_update = SDL2_Screens[screen].updated;
+ scaler (SDL_Screens[screen], src, &SDL2_Screens[screen].updated);
+ scaled_update.x *= 2;
+ scaled_update.y *= 2;
+ scaled_update.w *= 2;
+ scaled_update.h *= 2;
+ TFB_SDL2_UpdateTexture (texture, src, &scaled_update);
+ }
+ if (a == 255)
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_NONE);
+ }
+ else
+ {
+ SDL_SetTextureBlendMode (texture, SDL_BLENDMODE_BLEND);
+ SDL_SetTextureAlphaMod (texture, a);
+ }
+ /* The texture has twice the resolution when scaled, but the
+ * screen's logical resolution has not changed, so the clip
+ * rectangle does not need to be scaled. The *source* clip
+ * rect, however, must be scaled to match. */
+ if (rect)
+ {
+ srcRect = *rect;
+ srcRect.x *= 2;
+ srcRect.y *= 2;
+ srcRect.w *= 2;
+ srcRect.h *= 2;
+ pSrcRect = &srcRect;
+ }
+ SDL_RenderCopy (renderer, texture, pSrcRect, rect);
+}
+
+static void
+TFB_SDL2_ColorLayer (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect)
+{
+ SDL_SetRenderDrawBlendMode (renderer, a == 255 ? SDL_BLENDMODE_NONE
+ : SDL_BLENDMODE_BLEND);
+ SDL_SetRenderDrawColor (renderer, r, g, b, a);
+ SDL_RenderFillRect (renderer, rect);
+}
+
+static void
+TFB_SDL2_Postprocess (void)
+{
+ if (GfxFlags & TFB_GFXFLAGS_SCANLINES)
+ TFB_SDL2_ScanLines ();
+
+ SDL_RenderPresent (renderer);
+}
+
+#endif
diff --git a/src/libs/graphics/sdl/sdl_common.c b/src/libs/graphics/sdl/sdl_common.c
new file mode 100644
index 0000000..b699bf8
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl_common.c
@@ -0,0 +1,308 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdl_common.h"
+#include "opengl.h"
+#include "pure.h"
+#include "primitives.h"
+#include "options.h"
+#include "uqmversion.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/dcqueue.h"
+#include "libs/graphics/cmap.h"
+#include "libs/input/sdl/input.h"
+ // for ProcessInputEvent()
+#include "libs/graphics/bbox.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/vidlib.h"
+
+SDL_Surface *SDL_Screen;
+SDL_Surface *TransitionScreen;
+
+SDL_Surface *SDL_Screens[TFB_GFX_NUMSCREENS];
+
+SDL_Surface *format_conv_surf = NULL;
+
+static volatile BOOLEAN abortFlag = FALSE;
+
+int GfxFlags = 0;
+
+TFB_GRAPHICS_BACKEND *graphics_backend = NULL;
+
+volatile int QuitPosted = 0;
+volatile int GameActive = 1; // Track the SDL_ACTIVEEVENT state SDL_APPACTIVE
+
+int
+TFB_InitGraphics (int driver, int flags, const char *renderer, int width, int height)
+{
+ int result, i;
+ char caption[200];
+
+ /* Null out screen pointers the first time */
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ {
+ SDL_Screens[i] = NULL;
+ }
+
+ GfxFlags = flags;
+
+ if (driver == TFB_GFXDRIVER_SDL_OPENGL)
+ {
+#ifdef HAVE_OPENGL
+ result = TFB_GL_InitGraphics (driver, flags, width, height);
+#else
+ driver = TFB_GFXDRIVER_SDL_PURE;
+ log_add (log_Warning, "OpenGL support not compiled in,"
+ " so using pure SDL driver");
+ result = TFB_Pure_InitGraphics (driver, flags, renderer, width, height);
+#endif
+ }
+ else
+ {
+ result = TFB_Pure_InitGraphics (driver, flags, renderer, width, height);
+ }
+
+#if SDL_MAJOR_VERSION == 1
+ /* Other versions do this when setting up the window */
+ sprintf (caption, "The Ur-Quan Masters v%d.%d.%d%s",
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ SDL_WM_SetCaption (caption, NULL);
+#endif
+
+ if (flags & TFB_GFXFLAGS_FULLSCREEN)
+ SDL_ShowCursor (SDL_DISABLE);
+
+ Init_DrawCommandQueue ();
+
+ TFB_DrawCanvas_Initialize ();
+
+ return 0;
+}
+
+void
+TFB_UninitGraphics (void)
+{
+ int i;
+
+ Uninit_DrawCommandQueue ();
+
+ for (i = 0; i < TFB_GFX_NUMSCREENS; i++)
+ UnInit_Screen (&SDL_Screens[i]);
+
+ TFB_Pure_UninitGraphics ();
+#ifdef HAVE_OPENGL
+ TFB_GL_UninitGraphics ();
+#endif
+
+ UnInit_Screen (&format_conv_surf);
+}
+
+void
+TFB_ProcessEvents ()
+{
+ SDL_Event Event;
+
+ while (SDL_PollEvent (&Event) > 0)
+ {
+ /* Run through the InputEvent filter. */
+ ProcessInputEvent (&Event);
+ /* Handle graphics and exposure events. */
+ switch (Event.type) {
+#if 0 /* Currently disabled in mainline */
+ case SDL_ACTIVEEVENT: /* Lose/gain visibility or focus */
+ /* Up to three different state changes can occur in one event. */
+ /* Here, disregard least significant change (mouse focus). */
+ // This controls the automatic sleep/pause when minimized.
+ // On small displays (e.g. mobile devices), APPINPUTFOCUS would
+ // be an appropriate substitution for APPACTIVE:
+ // if (Event.active.state & SDL_APPINPUTFOCUS)
+ if (Event.active.state & SDL_APPACTIVE)
+ GameActive = Event.active.gain;
+ break;
+ case SDL_VIDEORESIZE: /* User resized video mode */
+ // TODO
+ break;
+#endif
+ case SDL_QUIT:
+ QuitPosted = 1;
+ break;
+#if SDL_MAJOR_VERSION == 1
+ case SDL_VIDEOEXPOSE: /* Screen needs to be redrawn */
+ TFB_SwapBuffers (TFB_REDRAW_EXPOSE);
+ break;
+#else
+ case SDL_WINDOWEVENT:
+ if (Event.window.event == SDL_WINDOWEVENT_EXPOSED)
+ {
+ /* Screen needs to be redrawn */
+ TFB_SwapBuffers (TFB_REDRAW_EXPOSE);
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+}
+
+static BOOLEAN system_box_active = 0;
+static SDL_Rect system_box;
+
+void
+SetSystemRect (const RECT *r)
+{
+ system_box_active = TRUE;
+ system_box.x = r->corner.x;
+ system_box.y = r->corner.y;
+ system_box.w = r->extent.width;
+ system_box.h = r->extent.height;
+}
+
+void
+ClearSystemRect (void)
+{
+ system_box_active = FALSE;
+}
+
+void
+TFB_SwapBuffers (int force_full_redraw)
+{
+ static int last_fade_amount = 255, last_transition_amount = 255;
+ static int fade_amount = 255, transition_amount = 255;
+
+ fade_amount = GetFadeAmount ();
+ transition_amount = TransitionAmount;
+
+ if (force_full_redraw == TFB_REDRAW_NO && !TFB_BBox.valid &&
+ fade_amount == 255 && transition_amount == 255 &&
+ last_fade_amount == 255 && last_transition_amount == 255)
+ return;
+
+ if (force_full_redraw == TFB_REDRAW_NO &&
+ (fade_amount != 255 || transition_amount != 255 ||
+ last_fade_amount != 255 || last_transition_amount != 255))
+ force_full_redraw = TFB_REDRAW_FADING;
+
+ last_fade_amount = fade_amount;
+ last_transition_amount = transition_amount;
+
+ graphics_backend->preprocess (force_full_redraw, transition_amount,
+ fade_amount);
+ graphics_backend->screen (TFB_SCREEN_MAIN, 255, NULL);
+
+ if (transition_amount != 255)
+ {
+ SDL_Rect r;
+ r.x = TransitionClipRect.corner.x;
+ r.y = TransitionClipRect.corner.y;
+ r.w = TransitionClipRect.extent.width;
+ r.h = TransitionClipRect.extent.height;
+ graphics_backend->screen (TFB_SCREEN_TRANSITION,
+ 255 - transition_amount, &r);
+ }
+
+ if (fade_amount != 255)
+ {
+ if (fade_amount < 255)
+ {
+ graphics_backend->color (0, 0, 0, 255 - fade_amount, NULL);
+ }
+ else
+ {
+ graphics_backend->color (255, 255, 255,
+ fade_amount - 255, NULL);
+ }
+ }
+
+ if (system_box_active)
+ {
+ graphics_backend->screen (TFB_SCREEN_MAIN, 255, &system_box);
+ }
+
+ graphics_backend->postprocess ();
+}
+
+/* Probably ought to clean this away at some point. */
+SDL_Surface *
+TFB_DisplayFormatAlpha (SDL_Surface *surface)
+{
+ SDL_Surface* newsurf;
+ SDL_PixelFormat* dstfmt;
+ const SDL_PixelFormat* srcfmt = surface->format;
+
+ // figure out what format to use (alpha/no alpha)
+ if (surface->format->Amask)
+ dstfmt = format_conv_surf->format;
+ else
+ dstfmt = SDL_Screen->format;
+
+ if (srcfmt->BytesPerPixel == dstfmt->BytesPerPixel &&
+ srcfmt->Rmask == dstfmt->Rmask &&
+ srcfmt->Gmask == dstfmt->Gmask &&
+ srcfmt->Bmask == dstfmt->Bmask &&
+ srcfmt->Amask == dstfmt->Amask)
+ return surface; // no conversion needed
+
+ newsurf = SDL_ConvertSurface (surface, dstfmt, surface->flags);
+ // Colorkeys and surface-level alphas cannot work at the same time,
+ // so we need to disable one of them
+ if (TFB_HasColorKey (surface) && newsurf &&
+ TFB_HasColorKey (newsurf) &&
+ TFB_HasSurfaceAlphaMod (newsurf))
+ {
+ TFB_DisableSurfaceAlphaMod (newsurf);
+ }
+
+ return newsurf;
+}
+
+// This function should only be called from the graphics thread,
+// like from a TFB_DrawCommand_Callback command.
+TFB_Canvas
+TFB_GetScreenCanvas (SCREEN screen)
+{
+ return SDL_Screens[screen];
+}
+
+void
+TFB_UploadTransitionScreen (void)
+{
+ graphics_backend->uploadTransitionScreen ();
+}
+
+int
+TFB_HasColorKey (SDL_Surface *surface)
+{
+ Uint32 key;
+ return TFB_GetColorKey (surface, &key) == 0;
+}
+
+void
+UnInit_Screen (SDL_Surface **screen)
+{
+ if (*screen == NULL) {
+ return;
+ }
+
+ SDL_FreeSurface (*screen);
+ *screen = NULL;
+}
diff --git a/src/libs/graphics/sdl/sdl_common.h b/src/libs/graphics/sdl/sdl_common.h
new file mode 100644
index 0000000..76d1cb6
--- /dev/null
+++ b/src/libs/graphics/sdl/sdl_common.h
@@ -0,0 +1,63 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SDL_COMMON_H
+#define SDL_COMMON_H
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+
+#include "../gfxintrn.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/graphics/gfx_common.h"
+
+// The Graphics Backend vtable
+typedef struct _tfb_graphics_backend {
+ void (*preprocess) (int force_redraw, int transition_amount, int fade_amount);
+ void (*postprocess) (void);
+ void (*uploadTransitionScreen) (void);
+ void (*screen) (SCREEN screen, Uint8 alpha, SDL_Rect *rect);
+ void (*color) (Uint8 r, Uint8 g, Uint8 b, Uint8 a, SDL_Rect *rect);
+} TFB_GRAPHICS_BACKEND;
+
+extern TFB_GRAPHICS_BACKEND *graphics_backend;
+
+extern SDL_Surface *SDL_Screen;
+extern SDL_Surface *TransitionScreen;
+
+extern SDL_Surface *SDL_Screens[TFB_GFX_NUMSCREENS];
+
+extern SDL_Surface *format_conv_surf;
+
+SDL_Surface* TFB_DisplayFormatAlpha (SDL_Surface *surface);
+int TFB_HasSurfaceAlphaMod (SDL_Surface *surface);
+int TFB_GetSurfaceAlphaMod (SDL_Surface *surface, Uint8 *alpha);
+int TFB_SetSurfaceAlphaMod (SDL_Surface *surface, Uint8 alpha);
+int TFB_DisableSurfaceAlphaMod (SDL_Surface *surface);
+int TFB_HasColorKey (SDL_Surface *surface);
+int TFB_GetColorKey (SDL_Surface *surface, Uint32 *key);
+int TFB_SetColorKey (SDL_Surface *surface, Uint32 key, int rleaccel);
+int TFB_DisableColorKey (SDL_Surface *surface);
+int TFB_SetColors (SDL_Surface *surface, SDL_Color *colors, int firstcolor, int ncolors);
+
+#if SDL_MAJOR_VERSION == 1
+int SDL1_ReInit_Screen (SDL_Surface **screen, SDL_Surface *templat, int w, int h);
+#endif
+void UnInit_Screen (SDL_Surface **screen);
+
+#endif
diff --git a/src/libs/graphics/sdl/sdluio.c b/src/libs/graphics/sdl/sdluio.c
new file mode 100644
index 0000000..5e4554d
--- /dev/null
+++ b/src/libs/graphics/sdl/sdluio.c
@@ -0,0 +1,153 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sdluio.h"
+
+#include "port.h"
+#include "libs/uio.h"
+#include SDL_INCLUDE(SDL.h)
+#include SDL_INCLUDE(SDL_error.h)
+#include SDL_INCLUDE(SDL_rwops.h)
+#include "libs/memlib.h"
+#include "png2sdl.h"
+#include <errno.h>
+#include <string.h>
+
+
+static SDL_RWops *sdluio_makeRWops (uio_Stream *stream);
+
+#if 0
+// For use for initialisation, using structure assignment.
+static SDL_RWops sdluio_templateRWops =
+{
+ .seek = sdluio_seek,
+ .read = sdluio_read,
+ .write = sdluio_write,
+ .close = sdluio_close,
+};
+#endif
+
+SDL_Surface *
+sdluio_loadImage (uio_DirHandle *dir, const char *fileName) {
+ uio_Stream *stream;
+ SDL_RWops *rwops;
+ SDL_Surface *result = NULL;
+
+ stream = uio_fopen (dir, fileName, "rb");
+ if (stream == NULL)
+ {
+ SDL_SetError ("Couldn't open '%s': %s", fileName,
+ strerror(errno));
+ return NULL;
+ }
+ rwops = sdluio_makeRWops (stream);
+ if (rwops) {
+ result = TFB_png_to_sdl (rwops);
+ SDL_RWclose (rwops);
+ }
+ return result;
+}
+
+#if SDL_MAJOR_VERSION == 1
+int
+sdluio_seek (SDL_RWops *context, int offset, int whence)
+#else
+Sint64
+sdluio_seek (SDL_RWops *context, Sint64 offset, int whence)
+#endif
+{
+ if (uio_fseek ((uio_Stream *) context->hidden.unknown.data1, offset,
+ whence) == -1)
+ {
+ SDL_SetError ("Error seeking in uio_Stream: %s",
+ strerror(errno));
+ return -1;
+ }
+ return uio_ftell ((uio_Stream *) context->hidden.unknown.data1);
+}
+
+#if SDL_MAJOR_VERSION == 1
+int
+sdluio_read (SDL_RWops *context, void *ptr, int size, int maxnum)
+#else
+size_t
+sdluio_read (SDL_RWops *context, void *ptr, size_t size, size_t maxnum)
+#endif
+{
+ size_t numRead;
+
+ numRead = uio_fread (ptr, (size_t) size, (size_t) maxnum,
+ (uio_Stream *) context->hidden.unknown.data1);
+ if (numRead == 0 && uio_ferror ((uio_Stream *)
+ context->hidden.unknown.data1))
+ {
+ SDL_SetError ("Error reading from uio_Stream: %s",
+ strerror(errno));
+ return 0;
+ }
+ return (int) numRead;
+}
+
+#if SDL_MAJOR_VERSION == 1
+int
+sdluio_write (SDL_RWops *context, const void *ptr, int size, int num)
+#else
+size_t
+sdluio_write (SDL_RWops *context, const void *ptr, size_t size, size_t num)
+#endif
+{
+ size_t numWritten;
+
+ numWritten = uio_fwrite (ptr, (size_t) size, (size_t) num,
+ (uio_Stream *) context->hidden.unknown.data1);
+ if (numWritten == 0 && uio_ferror ((uio_Stream *)
+ context->hidden.unknown.data1))
+ {
+ SDL_SetError ("Error writing to uio_Stream: %s",
+ strerror(errno));
+ return 0;
+ }
+ return (size_t) numWritten;
+}
+
+int
+sdluio_close (SDL_RWops *context) {
+ int result;
+
+ result = uio_fclose ((uio_Stream *) context->hidden.unknown.data1);
+ HFree (context);
+ return result;
+}
+
+static SDL_RWops *
+sdluio_makeRWops (uio_Stream *stream) {
+ SDL_RWops *result;
+
+ result = HMalloc (sizeof (SDL_RWops));
+#if 0
+ *(struct SDL_RWops *) result = sdluio_templateRWops;
+ // structure assignment
+#endif
+ result->seek = sdluio_seek;
+ result->read = sdluio_read;
+ result->write = sdluio_write;
+ result->close = sdluio_close;
+ result->hidden.unknown.data1 = stream;
+ return result;
+}
+
+
+
diff --git a/src/libs/graphics/sdl/sdluio.h b/src/libs/graphics/sdl/sdluio.h
new file mode 100644
index 0000000..0f8c701
--- /dev/null
+++ b/src/libs/graphics/sdl/sdluio.h
@@ -0,0 +1,39 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_SDL_SDLUIO_H_
+#define LIBS_GRAPHICS_SDL_SDLUIO_H_
+
+#include "port.h"
+#include "libs/uio.h"
+#include SDL_INCLUDE(SDL.h)
+#include SDL_INCLUDE(SDL_rwops.h)
+
+SDL_Surface *sdluio_loadImage (uio_DirHandle *dir, const char *fileName);
+#if SDL_MAJOR_VERSION == 1
+int sdluio_seek (SDL_RWops *context, int offset, int whence);
+int sdluio_read (SDL_RWops *context, void *ptr, int size, int maxnum);
+int sdluio_write (SDL_RWops *context, const void *ptr, int size, int num);
+#else
+Sint64 sdlui_seek (SDL_RWops *context, Sint64 offset, int whence);
+size_t sdlui_read (SDL_RWops *context, void *ptr, size_t size, size_t maxnum);
+size_t sdlui_write (SDL_RWops *contex, const void *ptr, size_t size, size_t num);
+#endif
+int sdluio_close (SDL_RWops *context);
+
+
+#endif /* LIBS_GRAPHICS_SDL_SDLUIO_H_ */
+
diff --git a/src/libs/graphics/sdl/triscan2x.c b/src/libs/graphics/sdl/triscan2x.c
new file mode 100644
index 0000000..830dc7c
--- /dev/null
+++ b/src/libs/graphics/sdl/triscan2x.c
@@ -0,0 +1,155 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// Core algorithm of the Triscan screen scaler (based on Scale2x)
+// (for scale2x please see http://scale2x.sf.net)
+// Template
+// When this file is built standalone is produces a plain C version
+// Also #included by 2xscalers_mmx.c for an MMX version
+
+#include "libs/graphics/sdl/sdl_common.h"
+#include "types.h"
+#include "scalers.h"
+#include "scaleint.h"
+#include "2xscalers.h"
+
+
+// Triscan scaling to 2x
+// derivative of scale2x -- scale2x.sf.net
+// The name expands to either
+// Scale_TriScanFilter (for plain C) or
+// Scale_MMX_TriScanFilter (for MMX)
+// [others when platforms are added]
+void
+SCALE_(TriScanFilter) (SDL_Surface *src, SDL_Surface *dst, SDL_Rect *r)
+{
+ int x, y;
+ const int w = src->w, h = src->h;
+ int xend, yend;
+ int dsrc, ddst;
+ SDL_Rect *region = r;
+ SDL_Rect limits;
+ SDL_PixelFormat *fmt = dst->format;
+ const int sp = src->pitch, dp = dst->pitch;
+ const int bpp = fmt->BytesPerPixel;
+ const int slen = sp / bpp, dlen = dp / bpp;
+ // for clarity purposes, the 'pixels' array here is transposed
+ Uint32 pixels[3][3];
+ Uint32 *src_p = (Uint32 *)src->pixels;
+ Uint32 *dst_p = (Uint32 *)dst->pixels;
+
+ int prevline, nextline;
+
+ // these macros are for clarity; they make the current pixel (0,0)
+ // and allow to access pixels in all directions
+ #define PIX(x, y) (pixels[1 + (x)][1 + (y)])
+
+ #define TRISCAN_YUV_MED 100
+ // medium tolerance pixel comparison
+ #define TRISCAN_CMPYUV(p1, p2) \
+ (PIX p1 == PIX p2 || SCALE_CMPYUV (PIX p1, PIX p2, TRISCAN_YUV_MED))
+
+
+ SCALE_(PlatInit) ();
+
+ // expand updated region if necessary
+ // pixels neighbooring the updated region may
+ // change as a result of updates
+ limits.x = 0;
+ limits.y = 0;
+ limits.w = src->w;
+ limits.h = src->h;
+ Scale_ExpandRect (region, 1, &limits);
+
+ xend = region->x + region->w;
+ yend = region->y + region->h;
+ dsrc = slen - region->w;
+ ddst = (dlen - region->w) * 2;
+
+ // move ptrs to the first updated pixel
+ src_p += slen * region->y + region->x;
+ dst_p += (dlen * region->y + region->x) * 2;
+
+ for (y = region->y; y < yend; ++y, dst_p += ddst, src_p += dsrc)
+ {
+ if (y > 0)
+ prevline = -slen;
+ else
+ prevline = 0;
+
+ if (y < h - 1)
+ nextline = slen;
+ else
+ nextline = 0;
+
+ // prime the (tiny) sliding-window pixel arrays
+ PIX( 1, 0) = src_p[0];
+
+ if (region->x > 0)
+ PIX( 0, 0) = src_p[-1];
+ else
+ PIX( 0, 0) = PIX( 1, 0);
+
+ for (x = region->x; x < xend; ++x, ++src_p, dst_p += 2)
+ {
+ // slide the window
+ PIX(-1, 0) = PIX( 0, 0);
+
+ PIX( 0, -1) = src_p[prevline];
+ PIX( 0, 0) = PIX( 1, 0);
+ PIX( 0, 1) = src_p[nextline];
+
+ if (x < w - 1)
+ PIX( 1, 0) = src_p[1];
+ else
+ PIX( 1, 0) = PIX( 0, 0);
+
+ if (!TRISCAN_CMPYUV (( 0, -1), ( 0, 1)) &&
+ !TRISCAN_CMPYUV ((-1, 0), ( 1, 0)))
+ {
+ if (TRISCAN_CMPYUV ((-1, 0), ( 0, -1)))
+ dst_p[0] = Scale_Blend_11 (PIX(-1, 0), PIX(0, -1));
+ else
+ dst_p[0] = PIX(0, 0);
+
+ if (TRISCAN_CMPYUV (( 1, 0), ( 0, -1)))
+ dst_p[1] = Scale_Blend_11 (PIX(1, 0), PIX(0, -1));
+ else
+ dst_p[1] = PIX(0, 0);
+
+ if (TRISCAN_CMPYUV ((-1, 0), ( 0, 1)))
+ dst_p[dlen] = Scale_Blend_11 (PIX(-1, 0), PIX(0, 1));
+ else
+ dst_p[dlen] = PIX(0, 0);
+
+ if (TRISCAN_CMPYUV (( 1, 0), ( 0, 1)))
+ dst_p[dlen+1] = Scale_Blend_11 (PIX(1, 0), PIX(0, 1));
+ else
+ dst_p[dlen+1] = PIX(0, 0);
+ }
+ else
+ {
+ dst_p[0] = PIX(0, 0);
+ dst_p[1] = PIX(0, 0);
+ dst_p[dlen] = PIX(0, 0);
+ dst_p[dlen+1] = PIX(0, 0);
+ }
+ }
+ }
+
+ SCALE_(PlatDone) ();
+}
+
diff --git a/src/libs/graphics/tfb_draw.c b/src/libs/graphics/tfb_draw.c
new file mode 100644
index 0000000..1ac3c34
--- /dev/null
+++ b/src/libs/graphics/tfb_draw.c
@@ -0,0 +1,493 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "gfx_common.h"
+#include "tfb_draw.h"
+#include "drawcmd.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+static const HOT_SPOT NullHs = {0, 0};
+
+void
+TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_LINE;
+ DC.data.line.x1 = x1;
+ DC.data.line.y1 = y1;
+ DC.data.line.x2 = x2;
+ DC.data.line.y2 = y2;
+ DC.data.line.color = color;
+ DC.data.line.drawMode = mode;
+ DC.data.line.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_Rect (RECT *rect, Color color, DrawMode mode, SCREEN dest)
+{
+ RECT locRect;
+ TFB_DrawCommand DC;
+
+ if (!rect)
+ {
+ locRect.corner.x = locRect.corner.y = 0;
+ locRect.extent.width = ScreenWidth;
+ locRect.extent.height = ScreenHeight;
+ rect = &locRect;
+ }
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_RECTANGLE;
+ DC.data.rect.rect = *rect;
+ DC.data.rect.color = color;
+ DC.data.rect.drawMode = mode;
+ DC.data.rect.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *cmap, DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_IMAGE;
+ DC.data.image.image = img;
+ DC.data.image.colormap = cmap;
+ DC.data.image.x = x;
+ DC.data.image.y = y;
+ DC.data.image.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
+ DC.data.image.scaleMode = scaleMode;
+ DC.data.image.drawMode = mode;
+ DC.data.image.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color color, DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_FILLEDIMAGE;
+ DC.data.filledimage.image = img;
+ DC.data.filledimage.x = x;
+ DC.data.filledimage.y = y;
+ DC.data.filledimage.scale = (scale == GSCALE_IDENTITY) ? 0 : scale;
+ DC.data.filledimage.scaleMode = scaleMode;
+ DC.data.filledimage.color = color;
+ DC.data.filledimage.drawMode = mode;
+ DC.data.filledimage.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_FontChar (TFB_Char *fontChar, TFB_Image *backing,
+ int x, int y, DrawMode mode, SCREEN dest)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_FONTCHAR;
+ DC.data.fontchar.fontchar = fontChar;
+ DC.data.fontchar.backing = backing;
+ DC.data.fontchar.x = x;
+ DC.data.fontchar.y = y;
+ DC.data.fontchar.drawMode = mode;
+ DC.data.fontchar.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_COPYTOIMAGE;
+ DC.data.copytoimage.rect = *r;
+ DC.data.copytoimage.image = img;
+ DC.data.copytoimage.srcBuffer = src;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest)
+{
+ RECT locRect;
+ TFB_DrawCommand DC;
+
+ if (!r)
+ {
+ locRect.corner.x = locRect.corner.y = 0;
+ locRect.extent.width = ScreenWidth;
+ locRect.extent.height = ScreenHeight;
+ r = &locRect;
+ }
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_COPY;
+ DC.data.copy.rect = *r;
+ DC.data.copy.srcBuffer = src;
+ DC.data.copy.destBuffer = dest;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
+{
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_SETMIPMAP;
+ DC.data.setmipmap.image = img;
+ DC.data.setmipmap.mipmap = mmimg;
+ DC.data.setmipmap.hotx = hotx;
+ DC.data.setmipmap.hoty = hoty;
+
+ TFB_EnqueueDrawCommand (&DC);
+}
+
+void
+TFB_DrawScreen_DeleteImage (TFB_Image *img)
+{
+ if (img)
+ {
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_DELETEIMAGE;
+ DC.data.deleteimage.image = img;
+
+ TFB_EnqueueDrawCommand (&DC);
+ }
+}
+
+void
+TFB_DrawScreen_DeleteData (void *data)
+ // data must be a result of HXalloc() call
+{
+ if (data)
+ {
+ TFB_DrawCommand DC;
+
+ DC.Type = TFB_DRAWCOMMANDTYPE_DELETEDATA;
+ DC.data.deletedata.data = data;
+
+ TFB_EnqueueDrawCommand (&DC);
+ }
+}
+
+void
+TFB_DrawScreen_WaitForSignal (void)
+{
+ TFB_DrawCommand DrawCommand;
+ Semaphore s;
+ s = GetMyThreadLocal ()->flushSem;
+ DrawCommand.Type = TFB_DRAWCOMMANDTYPE_SENDSIGNAL;
+ DrawCommand.data.sendsignal.sem = s;
+ Lock_DCQ (1);
+ TFB_BatchReset ();
+ TFB_EnqueueDrawCommand (&DrawCommand);
+ Unlock_DCQ();
+ SetSemaphore (s);
+}
+
+void
+TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height)
+{
+ TFB_DrawCommand DrawCommand;
+ DrawCommand.Type = TFB_DRAWCOMMANDTYPE_REINITVIDEO;
+ DrawCommand.data.reinitvideo.driver = driver;
+ DrawCommand.data.reinitvideo.flags = flags;
+ DrawCommand.data.reinitvideo.width = width;
+ DrawCommand.data.reinitvideo.height = height;
+ TFB_EnqueueDrawCommand (&DrawCommand);
+}
+
+void
+TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg)
+{
+ TFB_DrawCommand DrawCommand;
+ DrawCommand.Type = TFB_DRAWCOMMANDTYPE_CALLBACK;
+ DrawCommand.data.callback.callback = callback;
+ DrawCommand.data.callback.arg = arg;
+ TFB_EnqueueDrawCommand(&DrawCommand);
+}
+
+void
+TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_Line (x1, y1, x2, y2, color, mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_Rect (RECT *rect, Color color, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_Rect (rect, color, mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *cmap, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_Image (img, x, y, scale, scaleMode, cmap,
+ mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color color, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_FilledImage (img, x, y, scale, scaleMode, color,
+ mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+void
+TFB_DrawImage_FontChar (TFB_Char *fontChar, TFB_Image *backing,
+ int x, int y, DrawMode mode, TFB_Image *target)
+{
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_FontChar (fontChar, backing, x, y, mode, target->NormalImg);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+}
+
+
+TFB_Image *
+TFB_DrawImage_New (TFB_Canvas canvas)
+{
+ TFB_Image *img = HMalloc (sizeof (TFB_Image));
+ img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
+ img->ScaledImg = NULL;
+ img->MipmapImg = NULL;
+ img->FilledImg = NULL;
+ img->colormap_index = -1;
+ img->colormap_version = 0;
+ img->NormalHs = NullHs;
+ img->MipmapHs = NullHs;
+ img->last_scale_hs = NullHs;
+ img->last_scale_type = -1;
+ img->last_scale = 0;
+ img->dirty = FALSE;
+ TFB_DrawCanvas_GetExtent (canvas, &img->extent);
+
+ if (TFB_DrawCanvas_IsPaletted (canvas))
+ {
+ img->NormalImg = canvas;
+ }
+ else
+ {
+ img->NormalImg = TFB_DrawCanvas_ToScreenFormat (canvas);
+ }
+
+ return img;
+}
+
+TFB_Image*
+TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha)
+{
+ TFB_Image* img = HMalloc (sizeof (TFB_Image));
+ img->mutex = CreateMutex ("image lock", SYNC_CLASS_VIDEO);
+ img->ScaledImg = NULL;
+ img->MipmapImg = NULL;
+ img->FilledImg = NULL;
+ img->colormap_index = -1;
+ img->colormap_version = 0;
+ img->NormalHs = NullHs;
+ img->MipmapHs = NullHs;
+ img->last_scale_hs = NullHs;
+ img->last_scale_type = -1;
+ img->last_scale = 0;
+ img->extent.width = w;
+ img->extent.height = h;
+
+ img->NormalImg = TFB_DrawCanvas_New_ForScreen (w, h, withalpha);
+
+ return img;
+}
+
+TFB_Image *
+TFB_DrawImage_New_Rotated (TFB_Image *img, int angle)
+{
+ TFB_Canvas dst;
+ EXTENT size;
+ TFB_Image* newimg;
+
+ /* sanity check */
+ if (!img->NormalImg)
+ {
+ log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
+ "source canvas is NULL! Failing.");
+ return NULL;
+ }
+
+ TFB_DrawCanvas_GetRotatedExtent (img->NormalImg, angle, &size);
+ dst = TFB_DrawCanvas_New_RotationTarget (img->NormalImg, angle);
+ if (!dst)
+ {
+ log_add (log_Warning, "TFB_DrawImage_New_Rotated: "
+ "rotation target canvas not created! Failing.");
+ return NULL;
+ }
+ TFB_DrawCanvas_Rotate (img->NormalImg, dst, angle, size);
+
+ newimg = TFB_DrawImage_New (dst);
+ return newimg;
+}
+
+void
+TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx, int hoty)
+{
+ bool imgpal;
+ bool mmpal;
+
+ if (!img || !mmimg)
+ return;
+
+ LockMutex (img->mutex);
+ LockMutex (mmimg->mutex);
+
+ // Either both images must be using the same colormap, or mipmap image
+ // must not be paletted. This restriction is due to the current
+ // implementation of fill-stamp, which replaces the palette with
+ // fill color.
+ imgpal = TFB_DrawCanvas_IsPaletted (img->NormalImg);
+ mmpal = TFB_DrawCanvas_IsPaletted (mmimg->NormalImg);
+ if (!mmpal || (mmpal && imgpal &&
+ img->colormap_index == mmimg->colormap_index))
+ {
+ img->MipmapImg = mmimg->NormalImg;
+ img->MipmapHs.x = hotx;
+ img->MipmapHs.y = hoty;
+ }
+ else
+ {
+ img->MipmapImg = NULL;
+ }
+
+ UnlockMutex (mmimg->mutex);
+ UnlockMutex (img->mutex);
+}
+
+void
+TFB_DrawImage_Delete (TFB_Image *image)
+{
+ if (image == 0)
+ {
+ log_add (log_Warning, "INTERNAL ERROR: Tried to delete a null image!");
+ /* Should we die here? */
+ return;
+ }
+ LockMutex (image->mutex);
+
+ TFB_DrawCanvas_Delete (image->NormalImg);
+
+ if (image->ScaledImg)
+ {
+ TFB_DrawCanvas_Delete (image->ScaledImg);
+ image->ScaledImg = 0;
+ }
+
+ if (image->FilledImg)
+ {
+ TFB_DrawCanvas_Delete (image->FilledImg);
+ image->FilledImg = 0;
+ }
+
+ UnlockMutex (image->mutex);
+ DestroyMutex (image->mutex);
+
+ HFree (image);
+}
+
+void
+TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type)
+{
+ if (image->dirty || !image->ScaledImg ||
+ target != image->last_scale ||
+ type != image->last_scale_type)
+ {
+ image->dirty = FALSE;
+ image->ScaledImg = TFB_DrawCanvas_New_ScaleTarget (image->NormalImg,
+ image->ScaledImg, type, image->last_scale_type);
+
+ if (type == TFB_SCALE_NEAREST)
+ TFB_DrawCanvas_Rescale_Nearest (image->NormalImg,
+ image->ScaledImg, target, &image->NormalHs,
+ &image->extent, &image->last_scale_hs);
+ else if (type == TFB_SCALE_BILINEAR)
+ TFB_DrawCanvas_Rescale_Bilinear (image->NormalImg,
+ image->ScaledImg, target, &image->NormalHs,
+ &image->extent, &image->last_scale_hs);
+ else
+ TFB_DrawCanvas_Rescale_Trilinear (image->NormalImg,
+ image->MipmapImg, image->ScaledImg, target,
+ &image->NormalHs, &image->MipmapHs,
+ &image->extent, &image->last_scale_hs);
+
+ image->last_scale_type = type;
+ image->last_scale = target;
+ }
+}
+
+BOOLEAN
+TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org,
+ TFB_Image *img2, POINT img2org, const RECT *interRect)
+{
+ BOOLEAN ret;
+
+ LockMutex (img1->mutex);
+ LockMutex (img2->mutex);
+ ret = TFB_DrawCanvas_Intersect (img1->NormalImg, img1org,
+ img2->NormalImg, img2org, interRect);
+ UnlockMutex (img2->mutex);
+ UnlockMutex (img1->mutex);
+
+ return ret;
+}
+
+void
+TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect,
+ TFB_Image *target, POINT dstPt)
+{
+ LockMutex (source->mutex);
+ LockMutex (target->mutex);
+ TFB_DrawCanvas_CopyRect (source->NormalImg, srcRect,
+ target->NormalImg, dstPt);
+ target->dirty = TRUE;
+ UnlockMutex (target->mutex);
+ UnlockMutex (source->mutex);
+}
diff --git a/src/libs/graphics/tfb_draw.h b/src/libs/graphics/tfb_draw.h
new file mode 100644
index 0000000..11de5b0
--- /dev/null
+++ b/src/libs/graphics/tfb_draw.h
@@ -0,0 +1,199 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TFB_DRAW_H
+#define TFB_DRAW_H
+
+#include "libs/threadlib.h"
+
+
+typedef void *TFB_Canvas;
+
+typedef enum {
+ TFB_SCREEN_MAIN,
+ TFB_SCREEN_EXTRA,
+ TFB_SCREEN_TRANSITION,
+
+ TFB_GFX_NUMSCREENS
+} SCREEN;
+
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/cmap.h"
+
+typedef struct tfb_image
+{
+ TFB_Canvas NormalImg;
+ TFB_Canvas ScaledImg;
+ TFB_Canvas MipmapImg;
+ TFB_Canvas FilledImg;
+ int colormap_index;
+ int colormap_version;
+ HOT_SPOT NormalHs;
+ HOT_SPOT MipmapHs;
+ HOT_SPOT last_scale_hs;
+ int last_scale;
+ int last_scale_type;
+ Color last_fill;
+ EXTENT extent;
+ Mutex mutex;
+ BOOLEAN dirty;
+} TFB_Image;
+
+typedef struct tfb_char
+{
+ EXTENT extent;
+ EXTENT disp;
+ // Display extent
+ HOT_SPOT HotSpot;
+ BYTE* data;
+ DWORD pitch;
+ // Pitch is for storing all chars of a page
+ // in one rectangular pixel matrix
+} TFB_Char;
+
+// we do not support paletted format for now
+typedef struct tfb_pixelformat
+{
+ int BitsPerPixel;
+ int BytesPerPixel;
+ DWORD Rmask, Gmask, Bmask, Amask;
+ DWORD Rshift, Gshift, Bshift, Ashift;
+ DWORD Rloss, Gloss, Bloss, Aloss;
+} TFB_PixelFormat;
+
+// Drawing commands
+
+void TFB_DrawScreen_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode, SCREEN dest);
+void TFB_DrawScreen_Rect (RECT *rect, Color, DrawMode, SCREEN dest);
+void TFB_DrawScreen_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *, DrawMode, SCREEN dest);
+void TFB_DrawScreen_Copy (const RECT *r, SCREEN src, SCREEN dest);
+void TFB_DrawScreen_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color, DrawMode, SCREEN dest);
+void TFB_DrawScreen_FontChar (TFB_Char *, TFB_Image *backing, int x, int y,
+ DrawMode, SCREEN dest);
+
+void TFB_DrawScreen_CopyToImage (TFB_Image *img, const RECT *r, SCREEN src);
+void TFB_DrawScreen_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx,
+ int hoty);
+void TFB_DrawScreen_DeleteImage (TFB_Image *img);
+void TFB_DrawScreen_DeleteData (void *);
+void TFB_DrawScreen_WaitForSignal (void);
+void TFB_DrawScreen_ReinitVideo (int driver, int flags, int width, int height);
+void TFB_DrawScreen_Callback (void (*callback) (void *arg), void *arg);
+
+TFB_Image *TFB_DrawImage_New (TFB_Canvas canvas);
+TFB_Image *TFB_DrawImage_CreateForScreen (int w, int h, BOOLEAN withalpha);
+TFB_Image *TFB_DrawImage_New_Rotated (TFB_Image *img, int angle);
+void TFB_DrawImage_SetMipmap (TFB_Image *img, TFB_Image *mmimg, int hotx,
+ int hoty);
+void TFB_DrawImage_Delete (TFB_Image *image);
+void TFB_DrawImage_FixScaling (TFB_Image *image, int target, int type);
+BOOLEAN TFB_DrawImage_Intersect (TFB_Image *img1, POINT img1org,
+ TFB_Image *img2, POINT img2org, const RECT *interRect);
+void TFB_DrawImage_CopyRect (TFB_Image *source, const RECT *srcRect,
+ TFB_Image *target, POINT dstPt);
+
+void TFB_DrawImage_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode, TFB_Image *target);
+void TFB_DrawImage_Rect (RECT *rect, Color, DrawMode, TFB_Image *target);
+void TFB_DrawImage_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *, DrawMode, TFB_Image *target);
+void TFB_DrawImage_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color, DrawMode, TFB_Image *target);
+void TFB_DrawImage_FontChar (TFB_Char *, TFB_Image *backing, int x, int y,
+ DrawMode, TFB_Image *target);
+
+TFB_Canvas TFB_DrawCanvas_LoadFromFile (void *dir, const char *fileName);
+TFB_Canvas TFB_DrawCanvas_New_TrueColor (int w, int h, BOOLEAN hasalpha);
+TFB_Canvas TFB_DrawCanvas_New_ForScreen (int w, int h, BOOLEAN withalpha);
+TFB_Canvas TFB_DrawCanvas_New_Paletted (int w, int h, Color palette[256],
+ int transparent_index);
+TFB_Canvas TFB_DrawCanvas_New_ScaleTarget (TFB_Canvas canvas,
+ TFB_Canvas oldcanvas, int type, int last_type);
+TFB_Canvas TFB_DrawCanvas_New_RotationTarget (TFB_Canvas src, int angle);
+TFB_Canvas TFB_DrawCanvas_ToScreenFormat (TFB_Canvas canvas);
+BOOLEAN TFB_DrawCanvas_IsPaletted (TFB_Canvas canvas);
+void TFB_DrawCanvas_Rescale_Nearest (TFB_Canvas src, TFB_Canvas dst,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs);
+void TFB_DrawCanvas_Rescale_Bilinear (TFB_Canvas src, TFB_Canvas dst,
+ int scale, HOT_SPOT* src_hs, EXTENT* size, HOT_SPOT* dst_hs);
+void TFB_DrawCanvas_Rescale_Trilinear (TFB_Canvas src, TFB_Canvas mipmap,
+ TFB_Canvas dst, int scale, HOT_SPOT* src_hs, HOT_SPOT* mm_hs,
+ EXTENT* size, HOT_SPOT* dst_hs);
+void TFB_DrawCanvas_GetScaledExtent (TFB_Canvas src_canvas, HOT_SPOT* src_hs,
+ TFB_Canvas src_mipmap, HOT_SPOT* mm_hs,
+ int scale, int type, EXTENT *size, HOT_SPOT *hs);
+void TFB_DrawCanvas_Rotate (TFB_Canvas src, TFB_Canvas dst, int angle,
+ EXTENT size);
+void TFB_DrawCanvas_GetRotatedExtent (TFB_Canvas src, int angle, EXTENT *size);
+void TFB_DrawCanvas_GetExtent (TFB_Canvas canvas, EXTENT *size);
+void TFB_DrawCanvas_SetClipRect (TFB_Canvas canvas, const RECT *clipRect);
+
+void TFB_DrawCanvas_Delete (TFB_Canvas canvas);
+
+void TFB_DrawCanvas_Line (int x1, int y1, int x2, int y2, Color color,
+ DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_Rect (RECT *rect, Color, DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_Image (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, TFB_ColorMap *, DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_FilledImage (TFB_Image *img, int x, int y, int scale,
+ int scaleMode, Color, DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_FontChar (TFB_Char *, TFB_Image *backing, int x, int y,
+ DrawMode, TFB_Canvas target);
+void TFB_DrawCanvas_CopyRect (TFB_Canvas source, const RECT *srcRect,
+ TFB_Canvas target, POINT dstPt);
+
+BOOLEAN TFB_DrawCanvas_GetFontCharData (TFB_Canvas canvas, BYTE *outData,
+ unsigned dataPitch);
+Color *TFB_DrawCanvas_ExtractPalette (TFB_Canvas canvas);
+void TFB_DrawCanvas_SetPalette (TFB_Canvas target, Color palette[256]);
+int TFB_DrawCanvas_GetTransparentIndex (TFB_Canvas canvas);
+void TFB_DrawCanvas_SetTransparentIndex (TFB_Canvas canvas, int i,
+ BOOLEAN rleaccel);
+BOOLEAN TFB_DrawCanvas_GetTransparentColor (TFB_Canvas canvas,
+ Color *color);
+void TFB_DrawCanvas_SetTransparentColor (TFB_Canvas canvas,
+ Color color, BOOLEAN rleaccel);
+void TFB_DrawCanvas_CopyTransparencyInfo (TFB_Canvas src, TFB_Canvas dst);
+void TFB_DrawCanvas_Initialize (void);
+void TFB_DrawCanvas_Lock (TFB_Canvas canvas);
+void TFB_DrawCanvas_Unlock (TFB_Canvas canvas);
+void TFB_DrawCanvas_GetScreenFormat (TFB_PixelFormat *fmt);
+int TFB_DrawCanvas_GetStride (TFB_Canvas canvas);
+void *TFB_DrawCanvas_GetLine (TFB_Canvas canvas, int line);
+Color TFB_DrawCanvas_GetPixel (TFB_Canvas canvas, int x, int y);
+BOOLEAN TFB_DrawCanvas_Intersect (TFB_Canvas canvas1, POINT c1org,
+ TFB_Canvas canvas2, POINT c2org, const RECT *interRect);
+
+BOOLEAN TFB_DrawCanvas_GetPixelColors (TFB_Canvas, Color *pixels,
+ int width, int height);
+BOOLEAN TFB_DrawCanvas_SetPixelColors (TFB_Canvas, const Color *pixels,
+ int width, int height);
+BOOLEAN TFB_DrawCanvas_GetPixelIndexes (TFB_Canvas, BYTE *data,
+ int width, int height);
+BOOLEAN TFB_DrawCanvas_SetPixelIndexes (TFB_Canvas, const BYTE *data,
+ int width, int height);
+
+const char *TFB_DrawCanvas_GetError (void);
+
+TFB_Canvas TFB_GetScreenCanvas (SCREEN screen);
+
+#endif
+
diff --git a/src/libs/graphics/tfb_prim.c b/src/libs/graphics/tfb_prim.c
new file mode 100644
index 0000000..f8a2df4
--- /dev/null
+++ b/src/libs/graphics/tfb_prim.c
@@ -0,0 +1,237 @@
+// Copyright Michael Martin, 2003
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* The original Primitive routines do various elaborate checks to
+ * ensure we're within bounds for the clipping. Since clipping is
+ * handled by the underlying TFB_Canvas implementation, we need not
+ * worry about this. */
+
+#include "gfxintrn.h"
+#include "gfx_common.h"
+#include "tfb_draw.h"
+#include "tfb_prim.h"
+#include "cmap.h"
+#include "libs/log.h"
+
+void
+TFB_Prim_Point (POINT *p, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ RECT r;
+
+ // The caller must scale the origin!
+ r.corner.x = p->x + ctxOrigin.x;
+ r.corner.y = p->y + ctxOrigin.y;
+ r.extent.width = r.extent.height = 1;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ TFB_DrawScreen_Rect (&r, color, mode, TFB_SCREEN_MAIN);
+ else
+ TFB_DrawImage_Rect (&r, color, mode, _CurFramePtr->image);
+}
+
+void
+TFB_Prim_Rect (RECT *r, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ RECT arm;
+ int gscale;
+
+ // XXX: Rect prim scaling is currently unused
+ // We scale the rect size just to be consistent with stamp prim,
+ // which does same. The caller must scale the origin!
+ gscale = GetGraphicScale ();
+ arm = *r;
+ arm.extent.width = r->extent.width;
+ arm.extent.height = 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+ arm.extent.height = r->extent.height;
+ arm.extent.width = 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+ // rounding error correction here
+ arm.corner.x += ((r->extent.width * gscale + (GSCALE_IDENTITY >> 1))
+ / GSCALE_IDENTITY) - 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+ arm.corner.x = r->corner.x;
+ arm.corner.y += ((r->extent.height * gscale + (GSCALE_IDENTITY >> 1))
+ / GSCALE_IDENTITY) - 1;
+ arm.extent.width = r->extent.width;
+ arm.extent.height = 1;
+ TFB_Prim_FillRect (&arm, color, mode, ctxOrigin);
+}
+
+void
+TFB_Prim_FillRect (RECT *r, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ RECT rect;
+ int gscale;
+
+ rect.corner.x = r->corner.x + ctxOrigin.x;
+ rect.corner.y = r->corner.y + ctxOrigin.y;
+ rect.extent.width = r->extent.width;
+ rect.extent.height = r->extent.height;
+
+ // XXX: Rect prim scaling is currently unused
+ // We scale the rect size just to be consistent with stamp prim,
+ // which does same. The caller must scale the origin!
+ gscale = GetGraphicScale ();
+ if (gscale != GSCALE_IDENTITY)
+ { // rounding error correction here
+ rect.extent.width = (rect.extent.width * gscale
+ + (GSCALE_IDENTITY >> 1)) / GSCALE_IDENTITY;
+ rect.extent.height = (rect.extent.height * gscale
+ + (GSCALE_IDENTITY >> 1)) / GSCALE_IDENTITY;
+ }
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ TFB_DrawScreen_Rect (&rect, color, mode, TFB_SCREEN_MAIN);
+ else
+ TFB_DrawImage_Rect (&rect, color, mode, _CurFramePtr->image);
+}
+
+void
+TFB_Prim_Line (LINE *line, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ int x1, y1, x2, y2;
+
+ // The caller must scale the origins!
+ x1=line->first.x + ctxOrigin.x;
+ y1=line->first.y + ctxOrigin.y;
+ x2=line->second.x + ctxOrigin.x;
+ y2=line->second.y + ctxOrigin.y;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ TFB_DrawScreen_Line (x1, y1, x2, y2, color, mode, TFB_SCREEN_MAIN);
+ else
+ TFB_DrawImage_Line (x1, y1, x2, y2, color, mode, _CurFramePtr->image);
+}
+
+void
+TFB_Prim_Stamp (STAMP *stmp, DrawMode mode, POINT ctxOrigin)
+{
+ int x, y;
+ FRAME SrcFramePtr;
+ TFB_Image *img;
+ TFB_ColorMap *cmap = NULL;
+
+ SrcFramePtr = stmp->frame;
+ if (!SrcFramePtr)
+ {
+ log_add (log_Warning, "TFB_Prim_Stamp: Tried to draw a NULL frame"
+ " (Stamp address = %p)", (void *) stmp);
+ return;
+ }
+ img = SrcFramePtr->image;
+
+ if (!img)
+ {
+ log_add (log_Warning, "Non-existent image to TFB_Prim_Stamp()");
+ return;
+ }
+
+ LockMutex (img->mutex);
+
+ img->NormalHs = SrcFramePtr->HotSpot;
+ // We scale the image size here, but the caller must scale the origin!
+ x = stmp->origin.x + ctxOrigin.x;
+ y = stmp->origin.y + ctxOrigin.y;
+
+ if (TFB_DrawCanvas_IsPaletted(img->NormalImg) && img->colormap_index != -1)
+ {
+ // returned cmap is addrefed, must release later
+ cmap = TFB_GetColorMap (img->colormap_index);
+ }
+
+ UnlockMutex (img->mutex);
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ TFB_DrawScreen_Image (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), cmap, mode, TFB_SCREEN_MAIN);
+ }
+ else
+ {
+ TFB_DrawImage_Image (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), cmap, mode, _CurFramePtr->image);
+ }
+}
+
+void
+TFB_Prim_StampFill (STAMP *stmp, Color color, DrawMode mode, POINT ctxOrigin)
+{
+ int x, y;
+ FRAME SrcFramePtr;
+ TFB_Image *img;
+
+ SrcFramePtr = stmp->frame;
+ if (!SrcFramePtr)
+ {
+ log_add (log_Warning, "TFB_Prim_StampFill: Tried to draw a NULL frame"
+ " (Stamp address = %p)", (void *) stmp);
+ return;
+ }
+ img = SrcFramePtr->image;
+
+ if (!img)
+ {
+ log_add (log_Warning, "Non-existent image to TFB_Prim_StampFill()");
+ return;
+ }
+
+ LockMutex (img->mutex);
+
+ img->NormalHs = SrcFramePtr->HotSpot;
+ // We scale the image size here, but the caller must scale the origin!
+ x = stmp->origin.x + ctxOrigin.x;
+ y = stmp->origin.y + ctxOrigin.y;
+
+ UnlockMutex (img->mutex);
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ TFB_DrawScreen_FilledImage (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), color, mode, TFB_SCREEN_MAIN);
+ }
+ else
+ {
+ TFB_DrawImage_FilledImage (img, x, y, GetGraphicScale (),
+ GetGraphicScaleMode (), color, mode, _CurFramePtr->image);
+ }
+}
+
+void
+TFB_Prim_FontChar (POINT charOrigin, TFB_Char *fontChar, TFB_Image *backing,
+ DrawMode mode, POINT ctxOrigin)
+{
+ int x, y;
+
+ // Text prim does not scale
+ x = charOrigin.x + ctxOrigin.x;
+ y = charOrigin.y + ctxOrigin.y;
+
+ if (_CurFramePtr->Type == SCREEN_DRAWABLE)
+ {
+ TFB_DrawScreen_FontChar (fontChar, backing, x, y, mode,
+ TFB_SCREEN_MAIN);
+ }
+ else
+ {
+ TFB_DrawImage_FontChar (fontChar, backing, x, y, mode,
+ _CurFramePtr->image);
+ }
+}
+
+// Text rendering is in font.c, under the name _text_blt
diff --git a/src/libs/graphics/tfb_prim.h b/src/libs/graphics/tfb_prim.h
new file mode 100644
index 0000000..6fcd546
--- /dev/null
+++ b/src/libs/graphics/tfb_prim.h
@@ -0,0 +1,30 @@
+// Copyright Michael Martin, 2003
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/gfxlib.h"
+#include "tfb_draw.h"
+
+
+void TFB_Prim_Line (LINE *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_Point (POINT *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_Rect (RECT *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_FillRect (RECT *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_Stamp (STAMP *, DrawMode, POINT ctxOrigin);
+void TFB_Prim_StampFill (STAMP *, Color, DrawMode, POINT ctxOrigin);
+void TFB_Prim_FontChar (POINT charOrigin, TFB_Char *fontChar,
+ TFB_Image *backing, DrawMode, POINT ctxOrigin);
diff --git a/src/libs/graphics/widgets.c b/src/libs/graphics/widgets.c
new file mode 100644
index 0000000..0c444bc
--- /dev/null
+++ b/src/libs/graphics/widgets.c
@@ -0,0 +1,941 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gfx_common.h"
+#include "widgets.h"
+#include "libs/strlib.h"
+
+WIDGET *widget_focus = NULL;
+
+/* Some basic color defines */
+#define WIDGET_ACTIVE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x0E)
+#define WIDGET_INACTIVE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x18, 0x18, 0x1F), 0x00)
+#define WIDGET_INACTIVE_SELECTED_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+#define WIDGET_CURSOR_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+#define WIDGET_DIALOG_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x07)
+#define WIDGET_DIALOG_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+
+static Color win_bg_clr =
+ BUILD_COLOR (MAKE_RGB15_INIT (0x18, 0x18, 0x1F), 0x00);
+static Color win_medium_clr =
+ BUILD_COLOR (MAKE_RGB15_INIT (0x10, 0x10, 0x18), 0x00);
+static Color win_dark_clr =
+ BUILD_COLOR (MAKE_RGB15_INIT (0x08, 0x08, 0x10), 0x00);
+
+static FONT cur_font;
+
+void
+DrawShadowedBox (RECT *r, Color bg, Color dark, Color medium)
+{
+ RECT t;
+ Color oldcolor;
+
+ BatchGraphics ();
+
+ t.corner.x = r->corner.x - 2;
+ t.corner.y = r->corner.y - 2;
+ t.extent.width = r->extent.width + 4;
+ t.extent.height = r->extent.height + 4;
+ oldcolor = SetContextForeGroundColor (dark);
+ DrawFilledRectangle (&t);
+
+ t.corner.x += 2;
+ t.corner.y += 2;
+ t.extent.width -= 2;
+ t.extent.height -= 2;
+ SetContextForeGroundColor (medium);
+ DrawFilledRectangle (&t);
+
+ t.corner.x -= 1;
+ t.corner.y += r->extent.height + 1;
+ t.extent.height = 1;
+ DrawFilledRectangle (&t);
+
+ t.corner.x += r->extent.width + 2;
+ t.corner.y -= r->extent.height + 2;
+ t.extent.width = 1;
+ DrawFilledRectangle (&t);
+
+ SetContextForeGroundColor (bg);
+ DrawFilledRectangle (r);
+
+ SetContextForeGroundColor (oldcolor);
+ UnbatchGraphics ();
+}
+
+// windowRect, if not NULL, will be filled with the dimensions of the
+// window drawn.
+void
+DrawLabelAsWindow (WIDGET_LABEL *label, RECT *windowRect)
+{
+ Color oldfg = SetContextForeGroundColor (WIDGET_DIALOG_TEXT_COLOR);
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ RECT r;
+ TEXT t;
+ int i, win_w, win_h;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ /* Compute the dimensions of the label */
+ win_h = label->height ((WIDGET *)label) + 16;
+ win_w = 0;
+ for (i = 0; i < label->line_count; i++)
+ {
+ int len = utf8StringCount (label->lines[i]);
+ if (len > win_w)
+ {
+ win_w = len;
+ }
+ }
+ win_w = (win_w * 6) + 16;
+
+ BatchGraphics ();
+ r.corner.x = (ScreenWidth - win_w) >> 1;
+ r.corner.y = (ScreenHeight - win_h) >> 1;
+ r.extent.width = win_w;
+ r.extent.height = win_h;
+ DrawShadowedBox (&r, win_bg_clr, win_dark_clr, win_medium_clr);
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + 16;
+ for (i = 0; i < label->line_count; i++)
+ {
+ t.pStr = label->lines[i];
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 8;
+ }
+
+ UnbatchGraphics ();
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldfg);
+
+ if (windowRect != NULL) {
+ // Add the outer border added by DrawShadowedBox.
+ // XXX: It may be nicer to add a border size parameter to
+ // DrawShadowedBox, instead of assuming 2 here.
+ windowRect->corner.x = r.corner.x - 2;
+ windowRect->corner.y = r.corner.y - 2;
+ windowRect->extent.width = r.extent.width + 4;
+ windowRect->extent.height = r.extent.height + 4;
+ }
+}
+
+void
+Widget_SetWindowColors (Color bg, Color dark, Color medium)
+{
+ win_bg_clr = bg;
+ win_dark_clr = dark;
+ win_medium_clr = medium;
+}
+
+FONT
+Widget_SetFont (FONT newFont)
+{
+ FONT oldFont = cur_font;
+ cur_font = newFont;
+ return oldFont;
+}
+
+static void
+Widget_DrawToolTips (int numlines, const char **tips)
+{
+ RECT r;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR);
+ TEXT t;
+ int i;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ r.corner.x = 2;
+ r.corner.y = 2;
+ r.extent.width = ScreenWidth - 4;
+ r.extent.height = ScreenHeight - 4;
+
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + (r.extent.height - 8 - 8 * numlines);
+
+ for (i = 0; i < numlines; i++)
+ {
+ t.pStr = tips[i];
+ font_DrawText(&t);
+ t.baseline.y += 8;
+ }
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_DrawMenuScreen (WIDGET *_self, int x, int y)
+{
+ RECT r;
+ Color title, oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int widget_index, height, widget_y;
+
+ WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ r.corner.x = 2;
+ r.corner.y = 2;
+ r.extent.width = ScreenWidth - 4;
+ r.extent.height = ScreenHeight - 4;
+
+ title = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+ default_color = title;
+
+ DrawStamp (&self->bgStamp);
+
+ oldtext = SetContextForeGroundColor (title);
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + 8;
+ t.pStr = self->title;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ font_DrawText (&t);
+ t.baseline.y += 8;
+ t.pStr = self->subtitle;
+ font_DrawText (&t);
+
+ height = 0;
+ for (widget_index = 0; widget_index < self->num_children; widget_index++)
+ {
+ WIDGET *child = self->child[widget_index];
+ height += (*child->height)(child);
+ height += 8; /* spacing */
+ }
+
+ height -= 8;
+
+ widget_y = (ScreenHeight - height) >> 1;
+ for (widget_index = 0; widget_index < self->num_children; widget_index++)
+ {
+ WIDGET *c = self->child[widget_index];
+ (*c->draw)(c, 0, widget_y);
+ widget_y += (*c->height)(c) + 8;
+ }
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+
+ (void) x;
+ (void) y;
+}
+
+void
+Widget_DrawChoice (WIDGET *_self, int x, int y)
+{
+ WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int i, home_x, home_y;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ home_x = t.baseline.x + 3 * (ScreenWidth / ((self->maxcolumns + 1) * 2));
+ home_y = t.baseline.y;
+ t.align = ALIGN_CENTER;
+ for (i = 0; i < self->numopts; i++)
+ {
+ t.baseline.x = home_x + ((i % 3) *
+ (ScreenWidth / (self->maxcolumns + 1)));
+ t.baseline.y = home_y + (8 * (i / 3));
+ t.pStr = self->options[i].optname;
+ if ((widget_focus == _self) &&
+ (self->highlighted == i))
+ {
+ SetContextForeGroundColor (selected);
+ Widget_DrawToolTips (3, self->options[i].tooltip);
+ }
+ else if (i == self->selected)
+ {
+ SetContextForeGroundColor (default_color);
+ }
+ else
+ {
+ SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ }
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_DrawButton (WIDGET *_self, int x, int y)
+{
+ WIDGET_BUTTON *self = (WIDGET_BUTTON *)_self;
+ Color oldtext;
+ Color inactive, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = 160;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.pStr = self->name;
+ if (widget_focus == _self)
+ {
+ Widget_DrawToolTips (3, self->tooltip);
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+ (void) x;
+}
+
+void
+Widget_DrawLabel (WIDGET *_self, int x, int y)
+{
+ WIDGET_LABEL *self = (WIDGET_LABEL *)_self;
+ Color oldtext = SetContextForeGroundColor (WIDGET_INACTIVE_SELECTED_COLOR);
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int i;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ t.baseline.x = 160;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+
+ for (i = 0; i < self->line_count; i++)
+ {
+ t.pStr = self->lines[i];
+ font_DrawText (&t);
+ t.baseline.y += 8;
+ }
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+ (void) x;
+}
+
+void
+Widget_DrawSlider(WIDGET *_self, int x, int y)
+{
+ WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ RECT r;
+ int tick = (ScreenWidth - x) / 8;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ Widget_DrawToolTips (3, self->tooltip);
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ r.corner.x = t.baseline.x + 3 * tick;
+ r.corner.y = t.baseline.y - 4;
+ r.extent.height = 2;
+ r.extent.width = 3 * tick;
+ DrawFilledRectangle (&r);
+
+ r.extent.width = 3;
+ r.extent.height = 8;
+ r.corner.y = t.baseline.y - 7;
+ r.corner.x = t.baseline.x + 3 * tick + (3 * tick * (self->value - self->min) /
+ (self->max - self->min)) - 1;
+ DrawFilledRectangle (&r);
+
+ (*self->draw_value)(self, t.baseline.x + 7 * tick, t.baseline.y);
+
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y)
+{
+ TEXT t;
+ char buffer[16];
+
+ sprintf (buffer, "%d", self->value);
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.pStr = buffer;
+
+ font_DrawText (&t);
+}
+
+void
+Widget_DrawTextEntry (WIDGET *_self, int x, int y)
+{
+ WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ BatchGraphics ();
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ /* Force string termination */
+ self->value[WIDGET_TEXTENTRY_WIDTH-1] = 0;
+
+ t.baseline.y = y;
+ t.CharCount = utf8StringCount (self->value);
+ t.pStr = self->value;
+
+ if (!(self->state & WTE_EDITING))
+ { // normal or selected state
+ t.baseline.x = 160;
+ t.align = ALIGN_CENTER;
+
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ }
+ else
+ { // editing state
+ COUNT i;
+ RECT text_r;
+ BYTE char_deltas[WIDGET_TEXTENTRY_WIDTH];
+ BYTE *pchar_deltas;
+ RECT r;
+ SIZE leading;
+
+ t.baseline.x = 90;
+ t.align = ALIGN_LEFT;
+
+ // calc background box dimensions
+ // XXX: this may need some tuning, especially if a
+ // different font is used. The font 'leading' values
+ // are not what they should be.
+#define BOX_VERT_OFFSET 2
+ GetContextFontLeading (&leading);
+ r.corner.x = t.baseline.x - 1;
+ r.corner.y = t.baseline.y - leading + BOX_VERT_OFFSET;
+ r.extent.width = ScreenWidth - r.corner.x - 10;
+ r.extent.height = leading + 2;
+
+ TextRect (&t, &text_r, char_deltas);
+#if 0
+ // XXX: this should potentially be used in ChangeCallback
+ if ((text_r.extent.width + 2) >= r.extent.width)
+ { // the text does not fit the input box size and so
+ // will not fit when displayed later
+ UnbatchGraphics ();
+ // disallow the change
+ return (FALSE);
+ }
+#endif
+
+ oldtext = SetContextForeGroundColor (selected);
+ DrawFilledRectangle (&r);
+
+ // calculate the cursor position and draw it
+ pchar_deltas = char_deltas;
+ for (i = self->cursor_pos; i > 0; --i)
+ r.corner.x += (SIZE)*pchar_deltas++;
+ if (self->cursor_pos < t.CharCount) /* cursor mid-line */
+ --r.corner.x;
+ if (self->state & WTE_BLOCKCUR)
+ { // Use block cursor for keyboardless systems
+ if (self->cursor_pos == t.CharCount)
+ { // cursor at end-line -- use insertion point
+ r.extent.width = 1;
+ }
+ else if (self->cursor_pos + 1 == t.CharCount)
+ { // extra pixel for last char margin
+ r.extent.width = (SIZE)*pchar_deltas + 2;
+ }
+ else
+ { // normal mid-line char
+ r.extent.width = (SIZE)*pchar_deltas + 1;
+ }
+ }
+ else
+ { // Insertion point cursor
+ r.extent.width = 1;
+ }
+ // position cursor within input field rect
+ ++r.corner.x;
+ ++r.corner.y;
+ r.extent.height -= 2;
+ SetContextForeGroundColor (WIDGET_CURSOR_COLOR);
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (inactive);
+ font_DrawText (&t);
+ }
+
+ UnbatchGraphics ();
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+void
+Widget_DrawControlEntry (WIDGET *_self, int x, int y)
+{
+ WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = 0;
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ int i, home_x, home_y;
+
+ if (cur_font)
+ oldfont = SetContextFont (cur_font);
+
+ default_color = WIDGET_INACTIVE_SELECTED_COLOR;
+ selected = WIDGET_ACTIVE_COLOR;
+ inactive = WIDGET_INACTIVE_COLOR;
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ // 3 * ScreenWidth / ((self->maxcolumns + 1) * 2)) as per CHOICE, but only two options.
+ home_x = t.baseline.x + (ScreenWidth / 2);
+ home_y = t.baseline.y;
+ t.align = ALIGN_CENTER;
+ for (i = 0; i < 2; i++)
+ {
+ t.baseline.x = home_x + ((i % 3) * (ScreenWidth / 3)); // self->maxcolumns + 1 as per CHOICE.
+ t.baseline.y = home_y + (8 * (i / 3));
+ t.pStr = self->controlname[i];
+ if (!t.pStr[0])
+ {
+ t.pStr = "---";
+ }
+ if ((widget_focus == _self) &&
+ (self->highlighted == i))
+ {
+ SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+ }
+ SetContextFontEffect (oldFontEffect);
+ if (oldfont)
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+int
+Widget_HeightChoice (WIDGET *_self)
+{
+ return ((((WIDGET_CHOICE *)_self)->numopts + 2) / 3) * 8;
+}
+
+int
+Widget_HeightFullScreen (WIDGET *_self)
+{
+ (void)_self;
+ return ScreenHeight;
+}
+
+int
+Widget_HeightOneLine (WIDGET *_self)
+{
+ (void)_self;
+ return 8;
+}
+
+int
+Widget_HeightLabel (WIDGET *_self)
+{
+ WIDGET_LABEL *self = (WIDGET_LABEL *)_self;
+ return self->line_count * 8;
+}
+
+int
+Widget_WidthFullScreen (WIDGET *_self)
+{
+ (void)_self;
+ return ScreenWidth;
+}
+
+int
+Widget_ReceiveFocusSimple (WIDGET *_self, int event)
+{
+ widget_focus = _self;
+ (void)event;
+ return TRUE;
+}
+
+int
+Widget_ReceiveFocusChoice (WIDGET *_self, int event)
+{
+ WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self;
+ widget_focus = _self;
+ self->highlighted = self->selected;
+ (void)event;
+ return TRUE;
+}
+
+int
+Widget_ReceiveFocusControlEntry (WIDGET *_self, int event)
+{
+ WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self;
+ int oldval = 0;
+ if (widget_focus->tag == WIDGET_TYPE_CONTROLENTRY)
+ {
+ oldval = ((WIDGET_CONTROLENTRY *)widget_focus)->highlighted;
+ }
+ widget_focus = _self;
+ self->highlighted = oldval;
+ (void)event;
+ return TRUE;
+}
+
+int
+Widget_ReceiveFocusMenuScreen (WIDGET *_self, int event)
+{
+ WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
+ int x, last_x, dx;
+ for (x = 0; x < self->num_children; x++)
+ {
+ self->child[x]->parent = _self;
+ }
+ if (event == WIDGET_EVENT_UP)
+ {
+ x = self->num_children - 1;
+ dx = -1;
+ last_x = -1;
+ }
+ else if (event == WIDGET_EVENT_DOWN)
+ {
+ x = 0;
+ dx = 1;
+ last_x = self->num_children;
+ }
+ else
+ {
+ /* Leave highlighted value the same */
+ WIDGET *child = self->child[self->highlighted];
+ child->receiveFocus (child, event);
+ return TRUE;
+ }
+ for ( ; x != last_x; x += dx)
+ {
+ WIDGET *child = self->child[x];
+ if ((*child->receiveFocus)(child, event))
+ {
+ self->highlighted = x;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+int
+Widget_ReceiveFocusRefuseFocus (WIDGET *self, int event)
+{
+ (void)self;
+ (void)event;
+ return FALSE;
+}
+
+int
+Widget_HandleEventIgnoreAll (WIDGET *self, int event)
+{
+ (void)event;
+ (void)self;
+ return FALSE;
+}
+
+int
+Widget_HandleEventChoice (WIDGET *_self, int event)
+{
+ WIDGET_CHOICE *self = (WIDGET_CHOICE *)_self;
+ switch (event)
+ {
+ case WIDGET_EVENT_LEFT:
+ self->highlighted -= 1;
+ if (self->highlighted < 0)
+ self->highlighted = self->numopts - 1;
+ return TRUE;
+ case WIDGET_EVENT_RIGHT:
+ self->highlighted += 1;
+ if (self->highlighted >= self->numopts)
+ self->highlighted = 0;
+ return TRUE;
+ case WIDGET_EVENT_SELECT:
+ {
+ int oldval = self->selected;
+ self->selected = self->highlighted;
+ if (self->onChange)
+ {
+ (*(self->onChange))(self, oldval);
+ }
+ return TRUE;
+ }
+ default:
+ return FALSE;
+ }
+}
+
+int
+Widget_HandleEventSlider (WIDGET *_self, int event)
+{
+ WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
+ switch (event)
+ {
+ case WIDGET_EVENT_LEFT:
+ self->value -= self->step;
+ if (self->value < self->min)
+ self->value = self->min;
+ return TRUE;
+ case WIDGET_EVENT_RIGHT:
+ self->value += self->step;
+ if (self->value > self->max)
+ self->value = self->max;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+int
+Widget_HandleEventMenuScreen (WIDGET *_self, int event)
+{
+ WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
+ int x, last_x, dx;
+ switch (event)
+ {
+ case WIDGET_EVENT_UP:
+ dx = -1;
+ break;
+ case WIDGET_EVENT_DOWN:
+ dx = 1;
+ break;
+ case WIDGET_EVENT_CANCEL:
+ /* On cancel, shift focus to last element and send a SELECT. */
+ self->highlighted = self->num_children - 1;
+ widget_focus = self->child[self->highlighted];
+ return (widget_focus->handleEvent)(widget_focus, WIDGET_EVENT_SELECT);
+ default:
+ return FALSE;
+ }
+ last_x = self->highlighted;
+ x = self->highlighted + dx;
+ while (x != last_x)
+ {
+ WIDGET *child;
+ if (x == -1)
+ x = self->num_children - 1;
+ if (x == self->num_children)
+ x = 0;
+ child = self->child[x];
+ if ((*child->receiveFocus)(child, event))
+ {
+ self->highlighted = x;
+ return TRUE;
+ }
+ x += dx;
+ }
+ return FALSE;
+}
+
+int
+Widget_HandleEventTextEntry (WIDGET *_self, int event)
+{
+ WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self;
+ if (event == WIDGET_EVENT_SELECT) {
+ if (!self->handleEventSelect)
+ return FALSE;
+ return (*self->handleEventSelect)(self);
+ }
+ return FALSE;
+}
+
+int
+Widget_HandleEventControlEntry (WIDGET *_self, int event)
+{
+ WIDGET_CONTROLENTRY *self = (WIDGET_CONTROLENTRY *)_self;
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ if (self->onChange)
+ {
+ (self->onChange)(self);
+ return TRUE;
+ }
+ }
+ if (event == WIDGET_EVENT_DELETE)
+ {
+ if (self->onDelete)
+ {
+ (self->onDelete)(self);
+ return TRUE;
+ }
+ }
+ if ((event == WIDGET_EVENT_RIGHT) ||
+ (event == WIDGET_EVENT_LEFT))
+ {
+ self->highlighted = 1-self->highlighted;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+Widget_Event (int event)
+{
+ WIDGET *widget = widget_focus;
+ while (widget != NULL)
+ {
+ if ((*widget->handleEvent)(widget, event))
+ return TRUE;
+ widget = widget->parent;
+ }
+ return FALSE;
+}
diff --git a/src/libs/graphics/widgets.h b/src/libs/graphics/widgets.h
new file mode 100644
index 0000000..4548e35
--- /dev/null
+++ b/src/libs/graphics/widgets.h
@@ -0,0 +1,222 @@
+// Copyright Michael Martin, 2004.
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_GRAPHICS_WIDGETS_H_
+#define LIBS_GRAPHICS_WIDGETS_H_
+
+#include "libs/gfxlib.h"
+
+enum {
+ WIDGET_EVENT_UP,
+ WIDGET_EVENT_DOWN,
+ WIDGET_EVENT_LEFT,
+ WIDGET_EVENT_RIGHT,
+ WIDGET_EVENT_SELECT,
+ WIDGET_EVENT_CANCEL,
+ WIDGET_EVENT_DELETE,
+ NUM_WIDGET_EVENTS
+};
+
+typedef enum {
+ WIDGET_TYPE_MENU_SCREEN,
+ WIDGET_TYPE_CHOICE,
+ WIDGET_TYPE_BUTTON,
+ WIDGET_TYPE_LABEL,
+ WIDGET_TYPE_SLIDER,
+ WIDGET_TYPE_TEXTENTRY,
+ WIDGET_TYPE_CONTROLENTRY,
+ NUM_WIDGET_TYPES
+} WIDGET_TYPE;
+
+#define WIDGET_TEXTENTRY_WIDTH 50
+#define WIDGET_CONTROLENTRY_WIDTH 16
+
+typedef struct _widget {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+} WIDGET;
+
+typedef struct _widget_menu_screen {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ const char *title;
+ const char *subtitle;
+ STAMP bgStamp;
+ int num_children;
+ struct _widget **child;
+ int highlighted;
+} WIDGET_MENU_SCREEN;
+
+typedef struct {
+ const char *optname;
+ const char *tooltip[3];
+} CHOICE_OPTION;
+
+typedef struct _widget_choice {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ const char *category;
+ int numopts;
+ int maxcolumns;
+ CHOICE_OPTION *options;
+ int selected, highlighted;
+ void (*onChange)(struct _widget_choice *self, int oldval);
+} WIDGET_CHOICE;
+
+typedef struct _widget_button {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ const char *name;
+ const char *tooltip[3];
+} WIDGET_BUTTON;
+
+typedef struct _widget_label {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ int line_count;
+ const char **lines;
+} WIDGET_LABEL;
+
+typedef struct _widget_slider {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ void (*draw_value)(struct _widget_slider *self, int x, int y);
+ int min, max, step;
+ int value;
+ const char *category;
+ const char *tooltip[3];
+} WIDGET_SLIDER;
+
+typedef enum {
+ WTE_NORMAL = 0,
+ WTE_EDITING,
+ WTE_BLOCKCUR,
+
+} WIDGET_TEXTENTRY_STATE;
+
+typedef struct _widget_textentry {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ int (*handleEventSelect)(struct _widget_textentry *self);
+ // handleEventSelect is an overridable callback event
+ // called by the default handleEvent implementation
+ // can be NULL, in which case SELECT is ignored
+ void (*onChange)(struct _widget_textentry *self);
+ const char *category;
+ char value[WIDGET_TEXTENTRY_WIDTH];
+ int maxlen;
+ WIDGET_TEXTENTRY_STATE state;
+ int cursor_pos;
+} WIDGET_TEXTENTRY;
+
+typedef struct _widget_controlentry {
+ WIDGET_TYPE tag;
+ struct _widget *parent;
+ int (*handleEvent)(struct _widget *self, int event);
+ int (*receiveFocus)(struct _widget *self, int event);
+ void (*draw)(struct _widget *self, int x, int y);
+ int (*height)(struct _widget *self);
+ int (*width)(struct _widget *self);
+ void (*onChange)(struct _widget_controlentry *self);
+ void (*onDelete)(struct _widget_controlentry *self);
+ const char *category;
+ int controlindex;
+ int highlighted;
+ char controlname[2][WIDGET_CONTROLENTRY_WIDTH];
+} WIDGET_CONTROLENTRY;
+
+void DrawShadowedBox (RECT *r, Color bg, Color dark, Color medium);
+void DrawLabelAsWindow (WIDGET_LABEL *label, RECT *windowRect);
+void Widget_SetWindowColors (Color bg, Color dark, Color medium);
+FONT Widget_SetFont (FONT newFont);
+
+
+int Widget_Event (int event);
+
+/* Methods for filling in widgets with */
+
+int Widget_ReceiveFocusMenuScreen (WIDGET *_self, int event);
+int Widget_ReceiveFocusChoice (WIDGET *_self, int event);
+int Widget_ReceiveFocusSimple (WIDGET *_self, int event);
+int Widget_ReceiveFocusSlider (WIDGET *_self, int event);
+int Widget_ReceiveFocusControlEntry (WIDGET *_self, int event);
+int Widget_ReceiveFocusRefuseFocus (WIDGET *_self, int event);
+
+int Widget_HandleEventMenuScreen (WIDGET *_self, int event);
+int Widget_HandleEventChoice (WIDGET *_self, int event);
+int Widget_HandleEventSlider (WIDGET *_self, int event);
+int Widget_HandleEventTextEntry (WIDGET *_self, int event);
+int Widget_HandleEventControlEntry (WIDGET *_self, int event);
+int Widget_HandleEventIgnoreAll (WIDGET *_self, int event);
+
+int Widget_HeightChoice (WIDGET *_self);
+int Widget_HeightFullScreen (WIDGET *_self);
+int Widget_HeightOneLine (WIDGET *_self);
+int Widget_HeightLabel (WIDGET *_self);
+
+int Widget_WidthFullScreen (WIDGET *_self);
+
+void Widget_DrawMenuScreen (WIDGET *_self, int x, int y);
+void Widget_DrawChoice (WIDGET *_self, int x, int y);
+void Widget_DrawButton (WIDGET *_self, int x, int y);
+void Widget_DrawLabel (WIDGET *_self, int x, int y);
+void Widget_DrawSlider (WIDGET *_self, int x, int y);
+void Widget_DrawTextEntry (WIDGET *_self, int x, int y);
+void Widget_DrawControlEntry (WIDGET *_self, int x, int y);
+
+void Widget_Slider_DrawValue (WIDGET_SLIDER *self, int x, int y);
+
+/* Other implementations will need these values */
+extern WIDGET *widget_focus;
+
+#endif /* LIBS_GRAPHICS_WIDGETS_H_ */
diff --git a/src/libs/heap.h b/src/libs/heap.h
new file mode 100644
index 0000000..674649b
--- /dev/null
+++ b/src/libs/heap.h
@@ -0,0 +1,9 @@
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "heap/heap.h"
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/libs/heap/Makeinfo b/src/libs/heap/Makeinfo
new file mode 100644
index 0000000..1622e1c
--- /dev/null
+++ b/src/libs/heap/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="heap.c"
+uqm_HFILES="heap.h"
diff --git a/src/libs/heap/heap.c b/src/libs/heap/heap.c
new file mode 100644
index 0000000..1f4c0f7
--- /dev/null
+++ b/src/libs/heap/heap.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "heap.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include "port.h"
+
+static inline size_t nextPower2(size_t x);
+
+
+static void
+Heap_resize(Heap *heap, size_t size) {
+ heap->entries = realloc(heap->entries, size * sizeof (HeapValue *));
+ heap->size = size;
+}
+
+// Heap inv: comparator(parent, child) <= 0 for every child of every parent.
+Heap *
+Heap_new(HeapValue_Comparator comparator, size_t initialSize, size_t minSize,
+ double minFillQuotient) {
+ Heap *heap;
+
+ assert(minFillQuotient >= 0.0);
+
+ heap = malloc(sizeof (Heap));
+
+ if (initialSize < minSize)
+ initialSize = minSize;
+
+ heap->comparator = comparator;
+ heap->minSize = minSize;
+ heap->minFillQuotient = minFillQuotient;
+ heap->size = nextPower2(initialSize);
+ heap->minFill = ceil(((double) (heap->size >> 1))
+ * heap->minFillQuotient);
+ heap->entries = malloc(heap->size * sizeof (HeapValue *));
+ heap->numEntries = 0;
+
+ return heap;
+}
+
+void
+Heap_delete(Heap *heap) {
+ free(heap->entries);
+ free(heap);
+}
+
+void
+Heap_add(Heap *heap, HeapValue *value) {
+ size_t i;
+
+ if (heap->numEntries >= heap->size)
+ Heap_resize(heap, heap->size * 2);
+
+ i = heap->numEntries;
+ heap->numEntries++;
+
+ while (i > 0) {
+ size_t parentI = (i - 1) / 2;
+ if (heap->comparator(heap->entries[parentI], value) <= 0)
+ break;
+
+ heap->entries[i] = heap->entries[parentI];
+ heap->entries[i]->index = i;
+ i = parentI;
+ }
+ heap->entries[i] = value;
+ heap->entries[i]->index = i;
+}
+
+HeapValue *
+Heap_first(const Heap *heap) {
+ assert(heap->numEntries > 0);
+
+ return heap->entries[0];
+}
+
+static void
+Heap_removeByIndex(Heap *heap, size_t i) {
+ assert(heap->numEntries > i);
+
+ heap->numEntries--;
+
+ if (heap->numEntries != 0) {
+ // Restore the heap invariant. We're shifting entries into the
+ // gap that was created until we find the place where we can
+ // insert the last entry.
+ HeapValue *lastEntry = heap->entries[heap->numEntries];
+
+ for (;;) {
+ size_t childI = i * 2 + 1;
+ // The two children are childI and 'childI + 1'.
+
+ if (childI + 1 >= heap->numEntries) {
+ // There is no right child.
+
+ if (childI >= heap->numEntries) {
+ // There is no left child either.
+ break;
+ }
+ } else {
+ if (heap->comparator(heap->entries[childI + 1],
+ heap->entries[childI]) < 0) {
+ // The right child is the child with the lowest value.
+ childI++;
+ }
+ }
+ // childI is now the child with the lowest value.
+
+ if (heap->comparator(lastEntry, heap->entries[childI]) <= 0) {
+ // The last entry goes here.
+ break;
+ }
+
+ // Move the child into the gap.
+ heap->entries[i] = heap->entries[childI];
+ heap->entries[i]->index = i;
+
+ // and repeat for the child.
+ i = childI;
+ }
+
+ // Fill the gap with the last entry.
+ heap->entries[i] = lastEntry;
+ heap->entries[i]->index = i;
+ }
+
+ // Resize if necessary:
+ if (heap->numEntries < heap->minFill &&
+ heap->numEntries > heap->minSize)
+ Heap_resize(heap, heap->size / 2);
+}
+
+HeapValue *
+Heap_pop(Heap *heap) {
+ HeapValue *result;
+
+ assert(heap->numEntries > 0);
+
+ result = heap->entries[0];
+ Heap_removeByIndex(heap, 0);
+
+ return result;
+}
+
+size_t
+Heap_count(const Heap *heap) {
+ return heap->numEntries;
+}
+
+bool
+Heap_hasMore(const Heap *heap) {
+ return heap->numEntries > 0;
+}
+
+void
+Heap_remove(Heap *heap, HeapValue *value) {
+ Heap_removeByIndex(heap, value->index);
+}
+
+// Adapted from "Hackers Delight"
+// Returns the smallest power of two greater or equal to x.
+static inline size_t
+nextPower2(size_t x) {
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+# if (SIZE_MAX > 0xffff)
+ x |= x >> 16;
+# if (SIZE_MAX > 0xffffffff)
+ x |= x >> 32;
+# endif
+# endif
+ return x + 1;
+}
+
+
diff --git a/src/libs/heap/heap.h b/src/libs/heap/heap.h
new file mode 100644
index 0000000..9a3f829
--- /dev/null
+++ b/src/libs/heap/heap.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_HEAP_HEAP_H_
+#define LIBS_HEAP_HEAP_H_
+
+#include "types.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+
+
+typedef struct Heap Heap;
+typedef struct HeapValue HeapValue;
+
+// The actual value stored should "inherit" from this.
+struct HeapValue {
+ size_t index;
+};
+typedef int (*HeapValue_Comparator)(HeapValue *v1, HeapValue *v2);
+
+
+struct Heap {
+ HeapValue_Comparator comparator;
+ // Comparison function to determine the order of the
+ // elements.
+ size_t minSize;
+ // Never resize below this many number of entries.
+ double minFillQuotient;
+ // How much of half of the heap needs to be filled before
+ // resizing to size/2.
+
+ HeapValue **entries;
+ // Actual values
+ size_t numEntries;
+ size_t size;
+ // Number of entries that fit in the heap as it is now.
+ size_t minFill;
+ // Resize to size/2 when below this size.
+};
+
+
+Heap *Heap_new(HeapValue_Comparator comparator, size_t initialSize,
+ size_t minSize, double minFillQuotient);
+void Heap_delete(Heap *heap);
+void Heap_add(Heap *heap, HeapValue *value);
+HeapValue *Heap_first(const Heap *heap);
+HeapValue *Heap_pop(Heap *heap);
+size_t Heap_count(const Heap *heap);
+bool Heap_hasMore(const Heap *heap);
+void Heap_remove(Heap *heap, HeapValue *value);
+
+#endif /* LIBS_HEAP_HEAP_H_ */
+
diff --git a/src/libs/inplib.h b/src/libs/inplib.h
new file mode 100644
index 0000000..2df1f79
--- /dev/null
+++ b/src/libs/inplib.h
@@ -0,0 +1,70 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_INPLIB_H_
+#define LIBS_INPLIB_H_
+
+#include <stddef.h>
+#include "libs/compiler.h"
+#include "libs/uio.h"
+#include "libs/unicode.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+extern BOOLEAN AnyButtonPress (BOOLEAN DetectSpecial);
+
+extern void TFB_ResetControls (void);
+
+/*
+ * Not used right now
+extern BOOLEAN FindMouse (void);
+extern void MoveMouse (SWORD x, SWORD y);
+extern BYTE LocateMouse (SWORD *px, SWORD *py);
+*/
+
+extern volatile int MouseButtonDown;
+extern volatile int QuitPosted;
+extern volatile int GameActive;
+
+/* Functions for dealing with Character Mode */
+
+void EnterCharacterMode (void);
+void ExitCharacterMode (void);
+UniChar GetNextCharacter (void);
+UniChar GetLastCharacter (void);
+
+/* Interrogating the current key configuration */
+
+void InterrogateInputState (int templat, int control, int index, char *buffer, int maxlen);
+void RemoveInputState (int templat, int control, int index);
+void RebindInputState (int templat, int control, int index);
+
+void SaveKeyConfiguration (uio_DirHandle *path, const char *fname);
+
+/* Separate inputs into frames for dealing with very fast inputs */
+
+void BeginInputFrame (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_INPLIB_H */
diff --git a/src/libs/input/Makeinfo b/src/libs/input/Makeinfo
new file mode 100644
index 0000000..b76604f
--- /dev/null
+++ b/src/libs/input/Makeinfo
@@ -0,0 +1,6 @@
+if [ "$uqm_GFXMODULE" = "sdl" ]; then
+ uqm_SUBDIRS="sdl"
+fi
+
+uqm_CFILES="input_common.c"
+uqm_HFILES="inpintrn.h input_common.h"
diff --git a/src/libs/input/inpintrn.h b/src/libs/input/inpintrn.h
new file mode 100644
index 0000000..1d48ccc
--- /dev/null
+++ b/src/libs/input/inpintrn.h
@@ -0,0 +1,25 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_INPUT_INPINTRN_H_
+#define LIBS_INPUT_INPINTRN_H_
+
+#include "libs/inplib.h"
+#include "libs/input/input_common.h"
+
+#endif /* LIBS_INPUT_INPINTRN_H_ */
diff --git a/src/libs/input/input_common.c b/src/libs/input/input_common.c
new file mode 100644
index 0000000..7a0bbc1
--- /dev/null
+++ b/src/libs/input/input_common.c
@@ -0,0 +1,20 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "inpintrn.h"
diff --git a/src/libs/input/input_common.h b/src/libs/input/input_common.h
new file mode 100644
index 0000000..5979320
--- /dev/null
+++ b/src/libs/input/input_common.h
@@ -0,0 +1,39 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INPUT_COMMON_H
+#define INPUT_COMMON_H
+
+// driver for TFB_InitInput
+enum
+{
+ TFB_INPUTDRIVER_SDL
+};
+
+// flags for TFB_InitInput
+//#define TFB_INPUTFLAGS_ETC (1<<0)
+
+extern int TFB_InitInput (int driver, int flags);
+extern void TFB_UninitInput (void);
+
+#define MAX_FLIGHT_ALTERNATES 2
+
+extern void TFB_SetInputVectors (volatile int menu[], int num_menu,
+ volatile int flight[], int num_templ, int num_flight);
+
+#endif
diff --git a/src/libs/input/sdl/Makeinfo b/src/libs/input/sdl/Makeinfo
new file mode 100644
index 0000000..427da53
--- /dev/null
+++ b/src/libs/input/sdl/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="input.c keynames.c vcontrol.c"
+uqm_HFILES="input.h keynames.h vcontrol.h"
diff --git a/src/libs/input/sdl/input.c b/src/libs/input/sdl/input.c
new file mode 100644
index 0000000..bff17aa
--- /dev/null
+++ b/src/libs/input/sdl/input.c
@@ -0,0 +1,625 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include "input.h"
+#include "../inpintrn.h"
+#include "libs/threadlib.h"
+#include "libs/input/sdl/vcontrol.h"
+#include "libs/input/sdl/keynames.h"
+#include "libs/memlib.h"
+#include "libs/file.h"
+#include "libs/log.h"
+#include "libs/reslib.h"
+#include "options.h"
+
+
+#define KBDBUFSIZE (1 << 8)
+static int kbdhead=0, kbdtail=0;
+static UniChar kbdbuf[KBDBUFSIZE];
+static UniChar lastchar;
+#if SDL_MAJOR_VERSION == 1
+static unsigned int num_keys = 0;
+static int *kbdstate = NULL;
+ // Holds all SDL keys +1 for holding invalid values
+#else // Later versions of SDL use the text input API instead
+static BOOLEAN set_character_mode = FALSE;
+ // Records whether the UI thread has caught up with game thread
+ // on this setting
+#endif
+
+static volatile int *menu_vec;
+static int num_menu;
+// The last vector element is the character repeat "key"
+// This is only used in SDL1 input but it's mostly harmless everywhere else
+#define KEY_MENU_ANY (num_menu - 1)
+static volatile int *flight_vec;
+static int num_templ;
+static int num_flight;
+
+static BOOLEAN InputInitialized = FALSE;
+
+static BOOLEAN in_character_mode = FALSE;
+
+static const char *menu_res_names[] = {
+ "pause",
+ "exit",
+ "abort",
+ "debug",
+ "fullscreen",
+ "up",
+ "down",
+ "left",
+ "right",
+ "select",
+ "cancel",
+ "special",
+ "pageup",
+ "pagedown",
+ "home",
+ "end",
+ "zoomin",
+ "zoomout",
+ "delete",
+ "backspace",
+ "editcancel",
+ "search",
+ "next",
+ NULL
+};
+
+static const char *flight_res_names[] = {
+ "up",
+ "down",
+ "left",
+ "right",
+ "weapon",
+ "special",
+ "escape",
+ NULL
+};
+
+static void
+register_menu_controls (int index)
+{
+ int i;
+ char buf[40];
+ buf[39] = '\0';
+
+ i = 1;
+ while (TRUE)
+ {
+ VCONTROL_GESTURE g;
+ snprintf (buf, 39, "menu.%s.%d", menu_res_names[index], i);
+ if (!res_IsString (buf))
+ break;
+ VControl_ParseGesture (&g, res_GetString (buf));
+ VControl_AddGestureBinding (&g, (int *)&menu_vec[index]);
+ i++;
+ }
+}
+
+
+static VCONTROL_GESTURE *controls;
+#define CONTROL_PTR(i, j, k) \
+ (controls + ((i) * num_flight + (j)) * MAX_FLIGHT_ALTERNATES + (k))
+
+static void
+register_flight_controls (void)
+{
+ int i, j, k;
+ char buf[40];
+
+ buf[39] = '\0';
+
+ for (i = 0; i < num_templ; i++)
+ {
+ /* Copy in name */
+ snprintf (buf, 39, "keys.%d.name", i+1);
+ if (res_IsString (buf))
+ {
+ strncpy (input_templates[i].name, res_GetString (buf), 29);
+ input_templates[i].name[29] = '\0';
+ }
+ else
+ {
+ input_templates[i].name[0] = '\0';
+ }
+ for (j = 0; j < num_flight; j++)
+ {
+ for (k = 0; k < MAX_FLIGHT_ALTERNATES; k++)
+ {
+ VCONTROL_GESTURE *g = CONTROL_PTR(i, j, k);
+ snprintf (buf, 39, "keys.%d.%s.%d", i+1, flight_res_names[j], k+1);
+ if (!res_IsString (buf))
+ {
+ g->type = VCONTROL_NONE;
+ continue;
+ }
+ VControl_ParseGesture (g, res_GetString (buf));
+ VControl_AddGestureBinding (g, (int *)(flight_vec + i * num_flight + j));
+ }
+ }
+ }
+}
+
+static void
+initKeyConfig (void)
+{
+ int i;
+
+ if (!menu_vec || !flight_vec)
+ {
+ log_add (log_Fatal, "initKeyConfig(): invalid input vectors");
+ exit (EXIT_FAILURE);
+ }
+
+ controls = HCalloc (sizeof (*controls) * num_templ * num_flight
+ * MAX_FLIGHT_ALTERNATES);
+
+ /* First, load in the menu keys */
+ LoadResourceIndex (contentDir, "menu.key", "menu.");
+ LoadResourceIndex (configDir, "override.cfg", "menu.");
+ for (i = 0; i < num_menu; i++)
+ {
+ if (!menu_res_names[i])
+ break;
+ register_menu_controls (i);
+ }
+
+ LoadResourceIndex (configDir, "flight.cfg", "keys.");
+ if (!res_HasKey ("keys.1.name"))
+ {
+ /* Either flight.cfg doesn't exist, or we're using an old version
+ of flight.cfg, and thus we wound up loading untyped values into
+ 'keys.keys.1.name' and such. Load the defaults from the content
+ directory. */
+ LoadResourceIndex (contentDir, "uqm.key", "keys.");
+ }
+
+ register_flight_controls ();
+
+ return;
+}
+
+static void
+resetKeyboardState (void)
+{
+#if SDL_MAJOR_VERSION == 1
+ memset (kbdstate, 0, sizeof (int) * num_keys);
+ menu_vec[KEY_MENU_ANY] = 0;
+#endif
+}
+
+void
+TFB_SetInputVectors (volatile int menu[], int num_menu_, volatile int flight[],
+ int num_templ_, int num_flight_)
+{
+ if (num_menu_ < 0 || num_templ_ < 0 || num_flight_ < 0)
+ {
+ log_add (log_Fatal, "TFB_SetInputVectors(): invalid vector size");
+ exit (EXIT_FAILURE);
+ }
+ menu_vec = menu;
+ num_menu = num_menu_;
+ flight_vec = flight;
+ num_templ = num_templ_;
+ num_flight = num_flight_;
+}
+
+#ifdef HAVE_JOYSTICK
+
+static void
+initJoystick (void)
+{
+ int nJoysticks;
+
+ if ((SDL_InitSubSystem(SDL_INIT_JOYSTICK)) == -1)
+ {
+ log_add (log_Fatal, "Couldn't initialize joystick subsystem: %s",
+ SDL_GetError());
+ exit (EXIT_FAILURE);
+ }
+
+ log_add (log_Info, "%i joysticks were found.", SDL_NumJoysticks ());
+
+ nJoysticks = SDL_NumJoysticks ();
+ if (nJoysticks > 0)
+ {
+ int i;
+
+ log_add (log_Info, "The names of the joysticks are:");
+ for (i = 0; i < nJoysticks; i++)
+ {
+ log_add (log_Info, " %s",
+#if SDL_MAJOR_VERSION == 1
+ SDL_JoystickName (i));
+#else
+ SDL_JoystickNameForIndex (i));
+#endif
+ }
+ SDL_JoystickEventState (SDL_ENABLE);
+ }
+}
+
+#endif /* HAVE_JOYSTICK */
+
+int
+TFB_InitInput (int driver, int flags)
+{
+ (void)driver;
+ (void)flags;
+
+#if SDL_MAJOR_VERSION == 1
+ SDL_EnableUNICODE(1);
+ (void)SDL_GetKeyState (&num_keys);
+ kbdstate = (int *)HMalloc (sizeof (int) * (num_keys + 1));
+#endif
+
+#ifdef HAVE_JOYSTICK
+ initJoystick ();
+#endif
+
+ in_character_mode = FALSE;
+ resetKeyboardState ();
+
+ /* Prepare the Virtual Controller system. */
+ VControl_Init ();
+
+ initKeyConfig ();
+
+ VControl_ResetInput ();
+ InputInitialized = TRUE;
+
+ return 0;
+}
+
+void
+TFB_UninitInput (void)
+{
+ VControl_Uninit ();
+ HFree (controls);
+#if SDL_MAJOR_VERSION == 1
+ HFree (kbdstate);
+#endif
+}
+
+void
+EnterCharacterMode (void)
+{
+ kbdhead = kbdtail = 0;
+ lastchar = 0;
+ in_character_mode = TRUE;
+ VControl_ResetInput ();
+}
+
+void
+ExitCharacterMode (void)
+{
+ VControl_ResetInput ();
+ in_character_mode = FALSE;
+ kbdhead = kbdtail = 0;
+ lastchar = 0;
+}
+
+UniChar
+GetNextCharacter (void)
+{
+ UniChar result;
+ if (kbdhead == kbdtail)
+ return 0;
+ result = kbdbuf[kbdhead];
+ kbdhead = (kbdhead + 1) & (KBDBUFSIZE - 1);
+ return result;
+}
+
+UniChar
+GetLastCharacter (void)
+{
+ return lastchar;
+}
+
+volatile int MouseButtonDown = 0;
+
+static void
+ProcessMouseEvent (const SDL_Event *e)
+{
+ switch (e->type)
+ {
+ case SDL_MOUSEBUTTONDOWN:
+ MouseButtonDown = 1;
+ break;
+ case SDL_MOUSEBUTTONUP:
+ MouseButtonDown = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+#if SDL_MAJOR_VERSION == 1
+
+static inline int
+is_numpad_char_event (const SDL_Event *Event)
+{
+ return in_character_mode &&
+ (Event->type == SDL_KEYDOWN || Event->type == SDL_KEYUP) &&
+ (Event->key.keysym.mod & KMOD_NUM) && /* NumLock is ON */
+ Event->key.keysym.unicode > 0 && /* Printable char */
+ Event->key.keysym.sym >= SDLK_KP0 && /* Keypad key */
+ Event->key.keysym.sym <= SDLK_KP_PLUS;
+}
+
+void
+ProcessInputEvent (const SDL_Event *Event)
+{
+ if (!InputInitialized)
+ return;
+
+ ProcessMouseEvent (Event);
+
+ // In character mode with NumLock on, numpad chars bypass VControl
+ // so that menu arrow events are not produced
+ if (!is_numpad_char_event (Event))
+ VControl_HandleEvent (Event);
+
+ if (Event->type == SDL_KEYDOWN || Event->type == SDL_KEYUP)
+ { // process character input event, if any
+ // keysym.sym is an SDLKey type which is an enum and can be signed
+ // or unsigned on different platforms; we'll use a guaranteed type
+ int k = Event->key.keysym.sym;
+ UniChar map_key = Event->key.keysym.unicode;
+
+ if (k < 0 || k > num_keys)
+ k = num_keys; // for unknown keys
+
+ if (Event->type == SDL_KEYDOWN)
+ {
+ int newtail;
+
+ // dont care about the non-printable, non-char
+ if (!map_key)
+ return;
+
+ kbdstate[k]++;
+
+ newtail = (kbdtail + 1) & (KBDBUFSIZE - 1);
+ // ignore the char if the buffer is full
+ if (newtail != kbdhead)
+ {
+ kbdbuf[kbdtail] = map_key;
+ kbdtail = newtail;
+ lastchar = map_key;
+ menu_vec[KEY_MENU_ANY]++;
+ }
+ }
+ else if (Event->type == SDL_KEYUP)
+ {
+ if (kbdstate[k] == 0)
+ { // something is fishy -- better to reset the
+ // repeatable state to avoid big problems
+ menu_vec[KEY_MENU_ANY] = 0;
+ }
+ else
+ {
+ kbdstate[k]--;
+ if (menu_vec[KEY_MENU_ANY] > 0)
+ menu_vec[KEY_MENU_ANY]--;
+ }
+ }
+ }
+}
+#else
+void
+ProcessInputEvent (const SDL_Event *Event)
+{
+ if (!InputInitialized)
+ return;
+
+ ProcessMouseEvent (Event);
+
+ if (in_character_mode && !set_character_mode)
+ {
+ set_character_mode = TRUE;
+ SDL_StartTextInput ();
+ }
+
+ if (!in_character_mode && set_character_mode)
+ {
+ set_character_mode = FALSE;
+ SDL_StopTextInput ();
+ }
+
+ /* TODO: Block numpad input when NUM_LOCK is on */
+ VControl_HandleEvent (Event);
+
+ if (Event->type == SDL_TEXTINPUT)
+ {
+ int newtail;
+ int i = 0;
+
+ while (Event->text.text[i])
+ {
+ UniChar map_key = Event->text.text[i++];
+
+ /* Decode any UTF-8 keys */
+ if (map_key >= 0xC0 && map_key < 0xE0)
+ {
+ /* 2-byte UTF-8 */
+ map_key = (map_key & 0x1f) << 6;
+ map_key |= Event->text.text[i++] & 0x3f;
+ }
+ else if (map_key >= 0xE0 && map_key < 0xF0)
+ {
+ /* 3-byte UTF-8 */
+ map_key = (map_key & 0x0f) << 6;
+ map_key |= Event->text.text[i++] & 0x3f;
+ map_key <<= 6;
+ map_key |= Event->text.text[i++] & 0x3f;
+ }
+ else if (map_key >= 0xF0)
+ {
+ /* Out of the BMP, won't fit in a UniChar */
+ /* Use the replacement character instead */
+ map_key = 0xFFFD;
+ while ((UniChar)Event->text.text[i] > 0x7F)
+ {
+ ++i;
+ }
+ }
+
+ /* dont care about the non-printable, non-char */
+ if (!map_key)
+ return;
+
+ newtail = (kbdtail + 1) & (KBDBUFSIZE - 1);
+
+ /* ignore the char if the buffer is full */
+ if (newtail != kbdhead)
+ {
+ kbdbuf[kbdtail] = map_key;
+ kbdtail = newtail;
+ lastchar = map_key;
+ }
+
+ /* Loop back in case there are more chars in the
+ * text input buffer */
+ }
+ }
+}
+
+#endif
+
+void
+TFB_ResetControls (void)
+{
+ VControl_ResetInput ();
+ resetKeyboardState ();
+ // flush character buffer
+ kbdhead = kbdtail = 0;
+ lastchar = 0;
+}
+
+void
+InterrogateInputState (int templat, int control, int index, char *buffer, int maxlen)
+{
+ VCONTROL_GESTURE *g = CONTROL_PTR(templat, control, index);
+
+ if (templat >= num_templ || control >= num_flight
+ || index >= MAX_FLIGHT_ALTERNATES)
+ {
+ log_add (log_Warning, "InterrogateInputState(): invalid control index");
+ buffer[0] = 0;
+ return;
+ }
+
+ switch (g->type)
+ {
+ case VCONTROL_KEY:
+ snprintf (buffer, maxlen, "%s", VControl_code2name (g->gesture.key));
+ buffer[maxlen-1] = 0;
+ break;
+ case VCONTROL_JOYBUTTON:
+ snprintf (buffer, maxlen, "[J%d B%d]", g->gesture.button.port, g->gesture.button.index + 1);
+ buffer[maxlen-1] = 0;
+ break;
+ case VCONTROL_JOYAXIS:
+ snprintf (buffer, maxlen, "[J%d A%d %c]", g->gesture.axis.port, g->gesture.axis.index, g->gesture.axis.polarity > 0 ? '+' : '-');
+ break;
+ case VCONTROL_JOYHAT:
+ snprintf (buffer, maxlen, "[J%d H%d %d]", g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir);
+ break;
+ default:
+ /* Something we don't handle yet */
+ buffer[0] = 0;
+ break;
+ }
+ return;
+}
+
+void
+RemoveInputState (int templat, int control, int index)
+{
+ VCONTROL_GESTURE *g = CONTROL_PTR(templat, control, index);
+ char keybuf[40];
+ keybuf[39] = '\0';
+
+ if (templat >= num_templ || control >= num_flight
+ || index >= MAX_FLIGHT_ALTERNATES)
+ {
+ log_add (log_Warning, "RemoveInputState(): invalid control index");
+ return;
+ }
+
+ VControl_RemoveGestureBinding (g,
+ (int *)(flight_vec + templat * num_flight + control));
+ g->type = VCONTROL_NONE;
+
+ snprintf (keybuf, 39, "keys.%d.%s.%d", templat+1, flight_res_names[control], index+1);
+ res_Remove (keybuf);
+
+ return;
+}
+
+void
+RebindInputState (int templat, int control, int index)
+{
+ VCONTROL_GESTURE g;
+ char keybuf[40], valbuf[40];
+ keybuf[39] = valbuf[39] = '\0';
+
+ if (templat >= num_templ || control >= num_flight
+ || index >= MAX_FLIGHT_ALTERNATES)
+ {
+ log_add (log_Warning, "RebindInputState(): invalid control index");
+ return;
+ }
+
+ /* Remove the old binding on this spot */
+ RemoveInputState (templat, control, index);
+
+ /* Wait for the next interesting bit of user input */
+ VControl_ClearGesture ();
+ while (!VControl_GetLastGesture (&g))
+ {
+ TaskSwitch ();
+ }
+
+ /* And now, add the new binding. */
+ VControl_AddGestureBinding (&g,
+ (int *)(flight_vec + templat * num_flight + control));
+ *CONTROL_PTR(templat, control, index) = g;
+ snprintf (keybuf, 39, "keys.%d.%s.%d", templat+1, flight_res_names[control], index+1);
+ VControl_DumpGesture (valbuf, 39, &g);
+ res_PutString (keybuf, valbuf);
+}
+
+void
+SaveKeyConfiguration (uio_DirHandle *path, const char *fname)
+{
+ SaveResourceIndex (path, fname, "keys.", TRUE);
+}
+
+void
+BeginInputFrame (void)
+{
+ VControl_BeginFrame ();
+}
+
diff --git a/src/libs/input/sdl/input.h b/src/libs/input/sdl/input.h
new file mode 100644
index 0000000..3c599aa
--- /dev/null
+++ b/src/libs/input/sdl/input.h
@@ -0,0 +1,27 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef INPUT_H
+#define INPUT_H
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+
+extern void ProcessInputEvent (const SDL_Event *Event);
+
+#endif
diff --git a/src/libs/input/sdl/keynames.c b/src/libs/input/sdl/keynames.c
new file mode 100644
index 0000000..86c104a
--- /dev/null
+++ b/src/libs/input/sdl/keynames.c
@@ -0,0 +1,229 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+#include <string.h>
+#include "keynames.h"
+
+/* This code is adapted from the code in SDL_keysym.h. Though this
+ * would almost certainly be fast if we were to use a direct char *
+ * array, this technique permits us to be independent of the actual
+ * character encoding to keysyms. */
+
+/* These names are case-insensitive when compared, but we format
+ * them to look pretty when output */
+
+/* This version of Virtual Controller does not support SDLK_WORLD_*
+ * keysyms or the Num/Caps/ScrollLock keys. SDL treats locking keys
+ * specially, and we cannot treat them as normal keys. Pain,
+ * tragedy. */
+
+typedef struct vcontrol_keyname {
+ const char *name;
+ int code;
+} keyname;
+
+static keyname keynames[] = {
+ {"Backspace", SDLK_BACKSPACE},
+ {"Tab", SDLK_TAB},
+ {"Clear", SDLK_CLEAR},
+ {"Return", SDLK_RETURN},
+ {"Pause", SDLK_PAUSE},
+ {"Escape", SDLK_ESCAPE},
+ {"Space", SDLK_SPACE},
+ {"!", SDLK_EXCLAIM},
+ {"\"", SDLK_QUOTEDBL},
+ {"Hash", SDLK_HASH},
+ {"$", SDLK_DOLLAR},
+ {"&", SDLK_AMPERSAND},
+ {"'", SDLK_QUOTE},
+ {"(", SDLK_LEFTPAREN},
+ {")", SDLK_RIGHTPAREN},
+ {"*", SDLK_ASTERISK},
+ {"+", SDLK_PLUS},
+ {",", SDLK_COMMA},
+ {"-", SDLK_MINUS},
+ {".", SDLK_PERIOD},
+ {"/", SDLK_SLASH},
+ {"0", SDLK_0},
+ {"1", SDLK_1},
+ {"2", SDLK_2},
+ {"3", SDLK_3},
+ {"4", SDLK_4},
+ {"5", SDLK_5},
+ {"6", SDLK_6},
+ {"7", SDLK_7},
+ {"8", SDLK_8},
+ {"9", SDLK_9},
+ {":", SDLK_COLON},
+ {";", SDLK_SEMICOLON},
+ {"<", SDLK_LESS},
+ {"=", SDLK_EQUALS},
+ {">", SDLK_GREATER},
+ {"?", SDLK_QUESTION},
+ {"@", SDLK_AT},
+ {"[", SDLK_LEFTBRACKET},
+ {"\\", SDLK_BACKSLASH},
+ {"]", SDLK_RIGHTBRACKET},
+ {"^", SDLK_CARET},
+ {"_", SDLK_UNDERSCORE},
+ {"`", SDLK_BACKQUOTE},
+ {"a", SDLK_a},
+ {"b", SDLK_b},
+ {"c", SDLK_c},
+ {"d", SDLK_d},
+ {"e", SDLK_e},
+ {"f", SDLK_f},
+ {"g", SDLK_g},
+ {"h", SDLK_h},
+ {"i", SDLK_i},
+ {"j", SDLK_j},
+ {"k", SDLK_k},
+ {"l", SDLK_l},
+ {"m", SDLK_m},
+ {"n", SDLK_n},
+ {"o", SDLK_o},
+ {"p", SDLK_p},
+ {"q", SDLK_q},
+ {"r", SDLK_r},
+ {"s", SDLK_s},
+ {"t", SDLK_t},
+ {"u", SDLK_u},
+ {"v", SDLK_v},
+ {"w", SDLK_w},
+ {"x", SDLK_x},
+ {"y", SDLK_y},
+ {"z", SDLK_z},
+ {"Delete", SDLK_DELETE},
+#if SDL_MAJOR_VERSION == 1
+ {"Keypad-0", SDLK_KP0},
+ {"Keypad-1", SDLK_KP1},
+ {"Keypad-2", SDLK_KP2},
+ {"Keypad-3", SDLK_KP3},
+ {"Keypad-4", SDLK_KP4},
+ {"Keypad-5", SDLK_KP5},
+ {"Keypad-6", SDLK_KP6},
+ {"Keypad-7", SDLK_KP7},
+ {"Keypad-8", SDLK_KP8},
+ {"Keypad-9", SDLK_KP9},
+#else
+ {"Keypad-0", SDLK_KP_0},
+ {"Keypad-1", SDLK_KP_1},
+ {"Keypad-2", SDLK_KP_2},
+ {"Keypad-3", SDLK_KP_3},
+ {"Keypad-4", SDLK_KP_4},
+ {"Keypad-5", SDLK_KP_5},
+ {"Keypad-6", SDLK_KP_6},
+ {"Keypad-7", SDLK_KP_7},
+ {"Keypad-8", SDLK_KP_8},
+ {"Keypad-9", SDLK_KP_9},
+#endif
+ {"Keypad-.", SDLK_KP_PERIOD},
+ {"Keypad-/", SDLK_KP_DIVIDE},
+ {"Keypad-*", SDLK_KP_MULTIPLY},
+ {"Keypad--", SDLK_KP_MINUS},
+ {"Keypad-+", SDLK_KP_PLUS},
+ {"Keypad-Enter", SDLK_KP_ENTER},
+ {"Keypad-=", SDLK_KP_EQUALS},
+ {"Up", SDLK_UP},
+ {"Down", SDLK_DOWN},
+ {"Right", SDLK_RIGHT},
+ {"Left", SDLK_LEFT},
+ {"Insert", SDLK_INSERT},
+ {"Home", SDLK_HOME},
+ {"End", SDLK_END},
+ {"PageUp", SDLK_PAGEUP},
+ {"PageDown", SDLK_PAGEDOWN},
+ {"F1", SDLK_F1},
+ {"F2", SDLK_F2},
+ {"F3", SDLK_F3},
+ {"F4", SDLK_F4},
+ {"F5", SDLK_F5},
+ {"F6", SDLK_F6},
+ {"F7", SDLK_F7},
+ {"F8", SDLK_F8},
+ {"F9", SDLK_F9},
+ {"F10", SDLK_F10},
+ {"F11", SDLK_F11},
+ {"F12", SDLK_F12},
+ {"F13", SDLK_F13},
+ {"F14", SDLK_F14},
+ {"F15", SDLK_F15},
+ {"RightShift", SDLK_RSHIFT},
+ {"LeftShift", SDLK_LSHIFT},
+ {"RightControl", SDLK_RCTRL},
+ {"LeftControl", SDLK_LCTRL},
+ {"RightAlt", SDLK_RALT},
+ {"LeftAlt", SDLK_LALT},
+#if SDL_MAJOR_VERSION == 1
+ {"RightMeta", SDLK_RMETA},
+ {"LeftMeta", SDLK_LMETA},
+ {"RightSuper", SDLK_RSUPER},
+ {"LeftSuper", SDLK_LSUPER},
+ {"AltGr", SDLK_MODE},
+ {"Compose", SDLK_COMPOSE},
+ {"Help", SDLK_HELP},
+ {"Print", SDLK_PRINT},
+ {"SysReq", SDLK_SYSREQ},
+ {"Break", SDLK_BREAK},
+ {"Menu", SDLK_MENU},
+ {"Power", SDLK_POWER},
+ {"Euro", SDLK_EURO},
+ {"Undo", SDLK_UNDO},
+#ifdef _WIN32_WCE
+ {"App1", SDLK_APP1},
+ {"App2", SDLK_APP2},
+ {"App3", SDLK_APP3},
+ {"App4", SDLK_APP4},
+ {"App5", SDLK_APP5},
+ {"App6", SDLK_APP6},
+#endif /* _WIN32_WCE */
+#endif /* SDL_MAJOR_VERSION == 1 */
+
+ {"Unknown", 0}};
+/* Last element must have code zero */
+
+const char *
+VControl_code2name (int code)
+{
+ int i = 0;
+ while (1)
+ {
+ int test = keynames[i].code;
+ if (test == code || !test)
+ {
+ return keynames[i].name;
+ }
+ ++i;
+ }
+}
+
+int
+VControl_name2code (const char *name)
+{
+ int i = 0;
+ while (1)
+ {
+ const char *test = keynames[i].name;
+ int code = keynames[i].code;
+ if (!strcasecmp(test, name) || !code)
+ {
+ return code;
+ }
+ ++i;
+ }
+}
diff --git a/src/libs/input/sdl/keynames.h b/src/libs/input/sdl/keynames.h
new file mode 100644
index 0000000..affd854
--- /dev/null
+++ b/src/libs/input/sdl/keynames.h
@@ -0,0 +1,22 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_INPUT_SDL_KEYNAMES_H_
+#define LIBS_INPUT_SDL_KEYNAMES_H_
+
+const char *VControl_code2name (int code);
+int VControl_name2code (const char *code);
+#endif
diff --git a/src/libs/input/sdl/vcontrol.c b/src/libs/input/sdl/vcontrol.c
new file mode 100644
index 0000000..9c226ae
--- /dev/null
+++ b/src/libs/input/sdl/vcontrol.c
@@ -0,0 +1,1300 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "vcontrol.h"
+#include "libs/memlib.h"
+#include "keynames.h"
+#include "libs/log.h"
+#include "libs/reslib.h"
+
+/* How many binding slots are allocated at once. */
+#define POOL_CHUNK_SIZE 64
+
+/* Total number of key input buckets. SDL1 keys are a simple enum,
+ * but SDL2 scatters key symbols through the entire 32-bit space,
+ * so we do not rely on being able to declare an array with one
+ * entry per key. */
+#define KEYBOARD_INPUT_BUCKETS 512
+
+typedef struct vcontrol_keybinding {
+ int *target;
+ sdl_key_t keycode;
+ struct vcontrol_keypool *parent;
+ struct vcontrol_keybinding *next;
+} keybinding;
+
+typedef struct vcontrol_keypool {
+ keybinding pool[POOL_CHUNK_SIZE];
+ int remaining;
+ struct vcontrol_keypool *next;
+} keypool;
+
+
+#ifdef HAVE_JOYSTICK
+
+typedef struct vcontrol_joystick_axis {
+ keybinding *neg, *pos;
+ int polarity;
+} axis_type;
+
+typedef struct vcontrol_joystick_hat {
+ keybinding *left, *right, *up, *down;
+ Uint8 last;
+} hat_type;
+
+typedef struct vcontrol_joystick {
+ SDL_Joystick *stick;
+ int numaxes, numbuttons, numhats;
+ int threshold;
+ axis_type *axes;
+ keybinding **buttons;
+ hat_type *hats;
+} joystick;
+
+static joystick *joysticks;
+
+#endif /* HAVE_JOYSTICK */
+
+static unsigned int joycount;
+static keybinding *bindings[KEYBOARD_INPUT_BUCKETS];
+
+static keypool *pool;
+
+/* Last interesting event */
+static int event_ready;
+static SDL_Event last_interesting;
+
+static keypool *
+allocate_key_chunk (void)
+{
+ keypool *x = HMalloc (sizeof (keypool));
+ if (x)
+ {
+ int i;
+ x->remaining = POOL_CHUNK_SIZE;
+ x->next = NULL;
+ for (i = 0; i < POOL_CHUNK_SIZE; i++)
+ {
+ x->pool[i].target = NULL;
+ x->pool[i].keycode = SDLK_UNKNOWN;
+ x->pool[i].next = NULL;
+ x->pool[i].parent = x;
+ }
+ }
+ return x;
+}
+
+static void
+free_key_pool (keypool *x)
+{
+ if (x)
+ {
+ free_key_pool (x->next);
+ HFree (x);
+ }
+}
+
+#ifdef HAVE_JOYSTICK
+
+static void
+create_joystick (int index)
+{
+ SDL_Joystick *stick;
+ int axes, buttons, hats;
+ if ((unsigned int) index >= joycount)
+ {
+ log_add (log_Warning, "VControl warning: Tried to open a non-existent joystick!");
+ return;
+ }
+ if (joysticks[index].stick)
+ {
+ // Joystick is already created. Return.
+ return;
+ }
+ stick = SDL_JoystickOpen (index);
+ if (stick)
+ {
+ joystick *x = &joysticks[index];
+ int j;
+#if SDL_MAJOR_VERSION == 1
+ log_add (log_Info, "VControl opened joystick: %s", SDL_JoystickName (index));
+#else
+ log_add (log_Info, "VControl opened joystick: %s", SDL_JoystickName (stick));
+#endif
+ axes = SDL_JoystickNumAxes (stick);
+ buttons = SDL_JoystickNumButtons (stick);
+ hats = SDL_JoystickNumHats (stick);
+ log_add (log_Info, "%d axes, %d buttons, %d hats.", axes, buttons, hats);
+ x->numaxes = axes;
+ x->numbuttons = buttons;
+ x->numhats = hats;
+ x->axes = HMalloc (sizeof (axis_type) * axes);
+ x->buttons = HMalloc (sizeof (keybinding *) * buttons);
+ x->hats = HMalloc (sizeof (hat_type) * hats);
+ for (j = 0; j < axes; j++)
+ {
+ x->axes[j].neg = x->axes[j].pos = NULL;
+ }
+ for (j = 0; j < hats; j++)
+ {
+ x->hats[j].left = x->hats[j].right = NULL;
+ x->hats[j].up = x->hats[j].down = NULL;
+ x->hats[j].last = SDL_HAT_CENTERED;
+ }
+ for (j = 0; j < buttons; j++)
+ {
+ x->buttons[j] = NULL;
+ }
+ x->stick = stick;
+ }
+ else
+ {
+ log_add (log_Warning, "VControl: Could not initialize joystick #%d", index);
+ }
+}
+
+static void
+destroy_joystick (int index)
+{
+ SDL_Joystick *stick = joysticks[index].stick;
+ if (stick)
+ {
+ SDL_JoystickClose (stick);
+ joysticks[index].stick = NULL;
+ HFree (joysticks[index].axes);
+ HFree (joysticks[index].buttons);
+ HFree (joysticks[index].hats);
+ joysticks[index].numaxes = joysticks[index].numbuttons = 0;
+ joysticks[index].axes = NULL;
+ joysticks[index].buttons = NULL;
+ joysticks[index].hats = NULL;
+ }
+}
+
+#endif /* HAVE_JOYSTICK */
+
+static void
+key_init (void)
+{
+ unsigned int i;
+ int num_keys; // Temp to match type of param for SDL_GetKeyState().
+ pool = allocate_key_chunk ();
+ for (i = 0; i < KEYBOARD_INPUT_BUCKETS; i++)
+ bindings[i] = NULL;
+
+#ifdef HAVE_JOYSTICK
+ /* Prepare for possible joystick controls. We don't actually
+ GRAB joysticks unless we're asked to make a joystick
+ binding, though. */
+ joycount = SDL_NumJoysticks ();
+ if (joycount)
+ {
+ joysticks = HMalloc (sizeof (joystick) * joycount);
+ for (i = 0; i < joycount; i++)
+ {
+ joysticks[i].stick = NULL;
+ joysticks[i].numaxes = joysticks[i].numbuttons = 0;
+ joysticks[i].axes = NULL;
+ joysticks[i].buttons = NULL;
+ joysticks[i].threshold = 10000;
+ }
+ }
+ else
+ {
+ joysticks = NULL;
+ }
+#else
+ joycount = 0;
+#endif /* HAVE_JOYSTICK */
+}
+
+static void
+key_uninit (void)
+{
+ unsigned int i;
+ free_key_pool (pool);
+ for (i = 0; i < KEYBOARD_INPUT_BUCKETS; i++)
+ bindings[i] = NULL;
+ pool = NULL;
+
+#ifdef HAVE_JOYSTICK
+ for (i = 0; i < joycount; i++)
+ destroy_joystick (i);
+ HFree (joysticks);
+#endif /* HAVE_JOYSTICK */
+}
+
+void
+VControl_Init (void)
+{
+ key_init ();
+}
+
+void
+VControl_Uninit (void)
+{
+ key_uninit ();
+}
+
+int
+VControl_SetJoyThreshold (int port, int threshold)
+{
+#ifdef HAVE_JOYSTICK
+ if (port >= 0 && (unsigned int) port < joycount)
+ {
+ joysticks[port].threshold = threshold;
+ return 0;
+ }
+ else
+#else
+ (void) port;
+ (void) threshold;
+#endif /* HAVE_JOYSTICK */
+ {
+ // log_add (log_Warning, "VControl_SetJoyThreshold passed illegal port %d", port);
+ return -1;
+ }
+}
+
+
+static void
+add_binding (keybinding **newptr, int *target, sdl_key_t keycode)
+{
+ keybinding *newbinding;
+ keypool *searchbase;
+ int i;
+
+ /* Acquire a pointer to the keybinding * that we'll be
+ * overwriting. Along the way, ensure we haven't already
+ * bound this symbol to this target. If we have, return.*/
+ while (*newptr != NULL)
+ {
+ if (((*newptr)->target == target) && ((*newptr)->keycode == keycode))
+ {
+ return;
+ }
+ newptr = &((*newptr)->next);
+ }
+
+ /* Now hunt through the binding pool for a free binding. */
+
+ /* First, find a chunk with free spots in it */
+
+ searchbase = pool;
+ while (searchbase->remaining == 0)
+ {
+ /* If we're completely full, allocate a new chunk */
+ if (searchbase->next == NULL)
+ {
+ searchbase->next = allocate_key_chunk ();
+ }
+ searchbase = searchbase->next;
+ }
+
+ /* Now find a free binding within it */
+
+ newbinding = NULL;
+ for (i = 0; i < POOL_CHUNK_SIZE; i++)
+ {
+ if (searchbase->pool[i].target == NULL)
+ {
+ newbinding = &searchbase->pool[i];
+ break;
+ }
+ }
+
+ /* Sanity check. */
+ if (!newbinding)
+ {
+ log_add (log_Warning, "add_binding failed to find a free binding slot!");
+ return;
+ }
+
+ newbinding->target = target;
+ newbinding->keycode = keycode;
+ newbinding->next = NULL;
+ *newptr = newbinding;
+ searchbase->remaining--;
+}
+
+static void
+remove_binding (keybinding **ptr, int *target, sdl_key_t keycode)
+{
+ if (!(*ptr))
+ {
+ /* Nothing bound to symbol; return. */
+ return;
+ }
+ else if (((*ptr)->target == target) && ((*ptr)->keycode == keycode))
+ {
+ keybinding *todel = *ptr;
+ *ptr = todel->next;
+ todel->target = NULL;
+ todel->keycode = SDLK_UNKNOWN;
+ todel->next = NULL;
+ todel->parent->remaining++;
+ }
+ else
+ {
+ keybinding *prev = *ptr;
+ while (prev && prev->next != NULL)
+ {
+ if (prev->next->target == target)
+ {
+ keybinding *todel = prev->next;
+ prev->next = todel->next;
+ todel->target = NULL;
+ todel->keycode = SDLK_UNKNOWN;
+ todel->next = NULL;
+ todel->parent->remaining++;
+ }
+ prev = prev->next;
+ }
+ }
+}
+
+static void
+activate (keybinding *i, sdl_key_t keycode)
+{
+ while (i != NULL)
+ {
+ if (i->keycode == keycode)
+ {
+ *(i->target) = (*(i->target)+1) | VCONTROL_STARTBIT;
+ }
+ i = i->next;
+ }
+}
+
+static void
+deactivate (keybinding *i, sdl_key_t keycode)
+{
+ while (i != NULL)
+ {
+ int v = *(i->target) & VCONTROL_MASK;
+ if ((i->keycode == keycode) && (v > 0))
+ {
+ *(i->target) = (v-1) | (*(i->target) & VCONTROL_STARTBIT);
+ }
+ i = i->next;
+ }
+}
+
+static void
+event2gesture (SDL_Event *e, VCONTROL_GESTURE *g)
+{
+ switch (e->type)
+ {
+ case SDL_KEYDOWN:
+ g->type = VCONTROL_KEY;
+ g->gesture.key = e->key.keysym.sym;
+ break;
+ case SDL_JOYAXISMOTION:
+ g->type = VCONTROL_JOYAXIS;
+ g->gesture.axis.port = e->jaxis.which;
+ g->gesture.axis.index = e->jaxis.axis;
+ g->gesture.axis.polarity = (e->jaxis.value < 0) ? -1 : 1;
+ break;
+ case SDL_JOYHATMOTION:
+ g->type = VCONTROL_JOYHAT;
+ g->gesture.hat.port = e->jhat.which;
+ g->gesture.hat.index = e->jhat.hat;
+ g->gesture.hat.dir = e->jhat.value;
+ break;
+ case SDL_JOYBUTTONDOWN:
+ g->type = VCONTROL_JOYBUTTON;
+ g->gesture.button.port = e->jbutton.which;
+ g->gesture.button.index = e->jbutton.button;
+ break;
+
+ default:
+ g->type = VCONTROL_NONE;
+ break;
+ }
+}
+
+int
+VControl_AddGestureBinding (VCONTROL_GESTURE *g, int *target)
+{
+ int result = -1;
+ switch (g->type)
+ {
+ case VCONTROL_KEY:
+ result = VControl_AddKeyBinding (g->gesture.key, target);
+ break;
+
+ case VCONTROL_JOYAXIS:
+#ifdef HAVE_JOYSTICK
+ result = VControl_AddJoyAxisBinding (g->gesture.axis.port, g->gesture.axis.index, (g->gesture.axis.polarity < 0) ? -1 : 1, target);
+ break;
+#endif
+ case VCONTROL_JOYHAT:
+#ifdef HAVE_JOYSTICK
+ result = VControl_AddJoyHatBinding (g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir, target);
+ break;
+#endif
+ case VCONTROL_JOYBUTTON:
+#ifdef HAVE_JOYSTICK
+ result = VControl_AddJoyButtonBinding (g->gesture.button.port, g->gesture.button.index, target);
+ break;
+#endif /* HAVE_JOYSTICK */
+ case VCONTROL_NONE:
+ /* Do nothing */
+ break;
+
+ default:
+ log_add (log_Warning, "VControl_AddGestureBinding didn't understand argument gesture");
+ result = -1;
+ break;
+ }
+ return result;
+}
+
+void
+VControl_RemoveGestureBinding (VCONTROL_GESTURE *g, int *target)
+{
+ switch (g->type)
+ {
+ case VCONTROL_KEY:
+ VControl_RemoveKeyBinding (g->gesture.key, target);
+ break;
+
+ case VCONTROL_JOYAXIS:
+#ifdef HAVE_JOYSTICK
+ VControl_RemoveJoyAxisBinding (g->gesture.axis.port, g->gesture.axis.index, (g->gesture.axis.polarity < 0) ? -1 : 1, target);
+ break;
+#endif /* HAVE_JOYSTICK */
+ case VCONTROL_JOYHAT:
+#ifdef HAVE_JOYSTICK
+ VControl_RemoveJoyHatBinding (g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir, target);
+ break;
+#endif /* HAVE_JOYSTICK */
+ case VCONTROL_JOYBUTTON:
+#ifdef HAVE_JOYSTICK
+ VControl_RemoveJoyButtonBinding (g->gesture.button.port, g->gesture.button.index, target);
+ break;
+#endif /* HAVE_JOYSTICK */
+ case VCONTROL_NONE:
+ break;
+ default:
+ log_add (log_Warning, "VControl_RemoveGestureBinding didn't understand argument gesture");
+ break;
+ }
+}
+
+int
+VControl_AddKeyBinding (sdl_key_t symbol, int *target)
+{
+ add_binding(&bindings[symbol % KEYBOARD_INPUT_BUCKETS], target, symbol);
+ return 0;
+}
+
+void
+VControl_RemoveKeyBinding (sdl_key_t symbol, int *target)
+{
+ remove_binding (&bindings[symbol % KEYBOARD_INPUT_BUCKETS], target, symbol);
+}
+
+int
+VControl_AddJoyAxisBinding (int port, int axis, int polarity, int *target)
+{
+#ifdef HAVE_JOYSTICK
+ if (port >= 0 && (unsigned int) port < joycount)
+ {
+ joystick *j = &joysticks[port];
+ if (!(j->stick))
+ create_joystick (port);
+ if ((axis >= 0) && (axis < j->numaxes))
+ {
+ if (polarity < 0)
+ {
+ add_binding(&joysticks[port].axes[axis].neg, target, SDLK_UNKNOWN);
+ }
+ else if (polarity > 0)
+ {
+ add_binding(&joysticks[port].axes[axis].pos, target, SDLK_UNKNOWN);
+ }
+ else
+ {
+ log_add (log_Debug, "VControl: Attempted to bind to polarity zero");
+ return -1;
+ }
+ }
+ else
+ {
+ // log_add (log_Debug, "VControl: Attempted to bind to illegal axis %d", axis);
+ return -1;
+ }
+ }
+ else
+#else
+ (void) port;
+ (void) axis;
+ (void) polarity;
+ (void) target;
+#endif /* HAVE_JOYSTICK */
+ {
+ // log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port);
+ return -1;
+ }
+ return 0;
+}
+
+void
+VControl_RemoveJoyAxisBinding (int port, int axis, int polarity, int *target)
+{
+#ifdef HAVE_JOYSTICK
+ if (port >= 0 && (unsigned int) port < joycount)
+ {
+ joystick *j = &joysticks[port];
+ if (!(j->stick))
+ create_joystick (port);
+ if ((axis >= 0) && (axis < j->numaxes))
+ {
+ if (polarity < 0)
+ {
+ remove_binding(&joysticks[port].axes[axis].neg, target, SDLK_UNKNOWN);
+ }
+ else if (polarity > 0)
+ {
+ remove_binding(&joysticks[port].axes[axis].pos, target, SDLK_UNKNOWN);
+ }
+ else
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from polarity zero");
+ }
+ }
+ else
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from illegal axis %d", axis);
+ }
+ }
+ else
+#else
+ (void) port;
+ (void) axis;
+ (void) polarity;
+ (void) target;
+#endif /* HAVE_JOYSTICK */
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port);
+ }
+}
+
+int
+VControl_AddJoyButtonBinding (int port, int button, int *target)
+{
+#ifdef HAVE_JOYSTICK
+ if (port >= 0 && (unsigned int) port < joycount)
+ {
+ joystick *j = &joysticks[port];
+ if (!(j->stick))
+ create_joystick (port);
+ if ((button >= 0) && (button < j->numbuttons))
+ {
+ add_binding(&joysticks[port].buttons[button], target, SDLK_UNKNOWN);
+ return 0;
+ }
+ else
+ {
+ // log_add (log_Debug, "VControl: Attempted to bind to illegal button %d", button);
+ return -1;
+ }
+ }
+ else
+#else
+ (void) port;
+ (void) button;
+ (void) target;
+#endif /* HAVE_JOYSTICK */
+ {
+ // log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port);
+ return -1;
+ }
+}
+
+void
+VControl_RemoveJoyButtonBinding (int port, int button, int *target)
+{
+#ifdef HAVE_JOYSTICK
+ if (port >= 0 && (unsigned int) port < joycount)
+ {
+ joystick *j = &joysticks[port];
+ if (!(j->stick))
+ create_joystick (port);
+ if ((button >= 0) && (button < j->numbuttons))
+ {
+ remove_binding (&joysticks[port].buttons[button], target, SDLK_UNKNOWN);
+ }
+ else
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from illegal button %d", button);
+ }
+ }
+ else
+#else
+ (void) port;
+ (void) button;
+ (void) target;
+#endif /* HAVE_JOYSTICK */
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port);
+ }
+}
+
+int
+VControl_AddJoyHatBinding (int port, int which, Uint8 dir, int *target)
+{
+#ifdef HAVE_JOYSTICK
+ if (port >= 0 && (unsigned int) port < joycount)
+ {
+ joystick *j = &joysticks[port];
+ if (!(j->stick))
+ create_joystick (port);
+ if ((which >= 0) && (which < j->numhats))
+ {
+ if (dir == SDL_HAT_LEFT)
+ {
+ add_binding(&joysticks[port].hats[which].left, target, SDLK_UNKNOWN);
+ }
+ else if (dir == SDL_HAT_RIGHT)
+ {
+ add_binding(&joysticks[port].hats[which].right, target, SDLK_UNKNOWN);
+ }
+ else if (dir == SDL_HAT_UP)
+ {
+ add_binding(&joysticks[port].hats[which].up, target, SDLK_UNKNOWN);
+ }
+ else if (dir == SDL_HAT_DOWN)
+ {
+ add_binding(&joysticks[port].hats[which].down, target, SDLK_UNKNOWN);
+ }
+ else
+ {
+ // log_add (log_Debug, "VControl: Attempted to bind to illegal direction");
+ return -1;
+ }
+ return 0;
+ }
+ else
+ {
+ // log_add (log_Debug, "VControl: Attempted to bind to illegal hat %d", which);
+ return -1;
+ }
+ }
+ else
+#else
+ (void) port;
+ (void) which;
+ (void) dir;
+ (void) target;
+#endif /* HAVE_JOYSTICK */
+ {
+ // log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port);
+ return -1;
+ }
+}
+
+void
+VControl_RemoveJoyHatBinding (int port, int which, Uint8 dir, int *target)
+{
+#ifdef HAVE_JOYSTICK
+ if (port >= 0 && (unsigned int) port < joycount)
+ {
+ joystick *j = &joysticks[port];
+ if (!(j->stick))
+ create_joystick (port);
+ if ((which >= 0) && (which < j->numhats))
+ {
+ if (dir == SDL_HAT_LEFT)
+ {
+ remove_binding(&joysticks[port].hats[which].left, target, SDLK_UNKNOWN);
+ }
+ else if (dir == SDL_HAT_RIGHT)
+ {
+ remove_binding(&joysticks[port].hats[which].right, target, SDLK_UNKNOWN);
+ }
+ else if (dir == SDL_HAT_UP)
+ {
+ remove_binding(&joysticks[port].hats[which].up, target, SDLK_UNKNOWN);
+ }
+ else if (dir == SDL_HAT_DOWN)
+ {
+ remove_binding(&joysticks[port].hats[which].down, target, SDLK_UNKNOWN);
+ }
+ else
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from illegal direction");
+ }
+ }
+ else
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from illegal hat %d", which);
+ }
+ }
+ else
+#else
+ (void) port;
+ (void) which;
+ (void) dir;
+ (void) target;
+#endif /* HAVE_JOYSTICK */
+ {
+ log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port);
+ }
+}
+
+void
+VControl_RemoveAllBindings (void)
+{
+ key_uninit ();
+ key_init ();
+}
+
+void
+VControl_ProcessKeyDown (sdl_key_t symbol)
+{
+ activate (bindings[symbol % KEYBOARD_INPUT_BUCKETS], symbol);
+}
+
+void
+VControl_ProcessKeyUp (sdl_key_t symbol)
+{
+ deactivate (bindings[symbol % KEYBOARD_INPUT_BUCKETS], symbol);
+}
+
+void
+VControl_ProcessJoyButtonDown (int port, int button)
+{
+#ifdef HAVE_JOYSTICK
+ if (!joysticks[port].stick)
+ return;
+ activate (joysticks[port].buttons[button], SDLK_UNKNOWN);
+#else
+ (void) port;
+ (void) button;
+#endif /* HAVE_JOYSTICK */
+}
+
+void
+VControl_ProcessJoyButtonUp (int port, int button)
+{
+#ifdef HAVE_JOYSTICK
+ if (!joysticks[port].stick)
+ return;
+ deactivate (joysticks[port].buttons[button], SDLK_UNKNOWN);
+#else
+ (void) port;
+ (void) button;
+#endif /* HAVE_JOYSTICK */
+}
+
+void
+VControl_ProcessJoyAxis (int port, int axis, int value)
+{
+#ifdef HAVE_JOYSTICK
+ int t;
+ if (!joysticks[port].stick)
+ return;
+ t = joysticks[port].threshold;
+ if (value > t)
+ {
+ if (joysticks[port].axes[axis].polarity != 1)
+ {
+ if (joysticks[port].axes[axis].polarity == -1)
+ {
+ deactivate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN);
+ }
+ joysticks[port].axes[axis].polarity = 1;
+ activate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN);
+ }
+ }
+ else if (value < -t)
+ {
+ if (joysticks[port].axes[axis].polarity != -1)
+ {
+ if (joysticks[port].axes[axis].polarity == 1)
+ {
+ deactivate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN);
+ }
+ joysticks[port].axes[axis].polarity = -1;
+ activate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN);
+ }
+ }
+ else
+ {
+ if (joysticks[port].axes[axis].polarity == -1)
+ {
+ deactivate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN);
+ }
+ else if (joysticks[port].axes[axis].polarity == 1)
+ {
+ deactivate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN);
+ }
+ joysticks[port].axes[axis].polarity = 0;
+ }
+#else
+ (void) port;
+ (void) axis;
+ (void) value;
+#endif /* HAVE_JOYSTICK */
+}
+
+void
+VControl_ProcessJoyHat (int port, int which, Uint8 value)
+{
+#ifdef HAVE_JOYSTICK
+ Uint8 old;
+ if (!joysticks[port].stick)
+ return;
+ old = joysticks[port].hats[which].last;
+ if (!(old & SDL_HAT_LEFT) && (value & SDL_HAT_LEFT))
+ activate (joysticks[port].hats[which].left, SDLK_UNKNOWN);
+ if (!(old & SDL_HAT_RIGHT) && (value & SDL_HAT_RIGHT))
+ activate (joysticks[port].hats[which].right, SDLK_UNKNOWN);
+ if (!(old & SDL_HAT_UP) && (value & SDL_HAT_UP))
+ activate (joysticks[port].hats[which].up, SDLK_UNKNOWN);
+ if (!(old & SDL_HAT_DOWN) && (value & SDL_HAT_DOWN))
+ activate (joysticks[port].hats[which].down, SDLK_UNKNOWN);
+ if ((old & SDL_HAT_LEFT) && !(value & SDL_HAT_LEFT))
+ deactivate (joysticks[port].hats[which].left, SDLK_UNKNOWN);
+ if ((old & SDL_HAT_RIGHT) && !(value & SDL_HAT_RIGHT))
+ deactivate (joysticks[port].hats[which].right, SDLK_UNKNOWN);
+ if ((old & SDL_HAT_UP) && !(value & SDL_HAT_UP))
+ deactivate (joysticks[port].hats[which].up, SDLK_UNKNOWN);
+ if ((old & SDL_HAT_DOWN) && !(value & SDL_HAT_DOWN))
+ deactivate (joysticks[port].hats[which].down, SDLK_UNKNOWN);
+ joysticks[port].hats[which].last = value;
+#else
+ (void) port;
+ (void) which;
+ (void) value;
+#endif /* HAVE_JOYSTICK */
+}
+
+void
+VControl_ResetInput (void)
+{
+ /* Step through every valid entry in the binding pool and zero
+ * them out. This will probably zero entries multiple times;
+ * oh well, no harm done. */
+
+ keypool *base = pool;
+ while (base != NULL)
+ {
+ int i;
+ for (i = 0; i < POOL_CHUNK_SIZE; i++)
+ {
+ if(base->pool[i].target)
+ {
+ *(base->pool[i].target) = 0;
+ }
+ }
+ base = base->next;
+ }
+}
+
+void
+VControl_BeginFrame (void)
+{
+ /* Step through every valid entry in the binding pool and zero
+ * out the frame-start bit. This will probably zero entries
+ * multiple times; oh well, no harm done. */
+
+ keypool *base = pool;
+ while (base != NULL)
+ {
+ int i;
+ for (i = 0; i < POOL_CHUNK_SIZE; i++)
+ {
+ if(base->pool[i].target)
+ {
+ *(base->pool[i].target) &= VCONTROL_MASK;
+ }
+ }
+ base = base->next;
+ }
+}
+
+void
+VControl_HandleEvent (const SDL_Event *e)
+{
+ switch (e->type)
+ {
+ case SDL_KEYDOWN:
+#if SDL_MAJOR_VERSION > 1
+ if (!e->key.repeat)
+#endif
+ {
+ VControl_ProcessKeyDown (e->key.keysym.sym);
+ last_interesting = *e;
+ event_ready = 1;
+ }
+ break;
+ case SDL_KEYUP:
+ VControl_ProcessKeyUp (e->key.keysym.sym);
+ break;
+
+#ifdef HAVE_JOYSTICK
+ case SDL_JOYAXISMOTION:
+ VControl_ProcessJoyAxis (e->jaxis.which, e->jaxis.axis, e->jaxis.value);
+ if ((e->jaxis.value > 15000) || (e->jaxis.value < -15000))
+ {
+ last_interesting = *e;
+ event_ready = 1;
+ }
+ break;
+ case SDL_JOYHATMOTION:
+ VControl_ProcessJoyHat (e->jhat.which, e->jhat.hat, e->jhat.value);
+ last_interesting = *e;
+ event_ready = 1;
+ break;
+ case SDL_JOYBUTTONDOWN:
+ VControl_ProcessJoyButtonDown (e->jbutton.which, e->jbutton.button);
+ last_interesting = *e;
+ event_ready = 1;
+ break;
+ case SDL_JOYBUTTONUP:
+ VControl_ProcessJoyButtonUp (e->jbutton.which, e->jbutton.button);
+ break;
+#endif /* HAVE_JOYSTICK */
+
+ default:
+ break;
+ }
+}
+
+/* Tracking the last interesting event */
+
+void
+VControl_ClearGesture (void)
+{
+ event_ready = 0;
+}
+
+int
+VControl_GetLastGesture (VCONTROL_GESTURE *g)
+{
+ if (event_ready && g != NULL)
+ {
+ event2gesture(&last_interesting, g);
+ }
+ return event_ready;
+}
+
+/* Configuration file grammar is as follows: One command per line,
+ * hashes introduce comments that persist to end of line. Blank lines
+ * are ignored.
+ *
+ * Terminals are represented here as quoted strings, e.g. "foo" for
+ * the literal string foo. These are matched case-insensitively.
+ * Special terminals are:
+ *
+ * KEYNAME: This names a key, as defined in keynames.c.
+ * IDNAME: This is an arbitrary string of alphanumerics,
+ * case-insensitive, and ending with a colon. This
+ * names an application-specific control value.
+ * NUM: This is an unsigned integer.
+ * EOF: End of file
+ *
+ * Nonterminals (the grammar itself) have the following productions:
+ *
+ * configline <- IDNAME binding
+ * | "joystick" NUM "threshold" NUM
+ * | "version" NUM
+ *
+ * binding <- "key" KEYNAME
+ * | "joystick" NUM joybinding
+ *
+ * joybinding <- "axis" NUM polarity
+ * | "button" NUM
+ * | "hat" NUM direction
+ *
+ * polarity <- "positive" | "negative"
+ *
+ * dir <- "up" | "down" | "left" | "right"
+ *
+ * This grammar is amenable to simple recursive descent parsing;
+ * in fact, it's fully LL(1). */
+
+/* Actual maximum line and token sizes are two less than this, since
+ * we need space for the \n\0 at the end */
+#define LINE_SIZE 256
+#define TOKEN_SIZE 64
+
+typedef struct vcontrol_parse_state {
+ char line[LINE_SIZE];
+ char token[TOKEN_SIZE];
+ int index;
+ int error;
+ int linenum;
+} parse_state;
+
+static void
+next_token (parse_state *state)
+{
+ int index, base;
+
+ state->token[0] = 0;
+ /* skip preceding whitespace */
+ base = state->index;
+ while (state->line[base] && isspace (state->line[base]))
+ {
+ base++;
+ }
+
+ index = 0;
+ while (index < (TOKEN_SIZE-1) && state->line[base+index] && !isspace (state->line[base+index]))
+ {
+ state->token[index] = state->line[base+index];
+ index++;
+ }
+ state->token[index] = 0;
+
+ /* If the token was too long, skip ahead until we get to whitespace */
+ while (state->line[base+index] && !isspace (state->line[base+index]))
+ {
+ index++;
+ }
+
+ state->index = base+index;
+}
+
+static void
+expected_error (parse_state *state, const char *expected)
+{
+ log_add (log_Warning, "VControl: Expected '%s' on config file line %d",
+ expected, state->linenum);
+ state->error = 1;
+}
+
+static void
+consume (parse_state *state, const char *expected)
+{
+ if (strcasecmp (expected, state->token))
+ {
+ expected_error (state, expected);
+ }
+ next_token (state);
+}
+
+static int
+consume_keyname (parse_state *state)
+{
+ int keysym = VControl_name2code (state->token);
+ if (!keysym)
+ {
+ log_add (log_Warning, "VControl: Illegal key name '%s' on config file line %d",
+ state->token, state->linenum);
+ state->error = 1;
+ }
+ next_token (state);
+ return keysym;
+}
+
+static int
+consume_num (parse_state *state)
+{
+ char *end;
+ int result = strtol (state->token, &end, 10);
+ if (*end != '\0')
+ {
+ log_add (log_Warning, "VControl: Expected integer on config line %d",
+ state->linenum);
+ state->error = 1;
+ }
+ next_token (state);
+ return result;
+}
+
+static int
+consume_polarity (parse_state *state)
+{
+ int result = 0;
+ if (!strcasecmp (state->token, "positive"))
+ {
+ result = 1;
+ }
+ else if (!strcasecmp (state->token, "negative"))
+ {
+ result = -1;
+ }
+ else
+ {
+ expected_error (state, "positive' or 'negative");
+ }
+ next_token (state);
+ return result;
+}
+
+static Uint8
+consume_dir (parse_state *state)
+{
+ Uint8 result = 0;
+ if (!strcasecmp (state->token, "left"))
+ {
+ result = SDL_HAT_LEFT;
+ }
+ else if (!strcasecmp (state->token, "right"))
+ {
+ result = SDL_HAT_RIGHT;
+ }
+ else if (!strcasecmp (state->token, "up"))
+ {
+ result = SDL_HAT_UP;
+ }
+ else if (!strcasecmp (state->token, "down"))
+ {
+ result = SDL_HAT_DOWN;
+ }
+ else
+ {
+ expected_error (state, "left', 'right', 'up' or 'down");
+ }
+ next_token (state);
+ return result;
+}
+
+static void
+parse_joybinding (parse_state *state, VCONTROL_GESTURE *gesture)
+{
+ int sticknum;
+ consume (state, "joystick");
+ sticknum = consume_num (state);
+ if (!state->error)
+ {
+ if (!strcasecmp (state->token, "axis"))
+ {
+ int axisnum;
+ consume (state, "axis");
+ axisnum = consume_num (state);
+ if (!state->error)
+ {
+ int polarity = consume_polarity (state);
+ if (!state->error)
+ {
+ gesture->type = VCONTROL_JOYAXIS;
+ gesture->gesture.axis.port = sticknum;
+ gesture->gesture.axis.index = axisnum;
+ gesture->gesture.axis.polarity = polarity;
+ }
+ }
+ }
+ else if (!strcasecmp (state->token, "button"))
+ {
+ int buttonnum;
+ consume (state, "button");
+ buttonnum = consume_num (state);
+ if (!state->error)
+ {
+ gesture->type = VCONTROL_JOYBUTTON;
+ gesture->gesture.button.port = sticknum;
+ gesture->gesture.button.index = buttonnum;
+ }
+ }
+ else if (!strcasecmp (state->token, "hat"))
+ {
+ int hatnum;
+ consume (state, "hat");
+ hatnum = consume_num (state);
+ if (!state->error)
+ {
+ Uint8 dir = consume_dir (state);
+ if (!state->error)
+ {
+ gesture->type = VCONTROL_JOYHAT;
+ gesture->gesture.hat.port = sticknum;
+ gesture->gesture.hat.index = hatnum;
+ gesture->gesture.hat.dir = dir;
+ }
+ }
+ }
+ else
+ {
+ expected_error (state, "axis', 'button', or 'hat");
+ }
+ }
+}
+
+static void
+parse_gesture (parse_state *state, VCONTROL_GESTURE *gesture)
+{
+ gesture->type = VCONTROL_NONE; /* Default to error */
+ if (!strcasecmp (state->token, "key"))
+ {
+ /* Parse key binding */
+ int keysym;
+ consume (state, "key");
+ keysym = consume_keyname (state);
+ if (!state->error)
+ {
+ gesture->type = VCONTROL_KEY;
+ gesture->gesture.key = keysym;
+ }
+ }
+ else if (!strcasecmp (state->token, "joystick"))
+ {
+ parse_joybinding (state, gesture);
+ }
+ else
+ {
+ expected_error (state, "key' or 'joystick");
+ }
+}
+
+void
+VControl_ParseGesture (VCONTROL_GESTURE *g, const char *spec)
+{
+ parse_state ps;
+
+ strncpy (ps.line, spec, LINE_SIZE);
+ ps.line[LINE_SIZE - 1] = '\0';
+ ps.index = ps.error = 0;
+ ps.linenum = -1;
+
+ next_token (&ps);
+ parse_gesture (&ps, g);
+ if (ps.error)
+ printf ("Error parsing %s\n", spec);
+}
+
+int
+VControl_DumpGesture (char *buf, int n, VCONTROL_GESTURE *g)
+{
+ switch (g->type)
+ {
+ case VCONTROL_KEY:
+ return snprintf (buf, n, "key %s", VControl_code2name (g->gesture.key));
+ case VCONTROL_JOYAXIS:
+ return snprintf (buf, n, "joystick %d axis %d %s", g->gesture.axis.port, g->gesture.axis.index,
+ (g->gesture.axis.polarity > 0) ? "positive" : "negative");
+ case VCONTROL_JOYBUTTON:
+ return snprintf (buf, n, "joystick %d button %d", g->gesture.button.port, g->gesture.button.index);
+ case VCONTROL_JOYHAT:
+ return snprintf (buf, n, "joystick %d hat %d %s", g->gesture.hat.port, g->gesture.hat.index,
+ (g->gesture.hat.dir == SDL_HAT_UP) ? "up" :
+ ((g->gesture.hat.dir == SDL_HAT_DOWN) ? "down" :
+ ((g->gesture.hat.dir == SDL_HAT_LEFT) ? "left" : "right")));
+ default:
+ buf[0] = '\0';
+ return 0;
+ }
+}
diff --git a/src/libs/input/sdl/vcontrol.h b/src/libs/input/sdl/vcontrol.h
new file mode 100644
index 0000000..9c33445
--- /dev/null
+++ b/src/libs/input/sdl/vcontrol.h
@@ -0,0 +1,108 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_INPUT_SDL_VCONTROL_H_
+#define LIBS_INPUT_SDL_VCONTROL_H_
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+
+#if SDL_MAJOR_VERSION == 1
+typedef SDLKey sdl_key_t;
+#else
+typedef SDL_Keycode sdl_key_t;
+#endif
+
+/* Initialization routines */
+void VControl_Init (void);
+void VControl_Uninit (void);
+
+/* Structures for representing actual VControl Inputs. Returned by
+ iterators and used to construct bindings. */
+
+typedef enum {
+ VCONTROL_NONE,
+ VCONTROL_KEY,
+ VCONTROL_JOYAXIS,
+ VCONTROL_JOYBUTTON,
+ VCONTROL_JOYHAT,
+ NUM_VCONTROL_GESTURES
+} VCONTROL_GESTURE_TYPE;
+
+typedef struct {
+ VCONTROL_GESTURE_TYPE type;
+ union {
+ sdl_key_t key;
+ struct { int port, index, polarity; } axis;
+ struct { int port, index; } button;
+ struct { int port, index; Uint8 dir; } hat;
+ } gesture;
+} VCONTROL_GESTURE;
+
+/* Control of bindings */
+int VControl_AddGestureBinding (VCONTROL_GESTURE *g, int *target);
+void VControl_RemoveGestureBinding (VCONTROL_GESTURE *g, int *target);
+
+int VControl_AddKeyBinding (sdl_key_t symbol, int *target);
+void VControl_RemoveKeyBinding (sdl_key_t symbol, int *target);
+int VControl_AddJoyAxisBinding (int port, int axis, int polarity, int *target);
+void VControl_RemoveJoyAxisBinding (int port, int axis, int polarity, int *target);
+int VControl_SetJoyThreshold (int port, int threshold);
+int VControl_AddJoyButtonBinding (int port, int button, int *target);
+void VControl_RemoveJoyButtonBinding (int port, int button, int *target);
+int VControl_AddJoyHatBinding (int port, int which, Uint8 dir, int *target);
+void VControl_RemoveJoyHatBinding (int port, int which, Uint8 dir, int *target);
+
+void VControl_RemoveAllBindings (void);
+
+/* Signal to VControl that a frame is about to begin. */
+void VControl_BeginFrame (void);
+
+/* The listener. Routines besides HandleEvent may be used to 'fake' inputs without
+ * fabricating an SDL_Event.
+ */
+void VControl_HandleEvent (const SDL_Event *e);
+void VControl_ProcessKeyDown (sdl_key_t symbol);
+void VControl_ProcessKeyUp (sdl_key_t symbol);
+void VControl_ProcessJoyButtonDown (int port, int button);
+void VControl_ProcessJoyButtonUp (int port, int button);
+void VControl_ProcessJoyAxis (int port, int axis, int value);
+void VControl_ProcessJoyHat (int port, int which, Uint8 value);
+
+/* Force the input into the blank state. For preventing "sticky" keys. */
+void VControl_ResetInput (void);
+
+/* Translate between gestures and string representations thereof. */
+void VControl_ParseGesture (VCONTROL_GESTURE *g, const char *spec);
+int VControl_DumpGesture (char *buf, int n, VCONTROL_GESTURE *g);
+
+/* Tracking the "last interesting gesture." Used to poll to find new
+ control keys. */
+
+void VControl_ClearGesture (void);
+int VControl_GetLastGesture (VCONTROL_GESTURE *g);
+
+/* Constants for handling the "Start bit." If a gesture is made, and
+ * then ends, within a single frame, it will still, for one frame,
+ * have a nonzero value. This is because Bit 16 will be on for the
+ * first frame a gesture is struck. This bit is cleared when
+ * VControl_BeginFrame() is called. These constants are used to mask
+ * out results if necessary. */
+
+#define VCONTROL_STARTBIT 0x10000
+#define VCONTROL_MASK 0x0FFFF
+
+#endif
diff --git a/src/libs/list.h b/src/libs/list.h
new file mode 100644
index 0000000..42a28d4
--- /dev/null
+++ b/src/libs/list.h
@@ -0,0 +1,29 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "list/list.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+
diff --git a/src/libs/list/Makeinfo b/src/libs/list/Makeinfo
new file mode 100644
index 0000000..c2bc72b
--- /dev/null
+++ b/src/libs/list/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="list.c"
+uqm_HFILES="list.h"
diff --git a/src/libs/list/list.c b/src/libs/list/list.c
new file mode 100644
index 0000000..9d86538
--- /dev/null
+++ b/src/libs/list/list.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2005 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIST_INTERNAL
+ // If list is already defined, this file is included
+ // as a template. In this case list.h has already been included.
+# define LIST_INTERNAL
+# include "list.h"
+#endif
+
+#include "libs/memlib.h"
+#define malloc HMalloc
+#define free HFree
+#define realloc HRealloc
+
+#include <assert.h>
+
+static inline LIST_(List) *LIST_(allocList)(void);
+static inline void LIST_(freeList)(LIST_(List) *list);
+static inline LIST_(Link) * LIST_(allocLink)(void);
+static inline void LIST_(freeLink)(LIST_(Link) *link);
+
+
+LIST_(List) *
+LIST_(newList)(void) {
+ LIST_(List) *list;
+
+ list = LIST_(allocList)();
+ if (list == NULL)
+ return NULL;
+
+ list->first = NULL;
+ list->end = &list->first;
+ return list;
+}
+
+void
+LIST_(deleteList)(LIST_(List) *list)
+{
+ LIST_(Link) *link;
+ LIST_(Link) *next;
+
+ for (link = list->first; link != NULL; link = next)
+ {
+ next = link->next;
+ LIST_(freeLink)(link);
+ }
+
+ LIST_(freeList)(list);
+}
+
+void
+LIST_(add)(LIST_(List) *list, LIST_(Entry) entry) {
+ LIST_(Link) *link;
+
+ link = LIST_(allocLink)();
+ link->entry = entry;
+ link->next = NULL;
+ *list->end = link;
+ list->end = &link->next;
+}
+
+static inline LIST_(Link) **
+LIST_(findLink)(LIST_(List) *list, LIST_(Entry) entry) {
+ LIST_(Link) **linkPtr;
+
+ for (linkPtr = &list->first; *linkPtr != NULL;
+ linkPtr = &(*linkPtr)->next) {
+ if ((*linkPtr)->entry == entry)
+ return linkPtr;
+ }
+ return NULL;
+}
+
+static inline void
+LIST_(removeLink)(LIST_(List) *list, LIST_(Link) **linkPtr) {
+ LIST_(Link) *link = *linkPtr;
+
+ *linkPtr = link->next;
+ if (&link->next == list->end)
+ list->end = linkPtr;
+ LIST_(freeLink)(link);
+}
+
+void
+LIST_(remove)(LIST_(List) *list, LIST_(Entry) entry) {
+ LIST_(Link) **linkPtr;
+
+ linkPtr = LIST_(findLink)(list, entry);
+ assert(linkPtr != NULL);
+ LIST_(removeLink)(list, linkPtr);
+}
+
+
+static inline LIST_(List) *
+LIST_(allocList)(void) {
+ return malloc(sizeof (LIST_(List)));
+}
+
+static inline void
+LIST_(freeList)(LIST_(List) *list) {
+ free(list);
+}
+
+static inline LIST_(Link) *
+LIST_(allocLink)(void) {
+ return malloc(sizeof (LIST_(Link)));
+}
+
+static inline void
+LIST_(freeLink)(LIST_(Link) *link) {
+ free(link);
+}
+
+
diff --git a/src/libs/list/list.h b/src/libs/list/list.h
new file mode 100644
index 0000000..2fd3332
--- /dev/null
+++ b/src/libs/list/list.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2005 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// The 'already included' check must be done slightly more complicated
+// than usually. This file may be included directly only once,
+// but it may be included my derivative List definitions that use
+// this file as a template more than once.
+#if !defined(_LIST_H) || defined(LIST_GENERIC)
+#if defined(LIST_)
+# define LIST_GENERIC
+#endif
+
+#include "types.h"
+#include "port.h"
+
+// You can use inline lists, by using this file as a template.
+// To do this, make a new .h and .c file. In the .h file, define the macros
+// (and typedefs) from the LIST_ block below.
+// In the .c file, #define LIST_INTERNAL, #include the .h file
+// and list.c (in this order), and add the necessary functions.
+#ifndef LIST_
+# define LIST_(identifier) List ## _ ## identifier
+ typedef void *List_Entry;
+#endif
+
+
+typedef struct LIST_(List) LIST_(List);
+typedef struct LIST_(Link) LIST_(Link);
+
+struct LIST_(Link) {
+ LIST_(Entry) entry;
+ LIST_(Link) *next;
+};
+
+struct LIST_(List) {
+ LIST_(Link) *first;
+ LIST_(Link) **end;
+};
+
+
+LIST_(List) *LIST_(newList)(void);
+void LIST_(deleteList)(LIST_(List) *list);
+void LIST_(add)(LIST_(List) *list, LIST_(Entry) entry);
+void LIST_(remove)(LIST_(List) *list, LIST_(Entry) entry);
+
+
+#ifndef LIST_INTERNAL
+# undef LIST_
+#endif
+
+#endif /* !defined(_LIST_H) || defined(LIST_GENERIC) */
+
+
diff --git a/src/libs/log.h b/src/libs/log.h
new file mode 100644
index 0000000..11537c7
--- /dev/null
+++ b/src/libs/log.h
@@ -0,0 +1,25 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "log/uqmlog.h"
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/libs/log/Makeinfo b/src/libs/log/Makeinfo
new file mode 100644
index 0000000..124260f
--- /dev/null
+++ b/src/libs/log/Makeinfo
@@ -0,0 +1,15 @@
+uqm_CFILES="uqmlog.c"
+uqm_HFILES="loginternal.h msgbox.h uqmlog.h"
+
+case "$HOST_SYSTEM" in
+ Darwin)
+ uqm_MFILES="msgbox_macosx.m"
+ ;;
+ MINGW32*|CYGWIN*)
+ uqm_CFILES="$uqm_CFILES msgbox_win.c"
+ ;;
+ *)
+ uqm_CFILES="$uqm_CFILES msgbox_stub.c"
+ ;;
+esac
+
diff --git a/src/libs/log/loginternal.h b/src/libs/log/loginternal.h
new file mode 100644
index 0000000..4457155
--- /dev/null
+++ b/src/libs/log/loginternal.h
@@ -0,0 +1,24 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_LOGINTERNAL_H_INCL__
+#define UQM_LOGINTERNAL_H_INCL__
+
+#include <stdio.h>
+
+extern FILE *streamOut;
+
+#endif /* UQM_LOGINTERNAL_H_INCL__ */
diff --git a/src/libs/log/msgbox.h b/src/libs/log/msgbox.h
new file mode 100644
index 0000000..72934a5
--- /dev/null
+++ b/src/libs/log/msgbox.h
@@ -0,0 +1,23 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_MSGBOX_H_INCL__
+#define UQM_MSGBOX_H_INCL__
+
+extern void log_displayBox (const /*UTF-8*/char *title, int isError,
+ const /*UTF-8*/char *msg);
+
+#endif /* UQM_MSGBOX_H_INCL__ */
diff --git a/src/libs/log/msgbox_macosx.m b/src/libs/log/msgbox_macosx.m
new file mode 100644
index 0000000..eca32be
--- /dev/null
+++ b/src/libs/log/msgbox_macosx.m
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#import <Cocoa/Cocoa.h>
+#import "msgbox.h"
+
+void
+log_displayBox (const /*UTF-8*/char *title, int isError,
+ const /*UTF-8*/char *msg)
+{
+ @autoreleasepool {
+ NSAlert *alert = [[NSAlert alloc] init];
+ NSString *titleStr = [NSString stringWithUTF8String:title];
+ NSString *msgStr = [NSString stringWithUTF8String:msg];
+
+ if (alert && titleStr && msgStr) {
+ alert.alertStyle = isError ? NSAlertStyleCritical : NSAlertStyleInformational;
+ alert.messageText = titleStr;
+ alert.informativeText = msgStr;
+
+ [alert runModal];
+ }
+
+ [msgStr release];
+ [titleStr release];
+ [alert release];
+ }
+}
+
diff --git a/src/libs/log/msgbox_stub.c b/src/libs/log/msgbox_stub.c
new file mode 100644
index 0000000..8e0b6b6
--- /dev/null
+++ b/src/libs/log/msgbox_stub.c
@@ -0,0 +1,34 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "msgbox.h"
+#include "loginternal.h"
+
+void
+log_displayBox (const /*UTF-8*/char *title, int isError,
+ const /*UTF-8*/char *msg)
+{
+ // We do not know how to display a box. Perhaps it's done with a
+ // hefty dose of pixie dust, or perhaps with a hammer and nails.
+ // So just inform the user of our predicament
+ fprintf (streamOut, "Do not know how to display %s box\n",
+ isError ? "an error" : "a");
+
+ // Suppress the compiler warnings in any case.
+ (void)title;
+ (void)msg;
+}
+
diff --git a/src/libs/log/msgbox_win.c b/src/libs/log/msgbox_win.c
new file mode 100644
index 0000000..c4e0021
--- /dev/null
+++ b/src/libs/log/msgbox_win.c
@@ -0,0 +1,67 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "msgbox.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+
+// Converts a UTF-8 string to Windows WideChar.
+// Caller is responsible for free()ing the returned string.
+static LPWSTR
+toWideChar (const /*UTF-8*/char *str)
+{
+ int cch;
+ LPWSTR wstr;
+
+ cch = MultiByteToWideChar (CP_UTF8, 0, str, -1, NULL, 0);
+ if (cch == 0)
+ return NULL; // failed, probably no UTF8 converter
+
+ wstr = malloc (cch * sizeof (WCHAR));
+ if (!wstr)
+ return NULL; // out of memory
+
+ cch = MultiByteToWideChar (CP_UTF8, 0, str, -1, wstr, cch);
+ if (cch == 0)
+ { // Failed. It should not fail here if it succeeded just above,
+ // but it did. Not much can be done about it.
+ free (wstr);
+ return NULL;
+ }
+
+ return wstr;
+}
+
+void
+log_displayBox (const /*UTF-8*/char *title, int isError,
+ const /*UTF-8*/char *msg)
+{
+ LPWSTR swTitle = toWideChar (title);
+ LPWSTR swMsg = toWideChar (msg);
+ UINT uType = isError ? MB_ICONWARNING : MB_ICONINFORMATION;
+
+ if (swTitle && swMsg)
+ MessageBoxW (NULL, swMsg, swTitle, uType);
+ else // Could not convert; let's try ASCII, though it may look ugly
+ MessageBoxA (NULL, msg, title, uType);
+
+ free (swTitle);
+ free (swMsg);
+}
+
diff --git a/src/libs/log/uqmlog.c b/src/libs/log/uqmlog.c
new file mode 100644
index 0000000..e054edb
--- /dev/null
+++ b/src/libs/log/uqmlog.c
@@ -0,0 +1,331 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "uqmlog.h"
+#include "loginternal.h"
+#include "msgbox.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include "libs/threadlib.h"
+
+#ifndef MAX_LOG_ENTRY_SIZE
+# define MAX_LOG_ENTRY_SIZE 256
+#endif
+
+#ifndef MAX_LOG_ENTRIES
+# define MAX_LOG_ENTRIES 128
+#endif
+
+typedef char log_Entry[MAX_LOG_ENTRY_SIZE];
+
+// static buffers in case we run out of memory
+static log_Entry queue[MAX_LOG_ENTRIES];
+static log_Entry msgNoThread;
+static char msgBuf[16384];
+
+static int maxLevel = log_Error;
+static int maxStreamLevel = log_Debug;
+static int maxDisp = 10;
+static int qtotal = 0;
+static int qhead = 0;
+static int qtail = 0;
+static volatile bool noThreadReady = false;
+static bool showBox = true;
+static bool errorBox = true;
+
+FILE *streamOut;
+
+static volatile int qlock = 0;
+static Mutex qmutex;
+
+static void exitCallback (void);
+static void displayLog (bool isError);
+
+static void
+lockQueue (void)
+{
+ if (!qlock)
+ return;
+
+ LockMutex (qmutex);
+}
+
+static void
+unlockQueue (void)
+{
+ if (!qlock)
+ return;
+
+ UnlockMutex (qmutex);
+}
+
+static void
+removeExcess (int room)
+{
+ room = maxDisp - room;
+ if (room < 0)
+ room = 0;
+
+ for ( ; qtotal > room; --qtotal, ++qtail)
+ ;
+ qtail %= MAX_LOG_ENTRIES;
+}
+
+static int
+acquireSlot (void)
+{
+ int slot;
+
+ lockQueue ();
+
+ removeExcess (1);
+ slot = qhead;
+ qhead = (qhead + 1) % MAX_LOG_ENTRIES;
+ ++qtotal;
+
+ unlockQueue ();
+
+ return slot;
+}
+
+// queues the non-threaded message when present
+static void
+queueNonThreaded (void)
+{
+ int slot;
+
+ // This is not perfect. A race condition still exists
+ // between buffering the no-thread message and setting
+ // the noThreadReady flag. Neither does this prevent
+ // the fully or partially overwritten message (by
+ // another competing thread). But it is 'good enough'
+ if (!noThreadReady)
+ return;
+ noThreadReady = false;
+
+ slot = acquireSlot ();
+ memcpy (queue[slot], msgNoThread, sizeof (msgNoThread));
+}
+
+void
+log_init (int max_lines)
+{
+ int i;
+
+ maxDisp = max_lines;
+ streamOut = stderr;
+
+ // pre-term queue strings
+ for (i = 0; i < MAX_LOG_ENTRIES; ++i)
+ queue[i][MAX_LOG_ENTRY_SIZE - 1] = '\0';
+
+ msgBuf[sizeof (msgBuf) - 1] = '\0';
+ msgNoThread[sizeof (msgNoThread) - 1] = '\0';
+
+ // install exit handlers
+ atexit (exitCallback);
+}
+
+void
+log_initThreads (void)
+{
+ qmutex = CreateMutex ("Logging Lock", SYNC_CLASS_RESOURCE);
+ qlock = 1;
+}
+
+int
+log_exit (int code)
+{
+ showBox = false;
+
+ if (qlock)
+ {
+ qlock = 0;
+ DestroyMutex (qmutex);
+ qmutex = 0;
+ }
+
+ return code;
+}
+
+void
+log_setLevel (int level)
+{
+ maxLevel = level;
+ //maxStreamLevel = level;
+}
+
+FILE *
+log_setOutput (FILE *out)
+{
+ FILE *old = streamOut;
+ streamOut = out;
+
+ return old;
+}
+
+void
+log_addV (log_Level level, const char *fmt, va_list list)
+{
+ log_Entry full_msg;
+ vsnprintf (full_msg, sizeof (full_msg) - 1, fmt, list);
+ full_msg[sizeof (full_msg) - 1] = '\0';
+
+ if ((int)level <= maxStreamLevel)
+ {
+ fprintf (streamOut, "%s\n", full_msg);
+ }
+
+ if ((int)level <= maxLevel)
+ {
+ int slot;
+
+ queueNonThreaded ();
+
+ slot = acquireSlot ();
+ memcpy (queue[slot], full_msg, sizeof (queue[0]));
+ }
+}
+
+void
+log_add (log_Level level, const char *fmt, ...)
+{
+ va_list list;
+
+ va_start (list, fmt);
+ log_addV (level, fmt, list);
+ va_end (list);
+}
+
+// non-threaded version of 'add'
+// uses single-instance static storage with entry into the
+// queue delayed until the next threaded 'add' or 'exit'
+void
+log_add_nothreadV (log_Level level, const char *fmt, va_list list)
+{
+ log_Entry full_msg;
+ vsnprintf (full_msg, sizeof (full_msg) - 1, fmt, list);
+ full_msg[sizeof (full_msg) - 1] = '\0';
+
+ if ((int)level <= maxStreamLevel)
+ {
+ fprintf (streamOut, "%s\n", full_msg);
+ }
+
+ if ((int)level <= maxLevel)
+ {
+ memcpy (msgNoThread, full_msg, sizeof (msgNoThread));
+ noThreadReady = true;
+ }
+}
+
+void
+log_add_nothread (log_Level level, const char *fmt, ...)
+{
+ va_list list;
+
+ va_start (list, fmt);
+ log_add_nothreadV (level, fmt, list);
+ va_end (list);
+}
+
+void
+log_showBox (bool show, bool err)
+{
+ showBox = show;
+ errorBox = err;
+}
+
+// sets the maximum log lines captured for the final
+// display to the user on failure exit
+void
+log_captureLines (int num)
+{
+ if (num > MAX_LOG_ENTRIES)
+ num = MAX_LOG_ENTRIES;
+ if (num < 1)
+ num = 1;
+ maxDisp = num;
+
+ // remove any extra lines already on queue
+ lockQueue ();
+ removeExcess (0);
+ unlockQueue ();
+}
+
+static void
+exitCallback (void)
+{
+ if (showBox)
+ displayLog (errorBox);
+
+ log_exit (0);
+}
+
+static void
+displayLog (bool isError)
+{
+ char *p = msgBuf;
+ int left = sizeof (msgBuf) - 1;
+ int len;
+ int ptr;
+
+ if (isError)
+ {
+ strcpy (p, "The Ur-Quan Masters encountered a fatal error.\n"
+ "Part of the log follows:\n\n");
+ len = strlen (p);
+ p += len;
+ left -= len;
+ }
+
+ // Glue the log entries together
+ // Locking is not a good idea at this point and we do not
+ // really need it -- the worst that can happen is we get
+ // an extra or an incomplete message
+ for (ptr = qtail; ptr != qhead && left > 0;
+ ptr = (ptr + 1) % MAX_LOG_ENTRIES)
+ {
+ len = strlen (queue[ptr]) + 1;
+ if (len > left)
+ len = left;
+ memcpy (p, queue[ptr], len);
+ p[len - 1] = '\n';
+ p += len;
+ left -= len;
+ }
+
+ // Glue the non-threaded message if present
+ if (noThreadReady)
+ {
+ noThreadReady = false;
+ len = strlen (msgNoThread);
+ if (len > left)
+ len = left;
+ memcpy (p, msgNoThread, len);
+ p += len;
+ left -= len;
+ }
+
+ *p = '\0';
+
+ log_displayBox ("The Ur-Quan Masters", isError, msgBuf);
+}
+
diff --git a/src/libs/log/uqmlog.h b/src/libs/log/uqmlog.h
new file mode 100644
index 0000000..38db089
--- /dev/null
+++ b/src/libs/log/uqmlog.h
@@ -0,0 +1,59 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQMLOG_H_INCL__
+#define UQMLOG_H_INCL__
+
+#include "port.h"
+#include "types.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+extern void log_init (int max_lines);
+extern void log_initThreads (void);
+extern int log_exit (int code);
+
+extern FILE * log_setOutput (FILE *out);
+ // sets the new output stream and returns the previous one
+extern void log_setLevel (int level);
+extern void log_showBox (bool show, bool err);
+extern void log_captureLines (int num);
+#define LOG_CAPTURE_ALL 1000000 // unreasonably big number
+
+typedef enum
+{
+ log_Nothing = 0,
+ log_User,
+ log_Fatal = log_User,
+ log_Error,
+ log_Warning,
+ log_Info,
+ log_Debug,
+ log_All,
+
+} log_Level;
+
+extern void log_add (log_Level, const char *fmt, ...)
+ PRINTF_FUNCTION(2, 3);
+extern void log_addV (log_Level, const char *fmt, va_list)
+ VPRINTF_FUNCTION(2);
+extern void log_add_nothread (log_Level, const char *fmt, ...)
+ PRINTF_FUNCTION(2, 3);
+extern void log_add_nothreadV (log_Level, const char *fmt, va_list)
+ VPRINTF_FUNCTION(2);
+
+
+#endif /* UQMLOG_H_INCL__ */
diff --git a/src/libs/math/Makeinfo b/src/libs/math/Makeinfo
new file mode 100644
index 0000000..da10867
--- /dev/null
+++ b/src/libs/math/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="random.c random2.c sqrt.c"
+uqm_HFILES="mthintrn.h random.h"
diff --git a/src/libs/math/mthintrn.h b/src/libs/math/mthintrn.h
new file mode 100644
index 0000000..cfda767
--- /dev/null
+++ b/src/libs/math/mthintrn.h
@@ -0,0 +1,25 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_MATH_MTHINTRN_H_
+#define LIBS_MATH_MTHINTRN_H_
+
+#include "libs/mathlib.h"
+
+#endif /* LIBS_MATH_MTHINTRN_H_ */
+
diff --git a/src/libs/math/random.c b/src/libs/math/random.c
new file mode 100644
index 0000000..83fcff8
--- /dev/null
+++ b/src/libs/math/random.c
@@ -0,0 +1,101 @@
+///Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+ /****************************************************************************
+* FILE: random.c
+* DESC: a library of random number generators for general purpose use.
+*
+* References:
+* "Random Number Generators: Good ones are hard to find" S.K.Park & K.W.Miller
+* Communications of the ACM, Vol31 Number 10, October 1988, Pp 1192-1201
+*
+* HISTORY: Created 1/23/1989
+* LAST CHANGED:
+*
+* Copyright (c) 1989, Robert Leyland and Fred Ford
+****************************************************************************/
+
+/* ----------------------------INCLUDES----------------------------------- */
+#include "mthintrn.h" /* get the externs for error checking */
+
+/* ----------------------------DEFINES------------------------------------ */
+/* constants for licongruential random number generator from CACM article
+ referenced above */
+#define A 16807 /* a relatively prime number -- also M div Q */
+#define M 2147483647L /* 0xFFFFFFFF / 2 */
+#define Q 127773L /* M div A */
+#define R 2836 /* M mod A */
+
+/* ----------------------------STATIC DATA-------------------------------- */
+
+static DWORD seed = 12345L; /* random number seed */
+
+/* ----------------------------CODE--------------------------------------- */
+
+/*****************************************************************************
+* FUNC: DWORD TFB_Random()
+*
+* DESC: random number generator
+*
+* NOTES:
+*
+* HISTORY: Created By Robert leyland
+*
+*****************************************************************************/
+
+DWORD
+TFB_Random (void)
+{
+ seed = A * (seed % Q) - R * (seed / Q);
+ if (seed > M)
+ return (seed -= M);
+ else if (seed)
+ return (seed);
+ else
+ return (seed = 1L);
+}
+
+/*****************************************************************************
+* FUNC: DWORD TFB_SeedRandom(DWORD l)
+*
+* DESC: set the seed for the random number generator to parameter "l", and
+* return the value of the previously active seed, to allow for multiple
+* random number streams.
+*
+* NOTES: if the seed is not valid it will be coerced into a valid range
+*
+* HISTORY: Created By Robert leyland
+*
+*****************************************************************************/
+
+DWORD
+TFB_SeedRandom (DWORD new_seed)
+{
+ DWORD old_seed;
+
+ /* coerce the seed to be in the range 1..M */
+ if (new_seed == 0L) /* 0 becomes 1 */
+ new_seed = 1;
+ else if (new_seed > M) /* and less than M */
+ new_seed -= M;
+
+ old_seed = seed;
+ seed = new_seed;
+ return (old_seed);
+}
+
diff --git a/src/libs/math/random.h b/src/libs/math/random.h
new file mode 100644
index 0000000..0e8d602
--- /dev/null
+++ b/src/libs/math/random.h
@@ -0,0 +1,56 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/****************************************************************************
+* FILE: random.h
+* DESC: definitions and externs for random number generators
+*
+* HISTORY: Created 6/ 6/1989
+* LAST CHANGED:
+*
+* Copyright (c) 1989, Robert Leyland and Scott Anderson
+****************************************************************************/
+
+#ifndef LIBS_MATH_RANDOM_H_
+#define LIBS_MATH_RANDOM_H_
+
+/* ----------------------------GLOBALS/EXTERNS---------------------------- */
+
+DWORD TFB_SeedRandom (DWORD seed);
+DWORD TFB_Random (void);
+
+
+typedef struct RandomContext RandomContext;
+
+#ifdef RANDOM2_INTERNAL
+struct RandomContext {
+ DWORD seed;
+};
+#endif
+
+RandomContext *RandomContext_New (void);
+void RandomContext_Delete (RandomContext *context);
+RandomContext *RandomContext_Copy (const RandomContext *source);
+DWORD RandomContext_Random (RandomContext *context);
+DWORD RandomContext_SeedRandom (RandomContext *context, DWORD new_seed);
+DWORD RandomContext_GetSeed (RandomContext *context);
+
+
+#endif /* LIBS_MATH_RANDOM_H_ */
+
+
diff --git a/src/libs/math/random2.c b/src/libs/math/random2.c
new file mode 100644
index 0000000..9d354b4
--- /dev/null
+++ b/src/libs/math/random2.c
@@ -0,0 +1,89 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// This file contains variants of the random functions in random.c
+// that store the state of the RNG in a context, allowing for multiple
+// independant RNGs to be used simultaneously.
+// The RNG behavior itself is the same.
+
+#include "libs/compiler.h"
+
+#define RANDOM2_INTERNAL
+#include "random.h"
+
+#include "libs/memlib.h"
+
+
+#define A 16807 /* a relatively prime number -- also M div Q */
+#define M 2147483647 /* 0xFFFFFFFF / 2 */
+#define Q 127773 /* M div A */
+#define R 2836 /* M mod A */
+
+RandomContext *
+RandomContext_New (void)
+{
+ RandomContext *result = (RandomContext *) HMalloc (sizeof (RandomContext));
+ result->seed = 12345;
+ return result;
+}
+
+void
+RandomContext_Delete (RandomContext *context)
+{
+ HFree ((void *) context);
+}
+
+RandomContext *
+RandomContext_Copy (const RandomContext *source)
+{
+ RandomContext *result = (RandomContext *) HMalloc (sizeof (RandomContext));
+ *result = *source;
+ return result;
+}
+
+DWORD
+RandomContext_Random (RandomContext *context)
+{
+ context->seed = A * (context->seed % Q) - R * (context->seed / Q);
+ if (context->seed > M) {
+ context->seed -= M;
+ } else if (context->seed == 0)
+ context->seed = 1;
+
+ return context->seed;
+}
+
+DWORD
+RandomContext_SeedRandom (RandomContext *context, DWORD new_seed)
+{
+ DWORD old_seed;
+
+ /* coerce the seed to be in the range 1..M */
+ if (new_seed == 0) /* 0 becomes 1 */
+ new_seed = 1;
+ else if (new_seed > M) /* and less than M */
+ new_seed -= M;
+
+ old_seed = context->seed;
+ context->seed = new_seed;
+ return old_seed;
+}
+
+DWORD
+RandomContext_GetSeed (RandomContext *context)
+{
+ return context->seed;
+}
diff --git a/src/libs/math/sqrt.c b/src/libs/math/sqrt.c
new file mode 100644
index 0000000..1f02cac
--- /dev/null
+++ b/src/libs/math/sqrt.c
@@ -0,0 +1,97 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "mthintrn.h"
+
+COUNT
+square_root (DWORD value)
+{
+ UWORD sig_word, mask;
+ COUNT result, shift;
+
+ if ((sig_word = HIWORD (value)) > 0)
+ {
+ DWORD mask_squared, result_shift;
+
+ for (mask = 1 << 15, shift = 31;
+ !(mask & sig_word); mask >>= 1, --shift)
+ ;
+ shift >>= 1;
+ mask = 1 << shift;
+
+ result = mask;
+ mask_squared = result_shift = (DWORD)mask << shift;
+ value -= mask_squared;
+ while (mask >>= 1)
+ {
+ DWORD remainder;
+
+ mask_squared >>= 1;
+ mask_squared >>= 1;
+ if ((remainder = result_shift + mask_squared) > value)
+ result_shift >>= 1;
+ else
+ {
+ value -= remainder;
+
+ result_shift = (result_shift >> 1) + mask_squared;
+
+ result |= mask;
+ }
+ }
+
+ return (result);
+ }
+ else if ((sig_word = LOWORD (value)) > 0)
+ {
+ UWORD mask_squared, result_shift;
+
+ for (mask = 1 << 15, shift = 15;
+ !(mask & sig_word); mask >>= 1, --shift)
+ ;
+ shift >>= 1;
+ mask = 1 << shift;
+
+ result = mask;
+ mask_squared = result_shift = mask << shift;
+ sig_word -= mask_squared;
+ while (mask >>= 1)
+ {
+ UWORD remainder;
+
+ mask_squared >>= 1;
+ mask_squared >>= 1;
+ if ((remainder = result_shift + mask_squared) > sig_word)
+ result_shift >>= 1;
+ else
+ {
+ sig_word -= remainder;
+
+ result_shift = (result_shift >> 1) + mask_squared;
+
+ result |= mask;
+ }
+ }
+
+ return (result);
+ }
+
+ return (0);
+}
+
+
diff --git a/src/libs/mathlib.h b/src/libs/mathlib.h
new file mode 100644
index 0000000..a7b27d1
--- /dev/null
+++ b/src/libs/mathlib.h
@@ -0,0 +1,36 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_MATHLIB_H_
+#define LIBS_MATHLIB_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "math/random.h"
+
+extern COUNT square_root (DWORD value);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_MATHLIB_H_ */
diff --git a/src/libs/md5.h b/src/libs/md5.h
new file mode 100644
index 0000000..f7732dc
--- /dev/null
+++ b/src/libs/md5.h
@@ -0,0 +1,32 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_MD5_H_
+#define LIBS_MD5_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "md5/md5.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_MD5_H_ */
diff --git a/src/libs/md5/Makeinfo b/src/libs/md5/Makeinfo
new file mode 100644
index 0000000..7ba0a78
--- /dev/null
+++ b/src/libs/md5/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="md5.c"
+uqm_HFILES="md5.h"
diff --git a/src/libs/md5/README b/src/libs/md5/README
new file mode 100644
index 0000000..5ca6c5a
--- /dev/null
+++ b/src/libs/md5/README
@@ -0,0 +1,6 @@
+The files md5.c and md5.h from this directory come from the GNU TLS
+library (http://www.gnutls.org/), version 1.6.1.
+
+These files are unchanged, except for the replacement of the LGPL notices by
+GPL notices.
+
diff --git a/src/libs/md5/md5.c b/src/libs/md5/md5.c
new file mode 100644
index 0000000..d57b3df
--- /dev/null
+++ b/src/libs/md5/md5.c
@@ -0,0 +1,452 @@
+/* Functions to compute MD5 message digest of files or memory blocks.
+ according to the definition of MD5 in RFC 1321 from April 1992.
+ Copyright (C) 1995,1996,1997,1999,2000,2001,2005,2006
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */
+
+#include <config.h>
+
+#include "md5.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+#endif
+
+#ifdef _LIBC
+# include <endian.h>
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define WORDS_BIGENDIAN 1
+# endif
+/* We need to keep the namespace clean so define the MD5 function
+ protected using leading __ . */
+# define md5_init_ctx __md5_init_ctx
+# define md5_process_block __md5_process_block
+# define md5_process_bytes __md5_process_bytes
+# define md5_finish_ctx __md5_finish_ctx
+# define md5_read_ctx __md5_read_ctx
+# define md5_stream __md5_stream
+# define md5_buffer __md5_buffer
+#endif
+
+#ifdef WORDS_BIGENDIAN
+# define SWAP(n) \
+ (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
+#else
+# define SWAP(n) (n)
+#endif
+
+#define BLOCKSIZE 4096
+#if BLOCKSIZE % 64 != 0
+# error "invalid BLOCKSIZE"
+#endif
+
+/* This array contains the bytes used to pad the buffer to the next
+ 64-byte boundary. (RFC 1321, 3.1: Step 1) */
+static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
+
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+void
+md5_init_ctx (struct md5_ctx *ctx)
+{
+ ctx->A = 0x67452301;
+ ctx->B = 0xefcdab89;
+ ctx->C = 0x98badcfe;
+ ctx->D = 0x10325476;
+
+ ctx->total[0] = ctx->total[1] = 0;
+ ctx->buflen = 0;
+}
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result
+ must be in little endian byte order.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32-bit value. */
+void *
+md5_read_ctx (const struct md5_ctx *ctx, void *resbuf)
+{
+ ((uint32_t *) resbuf)[0] = SWAP (ctx->A);
+ ((uint32_t *) resbuf)[1] = SWAP (ctx->B);
+ ((uint32_t *) resbuf)[2] = SWAP (ctx->C);
+ ((uint32_t *) resbuf)[3] = SWAP (ctx->D);
+
+ return resbuf;
+}
+
+/* Process the remaining bytes in the internal buffer and the usual
+ prolog according to the standard and write the result to RESBUF.
+
+ IMPORTANT: On some systems it is required that RESBUF is correctly
+ aligned for a 32-bit value. */
+void *
+md5_finish_ctx (struct md5_ctx *ctx, void *resbuf)
+{
+ /* Take yet unprocessed bytes into account. */
+ uint32_t bytes = ctx->buflen;
+ size_t size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
+
+ /* Now count remaining bytes. */
+ ctx->total[0] += bytes;
+ if (ctx->total[0] < bytes)
+ ++ctx->total[1];
+
+ /* Put the 64-bit file length in *bits* at the end of the buffer. */
+ ctx->buffer[size - 2] = SWAP (ctx->total[0] << 3);
+ ctx->buffer[size - 1] = SWAP ((ctx->total[1] << 3) | (ctx->total[0] >> 29));
+
+ memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
+
+ /* Process last bytes. */
+ md5_process_block (ctx->buffer, size * 4, ctx);
+
+ return md5_read_ctx (ctx, resbuf);
+}
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+int
+md5_stream (FILE *stream, void *resblock)
+{
+ struct md5_ctx ctx;
+ char buffer[BLOCKSIZE + 72];
+ size_t sum;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Iterate over full file contents. */
+ while (1)
+ {
+ /* We read the file in blocks of BLOCKSIZE bytes. One call of the
+ computation function processes the whole buffer so that with the
+ next round of the loop another block can be read. */
+ size_t n;
+ sum = 0;
+
+ /* Read block. Take care for partial reads. */
+ while (1)
+ {
+ n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);
+
+ sum += n;
+
+ if (sum == BLOCKSIZE)
+ break;
+
+ if (n == 0)
+ {
+ /* Check for the error flag IFF N == 0, so that we don't
+ exit the loop after a partial read due to e.g., EAGAIN
+ or EWOULDBLOCK. */
+ if (ferror (stream))
+ return 1;
+ goto process_partial_block;
+ }
+
+ /* We've read at least one byte, so ignore errors. But always
+ check for EOF, since feof may be true even though N > 0.
+ Otherwise, we could end up calling fread after EOF. */
+ if (feof (stream))
+ goto process_partial_block;
+ }
+
+ /* Process buffer with BLOCKSIZE bytes. Note that
+ BLOCKSIZE % 64 == 0
+ */
+ md5_process_block (buffer, BLOCKSIZE, &ctx);
+ }
+
+process_partial_block:
+
+ /* Process any remaining bytes. */
+ if (sum > 0)
+ md5_process_bytes (buffer, sum, &ctx);
+
+ /* Construct result in desired memory. */
+ md5_finish_ctx (&ctx, resblock);
+ return 0;
+}
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+void *
+md5_buffer (const char *buffer, size_t len, void *resblock)
+{
+ struct md5_ctx ctx;
+
+ /* Initialize the computation context. */
+ md5_init_ctx (&ctx);
+
+ /* Process whole buffer but last len % 64 bytes. */
+ md5_process_bytes (buffer, len, &ctx);
+
+ /* Put result in desired memory area. */
+ return md5_finish_ctx (&ctx, resblock);
+}
+
+
+void
+md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx)
+{
+ /* When we already have some bits in our internal buffer concatenate
+ both inputs first. */
+ if (ctx->buflen != 0)
+ {
+ size_t left_over = ctx->buflen;
+ size_t add = 128 - left_over > len ? len : 128 - left_over;
+
+ memcpy (&((char *) ctx->buffer)[left_over], buffer, add);
+ ctx->buflen += add;
+
+ if (ctx->buflen > 64)
+ {
+ md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx);
+
+ ctx->buflen &= 63;
+ /* The regions in the following copy operation cannot overlap. */
+ memcpy (ctx->buffer,
+ &((char *) ctx->buffer)[(left_over + add) & ~63],
+ ctx->buflen);
+ }
+
+ buffer = (const char *) buffer + add;
+ len -= add;
+ }
+
+ /* Process available complete blocks. */
+ if (len >= 64)
+ {
+#if !_STRING_ARCH_unaligned
+# define alignof(type) offsetof (struct align_ ## type { char c; type x; }, x)
+# define UNALIGNED_P(p) (((size_t) p) % alignof (uint32_t) != 0)
+ if (UNALIGNED_P (buffer))
+ while (len > 64)
+ {
+ md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
+ buffer = (const char *) buffer + 64;
+ len -= 64;
+ }
+ else
+#endif
+ {
+ md5_process_block (buffer, len & ~63, ctx);
+ buffer = (const char *) buffer + (len & ~63);
+ len &= 63;
+ }
+ }
+
+ /* Move remaining bytes in internal buffer. */
+ if (len > 0)
+ {
+ size_t left_over = ctx->buflen;
+
+ memcpy (&((char *) ctx->buffer)[left_over], buffer, len);
+ left_over += len;
+ if (left_over >= 64)
+ {
+ md5_process_block (ctx->buffer, 64, ctx);
+ left_over -= 64;
+ memcpy (ctx->buffer, &ctx->buffer[16], left_over);
+ }
+ ctx->buflen = left_over;
+ }
+}
+
+
+/* These are the four functions used in the four steps of the MD5 algorithm
+ and defined in the RFC 1321. The first function is a little bit optimized
+ (as found in Colin Plumbs public domain implementation). */
+/* #define FF(b, c, d) ((b & c) | (~b & d)) */
+#define FF(b, c, d) (d ^ (b & (c ^ d)))
+#define FG(b, c, d) FF (d, b, c)
+#define FH(b, c, d) (b ^ c ^ d)
+#define FI(b, c, d) (c ^ (b | ~d))
+
+/* Process LEN bytes of BUFFER, accumulating context into CTX.
+ It is assumed that LEN % 64 == 0. */
+
+void
+md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx)
+{
+ uint32_t correct_words[16];
+ const uint32_t *words = buffer;
+ size_t nwords = len / sizeof (uint32_t);
+ const uint32_t *endp = words + nwords;
+ uint32_t A = ctx->A;
+ uint32_t B = ctx->B;
+ uint32_t C = ctx->C;
+ uint32_t D = ctx->D;
+
+ /* First increment the byte count. RFC 1321 specifies the possible
+ length of the file up to 2^64 bits. Here we only compute the
+ number of bytes. Do a double word increment. */
+ ctx->total[0] += len;
+ if (ctx->total[0] < len)
+ ++ctx->total[1];
+
+ /* Process all bytes in the buffer with 64 bytes in each round of
+ the loop. */
+ while (words < endp)
+ {
+ uint32_t *cwp = correct_words;
+ uint32_t A_save = A;
+ uint32_t B_save = B;
+ uint32_t C_save = C;
+ uint32_t D_save = D;
+
+ /* First round: using the given function, the context and a constant
+ the next context is computed. Because the algorithms processing
+ unit is a 32-bit word and it is determined to work on words in
+ little endian byte order we perhaps have to change the byte order
+ before the computation. To reduce the work for the next steps
+ we store the swapped words in the array CORRECT_WORDS. */
+
+#define OP(a, b, c, d, s, T) \
+ do \
+ { \
+ a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \
+ ++words; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* It is unfortunate that C does not provide an operator for
+ cyclic rotation. Hope the C compiler is smart enough. */
+#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
+
+ /* Before we start, one word to the strange constants.
+ They are defined in RFC 1321 as
+
+ T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
+
+ Here is an equivalent invocation using Perl:
+
+ perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}'
+ */
+
+ /* Round 1. */
+ OP (A, B, C, D, 7, 0xd76aa478);
+ OP (D, A, B, C, 12, 0xe8c7b756);
+ OP (C, D, A, B, 17, 0x242070db);
+ OP (B, C, D, A, 22, 0xc1bdceee);
+ OP (A, B, C, D, 7, 0xf57c0faf);
+ OP (D, A, B, C, 12, 0x4787c62a);
+ OP (C, D, A, B, 17, 0xa8304613);
+ OP (B, C, D, A, 22, 0xfd469501);
+ OP (A, B, C, D, 7, 0x698098d8);
+ OP (D, A, B, C, 12, 0x8b44f7af);
+ OP (C, D, A, B, 17, 0xffff5bb1);
+ OP (B, C, D, A, 22, 0x895cd7be);
+ OP (A, B, C, D, 7, 0x6b901122);
+ OP (D, A, B, C, 12, 0xfd987193);
+ OP (C, D, A, B, 17, 0xa679438e);
+ OP (B, C, D, A, 22, 0x49b40821);
+
+ /* For the second to fourth round we have the possibly swapped words
+ in CORRECT_WORDS. Redefine the macro to take an additional first
+ argument specifying the function to use. */
+#undef OP
+#define OP(f, a, b, c, d, k, s, T) \
+ do \
+ { \
+ a += f (b, c, d) + correct_words[k] + T; \
+ CYCLIC (a, s); \
+ a += b; \
+ } \
+ while (0)
+
+ /* Round 2. */
+ OP (FG, A, B, C, D, 1, 5, 0xf61e2562);
+ OP (FG, D, A, B, C, 6, 9, 0xc040b340);
+ OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
+ OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
+ OP (FG, A, B, C, D, 5, 5, 0xd62f105d);
+ OP (FG, D, A, B, C, 10, 9, 0x02441453);
+ OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
+ OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
+ OP (FG, A, B, C, D, 9, 5, 0x21e1cde6);
+ OP (FG, D, A, B, C, 14, 9, 0xc33707d6);
+ OP (FG, C, D, A, B, 3, 14, 0xf4d50d87);
+ OP (FG, B, C, D, A, 8, 20, 0x455a14ed);
+ OP (FG, A, B, C, D, 13, 5, 0xa9e3e905);
+ OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8);
+ OP (FG, C, D, A, B, 7, 14, 0x676f02d9);
+ OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
+
+ /* Round 3. */
+ OP (FH, A, B, C, D, 5, 4, 0xfffa3942);
+ OP (FH, D, A, B, C, 8, 11, 0x8771f681);
+ OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
+ OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
+ OP (FH, A, B, C, D, 1, 4, 0xa4beea44);
+ OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9);
+ OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60);
+ OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
+ OP (FH, A, B, C, D, 13, 4, 0x289b7ec6);
+ OP (FH, D, A, B, C, 0, 11, 0xeaa127fa);
+ OP (FH, C, D, A, B, 3, 16, 0xd4ef3085);
+ OP (FH, B, C, D, A, 6, 23, 0x04881d05);
+ OP (FH, A, B, C, D, 9, 4, 0xd9d4d039);
+ OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
+ OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
+ OP (FH, B, C, D, A, 2, 23, 0xc4ac5665);
+
+ /* Round 4. */
+ OP (FI, A, B, C, D, 0, 6, 0xf4292244);
+ OP (FI, D, A, B, C, 7, 10, 0x432aff97);
+ OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
+ OP (FI, B, C, D, A, 5, 21, 0xfc93a039);
+ OP (FI, A, B, C, D, 12, 6, 0x655b59c3);
+ OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92);
+ OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
+ OP (FI, B, C, D, A, 1, 21, 0x85845dd1);
+ OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f);
+ OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
+ OP (FI, C, D, A, B, 6, 15, 0xa3014314);
+ OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
+ OP (FI, A, B, C, D, 4, 6, 0xf7537e82);
+ OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
+ OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
+ OP (FI, B, C, D, A, 9, 21, 0xeb86d391);
+
+ /* Add the starting values of the context. */
+ A += A_save;
+ B += B_save;
+ C += C_save;
+ D += D_save;
+ }
+
+ /* Put checksum in context given as argument. */
+ ctx->A = A;
+ ctx->B = B;
+ ctx->C = C;
+ ctx->D = D;
+}
diff --git a/src/libs/md5/md5.h b/src/libs/md5/md5.h
new file mode 100644
index 0000000..0639525
--- /dev/null
+++ b/src/libs/md5/md5.h
@@ -0,0 +1,130 @@
+/* Declaration of functions and data types used for MD5 sum computing
+ library functions.
+ Copyright (C) 1995-1997,1999,2000,2001,2004,2005,2006
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_MD5_MD5_H_
+#define LIBS_MD5_MD5_H_ 1
+
+#include <stdio.h>
+
+#ifdef _MSC_VER
+typedef unsigned int uint32_t;
+#else
+#include <stdint.h>
+#endif
+
+#define MD5_DIGEST_SIZE 16
+#define MD5_BLOCK_SIZE 64
+
+#ifndef __GNUC_PREREQ
+# if defined __GNUC__ && defined __GNUC_MINOR__
+# define __GNUC_PREREQ(maj, min) \
+ ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+# else
+# define __GNUC_PREREQ(maj, min) 0
+# endif
+#endif
+
+#ifndef __THROW
+# if defined __cplusplus && __GNUC_PREREQ (2,8)
+# define __THROW throw ()
+# else
+# define __THROW
+# endif
+#endif
+
+#ifndef _LIBC
+# define __md5_buffer md5_buffer
+# define __md5_finish_ctx md5_finish_ctx
+# define __md5_init_ctx md5_init_ctx
+# define __md5_process_block md5_process_block
+# define __md5_process_bytes md5_process_bytes
+# define __md5_read_ctx md5_read_ctx
+# define __md5_stream md5_stream
+#endif
+
+/* Structure to save state of computation between the single steps. */
+struct md5_ctx
+{
+ uint32_t A;
+ uint32_t B;
+ uint32_t C;
+ uint32_t D;
+
+ uint32_t total[2];
+ uint32_t buflen;
+ uint32_t buffer[32];
+};
+
+/*
+ * The following three functions are build up the low level used in
+ * the functions `md5_stream' and `md5_buffer'.
+ */
+
+/* Initialize structure containing state of computation.
+ (RFC 1321, 3.3: Step 3) */
+extern void __md5_init_ctx (struct md5_ctx *ctx) __THROW;
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is necessary that LEN is a multiple of 64!!! */
+extern void __md5_process_block (const void *buffer, size_t len,
+ struct md5_ctx *ctx) __THROW;
+
+/* Starting with the result of former calls of this function (or the
+ initialization function update the context for the next LEN bytes
+ starting at BUFFER.
+ It is NOT required that LEN is a multiple of 64. */
+extern void __md5_process_bytes (const void *buffer, size_t len,
+ struct md5_ctx *ctx) __THROW;
+
+/* Process the remaining bytes in the buffer and put result from CTX
+ in first 16 bytes following RESBUF. The result is always in little
+ endian byte order, so that a byte-wise output yields to the wanted
+ ASCII representation of the message digest.
+
+ IMPORTANT: On some systems, RESBUF must be aligned to a 32-bit
+ boundary. */
+extern void *__md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) __THROW;
+
+
+/* Put result from CTX in first 16 bytes following RESBUF. The result is
+ always in little endian byte order, so that a byte-wise output yields
+ to the wanted ASCII representation of the message digest.
+
+ IMPORTANT: On some systems, RESBUF must be aligned to a 32-bit
+ boundary. */
+extern void *__md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) __THROW;
+
+
+/* Compute MD5 message digest for bytes read from STREAM. The
+ resulting message digest number will be written into the 16 bytes
+ beginning at RESBLOCK. */
+extern int __md5_stream (FILE *stream, void *resblock) __THROW;
+
+/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
+ result is always in little endian byte order, so that a byte-wise
+ output yields to the wanted ASCII representation of the message
+ digest. */
+extern void *__md5_buffer (const char *buffer, size_t len,
+ void *resblock) __THROW;
+
+#endif /* md5.h */
diff --git a/src/libs/memlib.h b/src/libs/memlib.h
new file mode 100644
index 0000000..c4fa385
--- /dev/null
+++ b/src/libs/memlib.h
@@ -0,0 +1,43 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_MEMLIB_H_
+#define LIBS_MEMLIB_H_
+
+#include <stddef.h>
+
+#include "types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern bool mem_init (void);
+extern bool mem_uninit (void);
+
+extern void *HMalloc (size_t size);
+extern void HFree (void *p);
+extern void *HCalloc (size_t size);
+extern void *HRealloc (void *p, size_t size);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_MEMLIB_H_ */
+
diff --git a/src/libs/memory/Makeinfo b/src/libs/memory/Makeinfo
new file mode 100644
index 0000000..74dfc29
--- /dev/null
+++ b/src/libs/memory/Makeinfo
@@ -0,0 +1 @@
+uqm_CFILES="w_memlib.c"
diff --git a/src/libs/memory/w_memlib.c b/src/libs/memory/w_memlib.c
new file mode 100644
index 0000000..342e34e
--- /dev/null
+++ b/src/libs/memory/w_memlib.c
@@ -0,0 +1,84 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include "libs/memlib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+
+bool
+mem_init (void)
+{ // This is a stub
+ return true;
+}
+
+bool
+mem_uninit (void)
+{ // This is a stub
+ return true;
+}
+
+void *
+HMalloc (size_t size)
+{
+ void *p = malloc (size);
+ if (p == NULL && size > 0)
+ {
+ log_add (log_Fatal, "HMalloc() FATAL: out of memory.");
+ fflush (stderr);
+ explode ();
+ }
+
+ return p;
+}
+
+void
+HFree (void *p)
+{
+ free (p);
+}
+
+void *
+HCalloc (size_t size)
+{
+ void *p;
+
+ p = HMalloc (size);
+ memset (p, 0, size);
+
+ return p;
+}
+
+void *
+HRealloc (void *p, size_t size)
+{
+ p = realloc (p, size);
+ if (p == NULL && size > 0)
+ {
+ log_add (log_Fatal, "HRealloc() FATAL: out of memory.");
+ fflush (stderr);
+ explode ();
+ }
+
+ return p;
+}
+
diff --git a/src/libs/mikmod/AUTHORS b/src/libs/mikmod/AUTHORS
new file mode 100644
index 0000000..5752fdf
--- /dev/null
+++ b/src/libs/mikmod/AUTHORS
@@ -0,0 +1,124 @@
+libmikmod main authors
+----------------------
+
+* Jean-Paul Mikkers (MikMak) <mikmak@via.nl>
+ wrote MikMod and maintained it until version 3.
+* Jake Stine (Air Richter) <dracoirs@epix.net>
+ [email doesn't work anymore...]
+ made decisive contributions to the code (esp. IT support) and
+ maintained MikMod version 3 until it was discontinued. He still works
+ on the WinAmp module plugin, roughly based on MikMod.
+* Miod Vallat <miod@mikmod.org>
+ current overbooked libmikmod maintainer (since version 3.0.4), made
+ an audit of the code resulting in many bugs fixed.
+
+Previous Unix maintainers
+-------------------------
+
+* Steve McIntyre <steven@chiark.greenend.org.uk>
+ maintained MikMod'Unix version 2. Used to maintain the Debian package
+ for MikMod.
+* Peter Amstutz <tetron@student.umass.edu>
+ maintained MikMod'Unix version 3 up to version 3.0.3.
+
+General contributors
+--------------------
+
+* Arne de Bruijn <arne@knoware.nl>
+ wrote the compressed IT sample support.
+* Shlomi Fish <shlomif@vipe.technion.ac.il>
+ wrote the Java port, bug fixes.
+* Juan Linietsky <coding@reduz.com.ar>
+ overall bug fixes.
+* Claudio Matsuoka <claudio@helllabs.org>
+ wrote the STX loader and submitted bug fixes.
+* Sebastiaan A. Megens <samegens@xs4all.nl>
+ fixed various bugs (memory leaks, endianness issues, etc).
+* ``UFO'' <ufo303@poczta.onet.pl>
+ wrote the OKT loader.
+* Kev Vance <kvance@zeux.org>
+ wrote the GDM loader.
+
+* Paul Fisher made decisive contributions and improvements.
+* Alexander Kerkhove fixed an ULT panning effect bug.
+* ``Kodiak'' helped on the interfaces of libmikmod.
+* Sylvain Marchand make MikMod more portable and GCC compilable.
+
+
+Contributors on the Unix side
+-----------------------------
+
+* Douglas Carmichael <dcarmich@mcs.com>
+ ported MikMod to FreeBSD.
+* Chris Conn <cconn@tohs.abacom.com>
+ wrote the OSS driver.
+* Roine Gustaffson <e93_rog@e.kth.se>
+ wrote the Digital AudioFile driver.
+* Stephan Kanthak <kanthak@informatik.rwth-aachen.de>
+ wrote the SGI driver.
+* Lutz Vieweg <lkv@mania.robin.de>
+ wrote the AIX and HP-UX drivers.
+* Valtteri Vuorikoski <vuori@sci.fi>
+ wrote the Sun driver.
+* Andy Lo A Foe <andy@alsa-project.org>
+ wrote the Ultra driver (for the Gravis Ultrasound sound card).
+* C Ray C <crayc@pyro.net>
+ updated the Ultra driver to work with libmikmod 3.
+* ``MenTaLguY'' <mental@kludge.org>
+ autoconfized the Unix libmikmod distribution.
+* Tobias Gloth <gloth@geomagic.com>
+ created the new I/O interface, made the code MT-safe and submitted bug fixes.
+* Simon Hosie <gumboot@clear.net.nz>
+ wrote the piped output driver, and submitted speed optimizations and bugfixes
+ for the software mixer.
+* Gerd Rausch <gerd@alf.gun.de>
+ wrote the sam9407 driver.
+* Joseph Carter <knghtbrd@debian.org>
+ maintains the Debian package for MikMod and libmikmod, submitted
+ bugfixes.
+
+Contributors on the Windows side
+--------------------------------
+
+* Brian McKinney <Brian.McKinney@colorado.edu>
+ created the DirectSound driver.
+* Bjornar Henden <bhenden@online.no>
+ created the Multimedia API windows driver.
+
+Contributors on the Dos side
+----------------------------
+
+Their code isn't there anymore, but they contributed to the success of
+libmikmod...
+
+* Jean-Philippe Ajirent wrote the EMS memory routines.
+* Peter Breitling ported MikMod to DJGPP.
+* Arnout Cosman wrote the PAS driver.
+* Mario Koeppen wrote the WSS driver.
+* Mike Leibow wrote the GUS driver.
+* Jeremy McDonald wrote a fast assembly-language mixer.
+* Steffen Rusitschka and Vince Vu wrote the AWE driver.
+
+Contributors on the Macintosh side
+----------------------------------
+
+* Anders Bjoerklund <afb@algonet.se>
+ ported libmikmod to the Macintosh.
+
+Contributors on the OS/2 side
+-----------------------------
+
+* Stefan Tibus <Stefan_Tibus@ThePentagon.com>
+ ported libmikmod to OS/2.
+* Andrew Zabolotny <bit@eltech.ru>
+ improved the existing OS/2 drivers.
+
+Contributors on the BeOS side
+-----------------------------
+
+* Thomas Neumann <tneumann@polycode.dk>
+ integrated libmikmod into his BeOS APlayer, and contributed many bug fixes.
+
+--
+If your name is missing, don't hesitate to remind me at
+<miod@mikmod.org>
diff --git a/src/libs/mikmod/Makeinfo b/src/libs/mikmod/Makeinfo
new file mode 100644
index 0000000..cd037f9
--- /dev/null
+++ b/src/libs/mikmod/Makeinfo
@@ -0,0 +1,5 @@
+uqm_CFILES="drv_nos.c load_it.c load_mod.c load_s3m.c load_stm.c load_xm.c
+ mdreg.c mdriver.c mloader.c
+ mlreg.c mlutil.c mmalloc.c mmerror.c mmio.c mplayer.c munitrk.c
+ mwav.c npertab.c sloader.c virtch.c virtch2.c virtch_common.c"
+uqm_HFILES="mikmod_build.h mikmod.h mikmod_internals.h"
diff --git a/src/libs/mikmod/README b/src/libs/mikmod/README
new file mode 100644
index 0000000..c5c0be2
--- /dev/null
+++ b/src/libs/mikmod/README
@@ -0,0 +1,5 @@
+NOTE by UQM developers: this is a modified version of libmikmod.
+
+This version of the library is based on the official libmikmod library
+version 3.1.11a with some internal and API changes backported from v3.2.2.
+The official library is found at http://sourceforge.net/projects/mikmod.
diff --git a/src/libs/mikmod/drv_nos.c b/src/libs/mikmod/drv_nos.c
new file mode 100644
index 0000000..0ea3d1f
--- /dev/null
+++ b/src/libs/mikmod/drv_nos.c
@@ -0,0 +1,107 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Driver for no output
+
+==============================================================================*/
+
+/*
+
+ Written by Jean-Paul Mikkers <mikmak@via.nl>
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "mikmod_internals.h"
+
+#define ZEROLEN 32768
+
+static SBYTE *zerobuf=NULL;
+
+static BOOL NS_IsThere(void)
+{
+ return 1;
+}
+
+static BOOL NS_Init(void)
+{
+ zerobuf=(SBYTE*)MikMod_malloc(ZEROLEN);
+ return VC_Init();
+}
+
+static void NS_Exit(void)
+{
+ VC_Exit();
+ MikMod_free(zerobuf);
+}
+
+static void NS_Update(void)
+{
+ if (zerobuf)
+ VC_WriteBytes(zerobuf,ZEROLEN);
+}
+
+MIKMODAPI MDRIVER drv_nos={
+ NULL,
+ "No Sound",
+ "Nosound Driver v3.0",
+ 255,255,
+ "nosound",
+
+ NULL,
+ NS_IsThere,
+ VC_SampleLoad,
+ VC_SampleUnload,
+ VC_SampleSpace,
+ VC_SampleLength,
+ NS_Init,
+ NS_Exit,
+ NULL,
+ VC_SetNumVoices,
+ VC_PlayStart,
+ VC_PlayStop,
+ NS_Update,
+ NULL,
+ VC_VoiceSetVolume,
+ VC_VoiceGetVolume,
+ VC_VoiceSetFrequency,
+ VC_VoiceGetFrequency,
+ VC_VoiceSetPanning,
+ VC_VoiceGetPanning,
+ VC_VoicePlay,
+ VC_VoiceStop,
+ VC_VoiceStopped,
+ VC_VoiceGetPosition,
+ VC_VoiceRealVolume
+};
+
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/load_it.c b/src/libs/mikmod/load_it.c
new file mode 100644
index 0000000..747f23a
--- /dev/null
+++ b/src/libs/mikmod/load_it.c
@@ -0,0 +1,1008 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
+ AUTHORS for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Impulse tracker (IT) module loader
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+extern int toupper(int);
+#endif
+
+/*========== Module structure */
+
+/* header */
+typedef struct ITHEADER {
+ CHAR songname[26];
+ UBYTE blank01[2];
+ UWORD ordnum;
+ UWORD insnum;
+ UWORD smpnum;
+ UWORD patnum;
+ UWORD cwt; /* Created with tracker (y.xx = 0x0yxx) */
+ UWORD cmwt; /* Compatible with tracker ver > than val. */
+ UWORD flags;
+ UWORD special; /* bit 0 set = song message attached */
+ UBYTE globvol;
+ UBYTE mixvol; /* mixing volume [ignored] */
+ UBYTE initspeed;
+ UBYTE inittempo;
+ UBYTE pansep; /* panning separation between channels */
+ UBYTE zerobyte;
+ UWORD msglength;
+ ULONG msgoffset;
+ UBYTE blank02[4];
+ UBYTE pantable[64];
+ UBYTE voltable[64];
+} ITHEADER;
+
+/* sample information */
+typedef struct ITSAMPLE {
+ CHAR filename[12];
+ UBYTE zerobyte;
+ UBYTE globvol;
+ UBYTE flag;
+ UBYTE volume;
+ UBYTE panning;
+ CHAR sampname[28];
+ UWORD convert; /* sample conversion flag */
+ ULONG length;
+ ULONG loopbeg;
+ ULONG loopend;
+ ULONG c5spd;
+ ULONG susbegin;
+ ULONG susend;
+ ULONG sampoffset;
+ UBYTE vibspeed;
+ UBYTE vibdepth;
+ UBYTE vibrate;
+ UBYTE vibwave; /* 0=sine, 1=rampdown, 2=square, 3=random (speed ignored) */
+} ITSAMPLE;
+
+/* instrument information */
+
+#define ITENVCNT 25
+#define ITNOTECNT 120
+typedef struct ITINSTHEADER {
+ ULONG size; /* (dword) Instrument size */
+ CHAR filename[12]; /* (char) Instrument filename */
+ UBYTE zerobyte; /* (byte) Instrument type (always 0) */
+ UBYTE volflg;
+ UBYTE volpts;
+ UBYTE volbeg; /* (byte) Volume loop start (node) */
+ UBYTE volend; /* (byte) Volume loop end (node) */
+ UBYTE volsusbeg; /* (byte) Volume sustain begin (node) */
+ UBYTE volsusend; /* (byte) Volume Sustain end (node) */
+ UBYTE panflg;
+ UBYTE panpts;
+ UBYTE panbeg; /* (byte) channel loop start (node) */
+ UBYTE panend; /* (byte) channel loop end (node) */
+ UBYTE pansusbeg; /* (byte) channel sustain begin (node) */
+ UBYTE pansusend; /* (byte) channel Sustain end (node) */
+ UBYTE pitflg;
+ UBYTE pitpts;
+ UBYTE pitbeg; /* (byte) pitch loop start (node) */
+ UBYTE pitend; /* (byte) pitch loop end (node) */
+ UBYTE pitsusbeg; /* (byte) pitch sustain begin (node) */
+ UBYTE pitsusend; /* (byte) pitch Sustain end (node) */
+ UWORD blank;
+ UBYTE globvol;
+ UBYTE chanpan;
+ UWORD fadeout; /* Envelope end / NNA volume fadeout */
+ UBYTE dnc; /* Duplicate note check */
+ UBYTE dca; /* Duplicate check action */
+ UBYTE dct; /* Duplicate check type */
+ UBYTE nna; /* New Note Action [0,1,2,3] */
+ UWORD trkvers; /* tracker version used to save [files only] */
+ UBYTE ppsep; /* Pitch-pan Separation */
+ UBYTE ppcenter; /* Pitch-pan Center */
+ UBYTE rvolvar; /* random volume varations */
+ UBYTE rpanvar; /* random panning varations */
+ UWORD numsmp; /* Number of samples in instrument [files only] */
+ CHAR name[26]; /* Instrument name */
+ UBYTE blank01[6];
+ UWORD samptable[ITNOTECNT];/* sample for each note [note / samp pairs] */
+ UBYTE volenv[200]; /* volume envelope (IT 1.x stuff) */
+ UBYTE oldvoltick[ITENVCNT];/* volume tick position (IT 1.x stuff) */
+ UBYTE volnode[ITENVCNT]; /* amplitude of volume nodes */
+ UWORD voltick[ITENVCNT]; /* tick value of volume nodes */
+ SBYTE pannode[ITENVCNT]; /* panenv - node points */
+ UWORD pantick[ITENVCNT]; /* tick value of panning nodes */
+ SBYTE pitnode[ITENVCNT]; /* pitchenv - node points */
+ UWORD pittick[ITENVCNT]; /* tick value of pitch nodes */
+} ITINSTHEADER;
+
+/* unpacked note */
+
+typedef struct ITNOTE {
+ UBYTE note,ins,volpan,cmd,inf;
+} ITNOTE;
+
+/*========== Loader data */
+
+static ULONG *paraptr=NULL; /* parapointer array (see IT docs) */
+static ITHEADER *mh=NULL;
+static ITNOTE *itpat=NULL; /* allocate to space for one full pattern */
+static UBYTE *mask=NULL; /* arrays allocated to 64 elements and used for */
+static ITNOTE *last=NULL; /* uncompressing IT's pattern information */
+static int numtrk=0;
+static unsigned int old_effect; /* if set, use S3M old-effects stuffs */
+
+static CHAR* IT_Version[]={
+ "ImpulseTracker . ",
+ "Compressed ImpulseTracker . ",
+ "ImpulseTracker 2.14p3",
+ "Compressed ImpulseTracker 2.14p3",
+ "ImpulseTracker 2.14p4",
+ "Compressed ImpulseTracker 2.14p4",
+};
+
+/* table for porta-to-note command within volume/panning column */
+static UBYTE portatable[10]= {0,1,4,8,16,32,64,96,128,255};
+
+/*========== Loader code */
+
+BOOL IT_Test(void)
+{
+ UBYTE id[4];
+
+ if(!_mm_read_UBYTES(id,4,modreader)) return 0;
+ if(!memcmp(id,"IMPM",4)) return 1;
+ return 0;
+}
+
+BOOL IT_Init(void)
+{
+ if(!(mh=(ITHEADER*)MikMod_malloc(sizeof(ITHEADER)))) return 0;
+ if(!(poslookup=(UBYTE*)MikMod_malloc(256*sizeof(UBYTE)))) return 0;
+ if(!(itpat=(ITNOTE*)MikMod_malloc(200*64*sizeof(ITNOTE)))) return 0;
+ if(!(mask=(UBYTE*)MikMod_malloc(64*sizeof(UBYTE)))) return 0;
+ if(!(last=(ITNOTE*)MikMod_malloc(64*sizeof(ITNOTE)))) return 0;
+
+ return 1;
+}
+
+void IT_Cleanup(void)
+{
+ FreeLinear();
+
+ MikMod_free(mh);
+ MikMod_free(poslookup);
+ MikMod_free(itpat);
+ MikMod_free(mask);
+ MikMod_free(last);
+ MikMod_free(paraptr);
+ MikMod_free(origpositions);
+}
+
+/* Because so many IT files have 64 channels as the set number used, but really
+ only use far less (usually from 8 to 24 still), I had to make this function,
+ which determines the number of channels that are actually USED by a pattern.
+
+ NOTE: You must first seek to the file location of the pattern before calling
+ this procedure.
+
+ Returns 1 on error
+*/
+static BOOL IT_GetNumChannels(UWORD patrows)
+{
+ int row=0,flag,ch;
+
+ do {
+ if((flag=_mm_read_UBYTE(modreader))==EOF) {
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return 1;
+ }
+ if(!flag)
+ row++;
+ else {
+ ch=(flag-1)&63;
+ remap[ch]=0;
+ if(flag & 128) mask[ch]=_mm_read_UBYTE(modreader);
+ if(mask[ch]&1) _mm_read_UBYTE(modreader);
+ if(mask[ch]&2) _mm_read_UBYTE(modreader);
+ if(mask[ch]&4) _mm_read_UBYTE(modreader);
+ if(mask[ch]&8) { _mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader); }
+ }
+ } while(row<patrows);
+
+ return 0;
+}
+
+static UBYTE* IT_ConvertTrack(ITNOTE* tr,UWORD numrows)
+{
+ int t;
+ UBYTE note,ins,volpan;
+
+ UniReset();
+
+ for(t=0;t<numrows;t++) {
+ note=tr[t*of.numchn].note;
+ ins=tr[t*of.numchn].ins;
+ volpan=tr[t*of.numchn].volpan;
+
+ if(note!=255) {
+ if(note==253)
+ UniWriteByte(UNI_KEYOFF);
+ else if(note==254) {
+ UniPTEffect(0xc,-1); /* note cut command */
+ volpan=255;
+ } else
+ UniNote(note);
+ }
+
+ if((ins)&&(ins<100))
+ UniInstrument(ins-1);
+ else if(ins==253)
+ UniWriteByte(UNI_KEYOFF);
+ else if(ins!=255) { /* crap */
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return NULL;
+ }
+
+ /* process volume / panning column
+ volume / panning effects do NOT all share the same memory address
+ yet. */
+ if(volpan<=64)
+ UniVolEffect(VOL_VOLUME,volpan);
+ else if(volpan==65) /* fine volume slide up (65-74) - A0 case */
+ UniVolEffect(VOL_VOLSLIDE,0);
+ else if(volpan<=74) { /* fine volume slide up (65-74) - general case */
+ UniVolEffect(VOL_VOLSLIDE,0x0f+((volpan-65)<<4));
+ } else if(volpan==75) /* fine volume slide down (75-84) - B0 case */
+ UniVolEffect(VOL_VOLSLIDE,0);
+ else if(volpan<=84) { /* fine volume slide down (75-84) - general case*/
+ UniVolEffect(VOL_VOLSLIDE,0xf0+(volpan-75));
+ } else if(volpan<=94) /* volume slide up (85-94) */
+ UniVolEffect(VOL_VOLSLIDE,((volpan-85)<<4));
+ else if(volpan<=104)/* volume slide down (95-104) */
+ UniVolEffect(VOL_VOLSLIDE,(volpan-95));
+ else if(volpan<=114)/* pitch slide down (105-114) */
+ UniVolEffect(VOL_PITCHSLIDEDN,(volpan-105));
+ else if(volpan<=124)/* pitch slide up (115-124) */
+ UniVolEffect(VOL_PITCHSLIDEUP,(volpan-115));
+ else if(volpan<=127) { /* crap */
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return NULL;
+ } else if(volpan<=192)
+ UniVolEffect(VOL_PANNING,((volpan-128)==64)?255:((volpan-128)<<2));
+ else if(volpan<=202)/* portamento to note */
+ UniVolEffect(VOL_PORTAMENTO,portatable[volpan-193]);
+ else if(volpan<=212)/* vibrato */
+ UniVolEffect(VOL_VIBRATO,(volpan-203));
+ else if((volpan!=239)&&(volpan!=255)) { /* crap */
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return NULL;
+ }
+
+ S3MIT_ProcessCmd(tr[t*of.numchn].cmd,tr[t*of.numchn].inf,
+ old_effect|S3MIT_IT);
+
+ UniNewline();
+ }
+ return UniDup();
+}
+
+static BOOL IT_ReadPattern(UWORD patrows)
+{
+ int row=0,flag,ch,blah;
+ ITNOTE *itt=itpat,dummy,*n,*l;
+
+ memset(itt,255,200*64*sizeof(ITNOTE));
+
+ do {
+ if((flag=_mm_read_UBYTE(modreader))==EOF) {
+ _mm_errno = MMERR_LOADING_PATTERN;
+ return 0;
+ }
+ if(!flag) {
+ itt=&itt[of.numchn];
+ row++;
+ } else {
+ ch=remap[(flag-1)&63];
+ if(ch!=-1) {
+ n=&itt[ch];
+ l=&last[ch];
+ } else
+ n=l=&dummy;
+
+ if(flag&128) mask[ch]=_mm_read_UBYTE(modreader);
+ if(mask[ch]&1)
+ /* convert IT note off to internal note off */
+ if((l->note=n->note=_mm_read_UBYTE(modreader))==255)
+ l->note=n->note=253;
+ if(mask[ch]&2)
+ l->ins=n->ins=_mm_read_UBYTE(modreader);
+ if(mask[ch]&4)
+ l->volpan=n->volpan=_mm_read_UBYTE(modreader);
+ if(mask[ch]&8) {
+ l->cmd=n->cmd=_mm_read_UBYTE(modreader);
+ l->inf=n->inf=_mm_read_UBYTE(modreader);
+ }
+ if(mask[ch]&16)
+ n->note=l->note;
+ if(mask[ch]&32)
+ n->ins=l->ins;
+ if(mask[ch]&64)
+ n->volpan=l->volpan;
+ if(mask[ch]&128) {
+ n->cmd=l->cmd;
+ n->inf=l->inf;
+ }
+ }
+ } while(row<patrows);
+
+ for(blah=0;blah<of.numchn;blah++) {
+ if(!(of.tracks[numtrk++]=IT_ConvertTrack(&itpat[blah],patrows)))
+ return 0;
+ }
+
+ return 1;
+}
+
+static void LoadMidiString(MREADER* modreader,CHAR* dest)
+{
+ CHAR *cur,*last;
+
+ _mm_read_UBYTES(dest,32,modreader);
+ cur=last=dest;
+ /* remove blanks and uppercase all */
+ while(*last) {
+ if(isalnum((int)*last)) *(cur++)=toupper((int)*last);
+ last++;
+ }
+ *cur=0;
+}
+
+/* Load embedded midi information for resonant filters */
+static void IT_LoadMidiConfiguration(MREADER* modreader)
+{
+ int i;
+
+ memset(filtermacros,0,sizeof(filtermacros));
+ memset(filtersettings,0,sizeof(filtersettings));
+
+ if (modreader) { /* information is embedded in file */
+ UWORD dat;
+ CHAR midiline[33];
+
+ dat=_mm_read_I_UWORD(modreader);
+ _mm_fseek(modreader,8*dat+0x120,SEEK_CUR);
+
+ /* read midi macros */
+ for(i=0;i<UF_MAXMACRO;i++) {
+ LoadMidiString(modreader,midiline);
+ if((!strncmp(midiline,"F0F00",5))&&
+ ((midiline[5]=='0')||(midiline[5]=='1')))
+ filtermacros[i]=(midiline[5]-'0')|0x80;
+ }
+
+ /* read standalone filters */
+ for(i=0x80;i<0x100;i++) {
+ LoadMidiString(modreader,midiline);
+ if((!strncmp(midiline,"F0F00",5))&&
+ ((midiline[5]=='0')||(midiline[5]=='1'))) {
+ filtersettings[i].filter=(midiline[5]-'0')|0x80;
+ dat=(midiline[6])?(midiline[6]-'0'):0;
+ if(midiline[7])dat=(dat<<4)|(midiline[7]-'0');
+ filtersettings[i].inf=dat;
+ }
+ }
+ } else { /* use default information */
+ filtermacros[0]=FILT_CUT;
+ for(i=0x80;i<0x90;i++) {
+ filtersettings[i].filter=FILT_RESONANT;
+ filtersettings[i].inf=(i&0x7f)<<3;
+ }
+ }
+ activemacro=0;
+ for(i=0;i<0x80;i++) {
+ filtersettings[i].filter=filtermacros[0];
+ filtersettings[i].inf=i;
+ }
+}
+
+BOOL IT_Load(BOOL curious)
+{
+ int t,u,lp;
+ INSTRUMENT *d;
+ SAMPLE *q;
+ BOOL compressed=0;
+
+ numtrk=0;
+ filters=0;
+
+ /* try to read module header */
+ _mm_read_I_ULONG(modreader); /* kill the 4 byte header */
+ _mm_read_string(mh->songname,26,modreader);
+ _mm_read_UBYTES(mh->blank01,2,modreader);
+ mh->ordnum =_mm_read_I_UWORD(modreader);
+ mh->insnum =_mm_read_I_UWORD(modreader);
+ mh->smpnum =_mm_read_I_UWORD(modreader);
+ mh->patnum =_mm_read_I_UWORD(modreader);
+ mh->cwt =_mm_read_I_UWORD(modreader);
+ mh->cmwt =_mm_read_I_UWORD(modreader);
+ mh->flags =_mm_read_I_UWORD(modreader);
+ mh->special =_mm_read_I_UWORD(modreader);
+ mh->globvol =_mm_read_UBYTE(modreader);
+ mh->mixvol =_mm_read_UBYTE(modreader);
+ mh->initspeed =_mm_read_UBYTE(modreader);
+ mh->inittempo =_mm_read_UBYTE(modreader);
+ mh->pansep =_mm_read_UBYTE(modreader);
+ mh->zerobyte =_mm_read_UBYTE(modreader);
+ mh->msglength =_mm_read_I_UWORD(modreader);
+ mh->msgoffset =_mm_read_I_ULONG(modreader);
+ _mm_read_UBYTES(mh->blank02,4,modreader);
+ _mm_read_UBYTES(mh->pantable,64,modreader);
+ _mm_read_UBYTES(mh->voltable,64,modreader);
+
+ if(_mm_eof(modreader)) {
+ _mm_errno=MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ /* set module variables */
+ of.songname = DupStr(mh->songname,26,0); /* make a cstr of songname */
+ of.reppos = 0;
+ of.numpat = mh->patnum;
+ of.numins = mh->insnum;
+ of.numsmp = mh->smpnum;
+ of.initspeed = mh->initspeed;
+ of.inittempo = mh->inittempo;
+ of.initvolume = mh->globvol;
+ of.flags |= UF_BGSLIDES | UF_ARPMEM;
+ if (!(mh->flags & 1))
+ of.flags |= UF_PANNING;
+ of.bpmlimit=32;
+
+ if(mh->songname[25]) {
+ of.numvoices=1+mh->songname[25];
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr,"Embedded IT limitation to %d voices\n",of.numvoices);
+#endif
+ }
+
+ /* set the module type */
+ /* 2.17 : IT 2.14p4 */
+ /* 2.16 : IT 2.14p3 with resonant filters */
+ /* 2.15 : IT 2.14p3 (improved compression) */
+ if((mh->cwt<=0x219)&&(mh->cwt>=0x217))
+ of.modtype=strdup(IT_Version[mh->cmwt<0x214?4:5]);
+ else if (mh->cwt>=0x215)
+ of.modtype=strdup(IT_Version[mh->cmwt<0x214?2:3]);
+ else {
+ of.modtype = strdup(IT_Version[mh->cmwt<0x214?0:1]);
+ of.modtype[mh->cmwt<0x214?15:26] = (mh->cwt>>8)+'0';
+ of.modtype[mh->cmwt<0x214?17:28] = ((mh->cwt>>4)&0xf)+'0';
+ of.modtype[mh->cmwt<0x214?18:29] = ((mh->cwt)&0xf)+'0';
+ }
+
+ if(mh->flags&8)
+ of.flags |= UF_XMPERIODS | UF_LINEAR;
+
+ if((mh->cwt>=0x106)&&(mh->flags&16))
+ old_effect=S3MIT_OLDSTYLE;
+ else
+ old_effect=0;
+
+ /* set panning positions */
+ if (mh->flags & 1)
+ for(t=0;t<64;t++) {
+ mh->pantable[t]&=0x7f;
+ if(mh->pantable[t]<64)
+ of.panning[t]=mh->pantable[t]<<2;
+ else if(mh->pantable[t]==64)
+ of.panning[t]=255;
+ else if(mh->pantable[t]==100)
+ of.panning[t]=PAN_SURROUND;
+ else if(mh->pantable[t]==127)
+ of.panning[t]=PAN_CENTER;
+ else {
+ _mm_errno=MMERR_LOADING_HEADER;
+ return 0;
+ }
+ }
+ else
+ for(t=0;t<64;t++)
+ of.panning[t]=PAN_CENTER;
+
+ /* set channel volumes */
+ memcpy(of.chanvol,mh->voltable,64);
+
+ /* read the order data */
+ if(!AllocPositions(mh->ordnum)) return 0;
+ if(!(origpositions=MikMod_calloc(mh->ordnum,sizeof(UWORD)))) return 0;
+
+ for(t=0;t<mh->ordnum;t++) {
+ origpositions[t]=_mm_read_UBYTE(modreader);
+ if((origpositions[t]>mh->patnum)&&(origpositions[t]<254))
+ origpositions[t]=255;
+ }
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ poslookupcnt=mh->ordnum;
+ S3MIT_CreateOrders(curious);
+
+ if(!(paraptr=(ULONG*)MikMod_malloc((mh->insnum+mh->smpnum+of.numpat)*
+ sizeof(ULONG)))) return 0;
+
+ /* read the instrument, sample, and pattern parapointers */
+ _mm_read_I_ULONGS(paraptr,mh->insnum+mh->smpnum+of.numpat,modreader);
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ /* Check for and load midi information for resonant filters */
+ if(mh->cmwt>=0x216) {
+ if(mh->special&8) {
+ IT_LoadMidiConfiguration(modreader);
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+ } else
+ IT_LoadMidiConfiguration(NULL);
+ filters=1;
+ }
+
+ /* Check for and load song comment */
+ if((mh->special&1)&&(mh->cwt>=0x104)&&(mh->msglength)) {
+ _mm_fseek(modreader,(long)(mh->msgoffset),SEEK_SET);
+ if(!ReadComment(mh->msglength)) return 0;
+ }
+
+ if(!(mh->flags&4)) of.numins=of.numsmp;
+ if(!AllocSamples()) return 0;
+
+ if(!AllocLinear()) return 0;
+
+ /* Load all samples */
+ q = of.samples;
+ for(t=0;t<mh->smpnum;t++) {
+ ITSAMPLE s;
+
+ /* seek to sample position */
+ _mm_fseek(modreader,(long)(paraptr[mh->insnum+t]+4),SEEK_SET);
+
+ /* load sample info */
+ _mm_read_string(s.filename,12,modreader);
+ s.zerobyte = _mm_read_UBYTE(modreader);
+ s.globvol = _mm_read_UBYTE(modreader);
+ s.flag = _mm_read_UBYTE(modreader);
+ s.volume = _mm_read_UBYTE(modreader);
+ _mm_read_string(s.sampname,26,modreader);
+ s.convert = _mm_read_UBYTE(modreader);
+ s.panning = _mm_read_UBYTE(modreader);
+ s.length = _mm_read_I_ULONG(modreader);
+ s.loopbeg = _mm_read_I_ULONG(modreader);
+ s.loopend = _mm_read_I_ULONG(modreader);
+ s.c5spd = _mm_read_I_ULONG(modreader);
+ s.susbegin = _mm_read_I_ULONG(modreader);
+ s.susend = _mm_read_I_ULONG(modreader);
+ s.sampoffset = _mm_read_I_ULONG(modreader);
+ s.vibspeed = _mm_read_UBYTE(modreader);
+ s.vibdepth = _mm_read_UBYTE(modreader);
+ s.vibrate = _mm_read_UBYTE(modreader);
+ s.vibwave = _mm_read_UBYTE(modreader);
+
+ /* Generate an error if c5spd is > 512k, or samplelength > 256 megs
+ (nothing would EVER be that high) */
+
+ if(_mm_eof(modreader)||(s.c5spd>0x7ffffL)||(s.length>0xfffffffUL)) {
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+
+ /* Reality check for sample loop information */
+ if((s.flag&16)&&
+ ((s.loopbeg>0xfffffffUL)||(s.loopend>0xfffffffUL))) {
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+
+ q->samplename = DupStr(s.sampname,26,0);
+ q->speed = s.c5spd / 2;
+ q->panning = ((s.panning&127)==64)?255:(s.panning&127)<<2;
+ q->length = s.length;
+ q->loopstart = s.loopbeg;
+ q->loopend = s.loopend;
+ q->volume = s.volume;
+ q->globvol = s.globvol;
+ q->seekpos = s.sampoffset;
+
+ /* Convert speed to XM linear finetune */
+ if(of.flags&UF_LINEAR)
+ q->speed=speed_to_finetune(s.c5spd,t);
+
+ if(s.panning&128) q->flags|=SF_OWNPAN;
+
+ if(s.vibrate) {
+ q->vibflags |= AV_IT;
+ q->vibtype = s.vibwave;
+ q->vibsweep = s.vibrate * 2;
+ q->vibdepth = s.vibdepth;
+ q->vibrate = s.vibspeed;
+ }
+
+ if(s.flag&2) q->flags|=SF_16BITS;
+ if((s.flag&8)&&(mh->cwt>=0x214)) {
+ q->flags|=SF_ITPACKED;
+ compressed=1;
+ }
+ if(s.flag&16) q->flags|=SF_LOOP;
+ if(s.flag&64) q->flags|=SF_BIDI;
+
+ if(mh->cwt>=0x200) {
+ if(s.convert&1) q->flags|=SF_SIGNED;
+ if(s.convert&4) q->flags|=SF_DELTA;
+ }
+ q++;
+ }
+
+ /* Load instruments if instrument mode flag enabled */
+ if(mh->flags&4) {
+ if(!AllocInstruments()) return 0;
+ d=of.instruments;
+ of.flags|=UF_NNA|UF_INST;
+
+ for(t=0;t<mh->insnum;t++) {
+ ITINSTHEADER ih;
+
+ /* seek to instrument position */
+ _mm_fseek(modreader,paraptr[t]+4,SEEK_SET);
+
+ /* load instrument info */
+ _mm_read_string(ih.filename,12,modreader);
+ ih.zerobyte = _mm_read_UBYTE(modreader);
+ if(mh->cwt<0x200) {
+ /* load IT 1.xx inst header */
+ ih.volflg = _mm_read_UBYTE(modreader);
+ ih.volbeg = _mm_read_UBYTE(modreader);
+ ih.volend = _mm_read_UBYTE(modreader);
+ ih.volsusbeg = _mm_read_UBYTE(modreader);
+ ih.volsusend = _mm_read_UBYTE(modreader);
+ _mm_read_I_UWORD(modreader);
+ ih.fadeout = _mm_read_I_UWORD(modreader);
+ ih.nna = _mm_read_UBYTE(modreader);
+ ih.dnc = _mm_read_UBYTE(modreader);
+ } else {
+ /* Read IT200+ header */
+ ih.nna = _mm_read_UBYTE(modreader);
+ ih.dct = _mm_read_UBYTE(modreader);
+ ih.dca = _mm_read_UBYTE(modreader);
+ ih.fadeout = _mm_read_I_UWORD(modreader);
+ ih.ppsep = _mm_read_UBYTE(modreader);
+ ih.ppcenter = _mm_read_UBYTE(modreader);
+ ih.globvol = _mm_read_UBYTE(modreader);
+ ih.chanpan = _mm_read_UBYTE(modreader);
+ ih.rvolvar = _mm_read_UBYTE(modreader);
+ ih.rpanvar = _mm_read_UBYTE(modreader);
+ }
+
+ ih.trkvers = _mm_read_I_UWORD(modreader);
+ ih.numsmp = _mm_read_UBYTE(modreader);
+ _mm_read_UBYTE(modreader);
+ _mm_read_string(ih.name,26,modreader);
+ _mm_read_UBYTES(ih.blank01,6,modreader);
+ _mm_read_I_UWORDS(ih.samptable,ITNOTECNT,modreader);
+ if(mh->cwt<0x200) {
+ /* load IT 1xx volume envelope */
+ _mm_read_UBYTES(ih.volenv,200,modreader);
+ for(lp=0;lp<ITENVCNT;lp++) {
+ ih.oldvoltick[lp] = _mm_read_UBYTE(modreader);
+ ih.volnode[lp] = _mm_read_UBYTE(modreader);
+ }
+ } else {
+ /* load IT 2xx volume, pan and pitch envelopes */
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C
+#define IT_LoadEnvelope(name,type) \
+ ih. name##flg =_mm_read_UBYTE(modreader); \
+ ih. name##pts =_mm_read_UBYTE(modreader); \
+ ih. name##beg =_mm_read_UBYTE(modreader); \
+ ih. name##end =_mm_read_UBYTE(modreader); \
+ ih. name##susbeg=_mm_read_UBYTE(modreader); \
+ ih. name##susend=_mm_read_UBYTE(modreader); \
+ for(lp=0;lp<ITENVCNT;lp++) { \
+ ih. name##node[lp]=_mm_read_##type (modreader); \
+ ih. name##tick[lp]=_mm_read_I_UWORD(modreader); \
+ } \
+ _mm_read_UBYTE(modreader)
+#else
+#define IT_LoadEnvelope(name,type) \
+ ih. name/**/flg =_mm_read_UBYTE(modreader); \
+ ih. name/**/pts =_mm_read_UBYTE(modreader); \
+ ih. name/**/beg =_mm_read_UBYTE(modreader); \
+ ih. name/**/end =_mm_read_UBYTE(modreader); \
+ ih. name/**/susbeg=_mm_read_UBYTE(modreader); \
+ ih. name/**/susend=_mm_read_UBYTE(modreader); \
+ for(lp=0;lp<ITENVCNT;lp++) { \
+ ih. name/**/node[lp]=_mm_read_/**/type (modreader); \
+ ih. name/**/tick[lp]=_mm_read_I_UWORD(modreader); \
+ } \
+ _mm_read_UBYTE(modreader)
+#endif
+
+ IT_LoadEnvelope(vol,UBYTE);
+ IT_LoadEnvelope(pan,SBYTE);
+ IT_LoadEnvelope(pit,SBYTE);
+#undef IT_LoadEnvelope
+ }
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+
+ d->volflg|=EF_VOLENV;
+ d->insname = DupStr(ih.name,26,0);
+ d->nnatype = ih.nna & NNA_MASK;
+
+ if(mh->cwt<0x200) {
+ d->volfade=ih.fadeout<< 6;
+ if(ih.dnc) {
+ d->dct=DCT_NOTE;
+ d->dca=DCA_CUT;
+ }
+
+ if(ih.volflg&1) d->volflg|=EF_ON;
+ if(ih.volflg&2) d->volflg|=EF_LOOP;
+ if(ih.volflg&4) d->volflg|=EF_SUSTAIN;
+
+ /* XM conversion of IT envelope Array */
+ d->volbeg = ih.volbeg;
+ d->volend = ih.volend;
+ d->volsusbeg = ih.volsusbeg;
+ d->volsusend = ih.volsusend;
+
+ if(ih.volflg&1) {
+ for(u=0;u<ITENVCNT;u++)
+ if(ih.oldvoltick[d->volpts]!=0xff) {
+ d->volenv[d->volpts].val=(ih.volnode[d->volpts]<<2);
+ d->volenv[d->volpts].pos=ih.oldvoltick[d->volpts];
+ d->volpts++;
+ } else
+ break;
+ }
+ } else {
+ d->panning=((ih.chanpan&127)==64)?255:(ih.chanpan&127)<<2;
+ if(!(ih.chanpan&128)) d->flags|=IF_OWNPAN;
+
+ if(!(ih.ppsep & 128)) {
+ d->pitpansep=ih.ppsep<<2;
+ d->pitpancenter=ih.ppcenter;
+ d->flags|=IF_PITCHPAN;
+ }
+ d->globvol=ih.globvol>>1;
+ d->volfade=ih.fadeout<<5;
+ d->dct =ih.dct;
+ d->dca =ih.dca;
+
+ if(mh->cwt>=0x204) {
+ d->rvolvar = ih.rvolvar;
+ d->rpanvar = ih.rpanvar;
+ }
+
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C
+#define IT_ProcessEnvelope(name) \
+ if(ih. name##flg&1) d-> name##flg|=EF_ON; \
+ if(ih. name##flg&2) d-> name##flg|=EF_LOOP; \
+ if(ih. name##flg&4) d-> name##flg|=EF_SUSTAIN; \
+ d-> name##pts=ih. name##pts; \
+ d-> name##beg=ih. name##beg; \
+ d-> name##end=ih. name##end; \
+ d-> name##susbeg=ih. name##susbeg; \
+ d-> name##susend=ih. name##susend; \
+ \
+ for(u=0;u<ih. name##pts;u++) \
+ d-> name##env[u].pos=ih. name##tick[u]; \
+ \
+ if((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \
+ d-> name##flg&=~EF_ON
+#else
+#define IT_ProcessEnvelope(name) \
+ if(ih. name/**/flg&1) d-> name/**/flg|=EF_ON; \
+ if(ih. name/**/flg&2) d-> name/**/flg|=EF_LOOP; \
+ if(ih. name/**/flg&4) d-> name/**/flg|=EF_SUSTAIN; \
+ d-> name/**/pts=ih. name/**/pts; \
+ d-> name/**/beg=ih. name/**/beg; \
+ d-> name/**/end=ih. name/**/end; \
+ d-> name/**/susbeg=ih. name/**/susbeg; \
+ d-> name/**/susend=ih. name/**/susend; \
+ \
+ for(u=0;u<ih. name/**/pts;u++) \
+ d-> name/**/env[u].pos=ih. name/**/tick[u]; \
+ \
+ if((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \
+ d-> name/**/flg&=~EF_ON
+#endif
+
+ IT_ProcessEnvelope(vol);
+ for(u=0;u<ih.volpts;u++)
+ d->volenv[u].val=(ih.volnode[u]<<2);
+
+ IT_ProcessEnvelope(pan);
+ for(u=0;u<ih.panpts;u++)
+ d->panenv[u].val=
+ ih.pannode[u]==32?255:(ih.pannode[u]+32)<<2;
+
+ IT_ProcessEnvelope(pit);
+ for(u=0;u<ih.pitpts;u++)
+ d->pitenv[u].val=ih.pitnode[u]+32;
+#undef IT_ProcessEnvelope
+
+ if(ih.pitflg&0x80) {
+ /* filter envelopes not supported yet */
+ d->pitflg&=~EF_ON;
+ ih.pitpts=ih.pitbeg=ih.pitend=0;
+#ifdef MIKMOD_DEBUG
+ {
+ static int warn=0;
+
+ if(!warn)
+ fprintf(stderr, "\rFilter envelopes not supported yet\n");
+ warn=1;
+ }
+#endif
+ }
+ }
+
+ for(u=0;u<ITNOTECNT;u++) {
+ d->samplenote[u]=(ih.samptable[u]&255);
+ d->samplenumber[u]=
+ (ih.samptable[u]>>8)?((ih.samptable[u]>>8)-1):0xffff;
+ if(d->samplenumber[u]>=of.numsmp)
+ d->samplenote[u]=255;
+ else if (of.flags&UF_LINEAR) {
+ int note=(int)d->samplenote[u]+noteindex[d->samplenumber[u]];
+ d->samplenote[u]=(note<0)?0:(note>255?255:note);
+ }
+ }
+
+ d++;
+ }
+ } else if(of.flags & UF_LINEAR) {
+ if(!AllocInstruments()) return 0;
+ d=of.instruments;
+ of.flags|=UF_INST;
+
+ for(t=0;t<mh->smpnum;t++,d++)
+ for(u=0;u<ITNOTECNT;u++) {
+ if(d->samplenumber[u]>=of.numsmp)
+ d->samplenote[u]=255;
+ else {
+ int note=(int)d->samplenote[u]+noteindex[d->samplenumber[u]];
+ d->samplenote[u]=(note<0)?0:(note>255?255:note);
+ }
+ }
+ }
+
+ /* Figure out how many channels this song actually uses */
+ of.numchn=0;
+ memset(remap,-1,UF_MAXCHAN*sizeof(UBYTE));
+ for(t=0;t<of.numpat;t++) {
+ UWORD packlen;
+
+ /* seek to pattern position */
+ if(paraptr[mh->insnum+mh->smpnum+t]) { /* 0 -> empty 64 row pattern */
+ _mm_fseek(modreader,((long)paraptr[mh->insnum+mh->smpnum+t]),SEEK_SET);
+ _mm_read_I_UWORD(modreader);
+ /* read pattern length (# of rows)
+ Impulse Tracker never creates patterns with less than 32 rows,
+ but some other trackers do, so we only check for more than 256
+ rows */
+ packlen=_mm_read_I_UWORD(modreader);
+ if(packlen>256) {
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return 0;
+ }
+ _mm_read_I_ULONG(modreader);
+ if(IT_GetNumChannels(packlen)) return 0;
+ }
+ }
+
+ /* give each of them a different number */
+ for(t=0;t<UF_MAXCHAN;t++)
+ if(!remap[t])
+ remap[t]=of.numchn++;
+
+ of.numtrk = of.numpat*of.numchn;
+ if(of.numvoices)
+ if (of.numvoices<of.numchn) of.numvoices=of.numchn;
+
+ if(!AllocPatterns()) return 0;
+ if(!AllocTracks()) return 0;
+
+ for(t=0;t<of.numpat;t++) {
+ UWORD packlen;
+
+ /* seek to pattern position */
+ if(!paraptr[mh->insnum+mh->smpnum+t]) { /* 0 -> empty 64 row pattern */
+ of.pattrows[t]=64;
+ for(u=0;u<of.numchn;u++) {
+ int k;
+
+ UniReset();
+ for(k=0;k<64;k++) UniNewline();
+ of.tracks[numtrk++]=UniDup();
+ }
+ } else {
+ _mm_fseek(modreader,((long)paraptr[mh->insnum+mh->smpnum+t]),SEEK_SET);
+ packlen=_mm_read_I_UWORD(modreader);
+ of.pattrows[t]=_mm_read_I_UWORD(modreader);
+ _mm_read_I_ULONG(modreader);
+ if(!IT_ReadPattern(of.pattrows[t])) return 0;
+ }
+ }
+
+ return 1;
+}
+
+CHAR *IT_LoadTitle(void)
+{
+ CHAR s[26];
+
+ _mm_fseek(modreader,4,SEEK_SET);
+ if(!_mm_read_UBYTES(s,26,modreader)) return NULL;
+
+ return(DupStr(s,26,0));
+}
+
+/*========== Loader information */
+
+MIKMODAPI MLOADER load_it={
+ NULL,
+ "IT",
+ "IT (Impulse Tracker)",
+ IT_Init,
+ IT_Test,
+ IT_Load,
+ IT_Cleanup,
+ IT_LoadTitle
+};
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/load_mod.c b/src/libs/mikmod/load_mod.c
new file mode 100644
index 0000000..40d4b9a
--- /dev/null
+++ b/src/libs/mikmod/load_mod.c
@@ -0,0 +1,512 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
+ AUTHORS for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Generic MOD loader (Protracker, StarTracker, FastTracker, etc)
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+/*========== Module structure */
+
+typedef struct MSAMPINFO {
+ CHAR samplename[23]; /* 22 in module, 23 in memory */
+ UWORD length;
+ UBYTE finetune;
+ UBYTE volume;
+ UWORD reppos;
+ UWORD replen;
+} MSAMPINFO;
+
+typedef struct MODULEHEADER {
+ CHAR songname[21]; /* the songname.. 20 in module, 21 in memory */
+ MSAMPINFO samples[31]; /* all sampleinfo */
+ UBYTE songlength; /* number of patterns used */
+ UBYTE magic1; /* should be 127 */
+ UBYTE positions[128]; /* which pattern to play at pos */
+ UBYTE magic2[4]; /* string "M.K." or "FLT4" or "FLT8" */
+} MODULEHEADER;
+
+typedef struct MODTYPE {
+ CHAR id[5];
+ UBYTE channels;
+ CHAR *name;
+} MODTYPE;
+
+typedef struct MODNOTE {
+ UBYTE a, b, c, d;
+} MODNOTE;
+
+/*========== Loader variables */
+
+#define MODULEHEADERSIZE 0x438
+
+static CHAR protracker[] = "Protracker";
+static CHAR startrekker[] = "Startrekker";
+static CHAR fasttracker[] = "Fasttracker";
+static CHAR oktalyser[] = "Oktalyser";
+static CHAR oktalyzer[] = "Oktalyzer";
+static CHAR taketracker[] = "TakeTracker";
+static CHAR orpheus[] = "Imago Orpheus (MOD format)";
+
+static MODULEHEADER *mh = NULL;
+static MODNOTE *patbuf = NULL;
+static int modtype, trekker;
+
+/*========== Loader code */
+
+/* given the module ID, determine the number of channels and the tracker
+ description ; also alters modtype */
+static BOOL MOD_CheckType(UBYTE *id, UBYTE *numchn, CHAR **descr)
+{
+ modtype = trekker = 0;
+
+ /* Protracker and variants */
+ if ((!memcmp(id, "M.K.", 4)) || (!memcmp(id, "M!K!", 4))) {
+ *descr = protracker;
+ modtype = 0;
+ *numchn = 4;
+ return 1;
+ }
+
+ /* Star Tracker */
+ if (((!memcmp(id, "FLT", 3)) || (!memcmp(id, "EXO", 3))) &&
+ (isdigit(id[3]))) {
+ *descr = startrekker;
+ modtype = trekker = 1;
+ *numchn = id[3] - '0';
+ if (*numchn == 4 || *numchn == 8)
+ return 1;
+#ifdef MIKMOD_DEBUG
+ else
+ fprintf(stderr, "\rUnknown FLT%d module type\n", *numchn);
+#endif
+ return 0;
+ }
+
+ /* Oktalyzer (Amiga) */
+ if (!memcmp(id, "OKTA", 4)) {
+ *descr = oktalyzer;
+ modtype = 1;
+ *numchn = 8;
+ return 1;
+ }
+
+ /* Oktalyser (Atari) */
+ if (!memcmp(id, "CD81", 4)) {
+ *descr = oktalyser;
+ modtype = 1;
+ *numchn = 8;
+ return 1;
+ }
+
+ /* Fasttracker */
+ if ((!memcmp(id + 1, "CHN", 3)) && (isdigit(id[0]))) {
+ *descr = fasttracker;
+ modtype = 1;
+ *numchn = id[0] - '0';
+ return 1;
+ }
+ /* Fasttracker or Taketracker */
+ if (((!memcmp(id + 2, "CH", 2)) || (!memcmp(id + 2, "CN", 2)))
+ && (isdigit(id[0])) && (isdigit(id[1]))) {
+ if (id[3] == 'H') {
+ *descr = fasttracker;
+ modtype = 2; /* this can also be Imago Orpheus */
+ } else {
+ *descr = taketracker;
+ modtype = 1;
+ }
+ *numchn = (id[0] - '0') * 10 + (id[1] - '0');
+ return 1;
+ }
+
+ return 0;
+}
+
+static BOOL MOD_Test(void)
+{
+ UBYTE id[4], numchn;
+ CHAR *descr;
+
+ _mm_fseek(modreader, MODULEHEADERSIZE, SEEK_SET);
+ if (!_mm_read_UBYTES(id, 4, modreader))
+ return 0;
+
+ if (MOD_CheckType(id, &numchn, &descr))
+ return 1;
+
+ return 0;
+}
+
+static BOOL MOD_Init(void)
+{
+ if (!(mh = (MODULEHEADER *)MikMod_malloc(sizeof(MODULEHEADER))))
+ return 0;
+ return 1;
+}
+
+static void MOD_Cleanup(void)
+{
+ MikMod_free(mh);
+ MikMod_free(patbuf);
+}
+
+/*
+Old (amiga) noteinfo:
+
+_____byte 1_____ byte2_ _____byte 3_____ byte4_
+/ \ / \ / \ / \
+0000 0000-00000000 0000 0000-00000000
+
+Upper four 12 bits for Lower four Effect command.
+bits of sam- note period. bits of sam-
+ple number. ple number.
+
+*/
+
+static UBYTE ConvertNote(MODNOTE *n, UBYTE lasteffect)
+{
+ UBYTE instrument, effect, effdat, note;
+ UWORD period;
+ UBYTE lastnote = 0;
+
+ /* extract the various information from the 4 bytes that make up a note */
+ instrument = (n->a & 0x10) | (n->c >> 4);
+ period = (((UWORD)n->a & 0xf) << 8) + n->b;
+ effect = n->c & 0xf;
+ effdat = n->d;
+
+ /* Convert the period to a note number */
+ note = 0;
+ if (period) {
+ for (note = 0; note < 7 * OCTAVE; note++)
+ if (period >= npertab[note])
+ break;
+ if (note == 7 * OCTAVE)
+ note = 0;
+ else
+ note++;
+ }
+
+ if (instrument) {
+ /* if instrument does not exist, note cut */
+ if ((instrument > 31) || (!mh->samples[instrument - 1].length)) {
+ UniPTEffect(0xc, 0);
+ if (effect == 0xc)
+ effect = effdat = 0;
+ } else {
+ /* Protracker handling */
+ if (!modtype) {
+ /* if we had a note, then change instrument... */
+ if (note)
+ UniInstrument(instrument - 1);
+ /* ...otherwise, only adjust volume... */
+ else {
+ /* ...unless an effect was specified, which forces a new
+ note to be played */
+ if (effect || effdat) {
+ UniInstrument(instrument - 1);
+ note = lastnote;
+ } else
+ UniPTEffect(0xc,
+ mh->samples[instrument -
+ 1].volume & 0x7f);
+ }
+ } else {
+ /* Fasttracker handling */
+ UniInstrument(instrument - 1);
+ if (!note)
+ note = lastnote;
+ }
+ }
+ }
+ if (note) {
+ UniNote(note + 2 * OCTAVE - 1);
+ lastnote = note;
+ }
+
+ /* Convert pattern jump from Dec to Hex */
+ if (effect == 0xd)
+ effdat = (((effdat & 0xf0) >> 4) * 10) + (effdat & 0xf);
+
+ /* Volume slide, up has priority */
+ if ((effect == 0xa) && (effdat & 0xf) && (effdat & 0xf0))
+ effdat &= 0xf0;
+
+ /* Handle ``heavy'' volumes correctly */
+ if ((effect == 0xc) && (effdat > 0x40))
+ effdat = 0x40;
+
+ /* An isolated 100, 200 or 300 effect should be ignored (no
+ "standalone" porta memory in mod files). However, a sequence such
+ as 1XX, 100, 100, 100 is fine. */
+ if ((!effdat) && ((effect == 1)||(effect == 2)||(effect ==3)) &&
+ (lasteffect < 0x10) && (effect != lasteffect))
+ effect = 0;
+
+ UniPTEffect(effect, effdat);
+ if (effect == 8)
+ of.flags |= UF_PANNING;
+
+ return effect;
+}
+
+static UBYTE *ConvertTrack(MODNOTE *n, int numchn)
+{
+ int t;
+ UBYTE lasteffect = 0x10; /* non existant effect */
+
+ UniReset();
+ for (t = 0; t < 64; t++) {
+ lasteffect = ConvertNote(n,lasteffect);
+ UniNewline();
+ n += numchn;
+ }
+ return UniDup();
+}
+
+/* Loads all patterns of a modfile and converts them into the 3 byte format. */
+static BOOL ML_LoadPatterns(void)
+{
+ int t, s, tracks = 0;
+
+ if (!AllocPatterns())
+ return 0;
+ if (!AllocTracks())
+ return 0;
+
+ /* Allocate temporary buffer for loading and converting the patterns */
+ if (!(patbuf = (MODNOTE *)MikMod_calloc(64U * of.numchn, sizeof(MODNOTE))))
+ return 0;
+
+ if (trekker && of.numchn == 8) {
+ /* Startrekker module dual pattern */
+ for (t = 0; t < of.numpat; t++) {
+ for (s = 0; s < (64 * 4); s++) {
+ patbuf[s].a = _mm_read_UBYTE(modreader);
+ patbuf[s].b = _mm_read_UBYTE(modreader);
+ patbuf[s].c = _mm_read_UBYTE(modreader);
+ patbuf[s].d = _mm_read_UBYTE(modreader);
+ }
+ for (s = 0; s < 4; s++)
+ if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))
+ return 0;
+ for (s = 0; s < (64 * 4); s++) {
+ patbuf[s].a = _mm_read_UBYTE(modreader);
+ patbuf[s].b = _mm_read_UBYTE(modreader);
+ patbuf[s].c = _mm_read_UBYTE(modreader);
+ patbuf[s].d = _mm_read_UBYTE(modreader);
+ }
+ for (s = 0; s < 4; s++)
+ if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, 4)))
+ return 0;
+ }
+ } else {
+ /* Generic module pattern */
+ for (t = 0; t < of.numpat; t++) {
+ /* Load the pattern into the temp buffer and convert it */
+ for (s = 0; s < (64 * of.numchn); s++) {
+ patbuf[s].a = _mm_read_UBYTE(modreader);
+ patbuf[s].b = _mm_read_UBYTE(modreader);
+ patbuf[s].c = _mm_read_UBYTE(modreader);
+ patbuf[s].d = _mm_read_UBYTE(modreader);
+ }
+ for (s = 0; s < of.numchn; s++)
+ if (!(of.tracks[tracks++] = ConvertTrack(patbuf + s, of.numchn)))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static BOOL MOD_Load(BOOL curious)
+{
+ int t, scan;
+ SAMPLE *q;
+ MSAMPINFO *s;
+ CHAR *descr;
+
+ /* try to read module header */
+ _mm_read_string((CHAR *)mh->songname, 20, modreader);
+ mh->songname[20] = 0; /* just in case */
+
+ for (t = 0; t < 31; t++) {
+ s = &mh->samples[t];
+ _mm_read_string(s->samplename, 22, modreader);
+ s->samplename[22] = 0; /* just in case */
+ s->length = _mm_read_M_UWORD(modreader);
+ s->finetune = _mm_read_UBYTE(modreader);
+ s->volume = _mm_read_UBYTE(modreader);
+ s->reppos = _mm_read_M_UWORD(modreader);
+ s->replen = _mm_read_M_UWORD(modreader);
+ }
+
+ mh->songlength = _mm_read_UBYTE(modreader);
+
+ /* this fixes mods which declare more than 128 positions.
+ * eg: beatwave.mod */
+ if (mh->songlength > 128) { mh->songlength = 128; }
+
+ mh->magic1 = _mm_read_UBYTE(modreader);
+ _mm_read_UBYTES(mh->positions, 128, modreader);
+ _mm_read_UBYTES(mh->magic2, 4, modreader);
+
+ if (_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ /* set module variables */
+ of.initspeed = 6;
+ of.inittempo = 125;
+ if (!(MOD_CheckType(mh->magic2, &of.numchn, &descr))) {
+ _mm_errno = MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ if (trekker && of.numchn == 8)
+ for (t = 0; t < 128; t++)
+ /* if module pretends to be FLT8, yet the order table
+ contains odd numbers, chances are it's a lying FLT4... */
+ if (mh->positions[t] & 1) {
+ of.numchn = 4;
+ break;
+ }
+ if (trekker && of.numchn == 8)
+ for (t = 0; t < 128; t++)
+ mh->positions[t] >>= 1;
+
+ of.songname = DupStr(mh->songname, 21, 1);
+ of.numpos = mh->songlength;
+ of.reppos = 0;
+
+ /* Count the number of patterns */
+ of.numpat = 0;
+ for (t = 0; t < of.numpos; t++)
+ if (mh->positions[t] > of.numpat)
+ of.numpat = mh->positions[t];
+
+ /* since some old modules embed extra patterns, we have to check the
+ whole list to get the samples' file offsets right - however we can find
+ garbage here, so check carefully */
+ scan = 1;
+ for (t = of.numpos; t < 128; t++)
+ if (mh->positions[t] >= 0x80)
+ scan = 0;
+ if (scan)
+ for (t = of.numpos; t < 128; t++) {
+ if (mh->positions[t] > of.numpat)
+ of.numpat = mh->positions[t];
+ if ((curious) && (mh->positions[t]))
+ of.numpos = t + 1;
+ }
+ of.numpat++;
+ of.numtrk = of.numpat * of.numchn;
+
+ if (!AllocPositions(of.numpos))
+ return 0;
+ for (t = 0; t < of.numpos; t++)
+ of.positions[t] = mh->positions[t];
+
+ /* Finally, init the sampleinfo structures */
+ of.numins = of.numsmp = 31;
+ if (!AllocSamples())
+ return 0;
+ s = mh->samples;
+ q = of.samples;
+ for (t = 0; t < of.numins; t++) {
+ /* convert the samplename */
+ q->samplename = DupStr(s->samplename, 23, 1);
+ /* init the sampleinfo variables and convert the size pointers */
+ q->speed = finetune[s->finetune & 0xf];
+ q->volume = s->volume & 0x7f;
+ q->loopstart = (ULONG)s->reppos << 1;
+ q->loopend = q->loopstart + ((ULONG)s->replen << 1);
+ q->length = (ULONG)s->length << 1;
+ q->flags = SF_SIGNED;
+ /* Imago Orpheus creates MODs with 16 bit samples, check */
+ if ((modtype == 2) && (s->volume & 0x80)) {
+ q->flags |= SF_16BITS;
+ descr = orpheus;
+ }
+ if (s->replen > 2)
+ q->flags |= SF_LOOP;
+
+ s++;
+ q++;
+ }
+
+ of.modtype = strdup(descr);
+
+ if (!ML_LoadPatterns())
+ return 0;
+
+ return 1;
+}
+
+static CHAR *MOD_LoadTitle(void)
+{
+ CHAR s[21];
+
+ _mm_fseek(modreader, 0, SEEK_SET);
+ if (!_mm_read_UBYTES(s, 20, modreader))
+ return NULL;
+ s[20] = 0; /* just in case */
+
+ return (DupStr(s, 21, 1));
+}
+
+/*========== Loader information */
+
+MIKMODAPI MLOADER load_mod = {
+ NULL,
+ "Standard module",
+ "MOD (31 instruments)",
+ MOD_Init,
+ MOD_Test,
+ MOD_Load,
+ MOD_Cleanup,
+ MOD_LoadTitle
+};
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/load_s3m.c b/src/libs/mikmod/load_s3m.c
new file mode 100644
index 0000000..782ee23
--- /dev/null
+++ b/src/libs/mikmod/load_s3m.c
@@ -0,0 +1,470 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
+ AUTHORS for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Screamtracker (S3M) module loader
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+/*========== Module structure */
+
+/* header */
+typedef struct S3MHEADER {
+ CHAR songname[28];
+ UBYTE t1a;
+ UBYTE type;
+ UBYTE unused1[2];
+ UWORD ordnum;
+ UWORD insnum;
+ UWORD patnum;
+ UWORD flags;
+ UWORD tracker;
+ UWORD fileformat;
+ CHAR scrm[4];
+ UBYTE mastervol;
+ UBYTE initspeed;
+ UBYTE inittempo;
+ UBYTE mastermult;
+ UBYTE ultraclick;
+ UBYTE pantable;
+ UBYTE unused2[8];
+ UWORD special;
+ UBYTE channels[32];
+} S3MHEADER;
+
+/* sample information */
+typedef struct S3MSAMPLE {
+ UBYTE type;
+ CHAR filename[12];
+ UBYTE memsegh;
+ UWORD memsegl;
+ ULONG length;
+ ULONG loopbeg;
+ ULONG loopend;
+ UBYTE volume;
+ UBYTE dsk;
+ UBYTE pack;
+ UBYTE flags;
+ ULONG c2spd;
+ UBYTE unused[12];
+ CHAR sampname[28];
+ CHAR scrs[4];
+} S3MSAMPLE;
+
+typedef struct S3MNOTE {
+ UBYTE note,ins,vol,cmd,inf;
+} S3MNOTE;
+
+/*========== Loader variables */
+
+static S3MNOTE *s3mbuf = NULL; /* pointer to a complete S3M pattern */
+static S3MHEADER *mh = NULL;
+static UWORD *paraptr = NULL; /* parapointer array (see S3M docs) */
+static unsigned int tracker; /* tracker id */
+
+/* tracker identifiers */
+#define NUMTRACKERS 4
+static CHAR* S3M_Version[] = {
+ "Screamtracker x.xx",
+ "Imago Orpheus x.xx (S3M format)",
+ "Impulse Tracker x.xx (S3M format)",
+ "Unknown tracker x.xx (S3M format)",
+ "Impulse Tracker 2.14p3 (S3M format)",
+ "Impulse Tracker 2.14p4 (S3M format)"
+};
+/* version number position in above array */
+static int numeric[NUMTRACKERS]={14,14,16,16};
+
+/*========== Loader code */
+
+BOOL S3M_Test(void)
+{
+ UBYTE id[4];
+
+ _mm_fseek(modreader,0x2c,SEEK_SET);
+ if(!_mm_read_UBYTES(id,4,modreader)) return 0;
+ if(!memcmp(id,"SCRM",4)) return 1;
+ return 0;
+}
+
+BOOL S3M_Init(void)
+{
+ if(!(s3mbuf=(S3MNOTE*)MikMod_malloc(32*64*sizeof(S3MNOTE)))) return 0;
+ if(!(mh=(S3MHEADER*)MikMod_malloc(sizeof(S3MHEADER)))) return 0;
+ if(!(poslookup=(UBYTE*)MikMod_malloc(sizeof(UBYTE)*256))) return 0;
+ memset(poslookup,-1,256);
+
+ return 1;
+}
+
+void S3M_Cleanup(void)
+{
+ MikMod_free(s3mbuf);
+ MikMod_free(paraptr);
+ MikMod_free(poslookup);
+ MikMod_free(mh);
+ MikMod_free(origpositions);
+}
+
+/* Because so many s3m files have 16 channels as the set number used, but really
+ only use far less (usually 8 to 12 still), I had to make this function, which
+ determines the number of channels that are actually USED by a pattern.
+
+ For every channel that's used, it sets the appropriate array entry of the
+ global variable 'remap'
+
+ NOTE: You must first seek to the file location of the pattern before calling
+ this procedure.
+
+ Returns 1 on fail. */
+static BOOL S3M_GetNumChannels(void)
+{
+ int row=0,flag,ch;
+
+ while(row<64) {
+ flag=_mm_read_UBYTE(modreader);
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_PATTERN;
+ return 1;
+ }
+
+ if(flag) {
+ ch=flag&31;
+ if(mh->channels[ch]<32) remap[ch] = 0;
+ if(flag&32) {_mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);}
+ if(flag&64) _mm_read_UBYTE(modreader);
+ if(flag&128){_mm_read_UBYTE(modreader);_mm_read_UBYTE(modreader);}
+ } else row++;
+ }
+ return 0;
+}
+
+static BOOL S3M_ReadPattern(void)
+{
+ int row=0,flag,ch;
+ S3MNOTE *n,dummy;
+
+ /* clear pattern data */
+ memset(s3mbuf,255,32*64*sizeof(S3MNOTE));
+
+ while(row<64) {
+ flag=_mm_read_UBYTE(modreader);
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_PATTERN;
+ return 0;
+ }
+
+ if(flag) {
+ ch=remap[flag&31];
+
+ if(ch!=-1)
+ n=&s3mbuf[(64U*ch)+row];
+ else
+ n=&dummy;
+
+ if(flag&32) {
+ n->note=_mm_read_UBYTE(modreader);
+ n->ins=_mm_read_UBYTE(modreader);
+ }
+ if(flag&64) {
+ n->vol=_mm_read_UBYTE(modreader);
+ if (n->vol>64) n->vol=64;
+ }
+ if(flag&128) {
+ n->cmd=_mm_read_UBYTE(modreader);
+ n->inf=_mm_read_UBYTE(modreader);
+ }
+ } else row++;
+ }
+ return 1;
+}
+
+static UBYTE* S3M_ConvertTrack(S3MNOTE* tr)
+{
+ int t;
+
+ UniReset();
+ for(t=0;t<64;t++) {
+ UBYTE note,ins,vol;
+
+ note=tr[t].note;
+ ins=tr[t].ins;
+ vol=tr[t].vol;
+
+ if((ins)&&(ins!=255)) UniInstrument(ins-1);
+ if(note!=255) {
+ if(note==254) {
+ UniPTEffect(0xc,0); /* note cut command */
+ vol=255;
+ } else
+ UniNote(((note>>4)*OCTAVE)+(note&0xf)); /* normal note */
+ }
+ if(vol<255) UniPTEffect(0xc,vol);
+
+ S3MIT_ProcessCmd(tr[t].cmd,tr[t].inf,
+ tracker == 1 ? S3MIT_OLDSTYLE | S3MIT_SCREAM : S3MIT_OLDSTYLE);
+ UniNewline();
+ }
+ return UniDup();
+}
+
+BOOL S3M_Load(BOOL curious)
+{
+ int t,u,track = 0;
+ SAMPLE *q;
+ UBYTE pan[32];
+
+ /* try to read module header */
+ _mm_read_string(mh->songname,28,modreader);
+ mh->t1a =_mm_read_UBYTE(modreader);
+ mh->type =_mm_read_UBYTE(modreader);
+ _mm_read_UBYTES(mh->unused1,2,modreader);
+ mh->ordnum =_mm_read_I_UWORD(modreader);
+ mh->insnum =_mm_read_I_UWORD(modreader);
+ mh->patnum =_mm_read_I_UWORD(modreader);
+ mh->flags =_mm_read_I_UWORD(modreader);
+ mh->tracker =_mm_read_I_UWORD(modreader);
+ mh->fileformat =_mm_read_I_UWORD(modreader);
+ _mm_read_string(mh->scrm,4,modreader);
+ mh->mastervol =_mm_read_UBYTE(modreader);
+ mh->initspeed =_mm_read_UBYTE(modreader);
+ mh->inittempo =_mm_read_UBYTE(modreader);
+ mh->mastermult =_mm_read_UBYTE(modreader);
+ mh->ultraclick =_mm_read_UBYTE(modreader);
+ mh->pantable =_mm_read_UBYTE(modreader);
+ _mm_read_UBYTES(mh->unused2,8,modreader);
+ mh->special =_mm_read_I_UWORD(modreader);
+ _mm_read_UBYTES(mh->channels,32,modreader);
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ /* then we can decide the module type */
+ tracker=mh->tracker>>12;
+ if((!tracker)||(tracker>=NUMTRACKERS))
+ tracker=NUMTRACKERS-1; /* unknown tracker */
+ else {
+ if(mh->tracker>=0x3217)
+ tracker=NUMTRACKERS+1; /* IT 2.14p4 */
+ else if(mh->tracker>=0x3216)
+ tracker=NUMTRACKERS; /* IT 2.14p3 */
+ else tracker--;
+ }
+ of.modtype = strdup(S3M_Version[tracker]);
+ if(tracker<NUMTRACKERS) {
+ of.modtype[numeric[tracker]] = ((mh->tracker>>8) &0xf)+'0';
+ of.modtype[numeric[tracker]+2] = ((mh->tracker>>4)&0xf)+'0';
+ of.modtype[numeric[tracker]+3] = ((mh->tracker)&0xf)+'0';
+ }
+ /* set module variables */
+ of.songname = DupStr(mh->songname,28,0);
+ of.numpat = mh->patnum;
+ of.reppos = 0;
+ of.numins = of.numsmp = mh->insnum;
+ of.initspeed = mh->initspeed;
+ of.inittempo = mh->inittempo;
+ of.initvolume = mh->mastervol<<1;
+ of.flags |= UF_ARPMEM | UF_PANNING;
+ if((mh->tracker==0x1300)||(mh->flags&64))
+ of.flags|=UF_S3MSLIDES;
+ of.bpmlimit = 32;
+
+ /* read the order data */
+ if(!AllocPositions(mh->ordnum)) return 0;
+ if(!(origpositions=MikMod_calloc(mh->ordnum,sizeof(UWORD)))) return 0;
+
+ for(t=0;t<mh->ordnum;t++) {
+ origpositions[t]=_mm_read_UBYTE(modreader);
+ if((origpositions[t]>=mh->patnum)&&(origpositions[t]<254))
+ origpositions[t]=255/*mh->patnum-1*/;
+ }
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ poslookupcnt=mh->ordnum;
+ S3MIT_CreateOrders(curious);
+
+ if(!(paraptr=(UWORD*)MikMod_malloc((of.numins+of.numpat)*sizeof(UWORD))))
+ return 0;
+
+ /* read the instrument+pattern parapointers */
+ _mm_read_I_UWORDS(paraptr,of.numins+of.numpat,modreader);
+
+ if(mh->pantable==252) {
+ /* read the panning table (ST 3.2 addition. See below for further
+ portions of channel panning [past reampper]). */
+ _mm_read_UBYTES(pan,32,modreader);
+ }
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ /* load samples */
+ if(!AllocSamples()) return 0;
+ q = of.samples;
+ for(t=0;t<of.numins;t++) {
+ S3MSAMPLE s;
+
+ /* seek to instrument position */
+ _mm_fseek(modreader,((long)paraptr[t])<<4,SEEK_SET);
+ /* and load sample info */
+ s.type =_mm_read_UBYTE(modreader);
+ _mm_read_string(s.filename,12,modreader);
+ s.memsegh =_mm_read_UBYTE(modreader);
+ s.memsegl =_mm_read_I_UWORD(modreader);
+ s.length =_mm_read_I_ULONG(modreader);
+ s.loopbeg =_mm_read_I_ULONG(modreader);
+ s.loopend =_mm_read_I_ULONG(modreader);
+ s.volume =_mm_read_UBYTE(modreader);
+ s.dsk =_mm_read_UBYTE(modreader);
+ s.pack =_mm_read_UBYTE(modreader);
+ s.flags =_mm_read_UBYTE(modreader);
+ s.c2spd =_mm_read_I_ULONG(modreader);
+ _mm_read_UBYTES(s.unused,12,modreader);
+ _mm_read_string(s.sampname,28,modreader);
+ _mm_read_string(s.scrs,4,modreader);
+
+ /* ScreamTracker imposes a 64000 bytes (not 64k !) limit */
+ if (s.length > 64000)
+ s.length = 64000;
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+
+ q->samplename = DupStr(s.sampname,28,0);
+ q->speed = s.c2spd;
+ q->length = s.length;
+ q->loopstart = s.loopbeg;
+ q->loopend = s.loopend;
+ q->volume = s.volume;
+ q->seekpos = (((long)s.memsegh)<<16|s.memsegl)<<4;
+
+ if(s.flags&1) q->flags |= SF_LOOP;
+ if(s.flags&4) q->flags |= SF_16BITS;
+ if(mh->fileformat==1) q->flags |= SF_SIGNED;
+
+ /* don't load sample if it doesn't have the SCRS tag */
+ if(memcmp(s.scrs,"SCRS",4)) q->length = 0;
+
+ q++;
+ }
+
+ /* determine the number of channels actually used. */
+ of.numchn = 0;
+ memset(remap,-1,32*sizeof(UBYTE));
+ for(t=0;t<of.numpat;t++) {
+ /* seek to pattern position (+2 skip pattern length) */
+ _mm_fseek(modreader,(long)((paraptr[of.numins+t])<<4)+2,SEEK_SET);
+ if(S3M_GetNumChannels()) return 0;
+ }
+
+ /* build the remap array */
+ for(t=0;t<32;t++)
+ if(!remap[t])
+ remap[t]=of.numchn++;
+
+ /* set panning positions after building remap chart! */
+ for(t=0;t<32;t++)
+ if((mh->channels[t]<32)&&(remap[t]!=-1)) {
+ if(mh->channels[t]<8)
+ of.panning[remap[t]]=0x30;
+ else
+ of.panning[remap[t]]=0xc0;
+ }
+ if(mh->pantable==252)
+ /* set panning positions according to panning table (new for st3.2) */
+ for(t=0;t<32;t++)
+ if((pan[t]&0x20)&&(mh->channels[t]<32)&&(remap[t]!=-1))
+ of.panning[remap[t]]=(pan[t]&0xf)<<4;
+
+ /* load pattern info */
+ of.numtrk=of.numpat*of.numchn;
+ if(!AllocTracks()) return 0;
+ if(!AllocPatterns()) return 0;
+
+ for(t=0;t<of.numpat;t++) {
+ /* seek to pattern position (+2 skip pattern length) */
+ _mm_fseek(modreader,(((long)paraptr[of.numins+t])<<4)+2,SEEK_SET);
+ if(!S3M_ReadPattern()) return 0;
+ for(u=0;u<of.numchn;u++)
+ if(!(of.tracks[track++]=S3M_ConvertTrack(&s3mbuf[u*64]))) return 0;
+ }
+
+ return 1;
+}
+
+CHAR *S3M_LoadTitle(void)
+{
+ CHAR s[28];
+
+ _mm_fseek(modreader,0,SEEK_SET);
+ if(!_mm_read_UBYTES(s,28,modreader)) return NULL;
+
+ return(DupStr(s,28,0));
+}
+
+/*========== Loader information */
+
+MIKMODAPI MLOADER load_s3m={
+ NULL,
+ "S3M",
+ "S3M (Scream Tracker 3)",
+ S3M_Init,
+ S3M_Test,
+ S3M_Load,
+ S3M_Cleanup,
+ S3M_LoadTitle
+};
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/load_stm.c b/src/libs/mikmod/load_stm.c
new file mode 100644
index 0000000..299f33b
--- /dev/null
+++ b/src/libs/mikmod/load_stm.c
@@ -0,0 +1,376 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
+ AUTHORS for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Screamtracker 2 (STM) module loader
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+/*========== Module structure */
+
+/* sample information */
+typedef struct STMSAMPLE {
+ CHAR filename[12];
+ UBYTE unused; /* 0x00 */
+ UBYTE instdisk; /* Instrument disk */
+ UWORD reserved;
+ UWORD length; /* Sample length */
+ UWORD loopbeg; /* Loop start point */
+ UWORD loopend; /* Loop end point */
+ UBYTE volume; /* Volume */
+ UBYTE reserved2;
+ UWORD c2spd; /* Good old c2spd */
+ ULONG reserved3;
+ UWORD isa;
+} STMSAMPLE;
+
+/* header */
+typedef struct STMHEADER {
+ CHAR songname[20];
+ CHAR trackername[8]; /* !Scream! for ST 2.xx */
+ UBYTE unused; /* 0x1A */
+ UBYTE filetype; /* 1=song, 2=module */
+ UBYTE ver_major;
+ UBYTE ver_minor;
+ UBYTE inittempo; /* initspeed= stm inittempo>>4 */
+ UBYTE numpat; /* number of patterns */
+ UBYTE globalvol;
+ UBYTE reserved[13];
+ STMSAMPLE sample[31]; /* STM sample data */
+ UBYTE patorder[128]; /* Docs say 64 - actually 128 */
+} STMHEADER;
+
+typedef struct STMNOTE {
+ UBYTE note,insvol,volcmd,cmdinf;
+} STMNOTE;
+
+/*========== Loader variables */
+
+static STMNOTE *stmbuf = NULL;
+static STMHEADER *mh = NULL;
+
+/* tracker identifiers */
+static CHAR* STM_Version[STM_NTRACKERS] = {
+ "Screamtracker 2",
+ "Converted by MOD2STM (STM format)",
+ "Wuzamod (STM format)"
+};
+
+/*========== Loader code */
+
+BOOL STM_Test(void)
+{
+ UBYTE str[44];
+ int t;
+
+ _mm_fseek(modreader,20,SEEK_SET);
+ _mm_read_UBYTES(str,44,modreader);
+ if(str[9]!=2) return 0; /* STM Module = filetype 2 */
+
+ /* Prevent false positives for S3M files */
+ if(!memcmp(str+40,"SCRM",4))
+ return 0;
+
+ for (t=0;t<STM_NTRACKERS;t++)
+ if(!memcmp(str,STM_Signatures[t],8))
+ return 1;
+
+ return 0;
+}
+
+BOOL STM_Init(void)
+{
+ if(!(mh=(STMHEADER*)MikMod_malloc(sizeof(STMHEADER)))) return 0;
+ if(!(stmbuf=(STMNOTE*)MikMod_calloc(64U*4,sizeof(STMNOTE)))) return 0;
+
+ return 1;
+}
+
+static void STM_Cleanup(void)
+{
+ MikMod_free(mh);
+ MikMod_free(stmbuf);
+}
+
+static void STM_ConvertNote(STMNOTE *n)
+{
+ UBYTE note,ins,vol,cmd,inf;
+
+ /* extract the various information from the 4 bytes that make up a note */
+ note = n->note;
+ ins = n->insvol>>3;
+ vol = (n->insvol&7)+((n->volcmd&0x70)>>1);
+ cmd = n->volcmd&15;
+ inf = n->cmdinf;
+
+ if((ins)&&(ins<32)) UniInstrument(ins-1);
+
+ /* special values of [SBYTE0] are handled here
+ we have no idea if these strange values will ever be encountered.
+ but it appears as those stms sound correct. */
+ if((note==254)||(note==252)) {
+ UniPTEffect(0xc,0); /* note cut */
+ n->volcmd|=0x80;
+ } else
+ /* if note < 251, then all three bytes are stored in the file */
+ if(note<251) UniNote((((note>>4)+2)*OCTAVE)+(note&0xf));
+
+ if((!(n->volcmd&0x80))&&(vol<65)) UniPTEffect(0xc,vol);
+ if(cmd!=255)
+ switch(cmd) {
+ case 1: /* Axx set speed to xx */
+ UniPTEffect(0xf,inf>>4);
+ break;
+ case 2: /* Bxx position jump */
+ UniPTEffect(0xb,inf);
+ break;
+ case 3: /* Cxx patternbreak to row xx */
+ UniPTEffect(0xd,(((inf&0xf0)>>4)*10)+(inf&0xf));
+ break;
+ case 4: /* Dxy volumeslide */
+ UniEffect(UNI_S3MEFFECTD,inf);
+ break;
+ case 5: /* Exy toneslide down */
+ UniEffect(UNI_S3MEFFECTE,inf);
+ break;
+ case 6: /* Fxy toneslide up */
+ UniEffect(UNI_S3MEFFECTF,inf);
+ break;
+ case 7: /* Gxx Tone portamento,speed xx */
+ UniPTEffect(0x3,inf);
+ break;
+ case 8: /* Hxy vibrato */
+ UniPTEffect(0x4,inf);
+ break;
+ case 9: /* Ixy tremor, ontime x, offtime y */
+ UniEffect(UNI_S3MEFFECTI,inf);
+ break;
+ case 0: /* protracker arpeggio */
+ if(!inf) break;
+ /* fall through */
+ case 0xa: /* Jxy arpeggio */
+ UniPTEffect(0x0,inf);
+ break;
+ case 0xb: /* Kxy Dual command H00 & Dxy */
+ UniPTEffect(0x4,0);
+ UniEffect(UNI_S3MEFFECTD,inf);
+ break;
+ case 0xc: /* Lxy Dual command G00 & Dxy */
+ UniPTEffect(0x3,0);
+ UniEffect(UNI_S3MEFFECTD,inf);
+ break;
+ /* Support all these above, since ST2 can LOAD these values but can
+ actually only play up to J - and J is only half-way implemented
+ in ST2 */
+ case 0x18: /* Xxx amiga panning command 8xx */
+ UniPTEffect(0x8,inf);
+ of.flags |= UF_PANNING;
+ break;
+ }
+}
+
+static UBYTE *STM_ConvertTrack(STMNOTE *n)
+{
+ int t;
+
+ UniReset();
+ for(t=0;t<64;t++) {
+ STM_ConvertNote(n);
+ UniNewline();
+ n+=of.numchn;
+ }
+ return UniDup();
+}
+
+static BOOL STM_LoadPatterns(void)
+{
+ int t,s,tracks=0;
+
+ if(!AllocPatterns()) return 0;
+ if(!AllocTracks()) return 0;
+
+ /* Allocate temporary buffer for loading and converting the patterns */
+ for(t=0;t<of.numpat;t++) {
+ for(s=0;s<(64*of.numchn);s++) {
+ stmbuf[s].note = _mm_read_UBYTE(modreader);
+ stmbuf[s].insvol = _mm_read_UBYTE(modreader);
+ stmbuf[s].volcmd = _mm_read_UBYTE(modreader);
+ stmbuf[s].cmdinf = _mm_read_UBYTE(modreader);
+ }
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_PATTERN;
+ return 0;
+ }
+
+ for(s=0;s<of.numchn;s++)
+ if(!(of.tracks[tracks++]=STM_ConvertTrack(stmbuf+s))) return 0;
+ }
+ return 1;
+}
+
+BOOL STM_Load(BOOL curious)
+{
+ int t;
+ ULONG MikMod_ISA; /* We must generate our own ISA, it's not stored in stm */
+ SAMPLE *q;
+
+ (void)curious; /* unused arg */
+
+ /* try to read stm header */
+ _mm_read_string(mh->songname,20,modreader);
+ _mm_read_string(mh->trackername,8,modreader);
+ mh->unused =_mm_read_UBYTE(modreader);
+ mh->filetype =_mm_read_UBYTE(modreader);
+ mh->ver_major =_mm_read_UBYTE(modreader);
+ mh->ver_minor =_mm_read_UBYTE(modreader);
+ mh->inittempo =_mm_read_UBYTE(modreader);
+ if(!mh->inittempo) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ mh->numpat =_mm_read_UBYTE(modreader);
+ mh->globalvol =_mm_read_UBYTE(modreader);
+ _mm_read_UBYTES(mh->reserved,13,modreader);
+
+ for(t=0;t<31;t++) {
+ STMSAMPLE *s=&mh->sample[t]; /* STM sample data */
+
+ _mm_read_string(s->filename,12,modreader);
+ s->unused =_mm_read_UBYTE(modreader);
+ s->instdisk =_mm_read_UBYTE(modreader);
+ s->reserved =_mm_read_I_UWORD(modreader);
+ s->length =_mm_read_I_UWORD(modreader);
+ s->loopbeg =_mm_read_I_UWORD(modreader);
+ s->loopend =_mm_read_I_UWORD(modreader);
+ s->volume =_mm_read_UBYTE(modreader);
+ s->reserved2=_mm_read_UBYTE(modreader);
+ s->c2spd =_mm_read_I_UWORD(modreader);
+ s->reserved3=_mm_read_I_ULONG(modreader);
+ s->isa =_mm_read_I_UWORD(modreader);
+ }
+ _mm_read_UBYTES(mh->patorder,128,modreader);
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ /* set module variables */
+ for(t=0;t<STM_NTRACKERS;t++)
+ if(!memcmp(mh->trackername,STM_Signatures[t],8)) break;
+ of.modtype = strdup(STM_Version[t]);
+ of.songname = DupStr(mh->songname,20,1); /* make a cstr of songname */
+ of.numpat = mh->numpat;
+ of.inittempo = 125; /* mh->inittempo+0x1c; */
+ of.initspeed = mh->inittempo>>4;
+ of.numchn = 4; /* get number of channels */
+ of.reppos = 0;
+ of.flags |= UF_S3MSLIDES;
+ of.bpmlimit = 32;
+
+ t=0;
+ if(!AllocPositions(0x80)) return 0;
+ /* 99 terminates the patorder list */
+ while((mh->patorder[t]<=99)&&(mh->patorder[t]<mh->numpat)) {
+ of.positions[t]=mh->patorder[t];
+ t++;
+ }
+ if(mh->patorder[t]<=99) t++;
+ of.numpos=t;
+ of.numtrk=of.numpat*of.numchn;
+ of.numins=of.numsmp=31;
+
+ if(!AllocSamples()) return 0;
+ if(!STM_LoadPatterns()) return 0;
+ MikMod_ISA=_mm_ftell(modreader);
+ MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; /* normalize */
+
+ for(q=of.samples,t=0;t<of.numsmp;t++,q++) {
+ /* load sample info */
+ q->samplename = DupStr(mh->sample[t].filename,12,1);
+ q->speed = (mh->sample[t].c2spd * 8363) / 8448;
+ q->volume = mh->sample[t].volume;
+ q->length = mh->sample[t].length;
+ if (/*(!mh->sample[t].volume)||*/(q->length==1)) q->length=0;
+ q->loopstart = mh->sample[t].loopbeg;
+ q->loopend = mh->sample[t].loopend;
+ q->seekpos = MikMod_ISA;
+
+ MikMod_ISA+=q->length;
+ MikMod_ISA=(MikMod_ISA+15)&0xfffffff0; /* normalize */
+
+ /* contrary to the STM specs, sample data is signed */
+ q->flags = SF_SIGNED;
+
+ if(q->loopend && q->loopend != 0xffff)
+ q->flags|=SF_LOOP;
+ }
+ return 1;
+}
+
+CHAR *STM_LoadTitle(void)
+{
+ CHAR s[20];
+
+ _mm_fseek(modreader,0,SEEK_SET);
+ if(!_mm_read_UBYTES(s,20,modreader)) return NULL;
+
+ return(DupStr(s,20,1));
+}
+
+/*========== Loader information */
+
+MIKMODAPI MLOADER load_stm={
+ NULL,
+ "STM",
+ "STM (Scream Tracker)",
+ STM_Init,
+ STM_Test,
+ STM_Load,
+ STM_Cleanup,
+ STM_LoadTitle
+};
+
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/load_xm.c b/src/libs/mikmod/load_xm.c
new file mode 100644
index 0000000..97e0b73
--- /dev/null
+++ b/src/libs/mikmod/load_xm.c
@@ -0,0 +1,817 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
+ AUTHORS for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Fasttracker (XM) module loader
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+/*========== Module structure */
+
+typedef struct XMHEADER {
+ CHAR id[17]; /* ID text: 'Extended module: ' */
+ CHAR songname[21]; /* Module name */
+ CHAR trackername[20]; /* Tracker name */
+ UWORD version; /* Version number */
+ ULONG headersize; /* Header size */
+ UWORD songlength; /* Song length (in patten order table) */
+ UWORD restart; /* Restart position */
+ UWORD numchn; /* Number of channels (2,4,6,8,10,...,32) */
+ UWORD numpat; /* Number of patterns (max 256) */
+ UWORD numins; /* Number of instruments (max 128) */
+ UWORD flags;
+ UWORD tempo; /* Default tempo */
+ UWORD bpm; /* Default BPM */
+ UBYTE orders[256]; /* Pattern order table */
+} XMHEADER;
+
+typedef struct XMINSTHEADER {
+ ULONG size; /* Instrument size */
+ CHAR name[22]; /* Instrument name */
+ UBYTE type; /* Instrument type (always 0) */
+ UWORD numsmp; /* Number of samples in instrument */
+ ULONG ssize;
+} XMINSTHEADER;
+
+#define XMENVCNT (12*2)
+#define XMNOTECNT (8*OCTAVE)
+typedef struct XMPATCHHEADER {
+ UBYTE what[XMNOTECNT]; /* Sample number for all notes */
+ UWORD volenv[XMENVCNT]; /* Points for volume envelope */
+ UWORD panenv[XMENVCNT]; /* Points for panning envelope */
+ UBYTE volpts; /* Number of volume points */
+ UBYTE panpts; /* Number of panning points */
+ UBYTE volsus; /* Volume sustain point */
+ UBYTE volbeg; /* Volume loop start point */
+ UBYTE volend; /* Volume loop end point */
+ UBYTE pansus; /* Panning sustain point */
+ UBYTE panbeg; /* Panning loop start point */
+ UBYTE panend; /* Panning loop end point */
+ UBYTE volflg; /* Volume type: bit 0: On; 1: Sustain; 2: Loop */
+ UBYTE panflg; /* Panning type: bit 0: On; 1: Sustain; 2: Loop */
+ UBYTE vibflg; /* Vibrato type */
+ UBYTE vibsweep; /* Vibrato sweep */
+ UBYTE vibdepth; /* Vibrato depth */
+ UBYTE vibrate; /* Vibrato rate */
+ UWORD volfade; /* Volume fadeout */
+} XMPATCHHEADER;
+
+typedef struct XMWAVHEADER {
+ ULONG length; /* Sample length */
+ ULONG loopstart; /* Sample loop start */
+ ULONG looplength; /* Sample loop length */
+ UBYTE volume; /* Volume */
+ SBYTE finetune; /* Finetune (signed byte -128..+127) */
+ UBYTE type; /* Loop type */
+ UBYTE panning; /* Panning (0-255) */
+ SBYTE relnote; /* Relative note number (signed byte) */
+ UBYTE reserved;
+ CHAR samplename[22]; /* Sample name */
+ UBYTE vibtype; /* Vibrato type */
+ UBYTE vibsweep; /* Vibrato sweep */
+ UBYTE vibdepth; /* Vibrato depth */
+ UBYTE vibrate; /* Vibrato rate */
+} XMWAVHEADER;
+
+typedef struct XMPATHEADER {
+ ULONG size; /* Pattern header length */
+ UBYTE packing; /* Packing type (always 0) */
+ UWORD numrows; /* Number of rows in pattern (1..256) */
+ SWORD packsize; /* Packed patterndata size */
+} XMPATHEADER;
+
+typedef struct XMNOTE {
+ UBYTE note,ins,vol,eff,dat;
+} XMNOTE;
+
+/*========== Loader variables */
+
+static XMNOTE *xmpat=NULL;
+static XMHEADER *mh=NULL;
+
+/* increment unit for sample array reallocation */
+#define XM_SMPINCR 64
+static ULONG *nextwav=NULL;
+static XMWAVHEADER *wh=NULL,*s=NULL;
+
+/*========== Loader code */
+
+BOOL XM_Test(void)
+{
+ UBYTE id[38];
+
+ if(!_mm_read_UBYTES(id,38,modreader)) return 0;
+ if(memcmp(id,"Extended Module: ",17)) return 0;
+ if(id[37]==0x1a) return 1;
+ return 0;
+}
+
+BOOL XM_Init(void)
+{
+ if(!(mh=(XMHEADER *)MikMod_malloc(sizeof(XMHEADER)))) return 0;
+ return 1;
+}
+
+void XM_Cleanup(void)
+{
+ MikMod_free(mh);
+}
+
+static int XM_ReadNote(XMNOTE* n)
+{
+ UBYTE cmp,result=1;
+
+ memset(n,0,sizeof(XMNOTE));
+ cmp=_mm_read_UBYTE(modreader);
+
+ if(cmp&0x80) {
+ if(cmp&1) { result++;n->note = _mm_read_UBYTE(modreader); }
+ if(cmp&2) { result++;n->ins = _mm_read_UBYTE(modreader); }
+ if(cmp&4) { result++;n->vol = _mm_read_UBYTE(modreader); }
+ if(cmp&8) { result++;n->eff = _mm_read_UBYTE(modreader); }
+ if(cmp&16) { result++;n->dat = _mm_read_UBYTE(modreader); }
+ } else {
+ n->note = cmp;
+ n->ins = _mm_read_UBYTE(modreader);
+ n->vol = _mm_read_UBYTE(modreader);
+ n->eff = _mm_read_UBYTE(modreader);
+ n->dat = _mm_read_UBYTE(modreader);
+ result += 4;
+ }
+ return result;
+}
+
+static UBYTE* XM_Convert(XMNOTE* xmtrack,UWORD rows)
+{
+ int t;
+ UBYTE note,ins,vol,eff,dat;
+
+ UniReset();
+ for(t=0;t<rows;t++) {
+ note = xmtrack->note;
+ ins = xmtrack->ins;
+ vol = xmtrack->vol;
+ eff = xmtrack->eff;
+ dat = xmtrack->dat;
+
+ if(note) {
+ if(note>XMNOTECNT)
+ UniEffect(UNI_KEYFADE,0);
+ else
+ UniNote(note-1);
+ }
+ if(ins) UniInstrument(ins-1);
+
+ switch(vol>>4) {
+ case 0x6: /* volslide down */
+ if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol&0xf);
+ break;
+ case 0x7: /* volslide up */
+ if(vol&0xf) UniEffect(UNI_XMEFFECTA,vol<<4);
+ break;
+
+ /* volume-row fine volume slide is compatible with protracker
+ EBx and EAx effects i.e. a zero nibble means DO NOT SLIDE, as
+ opposed to 'take the last sliding value'. */
+ case 0x8: /* finevol down */
+ UniPTEffect(0xe,0xb0|(vol&0xf));
+ break;
+ case 0x9: /* finevol up */
+ UniPTEffect(0xe,0xa0|(vol&0xf));
+ break;
+ case 0xa: /* set vibrato speed */
+ UniEffect(UNI_XMEFFECT4,vol<<4);
+ break;
+ case 0xb: /* vibrato */
+ UniEffect(UNI_XMEFFECT4,vol&0xf);
+ break;
+ case 0xc: /* set panning */
+ UniPTEffect(0x8,vol<<4);
+ break;
+ case 0xd: /* panning slide left (only slide when data not zero) */
+ if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol&0xf);
+ break;
+ case 0xe: /* panning slide right (only slide when data not zero) */
+ if(vol&0xf) UniEffect(UNI_XMEFFECTP,vol<<4);
+ break;
+ case 0xf: /* tone porta */
+ UniPTEffect(0x3,vol<<4);
+ break;
+ default:
+ if((vol>=0x10)&&(vol<=0x50))
+ UniPTEffect(0xc,vol-0x10);
+ }
+
+ switch(eff) {
+ case 0x4:
+ UniEffect(UNI_XMEFFECT4,dat);
+ break;
+ case 0x6:
+ UniEffect(UNI_XMEFFECT6,dat);
+ break;
+ case 0xa:
+ UniEffect(UNI_XMEFFECTA,dat);
+ break;
+ case 0xe: /* Extended effects */
+ switch(dat>>4) {
+ case 0x1: /* XM fine porta up */
+ UniEffect(UNI_XMEFFECTE1,dat&0xf);
+ break;
+ case 0x2: /* XM fine porta down */
+ UniEffect(UNI_XMEFFECTE2,dat&0xf);
+ break;
+ case 0xa: /* XM fine volume up */
+ UniEffect(UNI_XMEFFECTEA,dat&0xf);
+ break;
+ case 0xb: /* XM fine volume down */
+ UniEffect(UNI_XMEFFECTEB,dat&0xf);
+ break;
+ default:
+ UniPTEffect(eff,dat);
+ }
+ break;
+ case 'G'-55: /* G - set global volume */
+ UniEffect(UNI_XMEFFECTG,dat>64?128:dat<<1);
+ break;
+ case 'H'-55: /* H - global volume slide */
+ UniEffect(UNI_XMEFFECTH,dat);
+ break;
+ case 'K'-55: /* K - keyOff and KeyFade */
+ UniEffect(UNI_KEYFADE,dat);
+ break;
+ case 'L'-55: /* L - set envelope position */
+ UniEffect(UNI_XMEFFECTL,dat);
+ break;
+ case 'P'-55: /* P - panning slide */
+ UniEffect(UNI_XMEFFECTP,dat);
+ break;
+ case 'R'-55: /* R - multi retrig note */
+ UniEffect(UNI_S3MEFFECTQ,dat);
+ break;
+ case 'T'-55: /* T - Tremor */
+ UniEffect(UNI_S3MEFFECTI,dat);
+ break;
+ case 'X'-55:
+ switch(dat>>4) {
+ case 1: /* X1 - Extra Fine Porta up */
+ UniEffect(UNI_XMEFFECTX1,dat&0xf);
+ break;
+ case 2: /* X2 - Extra Fine Porta down */
+ UniEffect(UNI_XMEFFECTX2,dat&0xf);
+ break;
+ }
+ break;
+ default:
+ if(eff<=0xf) {
+ /* the pattern jump destination is written in decimal,
+ but it seems some poor tracker software writes them
+ in hexadecimal... (sigh) */
+ if (eff==0xd)
+ /* don't change anything if we're sure it's in hexa */
+ if ((((dat&0xf0)>>4)<=9)&&((dat&0xf)<=9))
+ /* otherwise, convert from dec to hex */
+ dat=(((dat&0xf0)>>4)*10)+(dat&0xf);
+ UniPTEffect(eff,dat);
+ }
+ break;
+ }
+ UniNewline();
+ xmtrack++;
+ }
+ return UniDup();
+}
+
+static BOOL LoadPatterns(BOOL dummypat)
+{
+ int t,u,v,numtrk;
+
+ if(!AllocTracks()) return 0;
+ if(!AllocPatterns()) return 0;
+
+ numtrk=0;
+ for(t=0;t<mh->numpat;t++) {
+ XMPATHEADER ph;
+
+ ph.size =_mm_read_I_ULONG(modreader);
+ if (ph.size<(mh->version==0x0102?8:9)) {
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return 0;
+ }
+ ph.packing =_mm_read_UBYTE(modreader);
+ if(ph.packing) {
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return 0;
+ }
+ if(mh->version==0x0102)
+ ph.numrows =_mm_read_UBYTE(modreader)+1;
+ else
+ ph.numrows =_mm_read_I_UWORD(modreader);
+ ph.packsize =_mm_read_I_UWORD(modreader);
+
+ ph.size-=(mh->version==0x0102?8:9);
+ if(ph.size)
+ _mm_fseek(modreader,ph.size,SEEK_CUR);
+
+ of.pattrows[t]=ph.numrows;
+
+ if(ph.numrows) {
+ if(!(xmpat=(XMNOTE*)MikMod_calloc(ph.numrows*of.numchn,sizeof(XMNOTE))))
+ return 0;
+
+ /* when packsize is 0, don't try to load a pattern.. it's empty. */
+ if(ph.packsize)
+ for(u=0;u<ph.numrows;u++)
+ for(v=0;v<of.numchn;v++) {
+ if(!ph.packsize) break;
+
+ ph.packsize-=XM_ReadNote(&xmpat[(v*ph.numrows)+u]);
+ if(ph.packsize<0) {
+ MikMod_free(xmpat);xmpat=NULL;
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return 0;
+ }
+ }
+
+ if(ph.packsize) {
+ _mm_fseek(modreader,ph.packsize,SEEK_CUR);
+ }
+
+ if(_mm_eof(modreader)) {
+ MikMod_free(xmpat);xmpat=NULL;
+ _mm_errno=MMERR_LOADING_PATTERN;
+ return 0;
+ }
+
+ for(v=0;v<of.numchn;v++)
+ of.tracks[numtrk++]=XM_Convert(&xmpat[v*ph.numrows],ph.numrows);
+
+ MikMod_free(xmpat);xmpat=NULL;
+ } else {
+ for(v=0;v<of.numchn;v++)
+ of.tracks[numtrk++]=XM_Convert(NULL,ph.numrows);
+ }
+ }
+
+ if(dummypat) {
+ of.pattrows[t]=64;
+ if(!(xmpat=(XMNOTE*)MikMod_calloc(64*of.numchn,sizeof(XMNOTE)))) return 0;
+ for(v=0;v<of.numchn;v++)
+ of.tracks[numtrk++]=XM_Convert(&xmpat[v*64],64);
+ MikMod_free(xmpat);xmpat=NULL;
+ }
+
+ return 1;
+}
+
+static void FixEnvelope(ENVPT *cur, int pts)
+{
+ int u, old, tmp;
+ ENVPT *prev;
+
+ /* Some broken XM editing program will only save the low byte
+ of the position value. Try to compensate by adding the
+ missing high byte. */
+
+ prev = cur++;
+ old = prev->pos;
+
+ for (u = 1; u < pts; u++, prev++, cur++) {
+ if (cur->pos < prev->pos) {
+ if (cur->pos < 0x100) {
+ if (cur->pos > old) /* same hex century */
+ tmp = cur->pos + (prev->pos - old);
+ else
+ tmp = cur->pos | ((prev->pos + 0x100) & 0xff00);
+ old = cur->pos;
+ cur->pos = tmp;
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d -> %d\n",
+ u, pts, prev->pos, old, cur->pos);
+#endif
+ } else {
+#ifdef MIKMOD_DEBUG
+ /* different brokenness style... fix unknown */
+ fprintf(stderr, "\rbroken envelope position(%d/%d), %d %d\n",
+ u, pts, old, cur->pos);
+#endif
+ old = cur->pos;
+ }
+ } else
+ old = cur->pos;
+ }
+}
+
+static BOOL LoadInstruments(void)
+{
+ int t,u;
+ INSTRUMENT *d;
+ ULONG next=0;
+ UWORD wavcnt=0;
+
+ if(!AllocInstruments()) return 0;
+ d=of.instruments;
+ for(t=0;t<of.numins;t++,d++) {
+ XMINSTHEADER ih;
+ long headend;
+
+ memset(d->samplenumber,0xff,INSTNOTES*sizeof(UWORD));
+
+ /* read instrument header */
+ headend = _mm_ftell(modreader);
+ ih.size = _mm_read_I_ULONG(modreader);
+ headend += ih.size;
+ _mm_read_string(ih.name, 22, modreader);
+ ih.type = _mm_read_UBYTE(modreader);
+ ih.numsmp = _mm_read_I_UWORD(modreader);
+
+ d->insname = DupStr(ih.name,22,1);
+
+ if((SWORD)ih.size>29) {
+ ih.ssize = _mm_read_I_ULONG(modreader);
+ if(((SWORD)ih.numsmp>0)&&(ih.numsmp<=XMNOTECNT)) {
+ XMPATCHHEADER pth;
+ int p;
+
+ _mm_read_UBYTES (pth.what,XMNOTECNT,modreader);
+ _mm_read_I_UWORDS (pth.volenv, XMENVCNT, modreader);
+ _mm_read_I_UWORDS (pth.panenv, XMENVCNT, modreader);
+ pth.volpts = _mm_read_UBYTE(modreader);
+ pth.panpts = _mm_read_UBYTE(modreader);
+ pth.volsus = _mm_read_UBYTE(modreader);
+ pth.volbeg = _mm_read_UBYTE(modreader);
+ pth.volend = _mm_read_UBYTE(modreader);
+ pth.pansus = _mm_read_UBYTE(modreader);
+ pth.panbeg = _mm_read_UBYTE(modreader);
+ pth.panend = _mm_read_UBYTE(modreader);
+ pth.volflg = _mm_read_UBYTE(modreader);
+ pth.panflg = _mm_read_UBYTE(modreader);
+ pth.vibflg = _mm_read_UBYTE(modreader);
+ pth.vibsweep = _mm_read_UBYTE(modreader);
+ pth.vibdepth = _mm_read_UBYTE(modreader);
+ pth.vibrate = _mm_read_UBYTE(modreader);
+ pth.volfade = _mm_read_I_UWORD(modreader);
+
+ /* read the remainder of the header
+ (2 bytes for 1.03, 22 for 1.04) */
+ for(u=headend-_mm_ftell(modreader);u;u--) _mm_read_UBYTE(modreader);
+
+ /* we can't trust the envelope point count here, as some
+ modules have incorrect values (K_OSPACE.XM reports 32 volume
+ points, for example). */
+ if(pth.volpts>XMENVCNT/2) pth.volpts=XMENVCNT/2;
+ if(pth.panpts>XMENVCNT/2) pth.panpts=XMENVCNT/2;
+
+ if((_mm_eof(modreader))||(pth.volpts>XMENVCNT/2)||(pth.panpts>XMENVCNT/2)) {
+ if(nextwav) { MikMod_free(nextwav);nextwav=NULL; }
+ if(wh) { MikMod_free(wh);wh=NULL; }
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+
+ for(u=0;u<XMNOTECNT;u++)
+ d->samplenumber[u]=pth.what[u]+of.numsmp;
+ d->volfade = pth.volfade;
+
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C
+#define XM_ProcessEnvelope(name) \
+ for (u = 0; u < (XMENVCNT >> 1); u++) { \
+ d-> name##env[u].pos = pth. name##env[u << 1]; \
+ d-> name##env[u].val = pth. name##env[(u << 1)+ 1]; \
+ } \
+ if (pth. name##flg&1) d-> name##flg|=EF_ON; \
+ if (pth. name##flg&2) d-> name##flg|=EF_SUSTAIN; \
+ if (pth. name##flg&4) d-> name##flg|=EF_LOOP; \
+ d-> name##susbeg=d-> name##susend=pth. name##sus; \
+ d-> name##beg=pth. name##beg; \
+ d-> name##end=pth. name##end; \
+ d-> name##pts=pth. name##pts; \
+ \
+ /* scale envelope */ \
+ for (p=0;p<XMENVCNT/2;p++) \
+ d-> name##env[p].val<<=2; \
+ \
+ if ((d-> name##flg&EF_ON)&&(d-> name##pts<2)) \
+ d-> name##flg&=~EF_ON
+#else
+#define XM_ProcessEnvelope(name) \
+ for (u = 0; u < (XMENVCNT >> 1); u++) { \
+ d-> name/**/env[u].pos = pth. name/**/env[u << 1]; \
+ d-> name/**/env[u].val = pth. name/**/env[(u << 1)+ 1]; \
+ } \
+ if (pth. name/**/flg&1) d-> name/**/flg|=EF_ON; \
+ if (pth. name/**/flg&2) d-> name/**/flg|=EF_SUSTAIN; \
+ if (pth. name/**/flg&4) d-> name/**/flg|=EF_LOOP; \
+ d-> name/**/susbeg=d-> name/**/susend= \
+ pth. name/**/sus; \
+ d-> name/**/beg=pth. name/**/beg; \
+ d-> name/**/end=pth. name/**/end; \
+ d-> name/**/pts=pth. name/**/pts; \
+ \
+ /* scale envelope */ \
+ for (p=0;p<XMENVCNT/2;p++) \
+ d-> name/**/env[p].val<<=2; \
+ \
+ if ((d-> name/**/flg&EF_ON)&&(d-> name/**/pts<2)) \
+ d-> name/**/flg&=~EF_ON
+#endif
+
+ XM_ProcessEnvelope(vol);
+ XM_ProcessEnvelope(pan);
+#undef XM_ProcessEnvelope
+
+ if (d->volflg & EF_ON)
+ FixEnvelope(d->volenv, d->volpts);
+ if (d->panflg & EF_ON)
+ FixEnvelope(d->panenv, d->panpts);
+
+ /* Samples are stored outside the instrument struct now, so we
+ have to load them all into a temp area, count the of.numsmp
+ along the way and then do an AllocSamples() and move
+ everything over */
+ if(mh->version>0x0103) next = 0;
+ for(u=0;u<ih.numsmp;u++,s++) {
+ /* Allocate more room for sample information if necessary */
+ if(of.numsmp+u==wavcnt) {
+ wavcnt+=XM_SMPINCR;
+ if(!(nextwav=MikMod_realloc(nextwav,wavcnt*sizeof(ULONG)))){
+ if(wh) { MikMod_free(wh);wh=NULL; }
+ _mm_errno = MMERR_OUT_OF_MEMORY;
+ return 0;
+ }
+ if(!(wh=MikMod_realloc(wh,wavcnt*sizeof(XMWAVHEADER)))) {
+ MikMod_free(nextwav);nextwav=NULL;
+ _mm_errno = MMERR_OUT_OF_MEMORY;
+ return 0;
+ }
+ s=wh+(wavcnt-XM_SMPINCR);
+ }
+
+ s->length =_mm_read_I_ULONG (modreader);
+ s->loopstart =_mm_read_I_ULONG (modreader);
+ s->looplength =_mm_read_I_ULONG (modreader);
+ s->volume =_mm_read_UBYTE (modreader);
+ s->finetune =_mm_read_SBYTE (modreader);
+ s->type =_mm_read_UBYTE (modreader);
+ s->panning =_mm_read_UBYTE (modreader);
+ s->relnote =_mm_read_SBYTE (modreader);
+ s->vibtype = pth.vibflg;
+ s->vibsweep = pth.vibsweep;
+ s->vibdepth = pth.vibdepth*4;
+ s->vibrate = pth.vibrate;
+ s->reserved =_mm_read_UBYTE (modreader);
+ _mm_read_string(s->samplename, 22, modreader);
+
+ nextwav[of.numsmp+u]=next;
+ next+=s->length;
+
+ if(_mm_eof(modreader)) {
+ MikMod_free(nextwav);MikMod_free(wh);
+ nextwav=NULL;wh=NULL;
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+ }
+
+ if(mh->version>0x0103) {
+ for(u=0;u<ih.numsmp;u++)
+ nextwav[of.numsmp++]+=_mm_ftell(modreader);
+ _mm_fseek(modreader,next,SEEK_CUR);
+ } else
+ of.numsmp+=ih.numsmp;
+ } else {
+ /* read the remainder of the header */
+ for(u=headend-_mm_ftell(modreader);u;u--) _mm_read_UBYTE(modreader);
+
+ if(_mm_eof(modreader)) {
+ MikMod_free(nextwav);MikMod_free(wh);
+ nextwav=NULL;wh=NULL;
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+ }
+ }
+ }
+
+ /* sanity check */
+ if(!of.numsmp) {
+ if(nextwav) { MikMod_free(nextwav);nextwav=NULL; }
+ if(wh) { MikMod_free(wh);wh=NULL; }
+ _mm_errno = MMERR_LOADING_SAMPLEINFO;
+ return 0;
+ }
+
+ return 1;
+}
+
+BOOL XM_Load(BOOL curious)
+{
+ INSTRUMENT *d;
+ SAMPLE *q;
+ int t,u;
+ BOOL dummypat=0;
+ char tracker[21],modtype[60];
+
+ (void)curious; /* unused arg */
+
+ /* try to read module header */
+ _mm_read_string(mh->id,17,modreader);
+ _mm_read_string(mh->songname,21,modreader);
+ _mm_read_string(mh->trackername,20,modreader);
+ mh->version =_mm_read_I_UWORD(modreader);
+ if((mh->version<0x102)||(mh->version>0x104)) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ mh->headersize =_mm_read_I_ULONG(modreader);
+ mh->songlength =_mm_read_I_UWORD(modreader);
+ mh->restart =_mm_read_I_UWORD(modreader);
+ mh->numchn =_mm_read_I_UWORD(modreader);
+ mh->numpat =_mm_read_I_UWORD(modreader);
+ mh->numins =_mm_read_I_UWORD(modreader);
+ mh->flags =_mm_read_I_UWORD(modreader);
+ mh->tempo =_mm_read_I_UWORD(modreader);
+ mh->bpm =_mm_read_I_UWORD(modreader);
+ if(!mh->bpm) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ _mm_read_UBYTES(mh->orders,256,modreader);
+
+ if(_mm_eof(modreader)) {
+ _mm_errno = MMERR_LOADING_HEADER;
+ return 0;
+ }
+
+ /* set module variables */
+ of.initspeed = mh->tempo;
+ of.inittempo = mh->bpm;
+ strncpy(tracker,mh->trackername,20);tracker[20]=0;
+ for(t=20;(tracker[t]<=' ')&&(t>=0);t--) tracker[t]=0;
+
+ /* some modules have the tracker name empty */
+ if (!tracker[0])
+ strcpy(tracker,"Unknown tracker");
+
+#ifdef HAVE_SNPRINTF
+ snprintf(modtype,60,"%s (XM format %d.%02d)",
+ tracker,mh->version>>8,mh->version&0xff);
+#else
+ sprintf(modtype,"%s (XM format %d.%02d)",
+ tracker,mh->version>>8,mh->version&0xff);
+#endif
+ of.modtype = strdup(modtype);
+ of.numchn = mh->numchn;
+ of.numpat = mh->numpat;
+ of.numtrk = (UWORD)of.numpat*of.numchn; /* get number of channels */
+ of.songname = DupStr(mh->songname,20,1);
+ of.numpos = mh->songlength; /* copy the songlength */
+ of.reppos = mh->restart<mh->songlength?mh->restart:0;
+ of.numins = mh->numins;
+ of.flags |= UF_XMPERIODS | UF_INST | UF_NOWRAP | UF_FT2QUIRKS |
+ UF_PANNING;
+ if(mh->flags&1) of.flags |= UF_LINEAR;
+ of.bpmlimit = 32;
+
+ memset(of.chanvol,64,of.numchn); /* store channel volumes */
+
+ if(!AllocPositions(of.numpos+1)) return 0;
+ for(t=0;t<of.numpos;t++)
+ of.positions[t]=mh->orders[t];
+
+ /* We have to check for any pattern numbers in the order list greater than
+ the number of patterns total. If one or more is found, we set it equal to
+ the pattern total and make a dummy pattern to workaround the problem */
+ for(t=0;t<of.numpos;t++) {
+ if(of.positions[t]>=of.numpat) {
+ of.positions[t]=of.numpat;
+ dummypat=1;
+ }
+ }
+ if(dummypat) {
+ of.numpat++;of.numtrk+=of.numchn;
+ }
+
+ if(mh->version<0x0104) {
+ if(!LoadInstruments()) return 0;
+ if(!LoadPatterns(dummypat)) return 0;
+ for(t=0;t<of.numsmp;t++)
+ nextwav[t]+=_mm_ftell(modreader);
+ } else {
+ if(!LoadPatterns(dummypat)) return 0;
+ if(!LoadInstruments()) return 0;
+ }
+
+ if(!AllocSamples()) {
+ MikMod_free(nextwav);MikMod_free(wh);
+ nextwav=NULL;wh=NULL;
+ return 0;
+ }
+ q = of.samples;
+ s = wh;
+ for(u=0;u<of.numsmp;u++,q++,s++) {
+ q->samplename = DupStr(s->samplename,22,1);
+ q->length = s->length;
+ q->loopstart = s->loopstart;
+ q->loopend = s->loopstart+s->looplength;
+ q->volume = s->volume;
+ q->speed = s->finetune+128;
+ q->panning = s->panning;
+ q->seekpos = nextwav[u];
+ q->vibtype = s->vibtype;
+ q->vibsweep = s->vibsweep;
+ q->vibdepth = s->vibdepth;
+ q->vibrate = s->vibrate;
+
+ if(s->type & 0x10) {
+ q->length >>= 1;
+ q->loopstart >>= 1;
+ q->loopend >>= 1;
+ }
+
+ q->flags|=SF_OWNPAN|SF_DELTA|SF_SIGNED;
+ if(s->type&0x3) q->flags|=SF_LOOP;
+ if(s->type&0x2) q->flags|=SF_BIDI;
+ if(s->type&0x10) q->flags|=SF_16BITS;
+ }
+
+ d=of.instruments;
+ s=wh;
+ for(u=0;u<of.numins;u++,d++)
+ for(t=0;t<XMNOTECNT;t++) {
+ if (d->samplenumber[t]>=of.numsmp)
+ d->samplenote[t]=255;
+ else {
+ int note=t+s[d->samplenumber[t]].relnote;
+ d->samplenote[t]=(note<0)?0:note;
+ }
+ }
+
+ MikMod_free(wh);MikMod_free(nextwav);
+ wh=NULL;nextwav=NULL;
+ return 1;
+}
+
+CHAR *XM_LoadTitle(void)
+{
+ CHAR s[21];
+
+ _mm_fseek(modreader,17,SEEK_SET);
+ if(!_mm_read_UBYTES(s,21,modreader)) return NULL;
+
+ return(DupStr(s,21,1));
+}
+
+/*========== Loader information */
+
+MIKMODAPI MLOADER load_xm={
+ NULL,
+ "XM",
+ "XM (FastTracker 2)",
+ XM_Init,
+ XM_Test,
+ XM_Load,
+ XM_Cleanup,
+ XM_LoadTitle
+};
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mdreg.c b/src/libs/mikmod/mdreg.c
new file mode 100644
index 0000000..2ad65c2
--- /dev/null
+++ b/src/libs/mikmod/mdreg.c
@@ -0,0 +1,47 @@
+/* MikMod sound library
+ (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Routine for registering all drivers in libmikmod for the current platform.
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mikmod_internals.h"
+
+void _mm_registeralldrivers(void)
+{
+ _mm_registerdriver(&drv_nos);
+}
+
+void MikMod_RegisterAllDrivers(void)
+{
+ MUTEX_LOCK(lists);
+ _mm_registeralldrivers();
+ MUTEX_UNLOCK(lists);
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mdriver.c b/src/libs/mikmod/mdriver.c
new file mode 100644
index 0000000..68a2e79
--- /dev/null
+++ b/src/libs/mikmod/mdriver.c
@@ -0,0 +1,935 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ These routines are used to access the available soundcard drivers.
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined unix || (defined __APPLE__ && defined __MACH__)
+#include <pwd.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+static MDRIVER *firstdriver=NULL;
+MIKMODAPI MDRIVER *md_driver=NULL;
+extern MODULE *pf; /* modfile being played */
+
+/* Initial global settings */
+MIKMODAPI UWORD md_device = 0; /* autodetect */
+MIKMODAPI UWORD md_mixfreq = 44100;
+MIKMODAPI UWORD md_mode = DMODE_STEREO | DMODE_16BITS |
+ DMODE_SURROUND |DMODE_SOFT_MUSIC |
+ DMODE_SOFT_SNDFX;
+MIKMODAPI UBYTE md_pansep = 128; /* 128 == 100% (full left/right) */
+MIKMODAPI UBYTE md_reverb = 0; /* no reverb */
+MIKMODAPI UBYTE md_volume = 128; /* global sound volume (0-128) */
+MIKMODAPI UBYTE md_musicvolume = 128; /* volume of song */
+MIKMODAPI UBYTE md_sndfxvolume = 128; /* volume of sound effects */
+ UWORD md_bpm = 125; /* tempo */
+
+/* Do not modify the numchn variables yourself! use MD_SetVoices() */
+ UBYTE md_numchn=0,md_sngchn=0,md_sfxchn=0;
+ UBYTE md_hardchn=0,md_softchn=0;
+
+ void (*md_player)(void) = Player_HandleTick;
+static BOOL isplaying=0, initialized = 0;
+static UBYTE *sfxinfo;
+static int sfxpool;
+
+static SAMPLE **md_sample = NULL;
+
+/* Previous driver in use */
+static SWORD olddevice = -1;
+
+/* Limits the number of hardware voices to the specified amount.
+ This function should only be used by the low-level drivers. */
+static void LimitHardVoices(int limit)
+{
+ int t=0;
+
+ if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;
+ if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;
+
+ if (!(md_mode & DMODE_SOFT_SNDFX))
+ md_hardchn=md_sfxchn;
+ else
+ md_hardchn=0;
+
+ if (!(md_mode & DMODE_SOFT_MUSIC)) md_hardchn += md_sngchn;
+
+ while (md_hardchn>limit) {
+ if (++t & 1) {
+ if (!(md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;
+ } else {
+ if (!(md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;
+ }
+
+ if (!(md_mode & DMODE_SOFT_SNDFX))
+ md_hardchn=md_sfxchn;
+ else
+ md_hardchn=0;
+
+ if (!(md_mode & DMODE_SOFT_MUSIC))
+ md_hardchn+=md_sngchn;
+ }
+ md_numchn=md_hardchn+md_softchn;
+}
+
+/* Limits the number of hardware voices to the specified amount.
+ This function should only be used by the low-level drivers. */
+static void LimitSoftVoices(int limit)
+{
+ int t=0;
+
+ if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>limit)) md_sfxchn=limit;
+ if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>limit)) md_sngchn=limit;
+
+ if (md_mode & DMODE_SOFT_SNDFX)
+ md_softchn=md_sfxchn;
+ else
+ md_softchn=0;
+
+ if (md_mode & DMODE_SOFT_MUSIC) md_softchn+=md_sngchn;
+
+ while (md_softchn>limit) {
+ if (++t & 1) {
+ if ((md_mode & DMODE_SOFT_SNDFX) && (md_sfxchn>4)) md_sfxchn--;
+ } else {
+ if ((md_mode & DMODE_SOFT_MUSIC) && (md_sngchn>8)) md_sngchn--;
+ }
+
+ if (!(md_mode & DMODE_SOFT_SNDFX))
+ md_softchn=md_sfxchn;
+ else
+ md_softchn=0;
+
+ if (!(md_mode & DMODE_SOFT_MUSIC))
+ md_softchn+=md_sngchn;
+ }
+ md_numchn=md_hardchn+md_softchn;
+}
+
+/* Note: 'type' indicates whether the returned value should be for music or for
+ sound effects. */
+ULONG MD_SampleSpace(int type)
+{
+ if(type==MD_MUSIC)
+ type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
+ else if(type==MD_SNDFX)
+ type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
+
+ return md_driver->FreeSampleSpace(type);
+}
+
+ULONG MD_SampleLength(int type,SAMPLE* s)
+{
+ if(type==MD_MUSIC)
+ type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
+ else
+ if(type==MD_SNDFX)
+ type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
+
+ return md_driver->RealSampleLength(type,s);
+}
+
+MIKMODAPI CHAR* MikMod_InfoDriver(void)
+{
+ int t;
+ size_t len=0;
+ MDRIVER *l;
+ CHAR *list=NULL;
+
+ MUTEX_LOCK(lists);
+ /* compute size of buffer */
+ for(l=firstdriver;l;l=l->next)
+ len+=4+(l->next?1:0)+strlen(l->Version);
+
+ if(len)
+ if((list=MikMod_malloc(len*sizeof(CHAR)))) {
+ list[0]=0;
+ /* list all registered device drivers : */
+ for(t=1,l=firstdriver;l;l=l->next,t++)
+ sprintf(list,(l->next)?"%s%2d %s\n":"%s%2d %s",
+ list,t,l->Version);
+ }
+ MUTEX_UNLOCK(lists);
+ return list;
+}
+
+void _mm_registerdriver(struct MDRIVER* drv)
+{
+ MDRIVER *cruise = firstdriver;
+
+ /* don't register a MISSING() driver */
+ if ((drv->Name) && (drv->Version)) {
+ if (cruise) {
+ while (cruise->next) cruise = cruise->next;
+ cruise->next = drv;
+ } else
+ firstdriver = drv;
+ }
+}
+
+MIKMODAPI void MikMod_RegisterDriver(struct MDRIVER* drv)
+{
+ /* if we try to register an invalid driver, or an already registered driver,
+ ignore this attempt */
+ if ((!drv)||(drv->next)||(!drv->Name))
+ return;
+
+ MUTEX_LOCK(lists);
+ _mm_registerdriver(drv);
+ MUTEX_UNLOCK(lists);
+}
+
+MIKMODAPI int MikMod_DriverFromAlias(CHAR *alias)
+{
+ int rank=1;
+ MDRIVER *cruise;
+
+ MUTEX_LOCK(lists);
+ cruise=firstdriver;
+ while(cruise) {
+ if (cruise->Alias) {
+ if (!(strcasecmp(alias,cruise->Alias))) break;
+ rank++;
+ }
+ cruise=cruise->next;
+ }
+ if(!cruise) rank=0;
+ MUTEX_UNLOCK(lists);
+
+ return rank;
+}
+
+SWORD MD_SampleLoad(SAMPLOAD* s, int type)
+{
+ SWORD result;
+
+ if(type==MD_MUSIC)
+ type=(md_mode & DMODE_SOFT_MUSIC)?MD_SOFTWARE:MD_HARDWARE;
+ else if(type==MD_SNDFX)
+ type=(md_mode & DMODE_SOFT_SNDFX)?MD_SOFTWARE:MD_HARDWARE;
+
+ SL_Init(s);
+ result=md_driver->SampleLoad(s,type);
+ SL_Exit(s);
+
+ return result;
+}
+
+void MD_SampleUnload(SWORD handle)
+{
+ md_driver->SampleUnload(handle);
+}
+
+MIKMODAPI MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t player)
+{
+ MikMod_player_t result;
+
+ MUTEX_LOCK(vars);
+ result=md_player;
+ md_player=player;
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI void MikMod_Update(void)
+{
+ MUTEX_LOCK(vars);
+ if(isplaying) {
+ if((!pf)||(!pf->forbid))
+ md_driver->Update();
+ else {
+ if (md_driver->Pause)
+ md_driver->Pause();
+ }
+ }
+ MUTEX_UNLOCK(vars);
+}
+
+void Voice_SetVolume_internal(SBYTE voice,UWORD vol)
+{
+ ULONG tmp;
+
+ if((voice<0)||(voice>=md_numchn)) return;
+
+ /* range checks */
+ if(md_musicvolume>128) md_musicvolume=128;
+ if(md_sndfxvolume>128) md_sndfxvolume=128;
+ if(md_volume>128) md_volume=128;
+
+ tmp=(ULONG)vol*(ULONG)md_volume*
+ ((voice<md_sngchn)?(ULONG)md_musicvolume:(ULONG)md_sndfxvolume);
+ md_driver->VoiceSetVolume(voice,tmp/16384UL);
+}
+
+MIKMODAPI void Voice_SetVolume(SBYTE voice,UWORD vol)
+{
+ MUTEX_LOCK(vars);
+ Voice_SetVolume_internal(voice,vol);
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI UWORD Voice_GetVolume(SBYTE voice)
+{
+ UWORD result=0;
+
+ MUTEX_LOCK(vars);
+ if((voice>=0)&&(voice<md_numchn))
+ result=md_driver->VoiceGetVolume(voice);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+void Voice_SetFrequency_internal(SBYTE voice,ULONG frq)
+{
+ if((voice<0)||(voice>=md_numchn)) return;
+ if((md_sample[voice])&&(md_sample[voice]->divfactor))
+ frq/=md_sample[voice]->divfactor;
+ md_driver->VoiceSetFrequency(voice,frq);
+}
+
+MIKMODAPI void Voice_SetFrequency(SBYTE voice,ULONG frq)
+{
+ MUTEX_LOCK(vars);
+ Voice_SetFrequency_internal(voice,frq);
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI ULONG Voice_GetFrequency(SBYTE voice)
+{
+ ULONG result=0;
+
+ MUTEX_LOCK(vars);
+ if((voice>=0)&&(voice<md_numchn))
+ result=md_driver->VoiceGetFrequency(voice);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+void Voice_SetPanning_internal(SBYTE voice,ULONG pan)
+{
+ if((voice<0)||(voice>=md_numchn)) return;
+ if(pan!=PAN_SURROUND) {
+ if(md_pansep>128) md_pansep=128;
+ if(md_mode & DMODE_REVERSE) pan=255-pan;
+ pan = (((SWORD)(pan-128)*md_pansep)/128)+128;
+ }
+ md_driver->VoiceSetPanning(voice, pan);
+}
+
+MIKMODAPI void Voice_SetPanning(SBYTE voice,ULONG pan)
+{
+#ifdef MIKMOD_DEBUG
+ if((pan!=PAN_SURROUND)&&((pan<0)||(pan>255)))
+ fprintf(stderr,"\rVoice_SetPanning called with pan=%ld\n",(long)pan);
+#endif
+
+ MUTEX_LOCK(vars);
+ Voice_SetPanning_internal(voice,pan);
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI ULONG Voice_GetPanning(SBYTE voice)
+{
+ ULONG result=PAN_CENTER;
+
+ MUTEX_LOCK(vars);
+ if((voice>=0)&&(voice<md_numchn))
+ result=md_driver->VoiceGetPanning(voice);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+void Voice_Play_internal(SBYTE voice,SAMPLE* s,ULONG start)
+{
+ ULONG repend;
+
+ if((voice<0)||(voice>=md_numchn)) return;
+
+ md_sample[voice]=s;
+ repend=s->loopend;
+
+ if(s->flags&SF_LOOP)
+ /* repend can't be bigger than size */
+ if(repend>s->length) repend=s->length;
+
+ md_driver->VoicePlay(voice,s->handle,start,s->length,s->loopstart,repend,s->flags);
+}
+
+MIKMODAPI void Voice_Play(SBYTE voice,SAMPLE* s,ULONG start)
+{
+ if(start>s->length) return;
+
+ MUTEX_LOCK(vars);
+ Voice_Play_internal(voice,s,start);
+ MUTEX_UNLOCK(vars);
+}
+
+void Voice_Stop_internal(SBYTE voice)
+{
+ if((voice<0)||(voice>=md_numchn)) return;
+ if(voice>=md_sngchn)
+ /* It is a sound effects channel, so flag the voice as non-critical! */
+ sfxinfo[voice-md_sngchn]=0;
+ md_driver->VoiceStop(voice);
+}
+
+MIKMODAPI void Voice_Stop(SBYTE voice)
+{
+ MUTEX_LOCK(vars);
+ Voice_Stop_internal(voice);
+ MUTEX_UNLOCK(vars);
+}
+
+BOOL Voice_Stopped_internal(SBYTE voice)
+{
+ if((voice<0)||(voice>=md_numchn)) return 0;
+ return(md_driver->VoiceStopped(voice));
+}
+
+MIKMODAPI BOOL Voice_Stopped(SBYTE voice)
+{
+ BOOL result;
+
+ MUTEX_LOCK(vars);
+ result=Voice_Stopped_internal(voice);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI SLONG Voice_GetPosition(SBYTE voice)
+{
+ SLONG result=0;
+
+ MUTEX_LOCK(vars);
+ if((voice>=0)&&(voice<md_numchn)) {
+ if (md_driver->VoiceGetPosition)
+ result=(md_driver->VoiceGetPosition(voice));
+ else
+ result=-1;
+ }
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI ULONG Voice_RealVolume(SBYTE voice)
+{
+ ULONG result=0;
+
+ MUTEX_LOCK(vars);
+ if((voice>=0)&&(voice<md_numchn)&& md_driver->VoiceRealVolume)
+ result=(md_driver->VoiceRealVolume(voice));
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+static BOOL _mm_init(CHAR *cmdline)
+{
+ UWORD t;
+
+ _mm_critical = 1;
+
+ /* if md_device==0, try to find a device number */
+ if(!md_device) {
+ cmdline=NULL;
+
+ for(t=1,md_driver=firstdriver;md_driver;md_driver=md_driver->next,t++)
+ if(md_driver->IsPresent()) break;
+
+ if(!md_driver) {
+ _mm_errno = MMERR_DETECTING_DEVICE;
+ if(_mm_errorhandler) _mm_errorhandler();
+ md_driver = &drv_nos;
+ return 1;
+ }
+
+ md_device = t;
+ } else {
+ /* if n>0, use that driver */
+ for(t=1,md_driver=firstdriver;(md_driver)&&(t!=md_device);md_driver=md_driver->next)
+ t++;
+
+ if(!md_driver) {
+ _mm_errno = MMERR_INVALID_DEVICE;
+ if(_mm_errorhandler) _mm_errorhandler();
+ md_driver = &drv_nos;
+ return 1;
+ }
+
+ /* arguments here might be necessary for the presence check to succeed */
+ if(cmdline&&(md_driver->CommandLine))
+ md_driver->CommandLine(cmdline);
+
+ if(!md_driver->IsPresent()) {
+ _mm_errno = MMERR_DETECTING_DEVICE;
+ if(_mm_errorhandler) _mm_errorhandler();
+ md_driver = &drv_nos;
+ return 1;
+ }
+ }
+
+ olddevice = md_device;
+ if(md_driver->Init()) {
+ MikMod_Exit_internal();
+ if(_mm_errorhandler) _mm_errorhandler();
+ return 1;
+ }
+
+ initialized=1;
+ _mm_critical=0;
+
+ return 0;
+}
+
+MIKMODAPI BOOL MikMod_Init(CHAR *cmdline)
+{
+ BOOL result;
+
+ MUTEX_LOCK(vars);
+ MUTEX_LOCK(lists);
+ result=_mm_init(cmdline);
+ MUTEX_UNLOCK(lists);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+void MikMod_Exit_internal(void)
+{
+ MikMod_DisableOutput_internal();
+ md_driver->Exit();
+ md_numchn = md_sfxchn = md_sngchn = 0;
+ md_driver = &drv_nos;
+
+ if(sfxinfo) MikMod_free(sfxinfo);
+ if(md_sample) MikMod_free(md_sample);
+ md_sample = NULL;
+ sfxinfo = NULL;
+
+ initialized = 0;
+}
+
+MIKMODAPI void MikMod_Exit(void)
+{
+ MUTEX_LOCK(vars);
+ MUTEX_LOCK(lists);
+ MikMod_Exit_internal();
+ MUTEX_UNLOCK(lists);
+ MUTEX_UNLOCK(vars);
+}
+
+/* Reset the driver using the new global variable settings.
+ If the driver has not been initialized, it will be now. */
+static BOOL _mm_reset(CHAR *cmdline)
+{
+ BOOL wasplaying = 0;
+
+ if(!initialized) return _mm_init(cmdline);
+
+ if (isplaying) {
+ wasplaying = 1;
+ md_driver->PlayStop();
+ }
+
+ if((!md_driver->Reset)||(md_device != olddevice)) {
+ /* md_driver->Reset was NULL, or md_device was changed, so do a full
+ reset of the driver. */
+ md_driver->Exit();
+ if(_mm_init(cmdline)) {
+ MikMod_Exit_internal();
+ if(_mm_errno)
+ if(_mm_errorhandler) _mm_errorhandler();
+ return 1;
+ }
+ } else {
+ if(md_driver->Reset()) {
+ MikMod_Exit_internal();
+ if(_mm_errno)
+ if(_mm_errorhandler) _mm_errorhandler();
+ return 1;
+ }
+ }
+
+ if (wasplaying) md_driver->PlayStart();
+ return 0;
+}
+
+MIKMODAPI BOOL MikMod_Reset(CHAR *cmdline)
+{
+ BOOL result;
+
+ MUTEX_LOCK(vars);
+ MUTEX_LOCK(lists);
+ result=_mm_reset(cmdline);
+ MUTEX_UNLOCK(lists);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+/* If either parameter is -1, the current set value will be retained. */
+BOOL MikMod_SetNumVoices_internal(int music, int sfx)
+{
+ BOOL resume = 0;
+ int t, oldchn = 0;
+
+ if((!music)&&(!sfx)) return 1;
+ _mm_critical = 1;
+ if(isplaying) {
+ MikMod_DisableOutput_internal();
+ oldchn = md_numchn;
+ resume = 1;
+ }
+
+ if(sfxinfo) MikMod_free(sfxinfo);
+ if(md_sample) MikMod_free(md_sample);
+ md_sample = NULL;
+ sfxinfo = NULL;
+
+ if(music!=-1) md_sngchn = music;
+ if(sfx!=-1) md_sfxchn = sfx;
+ md_numchn = md_sngchn + md_sfxchn;
+
+ LimitHardVoices(md_driver->HardVoiceLimit);
+ LimitSoftVoices(md_driver->SoftVoiceLimit);
+
+ if(md_driver->SetNumVoices()) {
+ MikMod_Exit_internal();
+ if(_mm_errno)
+ if(_mm_errorhandler!=NULL) _mm_errorhandler();
+ md_numchn = md_softchn = md_hardchn = md_sfxchn = md_sngchn = 0;
+ return 1;
+ }
+
+ if(md_sngchn+md_sfxchn)
+ md_sample=(SAMPLE**)MikMod_calloc(md_sngchn+md_sfxchn,sizeof(SAMPLE*));
+ if(md_sfxchn)
+ sfxinfo = (UBYTE *)MikMod_calloc(md_sfxchn,sizeof(UBYTE));
+
+ /* make sure the player doesn't start with garbage */
+ for(t=oldchn;t<md_numchn;t++) Voice_Stop_internal(t);
+
+ sfxpool = 0;
+ if(resume) MikMod_EnableOutput_internal();
+ _mm_critical = 0;
+
+ return 0;
+}
+
+MIKMODAPI BOOL MikMod_SetNumVoices(int music, int sfx)
+{
+ BOOL result;
+
+ MUTEX_LOCK(vars);
+ result=MikMod_SetNumVoices_internal(music,sfx);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+BOOL MikMod_EnableOutput_internal(void)
+{
+ _mm_critical = 1;
+ if(!isplaying) {
+ if(md_driver->PlayStart()) return 1;
+ isplaying = 1;
+ }
+ _mm_critical = 0;
+ return 0;
+}
+
+MIKMODAPI BOOL MikMod_EnableOutput(void)
+{
+ BOOL result;
+
+ MUTEX_LOCK(vars);
+ result=MikMod_EnableOutput_internal();
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+void MikMod_DisableOutput_internal(void)
+{
+ if(isplaying && md_driver) {
+ isplaying = 0;
+ md_driver->PlayStop();
+ }
+}
+
+MIKMODAPI void MikMod_DisableOutput(void)
+{
+ MUTEX_LOCK(vars);
+ MikMod_DisableOutput_internal();
+ MUTEX_UNLOCK(vars);
+}
+
+BOOL MikMod_Active_internal(void)
+{
+ return isplaying;
+}
+
+MIKMODAPI BOOL MikMod_Active(void)
+{
+ BOOL result;
+
+ MUTEX_LOCK(vars);
+ result=MikMod_Active_internal();
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+/* Plays a sound effects sample. Picks a voice from the number of voices
+ allocated for use as sound effects (loops through voices, skipping all active
+ criticals).
+
+ Returns the voice that the sound is being played on. */
+SBYTE Sample_Play_internal(SAMPLE *s,ULONG start,UBYTE flags)
+{
+ int orig=sfxpool;/* for cases where all channels are critical */
+ int c;
+
+ if(!md_sfxchn) return -1;
+ if(s->volume>64) s->volume = 64;
+
+ /* check the first location after sfxpool */
+ do {
+ if(sfxinfo[sfxpool]&SFX_CRITICAL) {
+ if(md_driver->VoiceStopped(c=sfxpool+md_sngchn)) {
+ sfxinfo[sfxpool]=flags;
+ Voice_Play_internal(c,s,start);
+ md_driver->VoiceSetVolume(c,s->volume<<2);
+ Voice_SetPanning_internal(c,s->panning);
+ md_driver->VoiceSetFrequency(c,s->speed);
+ sfxpool++;
+ if(sfxpool>=md_sfxchn) sfxpool=0;
+ return c;
+ }
+ } else {
+ sfxinfo[sfxpool]=flags;
+ Voice_Play_internal(c=sfxpool+md_sngchn,s,start);
+ md_driver->VoiceSetVolume(c,s->volume<<2);
+ Voice_SetPanning_internal(c,s->panning);
+ md_driver->VoiceSetFrequency(c,s->speed);
+ sfxpool++;
+ if(sfxpool>=md_sfxchn) sfxpool=0;
+ return c;
+ }
+
+ sfxpool++;
+ if(sfxpool>=md_sfxchn) sfxpool = 0;
+ } while(sfxpool!=orig);
+
+ return -1;
+}
+
+MIKMODAPI SBYTE Sample_Play(SAMPLE *s,ULONG start,UBYTE flags)
+{
+ SBYTE result;
+
+ MUTEX_LOCK(vars);
+ result=Sample_Play_internal(s,start,flags);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI long MikMod_GetVersion(void)
+{
+ return LIBMIKMOD_VERSION;
+}
+
+/*========== MT-safe stuff */
+
+#ifdef HAVE_PTHREAD
+#define INIT_MUTEX(name) \
+ pthread_mutex_t _mm_mutex_##name=PTHREAD_MUTEX_INITIALIZER
+#elif defined(__OS2__)||defined(__EMX__)
+#define INIT_MUTEX(name) \
+ HMTX _mm_mutex_##name
+#elif defined(WIN32)
+#define INIT_MUTEX(name) \
+ HANDLE _mm_mutex_##name
+#else
+#define INIT_MUTEX(name) \
+ void *_mm_mutex_##name = NULL
+#endif
+
+INIT_MUTEX(vars);
+INIT_MUTEX(lists);
+
+MIKMODAPI BOOL MikMod_InitThreads(void)
+{
+ static int firstcall=1;
+ static int result=0;
+
+ if (firstcall) {
+ firstcall=0;
+#ifdef HAVE_PTHREAD
+ result=1;
+#elif defined(__OS2__)||defined(__EMX__)
+ if(DosCreateMutexSem((PSZ)NULL,&_mm_mutex_lists,0,0) ||
+ DosCreateMutexSem((PSZ)NULL,&_mm_mutex_vars,0,0)) {
+ _mm_mutex_lists=_mm_mutex_vars=(HMTX)NULL;
+ result=0;
+ } else
+ result=1;
+#elif defined(WIN32)
+ if((!(_mm_mutex_lists=CreateMutex(NULL,FALSE,"libmikmod(lists)")))||
+ (!(_mm_mutex_vars=CreateMutex(NULL,FALSE,"libmikmod(vars)"))))
+ result=0;
+ else
+ result=1;
+#endif
+ }
+ return result;
+}
+
+MIKMODAPI void MikMod_Unlock(void)
+{
+ MUTEX_UNLOCK(lists);
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI void MikMod_Lock(void)
+{
+ MUTEX_LOCK(vars);
+ MUTEX_LOCK(lists);
+}
+
+/*========== Parameter extraction helper */
+
+CHAR *MD_GetAtom(CHAR *atomname,CHAR *cmdline,BOOL implicit)
+{
+ CHAR *ret=NULL;
+
+ if(cmdline) {
+ CHAR *buf=strstr(cmdline,atomname);
+
+ if((buf)&&((buf==cmdline)||(*(buf-1)==','))) {
+ CHAR *ptr=buf+strlen(atomname);
+
+ if(*ptr=='=') {
+ for(buf=++ptr;(*ptr)&&((*ptr)!=',');ptr++);
+ ret=MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
+ if(ret)
+ strncpy(ret,buf,ptr-buf);
+ } else if((*ptr==',')||(!*ptr)) {
+ if(implicit) {
+ ret=MikMod_malloc((1+ptr-buf)*sizeof(CHAR));
+ if(ret)
+ strncpy(ret,buf,ptr-buf);
+ }
+ }
+ }
+ }
+ return ret;
+}
+
+#if defined unix || (defined __APPLE__ && defined __MACH__)
+
+/*========== Posix helper functions */
+
+/* Check if the file is a regular or nonexistant file (or a link to a such a
+ file), and that, should the calling program be setuid, the access rights are
+ reasonable. Returns 1 if it is safe to rewrite the file, 0 otherwise.
+ The goal is to prevent a setuid root libmikmod application from overriding
+ files like /etc/passwd with digital sound... */
+BOOL MD_Access(CHAR *filename)
+{
+ struct stat buf;
+
+ if(!stat(filename,&buf)) {
+ /* not a regular file ? */
+ if(!S_ISREG(buf.st_mode)) return 0;
+ /* more than one hard link to the file ? */
+ if(buf.st_nlink>1) return 0;
+ /* check access rights with the real user and group id */
+ if(getuid()==buf.st_uid) {
+ if(!(buf.st_mode&S_IWUSR)) return 0;
+ } else if(getgid()==buf.st_gid) {
+ if(!(buf.st_mode&S_IWGRP)) return 0;
+ } else
+ if(!(buf.st_mode&S_IWOTH)) return 0;
+ }
+
+ return 1;
+}
+
+/* Drop all root privileges we might have */
+BOOL MD_DropPrivileges(void)
+{
+ if(!geteuid()) {
+ if(getuid()) {
+ /* we are setuid root -> drop setuid to become the real user */
+ if(setuid(getuid())) return 1;
+ } else {
+ /* we are run as root -> drop all and become user 'nobody' */
+ struct passwd *nobody;
+ int uid;
+
+ if(!(nobody=getpwnam("nobody"))) return 1; /* no such user ? */
+ uid=nobody->pw_uid;
+ if (!uid) /* user 'nobody' has root privileges ? weird... */
+ return 1;
+ if (setuid(uid)) return 1;
+ }
+ }
+ return 0;
+}
+
+#endif
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mikmod.h b/src/libs/mikmod/mikmod.h
new file mode 100644
index 0000000..1fd5322
--- /dev/null
+++ b/src/libs/mikmod/mikmod.h
@@ -0,0 +1,730 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ MikMod sound library include file
+
+==============================================================================*/
+
+#ifndef LIBS_MIKMOD_MIKMOD_H_
+#define LIBS_MIKMOD_MIKMOD_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * ========== Compiler magic for shared libraries
+ */
+
+#if defined(LIBMIKMOD_DLL)
+#if defined(DLL_EXPORTS)
+#define MIKMODAPI __declspec(dllexport)
+#else
+#define MIKMODAPI __declspec(dllimport)
+#endif
+#else
+#define MIKMODAPI
+#endif
+
+/*
+ * ========== Library version
+ */
+
+#define LIBMIKMOD_VERSION_MAJOR 3L
+#define LIBMIKMOD_VERSION_MINOR 1L
+#define LIBMIKMOD_REVISION 12L
+
+#define LIBMIKMOD_VERSION \
+ ((LIBMIKMOD_VERSION_MAJOR<<16)| \
+ (LIBMIKMOD_VERSION_MINOR<< 8)| \
+ (LIBMIKMOD_REVISION))
+
+MIKMODAPI extern long MikMod_GetVersion(void);
+
+/*
+ * ========== Platform independent-type definitions
+ */
+
+#if defined(WIN32)||defined(WIN64)
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <io.h>
+#include <mmsystem.h>
+#endif
+
+#if defined(__OS2__)||defined(__EMX__)
+#define INCL_DOSSEMAPHORES
+#include <os2.h>
+#else
+typedef char CHAR;
+#endif
+
+
+
+#if defined(__arch64__) || defined(__alpha) || defined(__x86_64) \
+ || defined(__powerpc64__) || defined(_M_IA64) || defined(_M_AMD64)
+/* 64 bit architectures */
+
+typedef signed char SBYTE; /* 1 byte, signed */
+typedef unsigned char UBYTE; /* 1 byte, unsigned */
+typedef signed short SWORD; /* 2 bytes, signed */
+typedef unsigned short UWORD; /* 2 bytes, unsigned */
+typedef signed int SLONG; /* 4 bytes, signed */
+typedef unsigned int ULONG; /* 4 bytes, unsigned */
+typedef int BOOL; /* 0=false, <>0 true */
+
+#else
+/* 32 bit architectures */
+
+typedef signed char SBYTE; /* 1 byte, signed */
+typedef unsigned char UBYTE; /* 1 byte, unsigned */
+typedef signed short SWORD; /* 2 bytes, signed */
+typedef unsigned short UWORD; /* 2 bytes, unsigned */
+typedef signed long SLONG; /* 4 bytes, signed */
+#if !defined(__OS2__)&&!defined(__EMX__)&&!defined(WIN32)
+typedef unsigned long ULONG; /* 4 bytes, unsigned */
+typedef int BOOL; /* 0=false, <>0 true */
+#endif
+#endif
+
+/*
+ * ========== Error codes
+ */
+
+enum {
+ MMERR_OPENING_FILE = 1,
+ MMERR_OUT_OF_MEMORY,
+ MMERR_DYNAMIC_LINKING,
+
+ MMERR_SAMPLE_TOO_BIG,
+ MMERR_OUT_OF_HANDLES,
+ MMERR_UNKNOWN_WAVE_TYPE,
+
+ MMERR_LOADING_PATTERN,
+ MMERR_LOADING_TRACK,
+ MMERR_LOADING_HEADER,
+ MMERR_LOADING_SAMPLEINFO,
+ MMERR_NOT_A_MODULE,
+ MMERR_NOT_A_STREAM,
+ MMERR_MED_SYNTHSAMPLES,
+ MMERR_ITPACK_INVALID_DATA,
+
+ MMERR_DETECTING_DEVICE,
+ MMERR_INVALID_DEVICE,
+ MMERR_INITIALIZING_MIXER,
+ MMERR_OPENING_AUDIO,
+ MMERR_8BIT_ONLY,
+ MMERR_16BIT_ONLY,
+ MMERR_STEREO_ONLY,
+ MMERR_ULAW,
+ MMERR_NON_BLOCK,
+
+ MMERR_AF_AUDIO_PORT,
+
+ MMERR_AIX_CONFIG_INIT,
+ MMERR_AIX_CONFIG_CONTROL,
+ MMERR_AIX_CONFIG_START,
+
+ MMERR_GUS_SETTINGS,
+ MMERR_GUS_RESET,
+ MMERR_GUS_TIMER,
+
+ MMERR_HP_SETSAMPLESIZE,
+ MMERR_HP_SETSPEED,
+ MMERR_HP_CHANNELS,
+ MMERR_HP_AUDIO_OUTPUT,
+ MMERR_HP_AUDIO_DESC,
+ MMERR_HP_BUFFERSIZE,
+
+ MMERR_OSS_SETFRAGMENT,
+ MMERR_OSS_SETSAMPLESIZE,
+ MMERR_OSS_SETSTEREO,
+ MMERR_OSS_SETSPEED,
+
+ MMERR_SGI_SPEED,
+ MMERR_SGI_16BIT,
+ MMERR_SGI_8BIT,
+ MMERR_SGI_STEREO,
+ MMERR_SGI_MONO,
+
+ MMERR_SUN_INIT,
+
+ MMERR_OS2_MIXSETUP,
+ MMERR_OS2_SEMAPHORE,
+ MMERR_OS2_TIMER,
+ MMERR_OS2_THREAD,
+
+ MMERR_DS_PRIORITY,
+ MMERR_DS_BUFFER,
+ MMERR_DS_FORMAT,
+ MMERR_DS_NOTIFY,
+ MMERR_DS_EVENT,
+ MMERR_DS_THREAD,
+ MMERR_DS_UPDATE,
+
+ MMERR_WINMM_HANDLE,
+ MMERR_WINMM_ALLOCATED,
+ MMERR_WINMM_DEVICEID,
+ MMERR_WINMM_FORMAT,
+ MMERR_WINMM_UNKNOWN,
+
+ MMERR_MAC_SPEED,
+ MMERR_MAC_START,
+
+ MMERR_MAX
+};
+
+/*
+ * ========== Error handling
+ */
+
+typedef void (MikMod_handler)(void);
+typedef MikMod_handler *MikMod_handler_t;
+
+MIKMODAPI extern int MikMod_errno;
+MIKMODAPI extern BOOL MikMod_critical;
+MIKMODAPI extern char *MikMod_strerror(int);
+
+MIKMODAPI extern MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t);
+
+/*
+ * ========== Library initialization and core functions
+ */
+
+struct MDRIVER;
+
+MIKMODAPI extern void MikMod_RegisterAllDrivers(void);
+
+MIKMODAPI extern CHAR* MikMod_InfoDriver(void);
+MIKMODAPI extern void MikMod_RegisterDriver(struct MDRIVER*);
+MIKMODAPI extern int MikMod_DriverFromAlias(CHAR*);
+
+MIKMODAPI extern BOOL MikMod_Init(CHAR*);
+MIKMODAPI extern void MikMod_Exit(void);
+MIKMODAPI extern BOOL MikMod_Reset(CHAR*);
+MIKMODAPI extern BOOL MikMod_SetNumVoices(int,int);
+MIKMODAPI extern BOOL MikMod_Active(void);
+MIKMODAPI extern BOOL MikMod_EnableOutput(void);
+MIKMODAPI extern void MikMod_DisableOutput(void);
+MIKMODAPI extern void MikMod_Update(void);
+
+MIKMODAPI extern BOOL MikMod_InitThreads(void);
+MIKMODAPI extern void MikMod_Lock(void);
+MIKMODAPI extern void MikMod_Unlock(void);
+
+MIKMODAPI extern void* MikMod_malloc(size_t);
+MIKMODAPI extern void* MikMod_realloc(void *, size_t);
+MIKMODAPI extern void* MikMod_calloc(size_t,size_t);
+MIKMODAPI extern void MikMod_free(void *);
+
+/*
+ * ========== Reader, Writer
+ */
+
+typedef struct MREADER {
+ BOOL (*Seek)(struct MREADER*,long,int);
+ long (*Tell)(struct MREADER*);
+ BOOL (*Read)(struct MREADER*,void*,size_t);
+ int (*Get)(struct MREADER*);
+ BOOL (*Eof)(struct MREADER*);
+ long iobase;
+ long prev_iobase;
+} MREADER;
+
+typedef struct MWRITER {
+ BOOL (*Seek)(struct MWRITER*,long,int);
+ long (*Tell)(struct MWRITER*);
+ BOOL (*Write)(struct MWRITER*,void*,size_t);
+ BOOL (*Put)(struct MWRITER*,int);
+} MWRITER;
+
+/*
+ * ========== Samples
+ */
+
+/* Sample playback should not be interrupted */
+#define SFX_CRITICAL 1
+
+/* Sample format [loading and in-memory] flags: */
+#define SF_16BITS 0x0001
+#define SF_STEREO 0x0002
+#define SF_SIGNED 0x0004
+#define SF_BIG_ENDIAN 0x0008
+#define SF_DELTA 0x0010
+#define SF_ITPACKED 0x0020
+
+#define SF_FORMATMASK 0x003F
+
+/* General Playback flags */
+
+#define SF_LOOP 0x0100
+#define SF_BIDI 0x0200
+#define SF_REVERSE 0x0400
+#define SF_SUSTAIN 0x0800
+
+#define SF_PLAYBACKMASK 0x0C00
+
+/* Module-only Playback Flags */
+
+#define SF_OWNPAN 0x1000
+#define SF_UST_LOOP 0x2000
+
+#define SF_EXTRAPLAYBACKMASK 0x3000
+
+/* Panning constants */
+#define PAN_LEFT 0
+#define PAN_HALFLEFT 64
+#define PAN_CENTER 128
+#define PAN_HALFRIGHT 192
+#define PAN_RIGHT 255
+#define PAN_SURROUND 512 /* panning value for Dolby Surround */
+
+typedef struct SAMPLE {
+ SWORD panning; /* panning (0-255 or PAN_SURROUND) */
+ ULONG speed; /* Base playing speed/frequency of note */
+ UBYTE volume; /* volume 0-64 */
+ UWORD inflags; /* sample format on disk */
+ UWORD flags; /* sample format in memory */
+ ULONG length; /* length of sample (in samples!) */
+ ULONG loopstart; /* repeat position (relative to start, in samples) */
+ ULONG loopend; /* repeat end */
+ ULONG susbegin; /* sustain loop begin (in samples) \ Not Supported */
+ ULONG susend; /* sustain loop end / Yet! */
+
+ /* Variables used by the module player only! (ignored for sound effects) */
+ UBYTE globvol; /* global volume */
+ UBYTE vibflags; /* autovibrato flag stuffs */
+ UBYTE vibtype; /* Vibratos moved from INSTRUMENT to SAMPLE */
+ UBYTE vibsweep;
+ UBYTE vibdepth;
+ UBYTE vibrate;
+ CHAR* samplename; /* name of the sample */
+
+ /* Values used internally only */
+ UWORD avibpos; /* autovibrato pos [player use] */
+ UBYTE divfactor; /* for sample scaling, maintains proper period slides */
+ ULONG seekpos; /* seek position in file */
+ SWORD handle; /* sample handle used by individual drivers */
+} SAMPLE;
+
+/* Sample functions */
+
+MIKMODAPI extern SAMPLE *Sample_Load(CHAR*);
+MIKMODAPI extern SAMPLE *Sample_LoadFP(FILE*);
+MIKMODAPI extern SAMPLE *Sample_LoadMem(const char *buf, int len);
+MIKMODAPI extern SAMPLE *Sample_LoadGeneric(MREADER*);
+MIKMODAPI extern void Sample_Free(SAMPLE*);
+MIKMODAPI extern SBYTE Sample_Play(SAMPLE*,ULONG,UBYTE);
+
+MIKMODAPI extern void Voice_SetVolume(SBYTE,UWORD);
+MIKMODAPI extern UWORD Voice_GetVolume(SBYTE);
+MIKMODAPI extern void Voice_SetFrequency(SBYTE,ULONG);
+MIKMODAPI extern ULONG Voice_GetFrequency(SBYTE);
+MIKMODAPI extern void Voice_SetPanning(SBYTE,ULONG);
+MIKMODAPI extern ULONG Voice_GetPanning(SBYTE);
+MIKMODAPI extern void Voice_Play(SBYTE,SAMPLE*,ULONG);
+MIKMODAPI extern void Voice_Stop(SBYTE);
+MIKMODAPI extern BOOL Voice_Stopped(SBYTE);
+MIKMODAPI extern SLONG Voice_GetPosition(SBYTE);
+MIKMODAPI extern ULONG Voice_RealVolume(SBYTE);
+
+/*
+ * ========== Internal module representation (UniMod)
+ */
+
+/*
+ Instrument definition - for information only, the only field which may be
+ of use in user programs is the name field
+*/
+
+/* Instrument note count */
+#define INSTNOTES 120
+
+/* Envelope point */
+typedef struct ENVPT {
+ SWORD pos;
+ SWORD val;
+} ENVPT;
+
+/* Envelope point count */
+#define ENVPOINTS 32
+
+/* Instrument structure */
+typedef struct INSTRUMENT {
+ CHAR* insname;
+
+ UBYTE flags;
+ UWORD samplenumber[INSTNOTES];
+ UBYTE samplenote[INSTNOTES];
+
+ UBYTE nnatype;
+ UBYTE dca; /* duplicate check action */
+ UBYTE dct; /* duplicate check type */
+ UBYTE globvol;
+ UWORD volfade;
+ SWORD panning; /* instrument-based panning var */
+
+ UBYTE pitpansep; /* pitch pan separation (0 to 255) */
+ UBYTE pitpancenter; /* pitch pan center (0 to 119) */
+ UBYTE rvolvar; /* random volume varations (0 - 100%) */
+ UBYTE rpanvar; /* random panning varations (0 - 100%) */
+
+ /* volume envelope */
+ UBYTE volflg; /* bit 0: on 1: sustain 2: loop */
+ UBYTE volpts;
+ UBYTE volsusbeg;
+ UBYTE volsusend;
+ UBYTE volbeg;
+ UBYTE volend;
+ ENVPT volenv[ENVPOINTS];
+ /* panning envelope */
+ UBYTE panflg; /* bit 0: on 1: sustain 2: loop */
+ UBYTE panpts;
+ UBYTE pansusbeg;
+ UBYTE pansusend;
+ UBYTE panbeg;
+ UBYTE panend;
+ ENVPT panenv[ENVPOINTS];
+ /* pitch envelope */
+ UBYTE pitflg; /* bit 0: on 1: sustain 2: loop */
+ UBYTE pitpts;
+ UBYTE pitsusbeg;
+ UBYTE pitsusend;
+ UBYTE pitbeg;
+ UBYTE pitend;
+ ENVPT pitenv[ENVPOINTS];
+} INSTRUMENT;
+
+struct MP_CONTROL;
+struct MP_VOICE;
+
+/*
+ Module definition
+*/
+
+/* maximum master channels supported */
+#define UF_MAXCHAN 64
+
+/* Module flags */
+#define UF_XMPERIODS 0x0001 /* XM periods / finetuning */
+#define UF_LINEAR 0x0002 /* LINEAR periods (UF_XMPERIODS must be set) */
+#define UF_INST 0x0004 /* Instruments are used */
+#define UF_NNA 0x0008 /* IT: NNA used, set numvoices rather
+ than numchn */
+#define UF_S3MSLIDES 0x0010 /* uses old S3M volume slides */
+#define UF_BGSLIDES 0x0020 /* continue volume slides in the background */
+#define UF_HIGHBPM 0x0040 /* MED: can use >255 bpm */
+#define UF_NOWRAP 0x0080 /* XM-type (i.e. illogical) pattern break
+ semantics */
+#define UF_ARPMEM 0x0100 /* IT: need arpeggio memory */
+#define UF_FT2QUIRKS 0x0200 /* emulate some FT2 replay quirks */
+#define UF_PANNING 0x0400 /* module uses panning effects or have
+ non-tracker default initial panning */
+
+typedef struct MODULE {
+ /* general module information */
+ CHAR* songname; /* name of the song */
+ CHAR* modtype; /* string type of module loaded */
+ CHAR* comment; /* module comments */
+
+ UWORD flags; /* See module flags above */
+ UBYTE numchn; /* number of module channels */
+ UBYTE numvoices; /* max # voices used for full NNA playback */
+ UWORD numpos; /* number of positions in this song */
+ UWORD numpat; /* number of patterns in this song */
+ UWORD numins; /* number of instruments */
+ UWORD numsmp; /* number of samples */
+struct INSTRUMENT* instruments; /* all instruments */
+struct SAMPLE* samples; /* all samples */
+ UBYTE realchn; /* real number of channels used */
+ UBYTE totalchn; /* total number of channels used (incl NNAs) */
+
+ /* playback settings */
+ UWORD reppos; /* restart position */
+ UBYTE initspeed; /* initial song speed */
+ UWORD inittempo; /* initial song tempo */
+ UBYTE initvolume; /* initial global volume (0 - 128) */
+ UWORD panning[UF_MAXCHAN]; /* panning positions */
+ UBYTE chanvol[UF_MAXCHAN]; /* channel positions */
+ UWORD bpm; /* current beats-per-minute speed */
+ UWORD sngspd; /* current song speed */
+ SWORD volume; /* song volume (0-128) (or user volume) */
+
+ BOOL extspd; /* extended speed flag (default enabled) */
+ BOOL panflag; /* panning flag (default enabled) */
+ BOOL wrap; /* wrap module ? (default disabled) */
+ BOOL loop; /* allow module to loop ? (default enabled) */
+ BOOL fadeout; /* volume fade out during last pattern */
+
+ UWORD patpos; /* current row number */
+ SWORD sngpos; /* current song position */
+ ULONG sngtime; /* current song time in 2^-10 seconds */
+
+ SWORD relspd; /* relative speed factor */
+
+ /* internal module representation */
+ UWORD numtrk; /* number of tracks */
+ UBYTE** tracks; /* array of numtrk pointers to tracks */
+ UWORD* patterns; /* array of Patterns */
+ UWORD* pattrows; /* array of number of rows for each pattern */
+ UWORD* positions; /* all positions */
+
+ BOOL forbid; /* if true, no player update! */
+ UWORD numrow; /* number of rows on current pattern */
+ UWORD vbtick; /* tick counter (counts from 0 to sngspd) */
+ UWORD sngremainder;/* used for song time computation */
+
+struct MP_CONTROL* control; /* Effects Channel info (size pf->numchn) */
+struct MP_VOICE* voice; /* Audio Voice information (size md_numchn) */
+
+ UBYTE globalslide; /* global volume slide rate */
+ UBYTE pat_repcrazy;/* module has just looped to position -1 */
+ UWORD patbrk; /* position where to start a new pattern */
+ UBYTE patdly; /* patterndelay counter (command memory) */
+ UBYTE patdly2; /* patterndelay counter (real one) */
+ SWORD posjmp; /* flag to indicate a jump is needed... */
+ UWORD bpmlimit; /* threshold to detect bpm or speed values */
+} MODULE;
+
+/*
+ * ========== Module loaders
+ */
+
+struct MLOADER;
+
+MIKMODAPI extern CHAR* MikMod_InfoLoader(void);
+MIKMODAPI extern void MikMod_RegisterAllLoaders(void);
+MIKMODAPI extern void MikMod_RegisterLoader(struct MLOADER*);
+
+MIKMODAPI extern struct MLOADER load_669; /* 669 and Extended-669 (by Tran/Renaissance) */
+MIKMODAPI extern struct MLOADER load_amf; /* DMP Advanced Module Format (by Otto Chrons) */
+MIKMODAPI extern struct MLOADER load_dsm; /* DSIK internal module format */
+MIKMODAPI extern struct MLOADER load_far; /* Farandole Composer (by Daniel Potter) */
+MIKMODAPI extern struct MLOADER load_gdm; /* General DigiMusic (by Edward Schlunder) */
+MIKMODAPI extern struct MLOADER load_it; /* Impulse Tracker (by Jeffrey Lim) */
+MIKMODAPI extern struct MLOADER load_imf; /* Imago Orpheus (by Lutz Roeder) */
+MIKMODAPI extern struct MLOADER load_med; /* Amiga MED modules (by Teijo Kinnunen) */
+MIKMODAPI extern struct MLOADER load_m15; /* Soundtracker 15-instrument */
+MIKMODAPI extern struct MLOADER load_mod; /* Standard 31-instrument Module loader */
+MIKMODAPI extern struct MLOADER load_mtm; /* Multi-Tracker Module (by Renaissance) */
+MIKMODAPI extern struct MLOADER load_okt; /* Amiga Oktalyzer */
+MIKMODAPI extern struct MLOADER load_stm; /* ScreamTracker 2 (by Future Crew) */
+MIKMODAPI extern struct MLOADER load_stx; /* STMIK 0.2 (by Future Crew) */
+MIKMODAPI extern struct MLOADER load_s3m; /* ScreamTracker 3 (by Future Crew) */
+MIKMODAPI extern struct MLOADER load_ult; /* UltraTracker (by MAS) */
+MIKMODAPI extern struct MLOADER load_uni; /* MikMod and APlayer internal module format */
+MIKMODAPI extern struct MLOADER load_xm; /* FastTracker 2 (by Triton) */
+
+/*
+ * ========== Module player
+ */
+
+MIKMODAPI extern MODULE* Player_Load(CHAR*,int,BOOL);
+MIKMODAPI extern MODULE* Player_LoadFP(FILE*,int,BOOL);
+MIKMODAPI extern MODULE* Player_LoadMem(const char *buffer,int len,int maxchan,BOOL curious);
+MIKMODAPI extern MODULE* Player_LoadGeneric(MREADER*,int,BOOL);
+MIKMODAPI extern CHAR* Player_LoadTitle(CHAR*);
+MIKMODAPI extern CHAR* Player_LoadTitleFP(FILE*);
+MIKMODAPI extern CHAR* Player_LoadTitleMem(const char *buffer,int len);
+MIKMODAPI extern CHAR* Player_LoadTitleGeneric(MREADER*);
+
+MIKMODAPI extern void Player_Free(MODULE*);
+MIKMODAPI extern void Player_Start(MODULE*);
+MIKMODAPI extern BOOL Player_Active(void);
+MIKMODAPI extern void Player_Stop(void);
+MIKMODAPI extern void Player_TogglePause(void);
+MIKMODAPI extern BOOL Player_Paused(void);
+MIKMODAPI extern void Player_NextPosition(void);
+MIKMODAPI extern void Player_PrevPosition(void);
+MIKMODAPI extern void Player_SetPosition(UWORD);
+MIKMODAPI extern BOOL Player_Muted(UBYTE);
+MIKMODAPI extern void Player_SetVolume(SWORD);
+MIKMODAPI extern MODULE* Player_GetModule(void);
+MIKMODAPI extern void Player_SetSpeed(UWORD);
+MIKMODAPI extern void Player_SetTempo(UWORD);
+MIKMODAPI extern void Player_Unmute(SLONG,...);
+MIKMODAPI extern void Player_Mute(SLONG,...);
+MIKMODAPI extern void Player_ToggleMute(SLONG,...);
+MIKMODAPI extern int Player_GetChannelVoice(UBYTE);
+MIKMODAPI extern UWORD Player_GetChannelPeriod(UBYTE);
+
+typedef void (MikMod_player)(void);
+typedef MikMod_player *MikMod_player_t;
+
+MIKMODAPI extern MikMod_player_t MikMod_RegisterPlayer(MikMod_player_t);
+
+#define MUTE_EXCLUSIVE 32000
+#define MUTE_INCLUSIVE 32001
+
+/*
+ * ========== Drivers
+ */
+
+enum {
+ MD_MUSIC = 0,
+ MD_SNDFX
+};
+
+enum {
+ MD_HARDWARE = 0,
+ MD_SOFTWARE
+};
+
+/* Mixing flags */
+
+/* These ones take effect only after MikMod_Init or MikMod_Reset */
+#define DMODE_16BITS 0x0001 /* enable 16 bit output */
+#define DMODE_STEREO 0x0002 /* enable stereo output */
+#define DMODE_SOFT_SNDFX 0x0004 /* Process sound effects via software mixer */
+#define DMODE_SOFT_MUSIC 0x0008 /* Process music via software mixer */
+#define DMODE_HQMIXER 0x0010 /* Use high-quality (slower) software mixer */
+/* These take effect immediately. */
+#define DMODE_SURROUND 0x0100 /* enable surround sound */
+#define DMODE_INTERP 0x0200 /* enable interpolation */
+#define DMODE_REVERSE 0x0400 /* reverse stereo */
+
+struct SAMPLOAD;
+typedef struct MDRIVER {
+struct MDRIVER* next;
+ CHAR* Name;
+ CHAR* Version;
+
+ UBYTE HardVoiceLimit; /* Limit of hardware mixer voices */
+ UBYTE SoftVoiceLimit; /* Limit of software mixer voices */
+
+ CHAR* Alias;
+
+ void (*CommandLine) (CHAR*);
+ BOOL (*IsPresent) (void);
+ SWORD (*SampleLoad) (struct SAMPLOAD*,int);
+ void (*SampleUnload) (SWORD);
+ ULONG (*FreeSampleSpace) (int);
+ ULONG (*RealSampleLength) (int,struct SAMPLE*);
+ BOOL (*Init) (void);
+ void (*Exit) (void);
+ BOOL (*Reset) (void);
+ BOOL (*SetNumVoices) (void);
+ BOOL (*PlayStart) (void);
+ void (*PlayStop) (void);
+ void (*Update) (void);
+ void (*Pause) (void);
+ void (*VoiceSetVolume) (UBYTE,UWORD);
+ UWORD (*VoiceGetVolume) (UBYTE);
+ void (*VoiceSetFrequency)(UBYTE,ULONG);
+ ULONG (*VoiceGetFrequency)(UBYTE);
+ void (*VoiceSetPanning) (UBYTE,ULONG);
+ ULONG (*VoiceGetPanning) (UBYTE);
+ void (*VoicePlay) (UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD);
+ void (*VoiceStop) (UBYTE);
+ BOOL (*VoiceStopped) (UBYTE);
+ SLONG (*VoiceGetPosition) (UBYTE);
+ ULONG (*VoiceRealVolume) (UBYTE);
+} MDRIVER;
+
+/* These variables can be changed at ANY time and results will be immediate */
+MIKMODAPI extern UBYTE md_volume; /* global sound volume (0-128) */
+MIKMODAPI extern UBYTE md_musicvolume; /* volume of song */
+MIKMODAPI extern UBYTE md_sndfxvolume; /* volume of sound effects */
+MIKMODAPI extern UBYTE md_reverb; /* 0 = none; 15 = chaos */
+MIKMODAPI extern UBYTE md_pansep; /* 0 = mono; 128 == 100% (full left/right) */
+
+/* The variables below can be changed at any time, but changes will not be
+ implemented until MikMod_Reset is called. A call to MikMod_Reset may result
+ in a skip or pop in audio (depending on the soundcard driver and the settings
+ changed). */
+MIKMODAPI extern UWORD md_device; /* device */
+MIKMODAPI extern UWORD md_mixfreq; /* mixing frequency */
+MIKMODAPI extern UWORD md_mode; /* mode. See DMODE_? flags above */
+
+/* The following variable should not be changed! */
+MIKMODAPI extern MDRIVER* md_driver; /* Current driver in use. */
+
+/* Known drivers list */
+
+MIKMODAPI extern struct MDRIVER drv_nos; /* no sound */
+MIKMODAPI extern struct MDRIVER drv_pipe; /* piped output */
+MIKMODAPI extern struct MDRIVER drv_raw; /* raw file disk writer [music.raw] */
+MIKMODAPI extern struct MDRIVER drv_stdout; /* output to stdout */
+MIKMODAPI extern struct MDRIVER drv_wav; /* RIFF WAVE file disk writer [music.wav] */
+
+MIKMODAPI extern struct MDRIVER drv_ultra; /* Linux Ultrasound driver */
+MIKMODAPI extern struct MDRIVER drv_sam9407; /* Linux sam9407 driver */
+
+MIKMODAPI extern struct MDRIVER drv_AF; /* Dec Alpha AudioFile */
+MIKMODAPI extern struct MDRIVER drv_aix; /* AIX audio device */
+MIKMODAPI extern struct MDRIVER drv_alsa; /* Advanced Linux Sound Architecture (ALSA) */
+MIKMODAPI extern struct MDRIVER drv_esd; /* Enlightened sound daemon (EsounD) */
+MIKMODAPI extern struct MDRIVER drv_hp; /* HP-UX audio device */
+MIKMODAPI extern struct MDRIVER drv_oss; /* OpenSound System (Linux,FreeBSD...) */
+MIKMODAPI extern struct MDRIVER drv_sgi; /* SGI audio library */
+MIKMODAPI extern struct MDRIVER drv_sun; /* Sun/NetBSD/OpenBSD audio device */
+
+MIKMODAPI extern struct MDRIVER drv_dart; /* OS/2 Direct Audio RealTime */
+MIKMODAPI extern struct MDRIVER drv_os2; /* OS/2 MMPM/2 */
+
+MIKMODAPI extern struct MDRIVER drv_ds; /* Win32 DirectSound driver */
+MIKMODAPI extern struct MDRIVER drv_win; /* Win32 multimedia API driver */
+
+MIKMODAPI extern struct MDRIVER drv_mac; /* Macintosh Sound Manager driver */
+
+/*========== Virtual channel mixer interface (for user-supplied drivers only) */
+
+MIKMODAPI extern BOOL VC_Init(void);
+MIKMODAPI extern void VC_Exit(void);
+MIKMODAPI extern BOOL VC_SetNumVoices(void);
+MIKMODAPI extern ULONG VC_SampleSpace(int);
+MIKMODAPI extern ULONG VC_SampleLength(int,SAMPLE*);
+
+MIKMODAPI extern BOOL VC_PlayStart(void);
+MIKMODAPI extern void VC_PlayStop(void);
+
+MIKMODAPI extern SWORD VC_SampleLoad(struct SAMPLOAD*,int);
+MIKMODAPI extern void VC_SampleUnload(SWORD);
+
+MIKMODAPI extern ULONG VC_WriteBytes(SBYTE*,ULONG);
+MIKMODAPI extern ULONG VC_SilenceBytes(SBYTE*,ULONG);
+
+MIKMODAPI extern void VC_VoiceSetVolume(UBYTE,UWORD);
+MIKMODAPI extern UWORD VC_VoiceGetVolume(UBYTE);
+MIKMODAPI extern void VC_VoiceSetFrequency(UBYTE,ULONG);
+MIKMODAPI extern ULONG VC_VoiceGetFrequency(UBYTE);
+MIKMODAPI extern void VC_VoiceSetPanning(UBYTE,ULONG);
+MIKMODAPI extern ULONG VC_VoiceGetPanning(UBYTE);
+MIKMODAPI extern void VC_VoicePlay(UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD);
+
+MIKMODAPI extern void VC_VoiceStop(UBYTE);
+MIKMODAPI extern BOOL VC_VoiceStopped(UBYTE);
+MIKMODAPI extern SLONG VC_VoiceGetPosition(UBYTE);
+MIKMODAPI extern ULONG VC_VoiceRealVolume(UBYTE);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mikmod_build.h b/src/libs/mikmod/mikmod_build.h
new file mode 100644
index 0000000..29fe3b4
--- /dev/null
+++ b/src/libs/mikmod/mikmod_build.h
@@ -0,0 +1,9 @@
+#include "mikmod.h"
+
+#if defined(WIN32) && !defined(__STDC__)
+# define __STDC__ 1
+#endif
+
+#if defined(WIN32) && defined(_MSC_VER)
+# pragma warning(disable: 4018 4244)
+#endif
diff --git a/src/libs/mikmod/mikmod_internals.h b/src/libs/mikmod/mikmod_internals.h
new file mode 100644
index 0000000..f84c4b7
--- /dev/null
+++ b/src/libs/mikmod/mikmod_internals.h
@@ -0,0 +1,679 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ MikMod sound library internal definitions
+
+==============================================================================*/
+
+#ifndef LIBS_MIKMOD_MIKMOD_INTERNALS_H_
+#define LIBS_MIKMOD_MIKMOD_INTERNALS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#if defined(__OS2__)||defined(__EMX__)||defined(WIN32)||defined(WIN64)
+#define strcasecmp(s,t) stricmp(s,t)
+#endif
+
+#define MIKMOD_INTERNAL
+#include "mikmod_build.h"
+
+/*========== More type definitions */
+
+/* SLONGLONG: 64bit, signed */
+#if defined(__arch64__) || defined(__alpha) || defined(__x86_64) \
+ || defined(_M_IA64) || defined(_M_AMD64)
+typedef long SLONGLONG;
+#define NATIVE_64BIT_INT
+#elif defined(__powerpc64__)
+typedef long long SLONGLONG;
+#define NATIVE_64BIT_INT
+#elif defined(__WATCOMC__)
+typedef __int64 SLONGLONG;
+#elif defined(WIN32) && !defined(__MWERKS__)
+typedef LONGLONG SLONGLONG;
+#elif macintosh && !TYPE_LONGLONG
+#include <Types.h>
+typedef SInt64 SLONGLONG;
+#else
+typedef long long SLONGLONG;
+#endif
+
+/*========== Error handling */
+
+#define _mm_errno MikMod_errno
+#define _mm_critical MikMod_critical
+extern MikMod_handler_t _mm_errorhandler;
+
+/*========== MT stuff */
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#define DECLARE_MUTEX(name) \
+ extern pthread_mutex_t _mm_mutex_##name
+#define MUTEX_LOCK(name) \
+ pthread_mutex_lock(&_mm_mutex_##name)
+#define MUTEX_UNLOCK(name) \
+ pthread_mutex_unlock(&_mm_mutex_##name)
+#elif defined(__OS2__)||defined(__EMX__)
+#define DECLARE_MUTEX(name) \
+ extern HMTX _mm_mutex_##name
+#define MUTEX_LOCK(name) \
+ if(_mm_mutex_##name) \
+ DosRequestMutexSem(_mm_mutex_##name,SEM_INDEFINITE_WAIT)
+#define MUTEX_UNLOCK(name) \
+ if(_mm_mutex_##name) \
+ DosReleaseMutexSem(_mm_mutex_##name)
+#elif defined(WIN32) || defined(WIN64)
+#include <windows.h>
+#define DECLARE_MUTEX(name) \
+ extern HANDLE _mm_mutex_##name
+#define MUTEX_LOCK(name) \
+ if(_mm_mutex_##name) \
+ WaitForSingleObject(_mm_mutex_##name,INFINITE)
+#define MUTEX_UNLOCK(name) \
+ if(_mm_mutex_##name) \
+ ReleaseMutex(_mm_mutex_##name)
+#else
+#define DECLARE_MUTEX(name) \
+ extern void *_mm_mutex_##name
+#define MUTEX_LOCK(name)
+#define MUTEX_UNLOCK(name)
+#endif
+
+DECLARE_MUTEX(lists);
+DECLARE_MUTEX(vars);
+
+/*========== Portable file I/O */
+
+extern MREADER* _mm_new_mem_reader(const void *buffer, int len);
+extern void _mm_delete_mem_reader(MREADER *reader);
+
+extern MREADER* _mm_new_file_reader(FILE* fp);
+extern void _mm_delete_file_reader(MREADER*);
+
+extern MWRITER* _mm_new_file_writer(FILE *fp);
+extern void _mm_delete_file_writer(MWRITER*);
+
+extern BOOL _mm_FileExists(CHAR *fname);
+
+#define _mm_write_SBYTE(x,y) y->Put(y,(int)x)
+#define _mm_write_UBYTE(x,y) y->Put(y,(int)x)
+
+#define _mm_read_SBYTE(x) (SBYTE)x->Get(x)
+#define _mm_read_UBYTE(x) (UBYTE)x->Get(x)
+
+#define _mm_write_SBYTES(x,y,z) z->Write(z,(void *)x,y)
+#define _mm_write_UBYTES(x,y,z) z->Write(z,(void *)x,y)
+#define _mm_read_SBYTES(x,y,z) z->Read(z,(void *)x,y)
+#define _mm_read_UBYTES(x,y,z) z->Read(z,(void *)x,y)
+
+#define _mm_fseek(x,y,z) x->Seek(x,y,z)
+#define _mm_ftell(x) x->Tell(x)
+#define _mm_rewind(x) _mm_fseek(x,0,SEEK_SET)
+
+#define _mm_eof(x) x->Eof(x)
+
+extern void _mm_iobase_setcur(MREADER*);
+extern void _mm_iobase_revert(MREADER*);
+extern FILE *_mm_fopen(CHAR*,CHAR*);
+extern int _mm_fclose(FILE *);
+extern void _mm_write_string(CHAR*,MWRITER*);
+extern int _mm_read_string (CHAR*,int,MREADER*);
+
+extern SWORD _mm_read_M_SWORD(MREADER*);
+extern SWORD _mm_read_I_SWORD(MREADER*);
+extern UWORD _mm_read_M_UWORD(MREADER*);
+extern UWORD _mm_read_I_UWORD(MREADER*);
+
+extern SLONG _mm_read_M_SLONG(MREADER*);
+extern SLONG _mm_read_I_SLONG(MREADER*);
+extern ULONG _mm_read_M_ULONG(MREADER*);
+extern ULONG _mm_read_I_ULONG(MREADER*);
+
+extern int _mm_read_M_SWORDS(SWORD*,int,MREADER*);
+extern int _mm_read_I_SWORDS(SWORD*,int,MREADER*);
+extern int _mm_read_M_UWORDS(UWORD*,int,MREADER*);
+extern int _mm_read_I_UWORDS(UWORD*,int,MREADER*);
+
+extern int _mm_read_M_SLONGS(SLONG*,int,MREADER*);
+extern int _mm_read_I_SLONGS(SLONG*,int,MREADER*);
+extern int _mm_read_M_ULONGS(ULONG*,int,MREADER*);
+extern int _mm_read_I_ULONGS(ULONG*,int,MREADER*);
+
+extern void _mm_write_M_SWORD(SWORD,MWRITER*);
+extern void _mm_write_I_SWORD(SWORD,MWRITER*);
+extern void _mm_write_M_UWORD(UWORD,MWRITER*);
+extern void _mm_write_I_UWORD(UWORD,MWRITER*);
+
+extern void _mm_write_M_SLONG(SLONG,MWRITER*);
+extern void _mm_write_I_SLONG(SLONG,MWRITER*);
+extern void _mm_write_M_ULONG(ULONG,MWRITER*);
+extern void _mm_write_I_ULONG(ULONG,MWRITER*);
+
+extern void _mm_write_M_SWORDS(SWORD*,int,MWRITER*);
+extern void _mm_write_I_SWORDS(SWORD*,int,MWRITER*);
+extern void _mm_write_M_UWORDS(UWORD*,int,MWRITER*);
+extern void _mm_write_I_UWORDS(UWORD*,int,MWRITER*);
+
+extern void _mm_write_M_SLONGS(SLONG*,int,MWRITER*);
+extern void _mm_write_I_SLONGS(SLONG*,int,MWRITER*);
+extern void _mm_write_M_ULONGS(ULONG*,int,MWRITER*);
+extern void _mm_write_I_ULONGS(ULONG*,int,MWRITER*);
+
+/*========== Samples */
+
+/* This is a handle of sorts attached to any sample registered with
+ SL_RegisterSample. Generally, this only need be used or changed by the
+ loaders and drivers of mikmod. */
+typedef struct SAMPLOAD {
+ struct SAMPLOAD *next;
+
+ ULONG length; /* length of sample (in samples!) */
+ ULONG loopstart; /* repeat position (relative to start, in samples) */
+ ULONG loopend; /* repeat end */
+ UWORD infmt,outfmt;
+ int scalefactor;
+ SAMPLE* sample;
+ MREADER* reader;
+} SAMPLOAD;
+
+/*========== Sample and waves loading interface */
+
+extern void SL_HalveSample(SAMPLOAD*,int);
+extern void SL_Sample8to16(SAMPLOAD*);
+extern void SL_Sample16to8(SAMPLOAD*);
+extern void SL_SampleSigned(SAMPLOAD*);
+extern void SL_SampleUnsigned(SAMPLOAD*);
+extern BOOL SL_LoadSamples(void);
+extern SAMPLOAD* SL_RegisterSample(SAMPLE*,int,MREADER*);
+extern BOOL SL_Load(void*,SAMPLOAD*,ULONG);
+extern BOOL SL_Init(SAMPLOAD*);
+extern void SL_Exit(SAMPLOAD*);
+
+/*========== Internal module representation (UniMod) interface */
+
+/* number of notes in an octave */
+#define OCTAVE 12
+
+extern void UniSetRow(UBYTE*);
+extern UBYTE UniGetByte(void);
+extern UWORD UniGetWord(void);
+extern UBYTE* UniFindRow(UBYTE*,UWORD);
+extern void UniSkipOpcode(void);
+extern void UniReset(void);
+extern void UniWriteByte(UBYTE);
+extern void UniWriteWord(UWORD);
+extern void UniNewline(void);
+extern UBYTE* UniDup(void);
+extern BOOL UniInit(void);
+extern void UniCleanup(void);
+extern void UniEffect(UWORD,UWORD);
+#define UniInstrument(x) UniEffect(UNI_INSTRUMENT,x)
+#define UniNote(x) UniEffect(UNI_NOTE,x)
+extern void UniPTEffect(UBYTE,UBYTE);
+extern void UniVolEffect(UWORD,UBYTE);
+
+/*========== Module Commands */
+
+enum {
+ /* Simple note */
+ UNI_NOTE = 1,
+ /* Instrument change */
+ UNI_INSTRUMENT,
+ /* Protracker effects */
+ UNI_PTEFFECT0, /* arpeggio */
+ UNI_PTEFFECT1, /* porta up */
+ UNI_PTEFFECT2, /* porta down */
+ UNI_PTEFFECT3, /* porta to note */
+ UNI_PTEFFECT4, /* vibrato */
+ UNI_PTEFFECT5, /* dual effect 3+A */
+ UNI_PTEFFECT6, /* dual effect 4+A */
+ UNI_PTEFFECT7, /* tremolo */
+ UNI_PTEFFECT8, /* pan */
+ UNI_PTEFFECT9, /* sample offset */
+ UNI_PTEFFECTA, /* volume slide */
+ UNI_PTEFFECTB, /* pattern jump */
+ UNI_PTEFFECTC, /* set volume */
+ UNI_PTEFFECTD, /* pattern break */
+ UNI_PTEFFECTE, /* extended effects */
+ UNI_PTEFFECTF, /* set speed */
+ /* Scream Tracker effects */
+ UNI_S3MEFFECTA, /* set speed */
+ UNI_S3MEFFECTD, /* volume slide */
+ UNI_S3MEFFECTE, /* porta down */
+ UNI_S3MEFFECTF, /* porta up */
+ UNI_S3MEFFECTI, /* tremor */
+ UNI_S3MEFFECTQ, /* retrig */
+ UNI_S3MEFFECTR, /* tremolo */
+ UNI_S3MEFFECTT, /* set tempo */
+ UNI_S3MEFFECTU, /* fine vibrato */
+ UNI_KEYOFF, /* note off */
+ /* Fast Tracker effects */
+ UNI_KEYFADE, /* note fade */
+ UNI_VOLEFFECTS, /* volume column effects */
+ UNI_XMEFFECT4, /* vibrato */
+ UNI_XMEFFECT6, /* dual effect 4+A */
+ UNI_XMEFFECTA, /* volume slide */
+ UNI_XMEFFECTE1, /* fine porta up */
+ UNI_XMEFFECTE2, /* fine porta down */
+ UNI_XMEFFECTEA, /* fine volume slide up */
+ UNI_XMEFFECTEB, /* fine volume slide down */
+ UNI_XMEFFECTG, /* set global volume */
+ UNI_XMEFFECTH, /* global volume slide */
+ UNI_XMEFFECTL, /* set envelope position */
+ UNI_XMEFFECTP, /* pan slide */
+ UNI_XMEFFECTX1, /* extra fine porta up */
+ UNI_XMEFFECTX2, /* extra fine porta down */
+ /* Impulse Tracker effects */
+ UNI_ITEFFECTG, /* porta to note */
+ UNI_ITEFFECTH, /* vibrato */
+ UNI_ITEFFECTI, /* tremor (xy not incremented) */
+ UNI_ITEFFECTM, /* set channel volume */
+ UNI_ITEFFECTN, /* slide / fineslide channel volume */
+ UNI_ITEFFECTP, /* slide / fineslide channel panning */
+ UNI_ITEFFECTT, /* slide tempo */
+ UNI_ITEFFECTU, /* fine vibrato */
+ UNI_ITEFFECTW, /* slide / fineslide global volume */
+ UNI_ITEFFECTY, /* panbrello */
+ UNI_ITEFFECTZ, /* resonant filters */
+ UNI_ITEFFECTS0,
+ /* UltraTracker effects */
+ UNI_ULTEFFECT9, /* Sample fine offset */
+ /* OctaMED effects */
+ UNI_MEDSPEED,
+ UNI_MEDEFFECTF1, /* play note twice */
+ UNI_MEDEFFECTF2, /* delay note */
+ UNI_MEDEFFECTF3, /* play note three times */
+ /* Oktalyzer effects */
+ UNI_OKTARP, /* arpeggio */
+
+ UNI_LAST
+};
+
+extern UWORD unioperands[UNI_LAST];
+
+/* IT / S3M Extended SS effects: */
+enum {
+ SS_GLISSANDO = 1,
+ SS_FINETUNE,
+ SS_VIBWAVE,
+ SS_TREMWAVE,
+ SS_PANWAVE,
+ SS_FRAMEDELAY,
+ SS_S7EFFECTS,
+ SS_PANNING,
+ SS_SURROUND,
+ SS_HIOFFSET,
+ SS_PATLOOP,
+ SS_NOTECUT,
+ SS_NOTEDELAY,
+ SS_PATDELAY
+};
+
+/* IT Volume column effects */
+enum {
+ VOL_VOLUME = 1,
+ VOL_PANNING,
+ VOL_VOLSLIDE,
+ VOL_PITCHSLIDEDN,
+ VOL_PITCHSLIDEUP,
+ VOL_PORTAMENTO,
+ VOL_VIBRATO
+};
+
+/* IT resonant filter information */
+
+#define UF_MAXMACRO 0x10
+#define UF_MAXFILTER 0x100
+
+#define FILT_CUT 0x80
+#define FILT_RESONANT 0x81
+
+typedef struct FILTER {
+ UBYTE filter,inf;
+} FILTER;
+
+/*========== Instruments */
+
+/* Instrument format flags */
+#define IF_OWNPAN 1
+#define IF_PITCHPAN 2
+
+/* Envelope flags: */
+#define EF_ON 1
+#define EF_SUSTAIN 2
+#define EF_LOOP 4
+#define EF_VOLENV 8
+
+/* New Note Action Flags */
+#define NNA_CUT 0
+#define NNA_CONTINUE 1
+#define NNA_OFF 2
+#define NNA_FADE 3
+
+#define NNA_MASK 3
+
+#define DCT_OFF 0
+#define DCT_NOTE 1
+#define DCT_SAMPLE 2
+#define DCT_INST 3
+
+#define DCA_CUT 0
+#define DCA_OFF 1
+#define DCA_FADE 2
+
+#define KEY_KICK 0
+#define KEY_OFF 1
+#define KEY_FADE 2
+#define KEY_KILL (KEY_OFF|KEY_FADE)
+
+#define KICK_ABSENT 0
+#define KICK_NOTE 1
+#define KICK_KEYOFF 2
+#define KICK_ENV 4
+
+#define AV_IT 1 /* IT vs. XM vibrato info */
+
+/*========== Playing */
+
+#define POS_NONE (-2) /* no loop position defined */
+
+#define LAST_PATTERN (UWORD)(-1) /* special ``end of song'' pattern */
+
+typedef struct ENVPR {
+ UBYTE flg; /* envelope flag */
+ UBYTE pts; /* number of envelope points */
+ UBYTE susbeg; /* envelope sustain index begin */
+ UBYTE susend; /* envelope sustain index end */
+ UBYTE beg; /* envelope loop begin */
+ UBYTE end; /* envelope loop end */
+ SWORD p; /* current envelope counter */
+ UWORD a; /* envelope index a */
+ UWORD b; /* envelope index b */
+ ENVPT* env; /* envelope points */
+} ENVPR;
+
+typedef struct MP_CHANNEL {
+ INSTRUMENT* i;
+ SAMPLE* s;
+ UBYTE sample; /* which sample number */
+ UBYTE note; /* the audible note as heard, direct rep of period */
+ SWORD outvolume; /* output volume (vol + sampcol + instvol) */
+ SBYTE chanvol; /* channel's "global" volume */
+ UWORD fadevol; /* fading volume rate */
+ SWORD panning; /* panning position */
+ UBYTE kick; /* if true = sample has to be restarted */
+ UWORD period; /* period to play the sample at */
+ UBYTE nna; /* New note action type + master/slave flags */
+
+ UBYTE volflg; /* volume envelope settings */
+ UBYTE panflg; /* panning envelope settings */
+ UBYTE pitflg; /* pitch envelope settings */
+
+ UBYTE keyoff; /* if true = fade out and stuff */
+ SWORD handle; /* which sample-handle */
+ UBYTE notedelay; /* (used for note delay) */
+ SLONG start; /* The starting byte index in the sample */
+} MP_CHANNEL;
+
+typedef struct MP_CONTROL {
+ struct MP_CHANNEL main;
+
+ struct MP_VOICE *slave; /* Audio Slave of current effects control channel */
+
+ UBYTE slavechn; /* Audio Slave of current effects control channel */
+ UBYTE muted; /* if set, channel not played */
+ UWORD ultoffset; /* fine sample offset memory */
+ UBYTE anote; /* the note that indexes the audible */
+ UBYTE oldnote;
+ SWORD ownper;
+ SWORD ownvol;
+ UBYTE dca; /* duplicate check action */
+ UBYTE dct; /* duplicate check type */
+ UBYTE* row; /* row currently playing on this channel */
+ SBYTE retrig; /* retrig value (0 means don't retrig) */
+ ULONG speed; /* what finetune to use */
+ SWORD volume; /* amiga volume (0 t/m 64) to play the sample at */
+
+ SWORD tmpvolume; /* tmp volume */
+ UWORD tmpperiod; /* tmp period */
+ UWORD wantedperiod; /* period to slide to (with effect 3 or 5) */
+
+ UBYTE arpmem; /* arpeggio command memory */
+ UBYTE pansspd; /* panslide speed */
+ UWORD slidespeed;
+ UWORD portspeed; /* noteslide speed (toneportamento) */
+
+ UBYTE s3mtremor; /* s3m tremor (effect I) counter */
+ UBYTE s3mtronof; /* s3m tremor ontime/offtime */
+ UBYTE s3mvolslide; /* last used volslide */
+ SBYTE sliding;
+ UBYTE s3mrtgspeed; /* last used retrig speed */
+ UBYTE s3mrtgslide; /* last used retrig slide */
+
+ UBYTE glissando; /* glissando (0 means off) */
+ UBYTE wavecontrol;
+
+ SBYTE vibpos; /* current vibrato position */
+ UBYTE vibspd; /* "" speed */
+ UBYTE vibdepth; /* "" depth */
+
+ SBYTE trmpos; /* current tremolo position */
+ UBYTE trmspd; /* "" speed */
+ UBYTE trmdepth; /* "" depth */
+
+ UBYTE fslideupspd;
+ UBYTE fslidednspd;
+ UBYTE fportupspd; /* fx E1 (extra fine portamento up) data */
+ UBYTE fportdnspd; /* fx E2 (extra fine portamento dn) data */
+ UBYTE ffportupspd; /* fx X1 (extra fine portamento up) data */
+ UBYTE ffportdnspd; /* fx X2 (extra fine portamento dn) data */
+
+ ULONG hioffset; /* last used high order of sample offset */
+ UWORD soffset; /* last used low order of sample-offset (effect 9) */
+
+ UBYTE sseffect; /* last used Sxx effect */
+ UBYTE ssdata; /* last used Sxx data info */
+ UBYTE chanvolslide; /* last used channel volume slide */
+
+ UBYTE panbwave; /* current panbrello waveform */
+ UBYTE panbpos; /* current panbrello position */
+ SBYTE panbspd; /* "" speed */
+ UBYTE panbdepth; /* "" depth */
+
+ UWORD newsamp; /* set to 1 upon a sample / inst change */
+ UBYTE voleffect; /* Volume Column Effect Memory as used by IT */
+ UBYTE voldata; /* Volume Column Data Memory */
+
+ SWORD pat_reppos; /* patternloop position */
+ UWORD pat_repcnt; /* times to loop */
+} MP_CONTROL;
+
+/* Used by NNA only player (audio control. AUDTMP is used for full effects
+ control). */
+typedef struct MP_VOICE {
+ struct MP_CHANNEL main;
+
+ ENVPR venv;
+ ENVPR penv;
+ ENVPR cenv;
+
+ UWORD avibpos; /* autovibrato pos */
+ UWORD aswppos; /* autovibrato sweep pos */
+
+ ULONG totalvol; /* total volume of channel (before global mixings) */
+
+ BOOL mflag;
+ SWORD masterchn;
+ UWORD masterperiod;
+
+ MP_CONTROL* master; /* index of "master" effects channel */
+} MP_VOICE;
+
+/*========== Loaders */
+
+typedef struct MLOADER {
+struct MLOADER* next;
+ CHAR* type;
+ CHAR* version;
+ BOOL (*Init)(void);
+ BOOL (*Test)(void);
+ BOOL (*Load)(BOOL);
+ void (*Cleanup)(void);
+ CHAR* (*LoadTitle)(void);
+} MLOADER;
+
+/* internal loader variables */
+extern MREADER* modreader;
+extern UWORD finetune[16];
+extern MODULE of; /* static unimod loading space */
+extern UWORD npertab[7*OCTAVE]; /* used by the original MOD loaders */
+
+extern SBYTE remap[UF_MAXCHAN]; /* for removing empty channels */
+extern UBYTE* poslookup; /* lookup table for pattern jumps after
+ blank pattern removal */
+extern UBYTE poslookupcnt;
+extern UWORD* origpositions;
+
+extern BOOL filters; /* resonant filters in use */
+extern UBYTE activemacro; /* active midi macro number for Sxx */
+extern UBYTE filtermacros[UF_MAXMACRO]; /* midi macro settings */
+extern FILTER filtersettings[UF_MAXFILTER]; /* computed filter settings */
+
+extern int* noteindex;
+
+/*========== Internal loader interface */
+
+extern BOOL ReadComment(UWORD);
+extern BOOL ReadLinedComment(UWORD,UWORD);
+extern BOOL AllocPositions(int);
+extern BOOL AllocPatterns(void);
+extern BOOL AllocTracks(void);
+extern BOOL AllocInstruments(void);
+extern BOOL AllocSamples(void);
+extern CHAR* DupStr(CHAR*,UWORD,BOOL);
+
+/* loader utility functions */
+extern int* AllocLinear(void);
+extern void FreeLinear(void);
+extern int speed_to_finetune(ULONG,int);
+extern void S3MIT_ProcessCmd(UBYTE,UBYTE,unsigned int);
+extern void S3MIT_CreateOrders(BOOL);
+
+/* flags for S3MIT_ProcessCmd */
+#define S3MIT_OLDSTYLE 1 /* behave as old scream tracker */
+#define S3MIT_IT 2 /* behave as impulse tracker */
+#define S3MIT_SCREAM 4 /* enforce scream tracker specific limits */
+
+/* used to convert c4spd to linear XM periods (IT and IMF loaders). */
+extern UWORD getlinearperiod(UWORD,ULONG);
+extern ULONG getfrequency(UWORD,ULONG);
+
+/* loader shared data */
+#define STM_NTRACKERS 3
+extern CHAR *STM_Signatures[STM_NTRACKERS];
+
+/*========== Player interface */
+
+extern BOOL Player_Init(MODULE*);
+extern void Player_Exit(MODULE*);
+extern void Player_HandleTick(void);
+
+/*========== Drivers */
+
+/* max. number of handles a driver has to provide. (not strict) */
+#define MAXSAMPLEHANDLES 384
+
+/* These variables can be changed at ANY time and results will be immediate */
+extern UWORD md_bpm; /* current song / hardware BPM rate */
+
+/* Variables below can be changed via MD_SetNumVoices at any time. However, a
+ call to MD_SetNumVoicess while the driver is active will cause the sound to
+ skip slightly. */
+extern UBYTE md_numchn; /* number of song + sound effects voices */
+extern UBYTE md_sngchn; /* number of song voices */
+extern UBYTE md_sfxchn; /* number of sound effects voices */
+extern UBYTE md_hardchn; /* number of hardware mixed voices */
+extern UBYTE md_softchn; /* number of software mixed voices */
+
+/* This is for use by the hardware drivers only. It points to the registered
+ tickhandler function. */
+extern void (*md_player)(void);
+
+extern SWORD MD_SampleLoad(SAMPLOAD*,int);
+extern void MD_SampleUnload(SWORD);
+extern ULONG MD_SampleSpace(int);
+extern ULONG MD_SampleLength(int,SAMPLE*);
+
+/* uLaw conversion */
+extern void unsignedtoulaw(char *,int);
+
+/* Parameter extraction helper */
+extern CHAR *MD_GetAtom(CHAR*,CHAR*,BOOL);
+
+/* Internal software mixer stuff */
+extern void VC_SetupPointers(void);
+extern BOOL VC1_Init(void);
+extern BOOL VC2_Init(void);
+
+#if defined(unix) || defined(__APPLE__) && defined(__MACH__)
+/* POSIX helper functions */
+extern BOOL MD_Access(CHAR *);
+extern BOOL MD_DropPrivileges(void);
+#endif
+
+/* Macro to define a missing driver, yet allowing binaries to dynamically link
+ with the library without missing symbol errors */
+#define MISSING(a) MDRIVER a = { NULL, NULL, NULL, 0, 0 }
+
+/*========== Prototypes for non-MT safe versions of some public functions */
+
+extern void _mm_registerdriver(struct MDRIVER*);
+extern void _mm_registerloader(struct MLOADER*);
+extern BOOL MikMod_Active_internal(void);
+extern void MikMod_DisableOutput_internal(void);
+extern BOOL MikMod_EnableOutput_internal(void);
+extern void MikMod_Exit_internal(void);
+extern BOOL MikMod_SetNumVoices_internal(int,int);
+extern void Player_Exit_internal(MODULE*);
+extern void Player_Stop_internal(void);
+extern BOOL Player_Paused_internal(void);
+extern void Sample_Free_internal(SAMPLE*);
+extern void Voice_Play_internal(SBYTE,SAMPLE*,ULONG);
+extern void Voice_SetFrequency_internal(SBYTE,ULONG);
+extern void Voice_SetPanning_internal(SBYTE,ULONG);
+extern void Voice_SetVolume_internal(SBYTE,UWORD);
+extern void Voice_Stop_internal(SBYTE);
+extern BOOL Voice_Stopped_internal(SBYTE);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mloader.c b/src/libs/mikmod/mloader.c
new file mode 100644
index 0000000..494334c
--- /dev/null
+++ b/src/libs/mikmod/mloader.c
@@ -0,0 +1,607 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
+ AUTHORS for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ These routines are used to access the available module loaders
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+ MREADER *modreader;
+ MODULE of;
+
+static MLOADER *firstloader=NULL;
+
+UWORD finetune[16]={
+ 8363,8413,8463,8529,8581,8651,8723,8757,
+ 7895,7941,7985,8046,8107,8169,8232,8280
+};
+
+MIKMODAPI CHAR* MikMod_InfoLoader(void)
+{
+ int len=0;
+ MLOADER *l;
+ CHAR *list=NULL;
+
+ MUTEX_LOCK(lists);
+ /* compute size of buffer */
+ for(l=firstloader;l;l=l->next) len+=1+(l->next?1:0)+strlen(l->version);
+
+ if(len)
+ if((list=MikMod_malloc(len*sizeof(CHAR)))) {
+ list[0]=0;
+ /* list all registered module loders */
+ for(l=firstloader;l;l=l->next)
+ sprintf(list,(l->next)?"%s%s\n":"%s%s",list,l->version);
+ }
+ MUTEX_UNLOCK(lists);
+ return list;
+}
+
+void _mm_registerloader(MLOADER* ldr)
+{
+ MLOADER *cruise=firstloader;
+
+ if(cruise) {
+ while(cruise->next) cruise = cruise->next;
+ cruise->next=ldr;
+ } else
+ firstloader=ldr;
+}
+
+MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr)
+{
+ /* if we try to register an invalid loader, or an already registered loader,
+ ignore this attempt */
+ if ((!ldr)||(ldr->next))
+ return;
+
+ MUTEX_LOCK(lists);
+ _mm_registerloader(ldr);
+ MUTEX_UNLOCK(lists);
+}
+
+BOOL ReadComment(UWORD len)
+{
+ if(len) {
+ int i;
+
+ if(!(of.comment=(CHAR*)MikMod_malloc(len+1))) return 0;
+ _mm_read_UBYTES(of.comment,len,modreader);
+
+ /* translate IT linefeeds */
+ for(i=0;i<len;i++)
+ if(of.comment[i]=='\r') of.comment[i]='\n';
+
+ of.comment[len]=0; /* just in case */
+ }
+ if(!of.comment[0]) {
+ MikMod_free(of.comment);
+ of.comment=NULL;
+ }
+ return 1;
+}
+
+BOOL ReadLinedComment(UWORD len,UWORD linelen)
+{
+ CHAR *tempcomment,*line,*storage;
+ UWORD total=0,t,lines;
+ int i;
+
+ lines = (len + linelen - 1) / linelen;
+ if (len) {
+ if(!(tempcomment=(CHAR*)MikMod_malloc(len+1))) return 0;
+ if(!(storage=(CHAR*)MikMod_malloc(linelen+1))) {
+ MikMod_free(tempcomment);
+ return 0;
+ }
+ memset(tempcomment, ' ', len);
+ _mm_read_UBYTES(tempcomment,len,modreader);
+
+ /* compute message length */
+ for(line=tempcomment,total=t=0;t<lines;t++,line+=linelen) {
+ for(i=linelen;(i>=0)&&(line[i]==' ');i--) line[i]=0;
+ for(i=0;i<linelen;i++) if (!line[i]) break;
+ total+=1+i;
+ }
+
+ if(total>lines) {
+ if(!(of.comment=(CHAR*)MikMod_malloc(total+1))) {
+ MikMod_free(storage);
+ MikMod_free(tempcomment);
+ return 0;
+ }
+
+ /* convert message */
+ for(line=tempcomment,t=0;t<lines;t++,line+=linelen) {
+ for(i=0;i<linelen;i++) if(!(storage[i]=line[i])) break;
+ storage[i]=0; /* if (i==linelen) */
+ strcat(of.comment,storage);strcat(of.comment,"\r");
+ }
+ MikMod_free(storage);
+ MikMod_free(tempcomment);
+ }
+ }
+ return 1;
+}
+
+BOOL AllocPositions(int total)
+{
+ if(!total) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ if(!(of.positions=MikMod_calloc(total,sizeof(UWORD)))) return 0;
+ return 1;
+}
+
+BOOL AllocPatterns(void)
+{
+ int s,t,tracks = 0;
+
+ if((!of.numpat)||(!of.numchn)) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ /* Allocate track sequencing array */
+ if(!(of.patterns=(UWORD*)MikMod_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0;
+ if(!(of.pattrows=(UWORD*)MikMod_calloc(of.numpat+1,sizeof(UWORD)))) return 0;
+
+ for(t=0;t<=of.numpat;t++) {
+ of.pattrows[t]=64;
+ for(s=0;s<of.numchn;s++)
+ of.patterns[(t*of.numchn)+s]=tracks++;
+ }
+
+ return 1;
+}
+
+BOOL AllocTracks(void)
+{
+ if(!of.numtrk) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ if(!(of.tracks=(UBYTE **)MikMod_calloc(of.numtrk,sizeof(UBYTE *)))) return 0;
+ return 1;
+}
+
+BOOL AllocInstruments(void)
+{
+ int t,n;
+
+ if(!of.numins) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ if(!(of.instruments=(INSTRUMENT*)MikMod_calloc(of.numins,sizeof(INSTRUMENT))))
+ return 0;
+
+ for(t=0;t<of.numins;t++) {
+ for(n=0;n<INSTNOTES;n++) {
+ /* Init note / sample lookup table */
+ of.instruments[t].samplenote[n] = n;
+ of.instruments[t].samplenumber[n] = t;
+ }
+ of.instruments[t].globvol = 64;
+ }
+ return 1;
+}
+
+BOOL AllocSamples(void)
+{
+ UWORD u;
+
+ if(!of.numsmp) {
+ _mm_errno=MMERR_NOT_A_MODULE;
+ return 0;
+ }
+ if(!(of.samples=(SAMPLE*)MikMod_calloc(of.numsmp,sizeof(SAMPLE)))) return 0;
+
+ for(u=0;u<of.numsmp;u++) {
+ of.samples[u].panning = 128; /* center */
+ of.samples[u].handle = -1;
+ of.samples[u].globvol = 64;
+ of.samples[u].volume = 64;
+ }
+ return 1;
+}
+
+static BOOL ML_LoadSamples(void)
+{
+ SAMPLE *s;
+ int u;
+
+ for(u=of.numsmp,s=of.samples;u;u--,s++)
+ if(s->length) SL_RegisterSample(s,MD_MUSIC,modreader);
+
+ return 1;
+}
+
+/* Creates a CSTR out of a character buffer of 'len' bytes, but strips any
+ terminating non-printing characters like 0, spaces etc. */
+CHAR *DupStr(CHAR* s,UWORD len,BOOL strict)
+{
+ UWORD t;
+ CHAR *d=NULL;
+
+ /* Scan for last printing char in buffer [includes high ascii up to 254] */
+ while(len) {
+ if(s[len-1]>0x20) break;
+ len--;
+ }
+
+ /* Scan forward for possible NULL character */
+ if(strict) {
+ for(t=0;t<len;t++) if (!s[t]) break;
+ if (t<len) len=t;
+ }
+
+ /* When the buffer wasn't completely empty, allocate a cstring and copy the
+ buffer into that string, except for any control-chars */
+ if((d=(CHAR*)MikMod_malloc(sizeof(CHAR)*(len+1)))) {
+ for(t=0;t<len;t++) d[t]=(s[t]<32)?'.':s[t];
+ d[len]=0;
+ }
+ return d;
+}
+
+static void ML_XFreeSample(SAMPLE *s)
+{
+ if(s->handle>=0)
+ MD_SampleUnload(s->handle);
+ if(s->samplename) MikMod_free(s->samplename);
+}
+
+static void ML_XFreeInstrument(INSTRUMENT *i)
+{
+ if(i->insname) MikMod_free(i->insname);
+}
+
+static void ML_FreeEx(MODULE *mf)
+{
+ UWORD t;
+
+ if(mf->songname) MikMod_free(mf->songname);
+ if(mf->comment) MikMod_free(mf->comment);
+
+ if(mf->modtype) MikMod_free(mf->modtype);
+ if(mf->positions) MikMod_free(mf->positions);
+ if(mf->patterns) MikMod_free(mf->patterns);
+ if(mf->pattrows) MikMod_free(mf->pattrows);
+
+ if(mf->tracks) {
+ for(t=0;t<mf->numtrk;t++)
+ if(mf->tracks[t]) MikMod_free(mf->tracks[t]);
+ MikMod_free(mf->tracks);
+ }
+ if(mf->instruments) {
+ for(t=0;t<mf->numins;t++)
+ ML_XFreeInstrument(&mf->instruments[t]);
+ MikMod_free(mf->instruments);
+ }
+ if(mf->samples) {
+ for(t=0;t<mf->numsmp;t++)
+ if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]);
+ MikMod_free(mf->samples);
+ }
+ memset(mf,0,sizeof(MODULE));
+ if(mf!=&of) MikMod_free(mf);
+}
+
+static MODULE *ML_AllocUniMod(void)
+{
+ MODULE *mf;
+
+ return (mf=MikMod_malloc(sizeof(MODULE)));
+}
+
+void Player_Free_internal(MODULE *mf)
+{
+ if(mf) {
+ Player_Exit_internal(mf);
+ ML_FreeEx(mf);
+ }
+}
+
+MIKMODAPI void Player_Free(MODULE *mf)
+{
+ MUTEX_LOCK(vars);
+ Player_Free_internal(mf);
+ MUTEX_UNLOCK(vars);
+}
+
+static CHAR* Player_LoadTitle_internal(MREADER *reader)
+{
+ MLOADER *l;
+
+ modreader=reader;
+ _mm_errno = 0;
+ _mm_critical = 0;
+ _mm_iobase_setcur(modreader);
+
+ /* Try to find a loader that recognizes the module */
+ for(l=firstloader;l;l=l->next) {
+ _mm_rewind(modreader);
+ if(l->Test()) break;
+ }
+
+ if(!l) {
+ _mm_errno = MMERR_NOT_A_MODULE;
+ if(_mm_errorhandler) _mm_errorhandler();
+ return NULL;
+ }
+
+ return l->LoadTitle();
+}
+
+MIKMODAPI CHAR* Player_LoadTitleFP(FILE *fp)
+{
+ CHAR* result=NULL;
+ MREADER* reader;
+
+ if(fp && (reader=_mm_new_file_reader(fp))) {
+ MUTEX_LOCK(lists);
+ result=Player_LoadTitle_internal(reader);
+ MUTEX_UNLOCK(lists);
+ _mm_delete_file_reader(reader);
+ }
+ return result;
+}
+
+MIKMODAPI CHAR* Player_LoadTitleMem(const char *buffer,int len)
+{
+ CHAR *result=NULL;
+ MREADER* reader;
+
+ if ((reader=_mm_new_mem_reader(buffer,len)))
+ {
+ MUTEX_LOCK(lists);
+ result=Player_LoadTitle_internal(reader);
+ MUTEX_UNLOCK(lists);
+ _mm_delete_mem_reader(reader);
+ }
+
+
+ return result;
+}
+
+MIKMODAPI CHAR* Player_LoadTitleGeneric(MREADER *reader)
+{
+ CHAR *result=NULL;
+
+ if (reader) {
+ MUTEX_LOCK(lists);
+ result=Player_LoadTitle_internal(reader);
+ MUTEX_UNLOCK(lists);
+ }
+ return result;
+}
+
+MIKMODAPI CHAR* Player_LoadTitle(CHAR* filename)
+{
+ CHAR* result=NULL;
+ FILE* fp;
+ MREADER* reader;
+
+ if((fp=_mm_fopen(filename,"rb"))) {
+ if((reader=_mm_new_file_reader(fp))) {
+ MUTEX_LOCK(lists);
+ result=Player_LoadTitle_internal(reader);
+ MUTEX_UNLOCK(lists);
+ _mm_delete_file_reader(reader);
+ }
+ _mm_fclose(fp);
+ }
+ return result;
+}
+
+/* Loads a module given an reader */
+MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,BOOL curious)
+{
+ int t;
+ MLOADER *l;
+ BOOL ok;
+ MODULE *mf;
+
+ modreader = reader;
+ _mm_errno = 0;
+ _mm_critical = 0;
+ _mm_iobase_setcur(modreader);
+
+ /* Try to find a loader that recognizes the module */
+ for(l=firstloader;l;l=l->next) {
+ _mm_rewind(modreader);
+ if(l->Test()) break;
+ }
+
+ if(!l) {
+ _mm_errno = MMERR_NOT_A_MODULE;
+ if(_mm_errorhandler) _mm_errorhandler();
+ _mm_rewind(modreader);_mm_iobase_revert(modreader);
+ return NULL;
+ }
+
+ /* init unitrk routines */
+ if(!UniInit()) {
+ if(_mm_errorhandler) _mm_errorhandler();
+ _mm_rewind(modreader);_mm_iobase_revert(modreader);
+ return NULL;
+ }
+
+ /* init the module structure with vanilla settings */
+ memset(&of,0,sizeof(MODULE));
+ of.bpmlimit = 33;
+ of.initvolume = 128;
+ for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64;
+ for (t = 0; t < UF_MAXCHAN; t++)
+ of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT;
+
+ /* init module loader and load the header / patterns */
+ if (!l->Init || l->Init()) {
+ _mm_rewind(modreader);
+ ok = l->Load(curious);
+ if (ok) {
+ /* propagate inflags=flags for in-module samples */
+ for (t = 0; t < of.numsmp; t++)
+ if (of.samples[t].inflags == 0)
+ of.samples[t].inflags = of.samples[t].flags;
+ }
+ } else
+ ok = 0;
+
+ /* free loader and unitrk allocations */
+ if (l->Cleanup) l->Cleanup();
+ UniCleanup();
+
+ if(!ok) {
+ ML_FreeEx(&of);
+ if(_mm_errorhandler) _mm_errorhandler();
+ _mm_rewind(modreader);_mm_iobase_revert(modreader);
+ return NULL;
+ }
+
+ if(!ML_LoadSamples()) {
+ ML_FreeEx(&of);
+ if(_mm_errorhandler) _mm_errorhandler();
+ _mm_rewind(modreader);_mm_iobase_revert(modreader);
+ return NULL;
+ }
+
+ if(!(mf=ML_AllocUniMod())) {
+ ML_FreeEx(&of);
+ _mm_rewind(modreader);_mm_iobase_revert(modreader);
+ if(_mm_errorhandler) _mm_errorhandler();
+ return NULL;
+ }
+
+ /* If the module doesn't have any specific panning, create a
+ MOD-like panning, with the channels half-separated. */
+ if (!(of.flags & UF_PANNING))
+ for (t = 0; t < of.numchn; t++)
+ of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT;
+
+ /* Copy the static MODULE contents into the dynamic MODULE struct. */
+ memcpy(mf,&of,sizeof(MODULE));
+
+ if(maxchan>0) {
+ if(!(mf->flags&UF_NNA)&&(mf->numchn<maxchan))
+ maxchan = mf->numchn;
+ else
+ if((mf->numvoices)&&(mf->numvoices<maxchan))
+ maxchan = mf->numvoices;
+
+ if(maxchan<mf->numchn) mf->flags |= UF_NNA;
+
+ if(MikMod_SetNumVoices_internal(maxchan,-1)) {
+ _mm_iobase_revert(modreader);
+ Player_Free(mf);
+ return NULL;
+ }
+ }
+ if(SL_LoadSamples()) {
+ _mm_iobase_revert(modreader);
+ Player_Free_internal(mf);
+ return NULL;
+ }
+ if(Player_Init(mf)) {
+ _mm_iobase_revert(modreader);
+ Player_Free_internal(mf);
+ mf=NULL;
+ }
+ _mm_iobase_revert(modreader);
+ return mf;
+}
+
+MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,BOOL curious)
+{
+ MODULE* result;
+
+ MUTEX_LOCK(vars);
+ MUTEX_LOCK(lists);
+ result=Player_LoadGeneric_internal(reader,maxchan,curious);
+ MUTEX_UNLOCK(lists);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI MODULE* Player_LoadMem(const char *buffer,int len,int maxchan,BOOL curious)
+{
+ MODULE* result=NULL;
+ MREADER* reader;
+
+ if ((reader=_mm_new_mem_reader(buffer, len))) {
+ result=Player_LoadGeneric(reader,maxchan,curious);
+ _mm_delete_mem_reader(reader);
+ }
+ return result;
+}
+
+/* Loads a module given a file pointer.
+ File is loaded from the current file seek position. */
+MIKMODAPI MODULE* Player_LoadFP(FILE* fp,int maxchan,BOOL curious)
+{
+ MODULE* result=NULL;
+ struct MREADER* reader=_mm_new_file_reader (fp);
+
+ if (reader) {
+ result=Player_LoadGeneric(reader,maxchan,curious);
+ _mm_delete_file_reader(reader);
+ }
+ return result;
+}
+
+/* Open a module via its filename. The loader will initialize the specified
+ song-player 'player'. */
+MIKMODAPI MODULE* Player_Load(CHAR* filename,int maxchan,BOOL curious)
+{
+ FILE *fp;
+ MODULE *mf=NULL;
+
+ if((fp=_mm_fopen(filename,"rb"))) {
+ mf=Player_LoadFP(fp,maxchan,curious);
+ _mm_fclose(fp);
+ }
+ return mf;
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mlreg.c b/src/libs/mikmod/mlreg.c
new file mode 100644
index 0000000..14f2d7f
--- /dev/null
+++ b/src/libs/mikmod/mlreg.c
@@ -0,0 +1,50 @@
+/* MikMod sound library
+ (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Routine for registering all loaders in libmikmod for the current platform.
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mikmod_internals.h"
+
+void MikMod_RegisterAllLoaders_internal(void)
+{
+ _mm_registerloader(&load_it);
+ _mm_registerloader(&load_mod);
+ _mm_registerloader(&load_s3m);
+ _mm_registerloader(&load_stm);
+ _mm_registerloader(&load_xm);
+}
+
+void MikMod_RegisterAllLoaders(void)
+{
+ MUTEX_LOCK(lists);
+ MikMod_RegisterAllLoaders_internal();
+ MUTEX_UNLOCK(lists);
+}
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mlutil.c b/src/libs/mikmod/mlutil.c
new file mode 100644
index 0000000..2ecf64e
--- /dev/null
+++ b/src/libs/mikmod/mlutil.c
@@ -0,0 +1,337 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Utility functions for the module loader
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+/*========== Shared tracker identifiers */
+
+CHAR *STM_Signatures[STM_NTRACKERS] = {
+ "!Scream!",
+ "BMOD2STM",
+ "WUZAMOD!"
+};
+
+CHAR *STM_Version[STM_NTRACKERS] = {
+ "Screamtracker 2",
+ "Converted by MOD2STM (STM format)",
+ "Wuzamod (STM format)"
+};
+
+/*========== Shared loader variables */
+
+SBYTE remap[UF_MAXCHAN]; /* for removing empty channels */
+UBYTE* poslookup=NULL; /* lookup table for pattern jumps after blank
+ pattern removal */
+UBYTE poslookupcnt;
+UWORD* origpositions=NULL;
+
+BOOL filters; /* resonant filters in use */
+UBYTE activemacro; /* active midi macro number for Sxx,xx<80h */
+UBYTE filtermacros[UF_MAXMACRO]; /* midi macro settings */
+FILTER filtersettings[UF_MAXFILTER]; /* computed filter settings */
+
+/*========== Linear periods stuff */
+
+int* noteindex=NULL; /* remap value for linear period modules */
+static int noteindexcount=0;
+
+int *AllocLinear(void)
+{
+ if(of.numsmp>noteindexcount) {
+ noteindexcount=of.numsmp;
+ noteindex=realloc(noteindex,noteindexcount*sizeof(int));
+ }
+ return noteindex;
+}
+
+void FreeLinear(void)
+{
+ if(noteindex) {
+ MikMod_free(noteindex);
+ noteindex=NULL;
+ }
+ noteindexcount=0;
+}
+
+int speed_to_finetune(ULONG speed,int sample)
+{
+ int note=1,finetune=0;
+ ULONG ctmp=0,tmp;
+
+ speed>>=1;
+ while((tmp=getfrequency(of.flags,getlinearperiod(note<<1,0)))<speed) {
+ ctmp=tmp;
+ note++;
+ }
+
+ if(tmp!=speed) {
+ if((tmp-speed)<(speed-ctmp))
+ while(tmp>speed)
+ tmp=getfrequency(of.flags,getlinearperiod(note<<1,--finetune));
+ else {
+ note--;
+ while(ctmp<speed)
+ ctmp=getfrequency(of.flags,getlinearperiod(note<<1,++finetune));
+ }
+ }
+
+ noteindex[sample]=note-4*OCTAVE;
+ return finetune;
+}
+
+/*========== Order stuff */
+
+/* handles S3M and IT orders */
+void S3MIT_CreateOrders(BOOL curious)
+{
+ int t;
+
+ of.numpos = 0;
+ memset(of.positions,0,poslookupcnt*sizeof(UWORD));
+ memset(poslookup,-1,256);
+ for(t=0;t<poslookupcnt;t++) {
+ int order=origpositions[t];
+ if(order==255) order=LAST_PATTERN;
+ of.positions[of.numpos]=order;
+ poslookup[t]=of.numpos; /* bug fix for freaky S3Ms / ITs */
+ if(origpositions[t]<254) of.numpos++;
+ else
+ /* end of song special order */
+ if((order==LAST_PATTERN)&&(!(curious--))) break;
+ }
+}
+
+/*========== Effect stuff */
+
+/* handles S3M and IT effects */
+void S3MIT_ProcessCmd(UBYTE cmd,UBYTE inf,unsigned int flags)
+{
+ UBYTE hi,lo;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ /* process S3M / IT specific command structure */
+
+ if(cmd!=255) {
+ switch(cmd) {
+ case 1: /* Axx set speed to xx */
+ UniEffect(UNI_S3MEFFECTA,inf);
+ break;
+ case 2: /* Bxx position jump */
+ if (inf<poslookupcnt) {
+ /* switch to curious mode if necessary, for example
+ sympex.it, deep joy.it */
+ if(((SBYTE)poslookup[inf]<0)&&(origpositions[inf]!=255))
+ S3MIT_CreateOrders(1);
+
+ if(!((SBYTE)poslookup[inf]<0))
+ UniPTEffect(0xb,poslookup[inf]);
+ }
+ break;
+ case 3: /* Cxx patternbreak to row xx */
+ if ((flags & S3MIT_OLDSTYLE) && !(flags & S3MIT_IT))
+ UniPTEffect(0xd,(inf>>4)*10+(inf&0xf));
+ else
+ UniPTEffect(0xd,inf);
+ break;
+ case 4: /* Dxy volumeslide */
+ UniEffect(UNI_S3MEFFECTD,inf);
+ break;
+ case 5: /* Exy toneslide down */
+ UniEffect(UNI_S3MEFFECTE,inf);
+ break;
+ case 6: /* Fxy toneslide up */
+ UniEffect(UNI_S3MEFFECTF,inf);
+ break;
+ case 7: /* Gxx Tone portamento, speed xx */
+ if (flags & S3MIT_OLDSTYLE)
+ UniPTEffect(0x3,inf);
+ else
+ UniEffect(UNI_ITEFFECTG,inf);
+ break;
+ case 8: /* Hxy vibrato */
+ if (flags & S3MIT_OLDSTYLE)
+ UniPTEffect(0x4,inf);
+ else
+ UniEffect(UNI_ITEFFECTH,inf);
+ break;
+ case 9: /* Ixy tremor, ontime x, offtime y */
+ if (flags & S3MIT_OLDSTYLE)
+ UniEffect(UNI_S3MEFFECTI,inf);
+ else
+ UniEffect(UNI_ITEFFECTI,inf);
+ break;
+ case 0xa: /* Jxy arpeggio */
+ UniPTEffect(0x0,inf);
+ break;
+ case 0xb: /* Kxy Dual command H00 & Dxy */
+ if (flags & S3MIT_OLDSTYLE)
+ UniPTEffect(0x4,0);
+ else
+ UniEffect(UNI_ITEFFECTH,0);
+ UniEffect(UNI_S3MEFFECTD,inf);
+ break;
+ case 0xc: /* Lxy Dual command G00 & Dxy */
+ if (flags & S3MIT_OLDSTYLE)
+ UniPTEffect(0x3,0);
+ else
+ UniEffect(UNI_ITEFFECTG,0);
+ UniEffect(UNI_S3MEFFECTD,inf);
+ break;
+ case 0xd: /* Mxx Set Channel Volume */
+ UniEffect(UNI_ITEFFECTM,inf);
+ break;
+ case 0xe: /* Nxy Slide Channel Volume */
+ UniEffect(UNI_ITEFFECTN,inf);
+ break;
+ case 0xf: /* Oxx set sampleoffset xx00h */
+ UniPTEffect(0x9,inf);
+ break;
+ case 0x10: /* Pxy Slide Panning Commands */
+ UniEffect(UNI_ITEFFECTP,inf);
+ break;
+ case 0x11: /* Qxy Retrig (+volumeslide) */
+ UniWriteByte(UNI_S3MEFFECTQ);
+ if(inf && !lo && !(flags & S3MIT_OLDSTYLE))
+ UniWriteByte(1);
+ else
+ UniWriteByte(inf);
+ break;
+ case 0x12: /* Rxy tremolo speed x, depth y */
+ UniEffect(UNI_S3MEFFECTR,inf);
+ break;
+ case 0x13: /* Sxx special commands */
+ if (inf>=0xf0) {
+ /* change resonant filter settings if necessary */
+ if((filters)&&((inf&0xf)!=activemacro)) {
+ activemacro=inf&0xf;
+ for(inf=0;inf<0x80;inf++)
+ filtersettings[inf].filter=filtermacros[activemacro];
+ }
+ } else {
+ /* Scream Tracker does not have samples larger than
+ 64 Kb, thus doesn't need the SAx effect */
+ if ((flags & S3MIT_SCREAM) && ((inf & 0xf0) == 0xa0))
+ break;
+
+ UniEffect(UNI_ITEFFECTS0,inf);
+ }
+ break;
+ case 0x14: /* Txx tempo */
+ if(inf>=0x20)
+ UniEffect(UNI_S3MEFFECTT,inf);
+ else {
+ if(!(flags & S3MIT_OLDSTYLE))
+ /* IT Tempo slide */
+ UniEffect(UNI_ITEFFECTT,inf);
+ }
+ break;
+ case 0x15: /* Uxy Fine Vibrato speed x, depth y */
+ if(flags & S3MIT_OLDSTYLE)
+ UniEffect(UNI_S3MEFFECTU,inf);
+ else
+ UniEffect(UNI_ITEFFECTU,inf);
+ break;
+ case 0x16: /* Vxx Set Global Volume */
+ UniEffect(UNI_XMEFFECTG,inf);
+ break;
+ case 0x17: /* Wxy Global Volume Slide */
+ UniEffect(UNI_ITEFFECTW,inf);
+ break;
+ case 0x18: /* Xxx amiga command 8xx */
+ if(flags & S3MIT_OLDSTYLE) {
+ if(inf>128)
+ UniEffect(UNI_ITEFFECTS0,0x91); /* surround */
+ else
+ UniPTEffect(0x8,(inf==128)?255:(inf<<1));
+ } else
+ UniPTEffect(0x8,inf);
+ break;
+ case 0x19: /* Yxy Panbrello speed x, depth y */
+ UniEffect(UNI_ITEFFECTY,inf);
+ break;
+ case 0x1a: /* Zxx midi/resonant filters */
+ if(filtersettings[inf].filter) {
+ UniWriteByte(UNI_ITEFFECTZ);
+ UniWriteByte(filtersettings[inf].filter);
+ UniWriteByte(filtersettings[inf].inf);
+ }
+ break;
+ }
+ }
+}
+
+/*========== Unitrk stuff */
+
+/* Generic effect writing routine */
+void UniEffect(UWORD eff,UWORD dat)
+{
+ if((!eff)||(eff>=UNI_LAST)) return;
+
+ UniWriteByte(eff);
+ if(unioperands[eff]==2)
+ UniWriteWord(dat);
+ else
+ UniWriteByte(dat);
+}
+
+/* Appends UNI_PTEFFECTX opcode to the unitrk stream. */
+void UniPTEffect(UBYTE eff, UBYTE dat)
+{
+#ifdef MIKMOD_DEBUG
+ if (eff>=0x10)
+ fprintf(stderr,"UniPTEffect called with incorrect eff value %d\n",eff);
+ else
+#endif
+ if((eff)||(dat)||(of.flags&UF_ARPMEM)) UniEffect(UNI_PTEFFECT0+eff,dat);
+}
+
+/* Appends UNI_VOLEFFECT + effect/dat to unistream. */
+void UniVolEffect(UWORD eff,UBYTE dat)
+{
+ if((eff)||(dat)) { /* don't write empty effect */
+ UniWriteByte(UNI_VOLEFFECTS);
+ UniWriteByte(eff);UniWriteByte(dat);
+ }
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mmalloc.c b/src/libs/mikmod/mmalloc.c
new file mode 100644
index 0000000..cc8f8d9
--- /dev/null
+++ b/src/libs/mikmod/mmalloc.c
@@ -0,0 +1,73 @@
+/* MikMod sound library
+ (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Dynamic memory routines
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mikmod_internals.h"
+
+void* MikMod_realloc(void *data, size_t size)
+{
+ if (data)
+ return realloc(data, size);
+ else
+ return MikMod_malloc(size);
+}
+
+/* Same as malloc, but sets error variable _mm_error when fails */
+void* MikMod_malloc(size_t size)
+{
+ void *d;
+
+ if(!(d=calloc(1,size))) {
+ _mm_errno = MMERR_OUT_OF_MEMORY;
+ if(_mm_errorhandler) _mm_errorhandler();
+ }
+ return d;
+}
+
+/* Same as calloc, but sets error variable _mm_error when fails */
+void* MikMod_calloc(size_t nitems,size_t size)
+{
+ void *d;
+
+ if(!(d=calloc(nitems,size))) {
+ _mm_errno = MMERR_OUT_OF_MEMORY;
+ if(_mm_errorhandler) _mm_errorhandler();
+ }
+ return d;
+}
+
+void MikMod_free(void *p)
+{
+ if (p)
+ free(p);
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mmerror.c b/src/libs/mikmod/mmerror.c
new file mode 100644
index 0000000..efbde19
--- /dev/null
+++ b/src/libs/mikmod/mmerror.c
@@ -0,0 +1,197 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Error handling functions.
+ Register an error handler with _mm_RegisterErrorHandler() and you're all set.
+
+==============================================================================*/
+
+/*
+
+ The global variables _mm_errno, and _mm_critical are set before the error
+ handler in called. See below for the values of these variables.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mikmod_internals.h"
+
+CHAR *_mm_errmsg[MMERR_MAX+1] =
+{
+/* No error */
+
+ "No error",
+
+/* Generic errors */
+
+ "Could not open requested file",
+ "Out of memory",
+ "Dynamic linking failed",
+
+/* Sample errors */
+
+ "Out of memory to load sample",
+ "Out of sample handles to load sample",
+ "Sample format not recognized",
+
+/* Module errors */
+
+ "Failure loading module pattern",
+ "Failure loading module track",
+ "Failure loading module header",
+ "Failure loading sampleinfo",
+ "Module format not recognized",
+ "Module sample format not recognized",
+ "Synthsounds not supported in MED files",
+ "Compressed sample is invalid",
+
+/* Driver errors: */
+
+ "Sound device not detected",
+ "Device number out of range",
+ "Software mixer failure",
+ "Could not open sound device",
+ "This driver supports 8 bit linear output only",
+ "This driver supports 16 bit linear output only",
+ "This driver supports stereo output only",
+ "This driver supports uLaw output (8 bit mono, 8 kHz) only",
+ "Unable to set non-blocking mode for audio device",
+
+/* AudioFile driver errors */
+
+ "Cannot find suitable AudioFile audio port",
+
+/* AIX driver errors */
+
+ "Configuration (init step) of audio device failed",
+ "Configuration (control step) of audio device failed",
+ "Configuration (start step) of audio device failed",
+
+/* ALSA driver errors */
+
+/* EsounD driver errors */
+
+/* Ultrasound driver errors */
+
+ "Ultrasound driver only works in 16 bit stereo 44 KHz",
+ "Ultrasound card could not be reset",
+ "Could not start Ultrasound timer",
+
+/* HP driver errors */
+
+ "Unable to select 16bit-linear sample format",
+ "Could not select requested sample-rate",
+ "Could not select requested number of channels",
+ "Unable to select audio output",
+ "Unable to get audio description",
+ "Could not set transmission buffer size",
+
+/* Open Sound System driver errors */
+
+ "Could not set fragment size",
+ "Could not set sample size",
+ "Could not set mono/stereo setting",
+ "Could not set sample rate",
+
+/* SGI driver errors */
+
+ "Unsupported sample rate",
+ "Hardware does not support 16 bit sound",
+ "Hardware does not support 8 bit sound",
+ "Hardware does not support stereo sound",
+ "Hardware does not support mono sound",
+
+/* Sun driver errors */
+
+ "Sound device initialization failed",
+
+/* OS/2 drivers errors */
+
+ "Could not set mixing parameters",
+ "Could not create playback semaphores",
+ "Could not create playback timer",
+ "Could not create playback thread",
+
+/* DirectSound driver errors */
+
+ "Could not set playback priority",
+ "Could not create playback buffers",
+ "Could not set playback format",
+ "Could not register callback",
+ "Could not register event",
+ "Could not create playback thread",
+ "Could not initialize playback thread",
+
+/* Windows Multimedia API driver errors */
+
+ "Invalid device handle",
+ "The resource is already allocated",
+ "Invalid device identifier",
+ "Unsupported output format",
+ "Unknown error",
+
+/* Macintosh driver errors */
+
+ "Unsupported sample rate",
+ "Could not start playback",
+
+/* Invalid error */
+
+ "Invalid error code"
+};
+
+MIKMODAPI char *MikMod_strerror(int code)
+{
+ if ((code<0)||(code>MMERR_MAX)) code=MMERR_MAX+1;
+ return _mm_errmsg[code];
+}
+
+/* User installed error callback */
+MikMod_handler_t _mm_errorhandler = NULL;
+MIKMODAPI int _mm_errno = 0;
+MIKMODAPI BOOL _mm_critical = 0;
+
+MikMod_handler_t _mm_registererrorhandler(MikMod_handler_t proc)
+{
+ MikMod_handler_t oldproc=_mm_errorhandler;
+
+ _mm_errorhandler = proc;
+ return oldproc;
+}
+
+MIKMODAPI MikMod_handler_t MikMod_RegisterErrorHandler(MikMod_handler_t proc)
+{
+ MikMod_handler_t result;
+
+ MUTEX_LOCK(vars);
+ result=_mm_registererrorhandler(proc);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mmio.c b/src/libs/mikmod/mmio.c
new file mode 100644
index 0000000..0f8a079
--- /dev/null
+++ b/src/libs/mikmod/mmio.c
@@ -0,0 +1,490 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Portable file I/O routines
+
+==============================================================================*/
+
+/*
+
+ The way this module works:
+
+ - _mm_fopen will call the errorhandler [see mmerror.c] in addition to
+ setting _mm_errno on exit.
+ - _mm_iobase is for internal use. It is used by Player_LoadFP to
+ ensure that it works properly with wad files.
+ - _mm_read_I_* and _mm_read_M_* differ : the first is for reading data
+ written by a little endian (intel) machine, and the second is for reading
+ big endian (Mac, RISC, Alpha) machine data.
+ - _mm_write functions work the same as the _mm_read functions.
+ - _mm_read_string is for reading binary strings. It is basically the same
+ as an fread of bytes.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+//#include "mikmod.h"
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fclose(FILE *);
+extern int fgetc(FILE *);
+extern int fputc(int, FILE *);
+extern size_t fread(void *, size_t, size_t, FILE *);
+extern int fseek(FILE *, long, int);
+extern size_t fwrite(const void *, size_t, size_t, FILE *);
+#endif
+
+#define COPY_BUFSIZE 1024
+
+/* some prototypes */
+static BOOL _mm_MemReader_Eof(MREADER* reader);
+static BOOL _mm_MemReader_Read(MREADER* reader,void* ptr,size_t size);
+static int _mm_MemReader_Get(MREADER* reader);
+static BOOL _mm_MemReader_Seek(MREADER* reader,long offset,int whence);
+static long _mm_MemReader_Tell(MREADER* reader);
+
+
+FILE* _mm_fopen(CHAR* fname,CHAR* attrib)
+{
+ FILE *fp;
+
+ if(!(fp=fopen(fname,attrib))) {
+ _mm_errno = MMERR_OPENING_FILE;
+ if(_mm_errorhandler) _mm_errorhandler();
+ }
+ return fp;
+}
+
+BOOL _mm_FileExists(CHAR* fname)
+{
+ FILE *fp;
+
+ if(!(fp=fopen(fname,"r"))) return 0;
+ fclose(fp);
+
+ return 1;
+}
+
+int _mm_fclose(FILE *fp)
+{
+ return fclose(fp);
+}
+
+/* Sets the current file-position as the new iobase */
+void _mm_iobase_setcur(MREADER* reader)
+{
+ reader->prev_iobase=reader->iobase; /* store old value in case of revert */
+ reader->iobase=reader->Tell(reader);
+}
+
+/* Reverts to the last known iobase value. */
+void _mm_iobase_revert(MREADER* reader)
+{
+ reader->iobase=reader->prev_iobase;
+}
+
+/*========== File Reader */
+
+typedef struct MFILEREADER {
+ MREADER core;
+ FILE* file;
+} MFILEREADER;
+
+static BOOL _mm_FileReader_Eof(MREADER* reader)
+{
+ return feof(((MFILEREADER*)reader)->file);
+}
+
+static BOOL _mm_FileReader_Read(MREADER* reader,void* ptr,size_t size)
+{
+ return !!fread(ptr,size,1,((MFILEREADER*)reader)->file);
+}
+
+static int _mm_FileReader_Get(MREADER* reader)
+{
+ return fgetc(((MFILEREADER*)reader)->file);
+}
+
+static BOOL _mm_FileReader_Seek(MREADER* reader,long offset,int whence)
+{
+ return fseek(((MFILEREADER*)reader)->file,
+ (whence==SEEK_SET)?offset+reader->iobase:offset,whence);
+}
+
+static long _mm_FileReader_Tell(MREADER* reader)
+{
+ return ftell(((MFILEREADER*)reader)->file)-reader->iobase;
+}
+
+MREADER *_mm_new_file_reader(FILE* fp)
+{
+ MFILEREADER* reader=(MFILEREADER*)MikMod_malloc(sizeof(MFILEREADER));
+ if (reader) {
+ reader->core.Eof =&_mm_FileReader_Eof;
+ reader->core.Read=&_mm_FileReader_Read;
+ reader->core.Get =&_mm_FileReader_Get;
+ reader->core.Seek=&_mm_FileReader_Seek;
+ reader->core.Tell=&_mm_FileReader_Tell;
+ reader->file=fp;
+ }
+ return (MREADER*)reader;
+}
+
+void _mm_delete_file_reader (MREADER* reader)
+{
+ if(reader) MikMod_free(reader);
+}
+
+/*========== File Writer */
+
+typedef struct MFILEWRITER {
+ MWRITER core;
+ FILE* file;
+} MFILEWRITER;
+
+static BOOL _mm_FileWriter_Seek(MWRITER* writer,long offset,int whence)
+{
+ return fseek(((MFILEWRITER*)writer)->file,offset,whence);
+}
+
+static long _mm_FileWriter_Tell(MWRITER* writer)
+{
+ return ftell(((MFILEWRITER*)writer)->file);
+}
+
+static BOOL _mm_FileWriter_Write(MWRITER* writer,void* ptr,size_t size)
+{
+ return (fwrite(ptr,size,1,((MFILEWRITER*)writer)->file)==size);
+}
+
+static BOOL _mm_FileWriter_Put(MWRITER* writer,int value)
+{
+ return fputc(value,((MFILEWRITER*)writer)->file);
+}
+
+MWRITER *_mm_new_file_writer(FILE* fp)
+{
+ MFILEWRITER* writer=(MFILEWRITER*)MikMod_malloc(sizeof(MFILEWRITER));
+ if (writer) {
+ writer->core.Seek =&_mm_FileWriter_Seek;
+ writer->core.Tell =&_mm_FileWriter_Tell;
+ writer->core.Write=&_mm_FileWriter_Write;
+ writer->core.Put =&_mm_FileWriter_Put;
+ writer->file=fp;
+ }
+ return (MWRITER*) writer;
+}
+
+void _mm_delete_file_writer (MWRITER* writer)
+{
+ if(writer) MikMod_free (writer);
+}
+
+/*========== Memory Reader */
+
+
+typedef struct MMEMREADER {
+ MREADER core;
+ const void *buffer;
+ long len;
+ long pos;
+} MMEMREADER;
+
+void _mm_delete_mem_reader(MREADER* reader)
+{
+ if (reader) { MikMod_free(reader); }
+}
+
+MREADER *_mm_new_mem_reader(const void *buffer, int len)
+{
+ MMEMREADER* reader=(MMEMREADER*)MikMod_malloc(sizeof(MMEMREADER));
+ if (reader)
+ {
+ reader->core.Eof =&_mm_MemReader_Eof;
+ reader->core.Read=&_mm_MemReader_Read;
+ reader->core.Get =&_mm_MemReader_Get;
+ reader->core.Seek=&_mm_MemReader_Seek;
+ reader->core.Tell=&_mm_MemReader_Tell;
+ reader->buffer = buffer;
+ reader->len = len;
+ reader->pos = 0;
+ }
+ return (MREADER*)reader;
+}
+
+static BOOL _mm_MemReader_Eof(MREADER* reader)
+{
+ if (!reader) { return 1; }
+ if ( ((MMEMREADER*)reader)->pos > ((MMEMREADER*)reader)->len ) {
+ return 1;
+ }
+ return 0;
+}
+
+static BOOL _mm_MemReader_Read(MREADER* reader,void* ptr,size_t size)
+{
+ unsigned char *d=ptr;
+ const unsigned char *s;
+
+ if (!reader) { return 0; }
+
+ if (reader->Eof(reader)) { return 0; }
+
+ s = ((MMEMREADER*)reader)->buffer;
+ s += ((MMEMREADER*)reader)->pos;
+
+ if ( ((MMEMREADER*)reader)->pos + (long)size > ((MMEMREADER*)reader)->len)
+ {
+ ((MMEMREADER*)reader)->pos = ((MMEMREADER*)reader)->len;
+ return 0; /* not enough remaining bytes */
+ }
+
+ ((MMEMREADER*)reader)->pos += (long)size;
+
+ while (size--)
+ {
+ *d = *s;
+ s++;
+ d++;
+ }
+
+ return 1;
+}
+
+static int _mm_MemReader_Get(MREADER* reader)
+{
+ int pos;
+
+ if (reader->Eof(reader)) { return 0; }
+
+ pos = ((MMEMREADER*)reader)->pos;
+ ((MMEMREADER*)reader)->pos++;
+
+ return ((unsigned char*)(((MMEMREADER*)reader)->buffer))[pos];
+}
+
+static BOOL _mm_MemReader_Seek(MREADER* reader,long offset,int whence)
+{
+ if (!reader) { return -1; }
+
+ switch(whence)
+ {
+ case SEEK_CUR:
+ ((MMEMREADER*)reader)->pos += offset;
+ break;
+ case SEEK_SET:
+ ((MMEMREADER*)reader)->pos = offset;
+ break;
+ case SEEK_END:
+ ((MMEMREADER*)reader)->pos = ((MMEMREADER*)reader)->len - offset - 1;
+ break;
+ }
+ if ( ((MMEMREADER*)reader)->pos < 0) { ((MMEMREADER*)reader)->pos = 0; }
+ if ( ((MMEMREADER*)reader)->pos > ((MMEMREADER*)reader)->len ) {
+ ((MMEMREADER*)reader)->pos = ((MMEMREADER*)reader)->len;
+ }
+ return 0;
+}
+
+static long _mm_MemReader_Tell(MREADER* reader)
+{
+ if (reader) {
+ return ((MMEMREADER*)reader)->pos;
+ }
+ return 0;
+}
+
+/*========== Write functions */
+
+void _mm_write_string(CHAR* data,MWRITER* writer)
+{
+ if(data)
+ _mm_write_UBYTES(data,strlen(data),writer);
+}
+
+void _mm_write_M_UWORD(UWORD data,MWRITER* writer)
+{
+ _mm_write_UBYTE(data>>8,writer);
+ _mm_write_UBYTE(data&0xff,writer);
+}
+
+void _mm_write_I_UWORD(UWORD data,MWRITER* writer)
+{
+ _mm_write_UBYTE(data&0xff,writer);
+ _mm_write_UBYTE(data>>8,writer);
+}
+
+void _mm_write_M_ULONG(ULONG data,MWRITER* writer)
+{
+ _mm_write_M_UWORD(data>>16,writer);
+ _mm_write_M_UWORD(data&0xffff,writer);
+}
+
+void _mm_write_I_ULONG(ULONG data,MWRITER* writer)
+{
+ _mm_write_I_UWORD(data&0xffff,writer);
+ _mm_write_I_UWORD(data>>16,writer);
+}
+
+void _mm_write_M_SWORD(SWORD data,MWRITER* writer)
+{
+ _mm_write_M_UWORD((UWORD)data,writer);
+}
+
+void _mm_write_I_SWORD(SWORD data,MWRITER* writer)
+{
+ _mm_write_I_UWORD((UWORD)data,writer);
+}
+
+void _mm_write_M_SLONG(SLONG data,MWRITER* writer)
+{
+ _mm_write_M_ULONG((ULONG)data,writer);
+}
+
+void _mm_write_I_SLONG(SLONG data,MWRITER* writer)
+{
+ _mm_write_I_ULONG((ULONG)data,writer);
+}
+
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C
+#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name,type) \
+void _mm_write_##type_name##S (type *buffer,int number,MWRITER* writer) \
+{ \
+ while(number-->0) \
+ _mm_write_##type_name(*(buffer++),writer); \
+}
+#else
+#define DEFINE_MULTIPLE_WRITE_FUNCTION(type_name,type) \
+void _mm_write_/**/type_name/**/S (type *buffer,int number,MWRITER* writer) \
+{ \
+ while(number-->0) \
+ _mm_write_/**/type_name(*(buffer++),writer); \
+}
+#endif
+
+DEFINE_MULTIPLE_WRITE_FUNCTION(M_SWORD,SWORD)
+DEFINE_MULTIPLE_WRITE_FUNCTION(M_UWORD,UWORD)
+DEFINE_MULTIPLE_WRITE_FUNCTION(I_SWORD,SWORD)
+DEFINE_MULTIPLE_WRITE_FUNCTION(I_UWORD,UWORD)
+
+DEFINE_MULTIPLE_WRITE_FUNCTION(M_SLONG,SLONG)
+DEFINE_MULTIPLE_WRITE_FUNCTION(M_ULONG,ULONG)
+DEFINE_MULTIPLE_WRITE_FUNCTION(I_SLONG,SLONG)
+DEFINE_MULTIPLE_WRITE_FUNCTION(I_ULONG,ULONG)
+
+/*========== Read functions */
+
+int _mm_read_string(CHAR* buffer,int number,MREADER* reader)
+{
+ return reader->Read(reader,buffer,number);
+}
+
+UWORD _mm_read_M_UWORD(MREADER* reader)
+{
+ UWORD result=((UWORD)_mm_read_UBYTE(reader))<<8;
+ result|=_mm_read_UBYTE(reader);
+ return result;
+}
+
+UWORD _mm_read_I_UWORD(MREADER* reader)
+{
+ UWORD result=_mm_read_UBYTE(reader);
+ result|=((UWORD)_mm_read_UBYTE(reader))<<8;
+ return result;
+}
+
+ULONG _mm_read_M_ULONG(MREADER* reader)
+{
+ ULONG result=((ULONG)_mm_read_M_UWORD(reader))<<16;
+ result|=_mm_read_M_UWORD(reader);
+ return result;
+}
+
+ULONG _mm_read_I_ULONG(MREADER* reader)
+{
+ ULONG result=_mm_read_I_UWORD(reader);
+ result|=((ULONG)_mm_read_I_UWORD(reader))<<16;
+ return result;
+}
+
+SWORD _mm_read_M_SWORD(MREADER* reader)
+{
+ return((SWORD)_mm_read_M_UWORD(reader));
+}
+
+SWORD _mm_read_I_SWORD(MREADER* reader)
+{
+ return((SWORD)_mm_read_I_UWORD(reader));
+}
+
+SLONG _mm_read_M_SLONG(MREADER* reader)
+{
+ return((SLONG)_mm_read_M_ULONG(reader));
+}
+
+SLONG _mm_read_I_SLONG(MREADER* reader)
+{
+ return((SLONG)_mm_read_I_ULONG(reader));
+}
+
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C
+#define DEFINE_MULTIPLE_READ_FUNCTION(type_name,type) \
+int _mm_read_##type_name##S (type *buffer,int number,MREADER* reader) \
+{ \
+ while(number-->0) \
+ *(buffer++)=_mm_read_##type_name(reader); \
+ return !reader->Eof(reader); \
+}
+#else
+#define DEFINE_MULTIPLE_READ_FUNCTION(type_name,type) \
+int _mm_read_/**/type_name/**/S (type *buffer,int number,MREADER* reader) \
+{ \
+ while(number-->0) \
+ *(buffer++)=_mm_read_/**/type_name(reader); \
+ return !reader->Eof(reader); \
+}
+#endif
+
+DEFINE_MULTIPLE_READ_FUNCTION(M_SWORD,SWORD)
+DEFINE_MULTIPLE_READ_FUNCTION(M_UWORD,UWORD)
+DEFINE_MULTIPLE_READ_FUNCTION(I_SWORD,SWORD)
+DEFINE_MULTIPLE_READ_FUNCTION(I_UWORD,UWORD)
+
+DEFINE_MULTIPLE_READ_FUNCTION(M_SLONG,SLONG)
+DEFINE_MULTIPLE_READ_FUNCTION(M_ULONG,ULONG)
+DEFINE_MULTIPLE_READ_FUNCTION(I_SLONG,SLONG)
+DEFINE_MULTIPLE_READ_FUNCTION(I_ULONG,ULONG)
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mplayer.c b/src/libs/mikmod/mplayer.c
new file mode 100644
index 0000000..812410a
--- /dev/null
+++ b/src/libs/mikmod/mplayer.c
@@ -0,0 +1,3561 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ The Protracker Player Driver
+
+ The protracker driver supports all base Protracker 3.x commands and features.
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdarg.h>
+#ifdef SRANDOM_IN_MATH_H
+#include <math.h>
+#else
+#include <stdlib.h>
+#endif
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+extern long int random(void);
+#endif
+
+/* The currently playing module */
+/* This variable should better be static, but it would break the ABI, so this
+ will wait */
+/*static*/ MODULE *pf = NULL;
+
+#define HIGH_OCTAVE 2 /* number of above-range octaves */
+
+static UWORD oldperiods[OCTAVE*2]={
+ 0x6b00, 0x6800, 0x6500, 0x6220, 0x5f50, 0x5c80,
+ 0x5a00, 0x5740, 0x54d0, 0x5260, 0x5010, 0x4dc0,
+ 0x4b90, 0x4960, 0x4750, 0x4540, 0x4350, 0x4160,
+ 0x3f90, 0x3dc0, 0x3c10, 0x3a40, 0x38b0, 0x3700
+};
+
+static UBYTE VibratoTable[32]={
+ 0, 24, 49, 74, 97,120,141,161,180,197,212,224,235,244,250,253,
+ 255,253,250,244,235,224,212,197,180,161,141,120, 97, 74, 49, 24
+};
+
+static UBYTE avibtab[128]={
+ 0, 1, 3, 4, 6, 7, 9,10,12,14,15,17,18,20,21,23,
+ 24,25,27,28,30,31,32,34,35,36,38,39,40,41,42,44,
+ 45,46,47,48,49,50,51,52,53,54,54,55,56,57,57,58,
+ 59,59,60,60,61,61,62,62,62,63,63,63,63,63,63,63,
+ 64,63,63,63,63,63,63,63,62,62,62,61,61,60,60,59,
+ 59,58,57,57,56,55,54,54,53,52,51,50,49,48,47,46,
+ 45,44,42,41,40,39,38,36,35,34,32,31,30,28,27,25,
+ 24,23,21,20,18,17,15,14,12,10, 9, 7, 6, 4, 3, 1
+};
+
+/* Triton's linear periods to frequency translation table (for XM modules) */
+static ULONG lintab[768]={
+ 535232,534749,534266,533784,533303,532822,532341,531861,
+ 531381,530902,530423,529944,529466,528988,528511,528034,
+ 527558,527082,526607,526131,525657,525183,524709,524236,
+ 523763,523290,522818,522346,521875,521404,520934,520464,
+ 519994,519525,519057,518588,518121,517653,517186,516720,
+ 516253,515788,515322,514858,514393,513929,513465,513002,
+ 512539,512077,511615,511154,510692,510232,509771,509312,
+ 508852,508393,507934,507476,507018,506561,506104,505647,
+ 505191,504735,504280,503825,503371,502917,502463,502010,
+ 501557,501104,500652,500201,499749,499298,498848,498398,
+ 497948,497499,497050,496602,496154,495706,495259,494812,
+ 494366,493920,493474,493029,492585,492140,491696,491253,
+ 490809,490367,489924,489482,489041,488600,488159,487718,
+ 487278,486839,486400,485961,485522,485084,484647,484210,
+ 483773,483336,482900,482465,482029,481595,481160,480726,
+ 480292,479859,479426,478994,478562,478130,477699,477268,
+ 476837,476407,475977,475548,475119,474690,474262,473834,
+ 473407,472979,472553,472126,471701,471275,470850,470425,
+ 470001,469577,469153,468730,468307,467884,467462,467041,
+ 466619,466198,465778,465358,464938,464518,464099,463681,
+ 463262,462844,462427,462010,461593,461177,460760,460345,
+ 459930,459515,459100,458686,458272,457859,457446,457033,
+ 456621,456209,455797,455386,454975,454565,454155,453745,
+ 453336,452927,452518,452110,451702,451294,450887,450481,
+ 450074,449668,449262,448857,448452,448048,447644,447240,
+ 446836,446433,446030,445628,445226,444824,444423,444022,
+ 443622,443221,442821,442422,442023,441624,441226,440828,
+ 440430,440033,439636,439239,438843,438447,438051,437656,
+ 437261,436867,436473,436079,435686,435293,434900,434508,
+ 434116,433724,433333,432942,432551,432161,431771,431382,
+ 430992,430604,430215,429827,429439,429052,428665,428278,
+ 427892,427506,427120,426735,426350,425965,425581,425197,
+ 424813,424430,424047,423665,423283,422901,422519,422138,
+ 421757,421377,420997,420617,420237,419858,419479,419101,
+ 418723,418345,417968,417591,417214,416838,416462,416086,
+ 415711,415336,414961,414586,414212,413839,413465,413092,
+ 412720,412347,411975,411604,411232,410862,410491,410121,
+ 409751,409381,409012,408643,408274,407906,407538,407170,
+ 406803,406436,406069,405703,405337,404971,404606,404241,
+ 403876,403512,403148,402784,402421,402058,401695,401333,
+ 400970,400609,400247,399886,399525,399165,398805,398445,
+ 398086,397727,397368,397009,396651,396293,395936,395579,
+ 395222,394865,394509,394153,393798,393442,393087,392733,
+ 392378,392024,391671,391317,390964,390612,390259,389907,
+ 389556,389204,388853,388502,388152,387802,387452,387102,
+ 386753,386404,386056,385707,385359,385012,384664,384317,
+ 383971,383624,383278,382932,382587,382242,381897,381552,
+ 381208,380864,380521,380177,379834,379492,379149,378807,
+ 378466,378124,377783,377442,377102,376762,376422,376082,
+ 375743,375404,375065,374727,374389,374051,373714,373377,
+ 373040,372703,372367,372031,371695,371360,371025,370690,
+ 370356,370022,369688,369355,369021,368688,368356,368023,
+ 367691,367360,367028,366697,366366,366036,365706,365376,
+ 365046,364717,364388,364059,363731,363403,363075,362747,
+ 362420,362093,361766,361440,361114,360788,360463,360137,
+ 359813,359488,359164,358840,358516,358193,357869,357547,
+ 357224,356902,356580,356258,355937,355616,355295,354974,
+ 354654,354334,354014,353695,353376,353057,352739,352420,
+ 352103,351785,351468,351150,350834,350517,350201,349885,
+ 349569,349254,348939,348624,348310,347995,347682,347368,
+ 347055,346741,346429,346116,345804,345492,345180,344869,
+ 344558,344247,343936,343626,343316,343006,342697,342388,
+ 342079,341770,341462,341154,340846,340539,340231,339924,
+ 339618,339311,339005,338700,338394,338089,337784,337479,
+ 337175,336870,336566,336263,335959,335656,335354,335051,
+ 334749,334447,334145,333844,333542,333242,332941,332641,
+ 332341,332041,331741,331442,331143,330844,330546,330247,
+ 329950,329652,329355,329057,328761,328464,328168,327872,
+ 327576,327280,326985,326690,326395,326101,325807,325513,
+ 325219,324926,324633,324340,324047,323755,323463,323171,
+ 322879,322588,322297,322006,321716,321426,321136,320846,
+ 320557,320267,319978,319690,319401,319113,318825,318538,
+ 318250,317963,317676,317390,317103,316817,316532,316246,
+ 315961,315676,315391,315106,314822,314538,314254,313971,
+ 313688,313405,313122,312839,312557,312275,311994,311712,
+ 311431,311150,310869,310589,310309,310029,309749,309470,
+ 309190,308911,308633,308354,308076,307798,307521,307243,
+ 306966,306689,306412,306136,305860,305584,305308,305033,
+ 304758,304483,304208,303934,303659,303385,303112,302838,
+ 302565,302292,302019,301747,301475,301203,300931,300660,
+ 300388,300117,299847,299576,299306,299036,298766,298497,
+ 298227,297958,297689,297421,297153,296884,296617,296349,
+ 296082,295815,295548,295281,295015,294749,294483,294217,
+ 293952,293686,293421,293157,292892,292628,292364,292100,
+ 291837,291574,291311,291048,290785,290523,290261,289999,
+ 289737,289476,289215,288954,288693,288433,288173,287913,
+ 287653,287393,287134,286875,286616,286358,286099,285841,
+ 285583,285326,285068,284811,284554,284298,284041,283785,
+ 283529,283273,283017,282762,282507,282252,281998,281743,
+ 281489,281235,280981,280728,280475,280222,279969,279716,
+ 279464,279212,278960,278708,278457,278206,277955,277704,
+ 277453,277203,276953,276703,276453,276204,275955,275706,
+ 275457,275209,274960,274712,274465,274217,273970,273722,
+ 273476,273229,272982,272736,272490,272244,271999,271753,
+ 271508,271263,271018,270774,270530,270286,270042,269798,
+ 269555,269312,269069,268826,268583,268341,268099,267857
+};
+
+#define LOGFAC 2*16
+static UWORD logtab[104]={
+ LOGFAC*907,LOGFAC*900,LOGFAC*894,LOGFAC*887,
+ LOGFAC*881,LOGFAC*875,LOGFAC*868,LOGFAC*862,
+ LOGFAC*856,LOGFAC*850,LOGFAC*844,LOGFAC*838,
+ LOGFAC*832,LOGFAC*826,LOGFAC*820,LOGFAC*814,
+ LOGFAC*808,LOGFAC*802,LOGFAC*796,LOGFAC*791,
+ LOGFAC*785,LOGFAC*779,LOGFAC*774,LOGFAC*768,
+ LOGFAC*762,LOGFAC*757,LOGFAC*752,LOGFAC*746,
+ LOGFAC*741,LOGFAC*736,LOGFAC*730,LOGFAC*725,
+ LOGFAC*720,LOGFAC*715,LOGFAC*709,LOGFAC*704,
+ LOGFAC*699,LOGFAC*694,LOGFAC*689,LOGFAC*684,
+ LOGFAC*678,LOGFAC*675,LOGFAC*670,LOGFAC*665,
+ LOGFAC*660,LOGFAC*655,LOGFAC*651,LOGFAC*646,
+ LOGFAC*640,LOGFAC*636,LOGFAC*632,LOGFAC*628,
+ LOGFAC*623,LOGFAC*619,LOGFAC*614,LOGFAC*610,
+ LOGFAC*604,LOGFAC*601,LOGFAC*597,LOGFAC*592,
+ LOGFAC*588,LOGFAC*584,LOGFAC*580,LOGFAC*575,
+ LOGFAC*570,LOGFAC*567,LOGFAC*563,LOGFAC*559,
+ LOGFAC*555,LOGFAC*551,LOGFAC*547,LOGFAC*543,
+ LOGFAC*538,LOGFAC*535,LOGFAC*532,LOGFAC*528,
+ LOGFAC*524,LOGFAC*520,LOGFAC*516,LOGFAC*513,
+ LOGFAC*508,LOGFAC*505,LOGFAC*502,LOGFAC*498,
+ LOGFAC*494,LOGFAC*491,LOGFAC*487,LOGFAC*484,
+ LOGFAC*480,LOGFAC*477,LOGFAC*474,LOGFAC*470,
+ LOGFAC*467,LOGFAC*463,LOGFAC*460,LOGFAC*457,
+ LOGFAC*453,LOGFAC*450,LOGFAC*447,LOGFAC*443,
+ LOGFAC*440,LOGFAC*437,LOGFAC*434,LOGFAC*431
+};
+
+static SBYTE PanbrelloTable[256]={
+ 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23,
+ 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59,
+ 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60,
+ 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46,
+ 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26,
+ 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2,
+ 0,- 2,- 3,- 5,- 6,- 8,- 9,-11,-12,-14,-16,-17,-19,-20,-22,-23,
+ -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44,
+ -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59,
+ -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64,
+ -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60,
+ -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46,
+ -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26,
+ -24,-23,-22,-20,-19,-17,-16,-14,-12,-11,- 9,- 8,- 6,- 5,- 3,- 2
+};
+
+/* returns a random value between 0 and ceil-1, ceil must be a power of two */
+static int getrandom(int ceil)
+{
+#ifdef HAVE_SRANDOM
+ return random()&(ceil-1);
+#else
+ return (rand()*ceil)/(RAND_MAX+1.0);
+#endif
+}
+
+/* New Note Action Scoring System :
+ --------------------------------
+ 1) total-volume (fadevol, chanvol, volume) is the main scorer.
+ 2) a looping sample is a bonus x2
+ 3) a foreground channel is a bonus x4
+ 4) an active envelope with keyoff is a handicap -x2
+*/
+static int MP_FindEmptyChannel(MODULE *mod)
+{
+ MP_VOICE *a;
+ ULONG t,k,tvol,pp;
+
+ for (t=0;t<md_sngchn;t++)
+ if (((mod->voice[t].main.kick==KICK_ABSENT)||
+ (mod->voice[t].main.kick==KICK_ENV))&&
+ Voice_Stopped_internal(t))
+ return t;
+
+ tvol=0xffffffUL;t=-1;a=mod->voice;
+ for (k=0;k<md_sngchn;k++,a++) {
+ /* allow us to take over a nonexisting sample */
+ if (!a->main.s)
+ return k;
+
+ if ((a->main.kick==KICK_ABSENT)||(a->main.kick==KICK_ENV)) {
+ pp=a->totalvol<<((a->main.s->flags&SF_LOOP)?1:0);
+ if ((a->master)&&(a==a->master->slave))
+ pp<<=2;
+
+ if (pp<tvol) {
+ tvol=pp;
+ t=k;
+ }
+ }
+ }
+
+ if (tvol>8000*7) return -1;
+ return t;
+}
+
+static SWORD Interpolate(SWORD p,SWORD p1,SWORD p2,SWORD v1,SWORD v2)
+{
+ if ((p1==p2)||(p==p1)) return v1;
+ return v1+((SLONG)((p-p1)*(v2-v1))/(p2-p1));
+}
+
+UWORD getlinearperiod(UWORD note,ULONG fine)
+{
+ UWORD t;
+
+ t=((20L+2*HIGH_OCTAVE)*OCTAVE+2-note)*32L-(fine>>1);
+ return t;
+}
+
+static UWORD getlogperiod(UWORD note,ULONG fine)
+{
+ UWORD n,o;
+ UWORD p1,p2;
+ ULONG i;
+
+ n=note%(2*OCTAVE);
+ o=note/(2*OCTAVE);
+ i=(n<<2)+(fine>>4); /* n*8 + fine/16 */
+
+ p1=logtab[i];
+ p2=logtab[i+1];
+
+ return (Interpolate(fine>>4,0,15,p1,p2)>>o);
+}
+
+static UWORD getoldperiod(UWORD note,ULONG speed)
+{
+ UWORD n,o;
+
+ /* This happens sometimes on badly converted AMF, and old MOD */
+ if (!speed) {
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr,"\rmplayer: getoldperiod() called with note=%d, speed=0 !\n",note);
+#endif
+ return 4242; /* <- prevent divide overflow.. (42 hehe) */
+ }
+
+ n=note%(2*OCTAVE);
+ o=note/(2*OCTAVE);
+ return ((8363L*(ULONG)oldperiods[n])>>o)/speed;
+}
+
+static UWORD GetPeriod(UWORD flags, UWORD note, ULONG speed)
+{
+ if (flags & UF_XMPERIODS) {
+ if (flags & UF_LINEAR)
+ return getlinearperiod(note, speed);
+ else
+ return getlogperiod(note, speed);
+ } else
+ return getoldperiod(note, speed);
+}
+
+static SWORD InterpolateEnv(SWORD p,ENVPT *a,ENVPT *b)
+{
+ return (Interpolate(p,a->pos,b->pos,a->val,b->val));
+}
+
+static SWORD DoPan(SWORD envpan,SWORD pan)
+{
+ int newpan;
+
+ newpan=pan+(((envpan-PAN_CENTER)*(128-abs(pan-PAN_CENTER)))/128);
+
+ return (newpan<PAN_LEFT)?PAN_LEFT:(newpan>PAN_RIGHT?PAN_RIGHT:newpan);
+}
+
+static SWORD StartEnvelope(ENVPR *t,UBYTE flg,UBYTE pts,UBYTE susbeg,UBYTE susend,UBYTE beg,UBYTE end,ENVPT *p,UBYTE keyoff)
+{
+ t->flg=flg;
+ t->pts=pts;
+ t->susbeg=susbeg;
+ t->susend=susend;
+ t->beg=beg;
+ t->end=end;
+ t->env=p;
+ t->p=0;
+ t->a=0;
+ t->b=((t->flg&EF_SUSTAIN)&&(!(keyoff&KEY_OFF)))?0:1;
+
+ /* Imago Orpheus sometimes stores an extra initial point in the envelope */
+ if ((t->pts>=2)&&(t->env[0].pos==t->env[1].pos)) {
+ t->a++;t->b++;
+ }
+
+ /* Fit in the envelope, still */
+ if (t->a >= t->pts)
+ t->a = t->pts - 1;
+ if (t->b >= t->pts)
+ t->b = t->pts-1;
+
+ return t->env[t->a].val;
+}
+
+/* This procedure processes all envelope types, include volume, pitch, and
+ panning. Envelopes are defined by a set of points, each with a magnitude
+ [relating either to volume, panning position, or pitch modifier] and a tick
+ position.
+
+ Envelopes work in the following manner:
+
+ (a) Each tick the envelope is moved a point further in its progression. For
+ an accurate progression, magnitudes between two envelope points are
+ interpolated.
+
+ (b) When progression reaches a defined point on the envelope, values are
+ shifted to interpolate between this point and the next, and checks for
+ loops or envelope end are done.
+
+ Misc:
+ Sustain loops are loops that are only active as long as the keyoff flag is
+ clear. When a volume envelope terminates, so does the current fadeout.
+*/
+static SWORD ProcessEnvelope(MP_VOICE *aout, ENVPR *t, SWORD v)
+{
+ if (t->flg & EF_ON) {
+ UBYTE a, b; /* actual points in the envelope */
+ UWORD p; /* the 'tick counter' - real point being played */
+
+ a = t->a;
+ b = t->b;
+ p = t->p;
+
+ /*
+ * Sustain loop on one point (XM type).
+ * Not processed if KEYOFF.
+ * Don't move and don't interpolate when the point is reached
+ */
+ if ((t->flg & EF_SUSTAIN) && t->susbeg == t->susend &&
+ (!(aout->main.keyoff & KEY_OFF) && p == t->env[t->susbeg].pos)) {
+ v = t->env[t->susbeg].val;
+ } else {
+ /*
+ * All following situations will require interpolation between
+ * two envelope points.
+ */
+
+ /*
+ * Sustain loop between two points (IT type).
+ * Not processed if KEYOFF.
+ */
+ /* if we were on a loop point, loop now */
+ if ((t->flg & EF_SUSTAIN) && !(aout->main.keyoff & KEY_OFF) &&
+ a >= t->susend) {
+ a = t->susbeg;
+ b = (t->susbeg==t->susend)?a:a+1;
+ p = t->env[a].pos;
+ v = t->env[a].val;
+ } else
+ /*
+ * Regular loop.
+ * Be sure to correctly handle single point loops.
+ */
+ if ((t->flg & EF_LOOP) && a >= t->end) {
+ a = t->beg;
+ b = t->beg == t->end ? a : a + 1;
+ p = t->env[a].pos;
+ v = t->env[a].val;
+ } else
+ /*
+ * Non looping situations.
+ */
+ if (a != b)
+ v = InterpolateEnv(p, &t->env[a], &t->env[b]);
+ else
+ v = t->env[a].val;
+
+ /*
+ * Start to fade if the volume envelope is finished.
+ */
+ if (p >= t->env[t->pts - 1].pos) {
+ if (t->flg & EF_VOLENV) {
+ aout->main.keyoff |= KEY_FADE;
+ if (!v)
+ aout->main.fadevol = 0;
+ }
+ } else {
+ p++;
+ /* did pointer reach point b? */
+ if (p >= t->env[b].pos)
+ a = b++; /* shift points a and b */
+ }
+ t->a = a;
+ t->b = b;
+ t->p = p;
+ }
+ }
+ return v;
+}
+
+/* XM linear period to frequency conversion */
+ULONG getfrequency(UWORD flags,ULONG period)
+{
+ if (flags & UF_LINEAR) {
+ SLONG shift = ((SLONG)period / 768) - HIGH_OCTAVE;
+
+ if (shift >= 0)
+ return lintab[period % 768] >> shift;
+ else
+ return lintab[period % 768] << (-shift);
+ } else
+ return (8363L*1712L)/(period?period:1);
+}
+
+/*========== Protracker effects */
+
+static void DoArpeggio(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE style)
+{
+ UBYTE note=a->main.note;
+
+ if (a->arpmem) {
+ switch (style) {
+ case 0: /* mod style: N, N+x, N+y */
+ switch (tick % 3) {
+ /* case 0: unchanged */
+ case 1:
+ note += (a->arpmem >> 4);
+ break;
+ case 2:
+ note += (a->arpmem & 0xf);
+ break;
+ }
+ break;
+ case 3: /* okt arpeggio 3: N-x, N, N+y */
+ switch (tick % 3) {
+ case 0:
+ note -= (a->arpmem >> 4);
+ break;
+ /* case 1: unchanged */
+ case 2:
+ note += (a->arpmem & 0xf);
+ break;
+ }
+ break;
+ case 4: /* okt arpeggio 4: N, N+y, N, N-x */
+ switch (tick % 4) {
+ /* case 0, case 2: unchanged */
+ case 1:
+ note += (a->arpmem & 0xf);
+ break;
+ case 3:
+ note -= (a->arpmem >> 4);
+ break;
+ }
+ break;
+ case 5: /* okt arpeggio 5: N-x, N+y, N, and nothing at tick 0 */
+ if (!tick)
+ break;
+ switch (tick % 3) {
+ /* case 0: unchanged */
+ case 1:
+ note -= (a->arpmem >> 4);
+ break;
+ case 2:
+ note += (a->arpmem & 0xf);
+ break;
+ }
+ break;
+ }
+ a->main.period = GetPeriod(flags, (UWORD)note << 1, a->speed);
+ a->ownper = 1;
+ }
+}
+
+static int DoPTEffect0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (!tick) {
+ if (!dat && (flags & UF_ARPMEM))
+ dat=a->arpmem;
+ else
+ a->arpmem=dat;
+ }
+ if (a->main.period)
+ DoArpeggio(tick, flags, a, 0);
+
+ return 0;
+}
+
+static int DoPTEffect1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (!tick && dat)
+ a->slidespeed = (UWORD)dat << 2;
+ if (a->main.period)
+ if (tick)
+ a->tmpperiod -= a->slidespeed;
+
+ return 0;
+}
+
+static int DoPTEffect2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (!tick && dat)
+ a->slidespeed = (UWORD)dat << 2;
+ if (a->main.period)
+ if (tick)
+ a->tmpperiod += a->slidespeed;
+
+ return 0;
+}
+
+static void DoToneSlide(UWORD tick, MP_CONTROL *a)
+{
+ if (!a->main.fadevol)
+ a->main.kick = (a->main.kick == KICK_NOTE)? KICK_NOTE : KICK_KEYOFF;
+ else
+ a->main.kick = (a->main.kick == KICK_NOTE)? KICK_ENV : KICK_ABSENT;
+
+ if (tick != 0) {
+ int dist;
+
+ /* We have to slide a->main.period towards a->wantedperiod, so compute
+ the difference between those two values */
+ dist=a->main.period-a->wantedperiod;
+
+ /* if they are equal or if portamentospeed is too big ...*/
+ if (dist == 0 || a->portspeed > abs(dist))
+ /* ...make tmpperiod equal tperiod */
+ a->tmpperiod=a->main.period=a->wantedperiod;
+ else if (dist>0) {
+ a->tmpperiod-=a->portspeed;
+ a->main.period-=a->portspeed; /* dist>0, slide up */
+ } else {
+ a->tmpperiod+=a->portspeed;
+ a->main.period+=a->portspeed; /* dist<0, slide down */
+ }
+ } else
+ a->tmpperiod=a->main.period;
+ a->ownper = 1;
+}
+
+static int DoPTEffect3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if ((!tick)&&(dat)) a->portspeed=(UWORD)dat<<2;
+ if (a->main.period)
+ DoToneSlide(tick, a);
+
+ return 0;
+}
+
+static void DoVibrato(UWORD tick, MP_CONTROL *a)
+{
+ UBYTE q;
+ UWORD temp = 0; /* silence warning */
+
+ if (!tick)
+ return;
+
+ q=(a->vibpos>>2)&0x1f;
+
+ switch (a->wavecontrol&3) {
+ case 0: /* sine */
+ temp=VibratoTable[q];
+ break;
+ case 1: /* ramp down */
+ q<<=3;
+ if (a->vibpos<0) q=255-q;
+ temp=q;
+ break;
+ case 2: /* square wave */
+ temp=255;
+ break;
+ case 3: /* random wave */
+ temp=getrandom(256);
+ break;
+ }
+
+ temp*=a->vibdepth;
+ temp>>=7;temp<<=2;
+
+ if (a->vibpos>=0)
+ a->main.period=a->tmpperiod+temp;
+ else
+ a->main.period=a->tmpperiod-temp;
+ a->ownper = 1;
+
+ if (tick != 0)
+ a->vibpos+=a->vibspd;
+}
+
+static int DoPTEffect4(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->vibdepth=dat&0xf;
+ if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
+ }
+ if (a->main.period)
+ DoVibrato(tick, a);
+
+ return 0;
+}
+
+static void DoVolSlide(MP_CONTROL *a, UBYTE dat)
+{
+ if (dat&0xf) {
+ a->tmpvolume-=(dat&0x0f);
+ if (a->tmpvolume<0)
+ a->tmpvolume=0;
+ } else {
+ a->tmpvolume+=(dat>>4);
+ if (a->tmpvolume>64)
+ a->tmpvolume=64;
+ }
+}
+
+static int DoPTEffect5(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (a->main.period)
+ DoToneSlide(tick, a);
+
+ if (tick)
+ DoVolSlide(a, dat);
+
+ return 0;
+}
+
+/* DoPTEffect6 after DoPTEffectA */
+
+static int DoPTEffect7(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+ UBYTE q;
+ UWORD temp = 0; /* silence warning */
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->trmdepth=dat&0xf;
+ if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
+ }
+ if (a->main.period) {
+ q=(a->trmpos>>2)&0x1f;
+
+ switch ((a->wavecontrol>>4)&3) {
+ case 0: /* sine */
+ temp=VibratoTable[q];
+ break;
+ case 1: /* ramp down */
+ q<<=3;
+ if (a->trmpos<0) q=255-q;
+ temp=q;
+ break;
+ case 2: /* square wave */
+ temp=255;
+ break;
+ case 3: /* random wave */
+ temp=getrandom(256);
+ break;
+ }
+ temp*=a->trmdepth;
+ temp>>=6;
+
+ if (a->trmpos>=0) {
+ a->volume=a->tmpvolume+temp;
+ if (a->volume>64) a->volume=64;
+ } else {
+ a->volume=a->tmpvolume-temp;
+ if (a->volume<0) a->volume=0;
+ }
+ a->ownvol = 1;
+
+ if (tick)
+ a->trmpos+=a->trmspd;
+ }
+
+ return 0;
+}
+
+static int DoPTEffect8(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+
+ dat = UniGetByte();
+ if (mod->panflag)
+ a->main.panning = mod->panning[channel] = dat;
+
+ return 0;
+}
+
+static int DoPTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat) a->soffset=(UWORD)dat<<8;
+ a->main.start=a->hioffset|a->soffset;
+
+ if ((a->main.s)&&(a->main.start>a->main.s->length))
+ a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
+ a->main.s->loopstart:a->main.s->length;
+ }
+
+ return 0;
+}
+
+static int DoPTEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (tick)
+ DoVolSlide(a, dat);
+
+ return 0;
+}
+
+static int DoPTEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ if (a->main.period)
+ DoVibrato(tick, a);
+ DoPTEffectA(tick, flags, a, mod, channel);
+
+ return 0;
+}
+
+static int DoPTEffectB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+
+ if (tick || mod->patdly2)
+ return 0;
+
+ /* Vincent Voois uses a nasty trick in "Universal Bolero" */
+ if (dat == mod->sngpos && mod->patbrk == mod->patpos)
+ return 0;
+
+ if (!mod->loop && !mod->patbrk &&
+ (dat < mod->sngpos ||
+ (mod->sngpos == (mod->numpos - 1) && !mod->patbrk) ||
+ (dat == mod->sngpos && (flags & UF_NOWRAP))
+ )) {
+ /* if we don't loop, better not to skip the end of the
+ pattern, after all... so:
+ mod->patbrk=0; */
+ mod->posjmp=3;
+ } else {
+ /* if we were fading, adjust... */
+ if (mod->sngpos == (mod->numpos-1))
+ mod->volume=mod->initvolume>128?128:mod->initvolume;
+ mod->sngpos=dat;
+ mod->posjmp=2;
+ mod->patpos=0;
+ }
+
+ return 0;
+}
+
+static int DoPTEffectC(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (tick) return 0;
+ if (dat==(UBYTE)-1) a->anote=dat=0; /* note cut */
+ else if (dat>64) dat=64;
+ a->tmpvolume=dat;
+
+ return 0;
+}
+
+static int DoPTEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if ((tick)||(mod->patdly2)) return 0;
+ if ((mod->positions[mod->sngpos]!=LAST_PATTERN)&&
+ (dat>mod->pattrows[mod->positions[mod->sngpos]]))
+ dat=mod->pattrows[mod->positions[mod->sngpos]];
+ mod->patbrk=dat;
+ if (!mod->posjmp) {
+ /* don't ask me to explain this code - it makes
+ backwards.s3m and children.xm (heretic's version) play
+ correctly, among others. Take that for granted, or write
+ the page of comments yourself... you might need some
+ aspirin - Miod */
+ if ((mod->sngpos==mod->numpos-1)&&(dat)&&((mod->loop)||
+ (mod->positions[mod->sngpos]==(mod->numpat-1)
+ && !(flags&UF_NOWRAP)))) {
+ mod->sngpos=0;
+ mod->posjmp=2;
+ } else
+ mod->posjmp=3;
+ }
+
+ return 0;
+}
+
+static void DoEEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod,
+ SWORD channel, UBYTE dat)
+{
+ UBYTE nib = dat & 0xf;
+
+ switch (dat>>4) {
+ case 0x0: /* hardware filter toggle, not supported */
+ break;
+ case 0x1: /* fineslide up */
+ if (a->main.period)
+ if (!tick)
+ a->tmpperiod-=(nib<<2);
+ break;
+ case 0x2: /* fineslide dn */
+ if (a->main.period)
+ if (!tick)
+ a->tmpperiod+=(nib<<2);
+ break;
+ case 0x3: /* glissando ctrl */
+ a->glissando=nib;
+ break;
+ case 0x4: /* set vibrato waveform */
+ a->wavecontrol&=0xf0;
+ a->wavecontrol|=nib;
+ break;
+ case 0x5: /* set finetune */
+ if (a->main.period) {
+ if (flags&UF_XMPERIODS)
+ a->speed=nib+128;
+ else
+ a->speed=finetune[nib];
+ a->tmpperiod=GetPeriod(flags, (UWORD)a->main.note<<1,a->speed);
+ }
+ break;
+ case 0x6: /* set patternloop */
+ if (tick)
+ break;
+ if (nib) { /* set reppos or repcnt ? */
+ /* set repcnt, so check if repcnt already is set, which means we
+ are already looping */
+ if (a->pat_repcnt)
+ a->pat_repcnt--; /* already looping, decrease counter */
+ else {
+#if 0
+ /* this would make walker.xm, shipped with Xsoundtracker,
+ play correctly, but it's better to remain compatible
+ with FT2 */
+ if ((!(flags&UF_NOWRAP))||(a->pat_reppos!=POS_NONE))
+#endif
+ a->pat_repcnt=nib; /* not yet looping, so set repcnt */
+ }
+
+ if (a->pat_repcnt) { /* jump to reppos if repcnt>0 */
+ if (a->pat_reppos==POS_NONE)
+ a->pat_reppos=mod->patpos-1;
+ if (a->pat_reppos==-1) {
+ mod->pat_repcrazy=1;
+ mod->patpos=0;
+ } else
+ mod->patpos=a->pat_reppos;
+ } else a->pat_reppos=POS_NONE;
+ } else
+ a->pat_reppos=mod->patpos-1; /* set reppos - can be (-1) */
+ break;
+ case 0x7: /* set tremolo waveform */
+ a->wavecontrol&=0x0f;
+ a->wavecontrol|=nib<<4;
+ break;
+ case 0x8: /* set panning */
+ if (mod->panflag) {
+ if (nib<=8) nib<<=4;
+ else nib*=17;
+ a->main.panning=mod->panning[channel]=nib;
+ }
+ break;
+ case 0x9: /* retrig note */
+ /* do not retrigger on tick 0, until we are emulating FT2 and effect
+ data is zero */
+ if (!tick && !((flags & UF_FT2QUIRKS) && (!nib)))
+ break;
+ /* only retrigger if data nibble > 0, or if tick 0 (FT2 compat) */
+ if (nib || !tick) {
+ if (!a->retrig) {
+ /* when retrig counter reaches 0, reset counter and restart
+ the sample */
+ if (a->main.period) a->main.kick=KICK_NOTE;
+ a->retrig=nib;
+ }
+ a->retrig--; /* countdown */
+ }
+ break;
+ case 0xa: /* fine volume slide up */
+ if (tick)
+ break;
+ a->tmpvolume+=nib;
+ if (a->tmpvolume>64) a->tmpvolume=64;
+ break;
+ case 0xb: /* fine volume slide dn */
+ if (tick)
+ break;
+ a->tmpvolume-=nib;
+ if (a->tmpvolume<0)a->tmpvolume=0;
+ break;
+ case 0xc: /* cut note */
+ /* When tick reaches the cut-note value, turn the volume to
+ zero (just like on the amiga) */
+ if (tick>=nib)
+ a->tmpvolume=0; /* just turn the volume down */
+ break;
+ case 0xd: /* note delay */
+ /* delay the start of the sample until tick==nib */
+ if (!tick)
+ a->main.notedelay=nib;
+ else if (a->main.notedelay)
+ a->main.notedelay--;
+ break;
+ case 0xe: /* pattern delay */
+ if (!tick)
+ if (!mod->patdly2)
+ mod->patdly=nib+1; /* only once, when tick=0 */
+ break;
+ case 0xf: /* invert loop, not supported */
+ break;
+ }
+}
+
+static int DoPTEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ DoEEffects(tick, flags, a, mod, channel, UniGetByte());
+
+ return 0;
+}
+
+static int DoPTEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (tick||mod->patdly2) return 0;
+ if (mod->extspd&&(dat>=mod->bpmlimit))
+ mod->bpm=dat;
+ else
+ if (dat) {
+ mod->sngspd=(dat>=mod->bpmlimit)?mod->bpmlimit-1:dat;
+ mod->vbtick=0;
+ }
+
+ return 0;
+}
+
+/*========== Scream Tracker effects */
+
+static int DoS3MEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE speed;
+
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ speed = UniGetByte();
+
+ if (tick || mod->patdly2)
+ return 0;
+
+ if (speed > 128)
+ speed -= 128;
+ if (speed) {
+ mod->sngspd = speed;
+ mod->vbtick = 0;
+ }
+
+ return 0;
+}
+
+static void DoS3MVolSlide(UWORD tick, UWORD flags, MP_CONTROL *a, UBYTE inf)
+{
+ UBYTE lo, hi;
+
+ if (inf)
+ a->s3mvolslide=inf;
+ else
+ inf=a->s3mvolslide;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!lo) {
+ if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume+=hi;
+ } else
+ if (!hi) {
+ if ((tick)||(flags&UF_S3MSLIDES)) a->tmpvolume-=lo;
+ } else
+ if (lo==0xf) {
+ if (!tick) a->tmpvolume+=(hi?hi:0xf);
+ } else
+ if (hi==0xf) {
+ if (!tick) a->tmpvolume-=(lo?lo:0xf);
+ } else
+ return;
+
+ if (a->tmpvolume<0)
+ a->tmpvolume=0;
+ else if (a->tmpvolume>64)
+ a->tmpvolume=64;
+}
+
+static int DoS3MEffectD(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ DoS3MVolSlide(tick, flags, a, UniGetByte());
+
+ return 1;
+}
+
+static void DoS3MSlideDn(UWORD tick, MP_CONTROL *a, UBYTE inf)
+{
+ UBYTE hi,lo;
+
+ if (inf)
+ a->slidespeed=inf;
+ else
+ inf=a->slidespeed;
+
+ hi=inf>>4;
+ lo=inf&0xf;
+
+ if (hi==0xf) {
+ if (!tick) a->tmpperiod+=(UWORD)lo<<2;
+ } else
+ if (hi==0xe) {
+ if (!tick) a->tmpperiod+=lo;
+ } else {
+ if (tick) a->tmpperiod+=(UWORD)inf<<2;
+ }
+}
+
+static int DoS3MEffectE(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (a->main.period)
+ DoS3MSlideDn(tick, a,dat);
+
+ return 0;
+}
+
+static void DoS3MSlideUp(UWORD tick, MP_CONTROL *a, UBYTE inf)
+{
+ UBYTE hi,lo;
+
+ if (inf) a->slidespeed=inf;
+ else inf=a->slidespeed;
+
+ hi=inf>>4;
+ lo=inf&0xf;
+
+ if (hi==0xf) {
+ if (!tick) a->tmpperiod-=(UWORD)lo<<2;
+ } else
+ if (hi==0xe) {
+ if (!tick) a->tmpperiod-=lo;
+ } else {
+ if (tick) a->tmpperiod-=(UWORD)inf<<2;
+ }
+}
+
+static int DoS3MEffectF(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (a->main.period)
+ DoS3MSlideUp(tick, a,dat);
+
+ return 0;
+}
+
+static int DoS3MEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf, on, off;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+ if (inf)
+ a->s3mtronof = inf;
+ else {
+ inf = a->s3mtronof;
+ if (!inf)
+ return 0;
+ }
+
+ if (!tick)
+ return 0;
+
+ on=(inf>>4)+1;
+ off=(inf&0xf)+1;
+ a->s3mtremor%=(on+off);
+ a->volume=(a->s3mtremor<on)?a->tmpvolume:0;
+ a->ownvol=1;
+ a->s3mtremor++;
+
+ return 0;
+}
+
+static int DoS3MEffectQ(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf;
+
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+ if (a->main.period) {
+ if (inf) {
+ a->s3mrtgslide=inf>>4;
+ a->s3mrtgspeed=inf&0xf;
+ }
+
+ /* only retrigger if low nibble > 0 */
+ if (a->s3mrtgspeed>0) {
+ if (!a->retrig) {
+ /* when retrig counter reaches 0, reset counter and restart the
+ sample */
+ if (a->main.kick!=KICK_NOTE) a->main.kick=KICK_KEYOFF;
+ a->retrig=a->s3mrtgspeed;
+
+ if ((tick)||(flags&UF_S3MSLIDES)) {
+ switch (a->s3mrtgslide) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ a->tmpvolume-=(1<<(a->s3mrtgslide-1));
+ break;
+ case 6:
+ a->tmpvolume=(2*a->tmpvolume)/3;
+ break;
+ case 7:
+ a->tmpvolume>>=1;
+ break;
+ case 9:
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ a->tmpvolume+=(1<<(a->s3mrtgslide-9));
+ break;
+ case 0xe:
+ a->tmpvolume=(3*a->tmpvolume)>>1;
+ break;
+ case 0xf:
+ a->tmpvolume=a->tmpvolume<<1;
+ break;
+ }
+ if (a->tmpvolume<0)
+ a->tmpvolume=0;
+ else if (a->tmpvolume>64)
+ a->tmpvolume=64;
+ }
+ }
+ a->retrig--; /* countdown */
+ }
+ }
+
+ return 0;
+}
+
+static int DoS3MEffectR(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat, q;
+ UWORD temp=0; /* silence warning */
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->trmdepth=dat&0xf;
+ if (dat&0xf0) a->trmspd=(dat&0xf0)>>2;
+ }
+
+ q=(a->trmpos>>2)&0x1f;
+
+ switch ((a->wavecontrol>>4)&3) {
+ case 0: /* sine */
+ temp=VibratoTable[q];
+ break;
+ case 1: /* ramp down */
+ q<<=3;
+ if (a->trmpos<0) q=255-q;
+ temp=q;
+ break;
+ case 2: /* square wave */
+ temp=255;
+ break;
+ case 3: /* random */
+ temp=getrandom(256);
+ break;
+ }
+
+ temp*=a->trmdepth;
+ temp>>=7;
+
+ if (a->trmpos>=0) {
+ a->volume=a->tmpvolume+temp;
+ if (a->volume>64) a->volume=64;
+ } else {
+ a->volume=a->tmpvolume-temp;
+ if (a->volume<0) a->volume=0;
+ }
+ a->ownvol = 1;
+
+ if (tick)
+ a->trmpos+=a->trmspd;
+
+ return 0;
+}
+
+static int DoS3MEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE tempo;
+
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ tempo = UniGetByte();
+
+ if (tick || mod->patdly2)
+ return 0;
+
+ mod->bpm = (tempo < 32) ? 32 : tempo;
+
+ return 0;
+}
+
+static int DoS3MEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat, q;
+ UWORD temp = 0; /* silence warning */
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->vibdepth=dat&0xf;
+ if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
+ } else
+ if (a->main.period) {
+ q=(a->vibpos>>2)&0x1f;
+
+ switch (a->wavecontrol&3) {
+ case 0: /* sine */
+ temp=VibratoTable[q];
+ break;
+ case 1: /* ramp down */
+ q<<=3;
+ if (a->vibpos<0) q=255-q;
+ temp=q;
+ break;
+ case 2: /* square wave */
+ temp=255;
+ break;
+ case 3: /* random */
+ temp=getrandom(256);
+ break;
+ }
+
+ temp*=a->vibdepth;
+ temp>>=8;
+
+ if (a->vibpos>=0)
+ a->main.period=a->tmpperiod+temp;
+ else
+ a->main.period=a->tmpperiod-temp;
+ a->ownper = 1;
+
+ a->vibpos+=a->vibspd;
+ }
+
+ return 0;
+}
+
+/*========== Envelope helpers */
+
+static int DoKeyOff(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ a->main.keyoff|=KEY_OFF;
+ if ((!(a->main.volflg&EF_ON))||(a->main.volflg&EF_LOOP))
+ a->main.keyoff=KEY_KILL;
+
+ return 0;
+}
+
+static int DoKeyFade(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if ((tick>=dat)||(tick==mod->sngspd-1)) {
+ a->main.keyoff=KEY_KILL;
+ if (!(a->main.volflg&EF_ON))
+ a->main.fadevol=0;
+ }
+
+ return 0;
+}
+
+/*========== Fast Tracker effects */
+
+/* DoXMEffect6 after DoXMEffectA */
+
+static int DoXMEffectA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf, lo, hi;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+ if (inf)
+ a->s3mvolslide = inf;
+ else
+ inf = a->s3mvolslide;
+
+ if (tick) {
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!hi) {
+ a->tmpvolume-=lo;
+ if (a->tmpvolume<0) a->tmpvolume=0;
+ } else {
+ a->tmpvolume+=hi;
+ if (a->tmpvolume>64) a->tmpvolume=64;
+ }
+ }
+
+ return 0;
+}
+
+static int DoXMEffect6(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ if (a->main.period)
+ DoVibrato(tick, a);
+
+ return DoXMEffectA(tick, flags, a, mod, channel);
+}
+
+static int DoXMEffectE1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat) a->fportupspd=dat;
+ if (a->main.period)
+ a->tmpperiod-=(a->fportupspd<<2);
+ }
+
+ return 0;
+}
+
+static int DoXMEffectE2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat) a->fportdnspd=dat;
+ if (a->main.period)
+ a->tmpperiod+=(a->fportdnspd<<2);
+ }
+
+ return 0;
+}
+
+static int DoXMEffectEA(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick)
+ if (dat) a->fslideupspd=dat;
+ a->tmpvolume+=a->fslideupspd;
+ if (a->tmpvolume>64) a->tmpvolume=64;
+
+ return 0;
+}
+
+static int DoXMEffectEB(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick)
+ if (dat) a->fslidednspd=dat;
+ a->tmpvolume-=a->fslidednspd;
+ if (a->tmpvolume<0) a->tmpvolume=0;
+
+ return 0;
+}
+
+static int DoXMEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ mod->volume=UniGetByte()<<1;
+ if (mod->volume>128) mod->volume=128;
+
+ return 0;
+}
+
+static int DoXMEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf;
+
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+
+ if (tick) {
+ if (inf) mod->globalslide=inf;
+ else inf=mod->globalslide;
+ if (inf & 0xf0) inf&=0xf0;
+ mod->volume=mod->volume+((inf>>4)-(inf&0xf))*2;
+
+ if (mod->volume<0)
+ mod->volume=0;
+ else if (mod->volume>128)
+ mod->volume=128;
+ }
+
+ return 0;
+}
+
+static int DoXMEffectL(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat=UniGetByte();
+ if ((!tick)&&(a->main.i)) {
+ UWORD points;
+ INSTRUMENT *i=a->main.i;
+ MP_VOICE *aout;
+
+ if ((aout=a->slave)) {
+ if (aout->venv.env) {
+ points=i->volenv[i->volpts-1].pos;
+ aout->venv.p=aout->venv.env[(dat>points)?points:dat].pos;
+ }
+ if (aout->penv.env) {
+ points=i->panenv[i->panpts-1].pos;
+ aout->penv.p=aout->penv.env[(dat>points)?points:dat].pos;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int DoXMEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf, lo, hi;
+ SWORD pan;
+
+ (void)flags; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+ if (!mod->panflag)
+ return 0;
+
+ if (inf)
+ a->pansspd = inf;
+ else
+ inf =a->pansspd;
+
+ if (tick) {
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ /* slide right has absolute priority */
+ if (hi)
+ lo = 0;
+
+ pan=((a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning)+hi-lo;
+ a->main.panning=(pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
+ }
+
+ return 0;
+}
+
+static int DoXMEffectX1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (dat)
+ a->ffportupspd = dat;
+ else
+ dat = a->ffportupspd;
+
+ if (a->main.period)
+ if (!tick) {
+ a->main.period-=dat;
+ a->tmpperiod-=dat;
+ a->ownper = 1;
+ }
+
+ return 0;
+}
+
+static int DoXMEffectX2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (dat)
+ a->ffportdnspd=dat;
+ else
+ dat = a->ffportdnspd;
+
+ if (a->main.period)
+ if (!tick) {
+ a->main.period+=dat;
+ a->tmpperiod+=dat;
+ a->ownper = 1;
+ }
+
+ return 0;
+}
+
+/*========== Impulse Tracker effects */
+
+static void DoITToneSlide(UWORD tick, MP_CONTROL *a, UBYTE dat)
+{
+ if (dat)
+ a->portspeed = dat;
+
+ /* if we don't come from another note, ignore the slide and play the note
+ as is */
+ if (!a->oldnote || !a->main.period)
+ return;
+
+ if ((!tick)&&(a->newsamp)){
+ a->main.kick=KICK_NOTE;
+ a->main.start=-1;
+ } else
+ a->main.kick=(a->main.kick==KICK_NOTE)?KICK_ENV:KICK_ABSENT;
+
+ if (tick) {
+ int dist;
+
+ /* We have to slide a->main.period towards a->wantedperiod, compute the
+ difference between those two values */
+ dist=a->main.period-a->wantedperiod;
+
+ /* if they are equal or if portamentospeed is too big... */
+ if ((!dist)||((a->portspeed<<2)>abs(dist)))
+ /* ... make tmpperiod equal tperiod */
+ a->tmpperiod=a->main.period=a->wantedperiod;
+ else
+ if (dist>0) {
+ a->tmpperiod-=a->portspeed<<2;
+ a->main.period-=a->portspeed<<2; /* dist>0 slide up */
+ } else {
+ a->tmpperiod+=a->portspeed<<2;
+ a->main.period+=a->portspeed<<2; /* dist<0 slide down */
+ }
+ } else
+ a->tmpperiod=a->main.period;
+ a->ownper=1;
+}
+
+static int DoITEffectG(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ DoITToneSlide(tick, a, UniGetByte());
+
+ return 0;
+}
+
+static void DoITVibrato(UWORD tick, MP_CONTROL *a, UBYTE dat)
+{
+ UBYTE q;
+ UWORD temp=0;
+
+ if (!tick) {
+ if (dat&0x0f) a->vibdepth=dat&0xf;
+ if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
+ }
+ if (!a->main.period)
+ return;
+
+ q=(a->vibpos>>2)&0x1f;
+
+ switch (a->wavecontrol&3) {
+ case 0: /* sine */
+ temp=VibratoTable[q];
+ break;
+ case 1: /* square wave */
+ temp=255;
+ break;
+ case 2: /* ramp down */
+ q<<=3;
+ if (a->vibpos<0) q=255-q;
+ temp=q;
+ break;
+ case 3: /* random */
+ temp=getrandom(256);
+ break;
+ }
+
+ temp*=a->vibdepth;
+ temp>>=8;
+ temp<<=2;
+
+ if (a->vibpos>=0)
+ a->main.period=a->tmpperiod+temp;
+ else
+ a->main.period=a->tmpperiod-temp;
+ a->ownper=1;
+
+ a->vibpos+=a->vibspd;
+}
+
+static int DoITEffectH(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ DoITVibrato(tick, a, UniGetByte());
+
+ return 0;
+}
+
+static int DoITEffectI(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf, on, off;
+
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+ if (inf)
+ a->s3mtronof = inf;
+ else {
+ inf = a->s3mtronof;
+ if (!inf)
+ return 0;
+ }
+
+ on=(inf>>4);
+ off=(inf&0xf);
+
+ a->s3mtremor%=(on+off);
+ a->volume=(a->s3mtremor<on)?a->tmpvolume:0;
+ a->ownvol = 1;
+ a->s3mtremor++;
+
+ return 0;
+}
+
+static int DoITEffectM(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ a->main.chanvol=UniGetByte();
+ if (a->main.chanvol>64)
+ a->main.chanvol=64;
+ else if (a->main.chanvol<0)
+ a->main.chanvol=0;
+
+ return 0;
+}
+
+static int DoITEffectN(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf, lo, hi;
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+
+ if (inf)
+ a->chanvolslide = inf;
+ else
+ inf = a->chanvolslide;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!hi)
+ a->main.chanvol-=lo;
+ else
+ if (!lo) {
+ a->main.chanvol+=hi;
+ } else
+ if (hi==0xf) {
+ if (!tick) a->main.chanvol-=lo;
+ } else
+ if (lo==0xf) {
+ if (!tick) a->main.chanvol+=hi;
+ }
+
+ if (a->main.chanvol<0)
+ a->main.chanvol=0;
+ else if (a->main.chanvol>64)
+ a->main.chanvol=64;
+
+ return 0;
+}
+
+static int DoITEffectP(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf, lo, hi;
+ SWORD pan;
+
+ (void)flags; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+ if (inf)
+ a->pansspd = inf;
+ else
+ inf = a->pansspd;
+
+ if (!mod->panflag)
+ return 0;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ pan=(a->main.panning==PAN_SURROUND)?PAN_CENTER:a->main.panning;
+
+ if (!hi)
+ pan+=lo<<2;
+ else
+ if (!lo) {
+ pan-=hi<<2;
+ } else
+ if (hi==0xf) {
+ if (!tick) pan+=lo<<2;
+ } else
+ if (lo==0xf) {
+ if (!tick) pan-=hi<<2;
+ }
+ a->main.panning=
+ (pan<PAN_LEFT)?PAN_LEFT:(pan>PAN_RIGHT?PAN_RIGHT:pan);
+
+ return 0;
+}
+
+static int DoITEffectT(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE tempo;
+ SWORD temp;
+
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ tempo = UniGetByte();
+
+ if (mod->patdly2)
+ return 0;
+
+ temp = mod->bpm;
+ if (tempo & 0x10)
+ temp += (tempo & 0x0f);
+ else
+ temp -= tempo;
+
+ mod->bpm=(temp>255)?255:(temp<1?1:temp);
+
+ return 0;
+}
+
+static int DoITEffectU(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat, q;
+ UWORD temp = 0; /* silence warning */
+
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat = UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->vibdepth=dat&0xf;
+ if (dat&0xf0) a->vibspd=(dat&0xf0)>>2;
+ }
+ if (a->main.period) {
+ q=(a->vibpos>>2)&0x1f;
+
+ switch (a->wavecontrol&3) {
+ case 0: /* sine */
+ temp=VibratoTable[q];
+ break;
+ case 1: /* square wave */
+ temp=255;
+ break;
+ case 2: /* ramp down */
+ q<<=3;
+ if (a->vibpos<0) q=255-q;
+ temp=q;
+ break;
+ case 3: /* random */
+ temp=getrandom(256);
+ break;
+ }
+
+ temp*=a->vibdepth;
+ temp>>=8;
+
+ if (a->vibpos>=0)
+ a->main.period=a->tmpperiod+temp;
+ else
+ a->main.period=a->tmpperiod-temp;
+ a->ownper = 1;
+
+ a->vibpos+=a->vibspd;
+ }
+
+ return 0;
+}
+
+static int DoITEffectW(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE inf, lo, hi;
+
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ inf = UniGetByte();
+
+ if (inf)
+ mod->globalslide = inf;
+ else
+ inf = mod->globalslide;
+
+ lo=inf&0xf;
+ hi=inf>>4;
+
+ if (!lo) {
+ if (tick) mod->volume+=hi;
+ } else
+ if (!hi) {
+ if (tick) mod->volume-=lo;
+ } else
+ if (lo==0xf) {
+ if (!tick) mod->volume+=hi;
+ } else
+ if (hi==0xf) {
+ if (!tick) mod->volume-=lo;
+ }
+
+ if (mod->volume<0)
+ mod->volume=0;
+ else if (mod->volume>128)
+ mod->volume=128;
+
+ return 0;
+}
+
+static int DoITEffectY(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat, q;
+ SLONG temp = 0; /* silence warning */
+
+ (void)flags; /* unused arg */
+
+ dat=UniGetByte();
+ if (!tick) {
+ if (dat&0x0f) a->panbdepth=(dat&0xf);
+ if (dat&0xf0) a->panbspd=(dat&0xf0)>>4;
+ }
+ if (mod->panflag) {
+ q=a->panbpos;
+
+ switch (a->panbwave) {
+ case 0: /* sine */
+ temp=PanbrelloTable[q];
+ break;
+ case 1: /* square wave */
+ temp=(q<0x80)?64:0;
+ break;
+ case 2: /* ramp down */
+ q<<=3;
+ temp=q;
+ break;
+ case 3: /* random */
+ temp=getrandom(256);
+ break;
+ }
+
+ temp*=a->panbdepth;
+ temp=(temp/8)+mod->panning[channel];
+
+ a->main.panning=
+ (temp<PAN_LEFT)?PAN_LEFT:(temp>PAN_RIGHT?PAN_RIGHT:temp);
+ a->panbpos+=a->panbspd;
+
+ }
+
+ return 0;
+}
+
+static void DoNNAEffects(MODULE *, MP_CONTROL *, UBYTE);
+
+/* Impulse/Scream Tracker Sxx effects.
+ All Sxx effects share the same memory space. */
+static int DoITEffectS0(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat, inf, c;
+
+ dat = UniGetByte();
+ inf=dat&0xf;
+ c=dat>>4;
+
+ if (!dat) {
+ c=a->sseffect;
+ inf=a->ssdata;
+ } else {
+ a->sseffect=c;
+ a->ssdata=inf;
+ }
+
+ switch (c) {
+ case SS_GLISSANDO: /* S1x set glissando voice */
+ DoEEffects(tick, flags, a, mod, channel, 0x30|inf);
+ break;
+ case SS_FINETUNE: /* S2x set finetune */
+ DoEEffects(tick, flags, a, mod, channel, 0x50|inf);
+ break;
+ case SS_VIBWAVE: /* S3x set vibrato waveform */
+ DoEEffects(tick, flags, a, mod, channel, 0x40|inf);
+ break;
+ case SS_TREMWAVE: /* S4x set tremolo waveform */
+ DoEEffects(tick, flags, a, mod, channel, 0x70|inf);
+ break;
+ case SS_PANWAVE: /* S5x panbrello */
+ a->panbwave=inf;
+ break;
+ case SS_FRAMEDELAY: /* S6x delay x number of frames (patdly) */
+ DoEEffects(tick, flags, a, mod, channel, 0xe0|inf);
+ break;
+ case SS_S7EFFECTS: /* S7x instrument / NNA commands */
+ DoNNAEffects(mod, a, inf);
+ break;
+ case SS_PANNING: /* S8x set panning position */
+ DoEEffects(tick, flags, a, mod, channel, 0x80 | inf);
+ break;
+ case SS_SURROUND: /* S9x set surround sound */
+ if (mod->panflag)
+ a->main.panning = mod->panning[channel] = PAN_SURROUND;
+ break;
+ case SS_HIOFFSET: /* SAy set high order sample offset yxx00h */
+ if (!tick) {
+ a->hioffset=inf<<16;
+ a->main.start=a->hioffset|a->soffset;
+
+ if ((a->main.s)&&(a->main.start>a->main.s->length))
+ a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
+ a->main.s->loopstart:a->main.s->length;
+ }
+ break;
+ case SS_PATLOOP: /* SBx pattern loop */
+ DoEEffects(tick, flags, a, mod, channel, 0x60|inf);
+ break;
+ case SS_NOTECUT: /* SCx notecut */
+ if (!inf) inf = 1;
+ DoEEffects(tick, flags, a, mod, channel, 0xC0|inf);
+ break;
+ case SS_NOTEDELAY: /* SDx notedelay */
+ DoEEffects(tick, flags, a, mod, channel, 0xD0|inf);
+ break;
+ case SS_PATDELAY: /* SEx patterndelay */
+ DoEEffects(tick, flags, a, mod, channel, 0xE0|inf);
+ break;
+ }
+
+ return 0;
+}
+
+/*========== Impulse Tracker Volume/Pan Column effects */
+
+/*
+ * All volume/pan column effects share the same memory space.
+ */
+
+static int DoVolEffects(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE c, inf;
+
+ (void)channel; /* unused arg */
+
+ c = UniGetByte();
+ inf = UniGetByte();
+
+ if ((!c)&&(!inf)) {
+ c=a->voleffect;
+ inf=a->voldata;
+ } else {
+ a->voleffect=c;
+ a->voldata=inf;
+ }
+
+ if (c)
+ switch (c) {
+ case VOL_VOLUME:
+ if (tick) break;
+ if (inf>64) inf=64;
+ a->tmpvolume=inf;
+ break;
+ case VOL_PANNING:
+ if (mod->panflag)
+ a->main.panning=inf;
+ break;
+ case VOL_VOLSLIDE:
+ DoS3MVolSlide(tick, flags, a, inf);
+ return 1;
+ case VOL_PITCHSLIDEDN:
+ if (a->main.period)
+ DoS3MSlideDn(tick, a, inf);
+ break;
+ case VOL_PITCHSLIDEUP:
+ if (a->main.period)
+ DoS3MSlideUp(tick, a, inf);
+ break;
+ case VOL_PORTAMENTO:
+ DoITToneSlide(tick, a, inf);
+ break;
+ case VOL_VIBRATO:
+ DoITVibrato(tick, a, inf);
+ break;
+ }
+
+ return 0;
+}
+
+/*========== UltraTracker effects */
+
+static int DoULTEffect9(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UWORD offset=UniGetWord();
+
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ if (offset)
+ a->ultoffset=offset;
+
+ a->main.start=a->ultoffset<<2;
+ if ((a->main.s)&&(a->main.start>a->main.s->length))
+ a->main.start=a->main.s->flags&(SF_LOOP|SF_BIDI)?
+ a->main.s->loopstart:a->main.s->length;
+
+ return 0;
+}
+
+/*========== OctaMED effects */
+
+static int DoMEDSpeed(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UWORD speed=UniGetWord();
+
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)channel; /* unused arg */
+
+ mod->bpm=speed;
+
+ return 0;
+}
+
+static int DoMEDEffectF1(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/2));
+
+ return 0;
+}
+
+static int DoMEDEffectF2(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ DoEEffects(tick, flags, a, mod, channel, 0xd0|(mod->sngspd/2));
+
+ return 0;
+}
+
+static int DoMEDEffectF3(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ DoEEffects(tick, flags, a, mod, channel, 0x90|(mod->sngspd/3));
+
+ return 0;
+}
+
+/*========== Oktalyzer effects */
+
+static int DoOktArp(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ UBYTE dat, dat2;
+
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ dat2 = UniGetByte(); /* arpeggio style */
+ dat = UniGetByte();
+ if (!tick) {
+ if (!dat && (flags & UF_ARPMEM))
+ dat=a->arpmem;
+ else
+ a->arpmem=dat;
+ }
+ if (a->main.period)
+ DoArpeggio(tick, flags, a, dat2);
+
+ return 0;
+}
+
+/*========== General player functions */
+
+static int DoNothing(UWORD tick, UWORD flags, MP_CONTROL *a, MODULE *mod, SWORD channel)
+{
+ (void)tick; /* unused arg */
+ (void)flags; /* unused arg */
+ (void)a; /* unused arg */
+ (void)mod; /* unused arg */
+ (void)channel; /* unused arg */
+
+ UniSkipOpcode();
+
+ return 0;
+}
+
+typedef int (*effect_func) (UWORD, UWORD, MP_CONTROL *, MODULE *, SWORD);
+
+static effect_func effects[UNI_LAST] = {
+ DoNothing, /* 0 */
+ DoNothing, /* UNI_NOTE */
+ DoNothing, /* UNI_INSTRUMENT */
+ DoPTEffect0, /* UNI_PTEFFECT0 */
+ DoPTEffect1, /* UNI_PTEFFECT1 */
+ DoPTEffect2, /* UNI_PTEFFECT2 */
+ DoPTEffect3, /* UNI_PTEFFECT3 */
+ DoPTEffect4, /* UNI_PTEFFECT4 */
+ DoPTEffect5, /* UNI_PTEFFECT5 */
+ DoPTEffect6, /* UNI_PTEFFECT6 */
+ DoPTEffect7, /* UNI_PTEFFECT7 */
+ DoPTEffect8, /* UNI_PTEFFECT8 */
+ DoPTEffect9, /* UNI_PTEFFECT9 */
+ DoPTEffectA, /* UNI_PTEFFECTA */
+ DoPTEffectB, /* UNI_PTEFFECTB */
+ DoPTEffectC, /* UNI_PTEFFECTC */
+ DoPTEffectD, /* UNI_PTEFFECTD */
+ DoPTEffectE, /* UNI_PTEFFECTE */
+ DoPTEffectF, /* UNI_PTEFFECTF */
+ DoS3MEffectA, /* UNI_S3MEFFECTA */
+ DoS3MEffectD, /* UNI_S3MEFFECTD */
+ DoS3MEffectE, /* UNI_S3MEFFECTE */
+ DoS3MEffectF, /* UNI_S3MEFFECTF */
+ DoS3MEffectI, /* UNI_S3MEFFECTI */
+ DoS3MEffectQ, /* UNI_S3MEFFECTQ */
+ DoS3MEffectR, /* UNI_S3MEFFECTR */
+ DoS3MEffectT, /* UNI_S3MEFFECTT */
+ DoS3MEffectU, /* UNI_S3MEFFECTU */
+ DoKeyOff, /* UNI_KEYOFF */
+ DoKeyFade, /* UNI_KEYFADE */
+ DoVolEffects, /* UNI_VOLEFFECTS */
+ DoPTEffect4, /* UNI_XMEFFECT4 */
+ DoXMEffect6, /* UNI_XMEFFECT6 */
+ DoXMEffectA, /* UNI_XMEFFECTA */
+ DoXMEffectE1, /* UNI_XMEFFECTE1 */
+ DoXMEffectE2, /* UNI_XMEFFECTE2 */
+ DoXMEffectEA, /* UNI_XMEFFECTEA */
+ DoXMEffectEB, /* UNI_XMEFFECTEB */
+ DoXMEffectG, /* UNI_XMEFFECTG */
+ DoXMEffectH, /* UNI_XMEFFECTH */
+ DoXMEffectL, /* UNI_XMEFFECTL */
+ DoXMEffectP, /* UNI_XMEFFECTP */
+ DoXMEffectX1, /* UNI_XMEFFECTX1 */
+ DoXMEffectX2, /* UNI_XMEFFECTX2 */
+ DoITEffectG, /* UNI_ITEFFECTG */
+ DoITEffectH, /* UNI_ITEFFECTH */
+ DoITEffectI, /* UNI_ITEFFECTI */
+ DoITEffectM, /* UNI_ITEFFECTM */
+ DoITEffectN, /* UNI_ITEFFECTN */
+ DoITEffectP, /* UNI_ITEFFECTP */
+ DoITEffectT, /* UNI_ITEFFECTT */
+ DoITEffectU, /* UNI_ITEFFECTU */
+ DoITEffectW, /* UNI_ITEFFECTW */
+ DoITEffectY, /* UNI_ITEFFECTY */
+ DoNothing, /* UNI_ITEFFECTZ */
+ DoITEffectS0, /* UNI_ITEFFECTS0 */
+ DoULTEffect9, /* UNI_ULTEFFECT9 */
+ DoMEDSpeed, /* UNI_MEDSPEED */
+ DoMEDEffectF1, /* UNI_MEDEFFECTF1 */
+ DoMEDEffectF2, /* UNI_MEDEFFECTF2 */
+ DoMEDEffectF3, /* UNI_MEDEFFECTF3 */
+ DoOktArp, /* UNI_OKTARP */
+};
+
+static int pt_playeffects(MODULE *mod, SWORD channel, MP_CONTROL *a)
+{
+ UWORD tick = mod->vbtick;
+ UWORD flags = mod->flags;
+ UBYTE c;
+ int explicitslides = 0;
+ effect_func f;
+
+ while((c=UniGetByte())) {
+ f = effects[c];
+ if (f != DoNothing)
+ a->sliding = 0;
+ explicitslides |= f(tick, flags, a, mod, channel);
+ }
+ return explicitslides;
+}
+
+static void DoNNAEffects(MODULE *mod, MP_CONTROL *a, UBYTE dat)
+{
+ int t;
+ MP_VOICE *aout;
+
+ dat&=0xf;
+ aout=(a->slave)?a->slave:NULL;
+
+ switch (dat) {
+ case 0x0: /* past note cut */
+ for (t=0;t<md_sngchn;t++)
+ if (mod->voice[t].master==a)
+ mod->voice[t].main.fadevol=0;
+ break;
+ case 0x1: /* past note off */
+ for (t=0;t<md_sngchn;t++)
+ if (mod->voice[t].master==a) {
+ mod->voice[t].main.keyoff|=KEY_OFF;
+ if ((!(mod->voice[t].venv.flg & EF_ON))||
+ (mod->voice[t].venv.flg & EF_LOOP))
+ mod->voice[t].main.keyoff=KEY_KILL;
+ }
+ break;
+ case 0x2: /* past note fade */
+ for (t=0;t<md_sngchn;t++)
+ if (mod->voice[t].master==a)
+ mod->voice[t].main.keyoff|=KEY_FADE;
+ break;
+ case 0x3: /* set NNA note cut */
+ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CUT;
+ break;
+ case 0x4: /* set NNA note continue */
+ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_CONTINUE;
+ break;
+ case 0x5: /* set NNA note off */
+ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_OFF;
+ break;
+ case 0x6: /* set NNA note fade */
+ a->main.nna=(a->main.nna&~NNA_MASK)|NNA_FADE;
+ break;
+ case 0x7: /* disable volume envelope */
+ if (aout)
+ aout->main.volflg&=~EF_ON;
+ break;
+ case 0x8: /* enable volume envelope */
+ if (aout)
+ aout->main.volflg|=EF_ON;
+ break;
+ case 0x9: /* disable panning envelope */
+ if (aout)
+ aout->main.panflg&=~EF_ON;
+ break;
+ case 0xa: /* enable panning envelope */
+ if (aout)
+ aout->main.panflg|=EF_ON;
+ break;
+ case 0xb: /* disable pitch envelope */
+ if (aout)
+ aout->main.pitflg&=~EF_ON;
+ break;
+ case 0xc: /* enable pitch envelope */
+ if (aout)
+ aout->main.pitflg|=EF_ON;
+ break;
+ }
+}
+
+void pt_UpdateVoices(MODULE *mod, int max_volume)
+{
+ SWORD envpan,envvol,envpit,channel;
+ UWORD playperiod;
+ SLONG vibval,vibdpt;
+ ULONG tmpvol;
+
+ MP_VOICE *aout;
+ INSTRUMENT *i;
+ SAMPLE *s;
+
+ mod->totalchn=mod->realchn=0;
+ for (channel=0;channel<md_sngchn;channel++) {
+ aout=&mod->voice[channel];
+ i=aout->main.i;
+ s=aout->main.s;
+
+ if (!s || !s->length) continue;
+
+ if (aout->main.period<40)
+ aout->main.period=40;
+ else if (aout->main.period>50000)
+ aout->main.period=50000;
+
+ if ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_KEYOFF)) {
+ Voice_Play_internal(channel,s,(aout->main.start==-1)?
+ ((s->flags&SF_UST_LOOP)?s->loopstart:0):aout->main.start);
+ aout->main.fadevol=32768;
+ aout->aswppos=0;
+ }
+
+ envvol = 256;
+ envpan = PAN_CENTER;
+ envpit = 32;
+ if (i && ((aout->main.kick==KICK_NOTE)||(aout->main.kick==KICK_ENV))) {
+ if (aout->main.volflg & EF_ON)
+ envvol = StartEnvelope(&aout->venv,aout->main.volflg,
+ i->volpts,i->volsusbeg,i->volsusend,
+ i->volbeg,i->volend,i->volenv,aout->main.keyoff);
+ if (aout->main.panflg & EF_ON)
+ envpan = StartEnvelope(&aout->penv,aout->main.panflg,
+ i->panpts,i->pansusbeg,i->pansusend,
+ i->panbeg,i->panend,i->panenv,aout->main.keyoff);
+ if (aout->main.pitflg & EF_ON)
+ envpit = StartEnvelope(&aout->cenv,aout->main.pitflg,
+ i->pitpts,i->pitsusbeg,i->pitsusend,
+ i->pitbeg,i->pitend,i->pitenv,aout->main.keyoff);
+
+ if (aout->cenv.flg & EF_ON)
+ aout->masterperiod=GetPeriod(mod->flags,
+ (UWORD)aout->main.note<<1, aout->master->speed);
+ } else {
+ if (aout->main.volflg & EF_ON)
+ envvol = ProcessEnvelope(aout,&aout->venv,256);
+ if (aout->main.panflg & EF_ON)
+ envpan = ProcessEnvelope(aout,&aout->penv,PAN_CENTER);
+ if (aout->main.pitflg & EF_ON)
+ envpit = ProcessEnvelope(aout,&aout->cenv,32);
+ }
+ aout->main.kick=KICK_ABSENT;
+
+ tmpvol = aout->main.fadevol; /* max 32768 */
+ tmpvol *= aout->main.chanvol; /* * max 64 */
+ tmpvol *= aout->main.outvolume; /* * max 256 */
+ tmpvol /= (256 * 64); /* tmpvol is max 32768 again */
+ aout->totalvol = tmpvol >> 2; /* used to determine samplevolume */
+ tmpvol *= envvol; /* * max 256 */
+ tmpvol *= mod->volume; /* * max 128 */
+ tmpvol /= (128 * 256 * 128);
+
+ /* fade out */
+ if (mod->sngpos>=mod->numpos)
+ tmpvol=0;
+ else
+ tmpvol=(tmpvol*max_volume)/128;
+
+ if ((aout->masterchn!=-1)&& mod->control[aout->masterchn].muted)
+ Voice_SetVolume_internal(channel,0);
+ else {
+ Voice_SetVolume_internal(channel,tmpvol);
+ if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))
+ mod->realchn++;
+ mod->totalchn++;
+ }
+
+ if (aout->main.panning==PAN_SURROUND)
+ Voice_SetPanning_internal(channel,PAN_SURROUND);
+ else
+ if ((mod->panflag)&&(aout->penv.flg & EF_ON))
+ Voice_SetPanning_internal(channel,
+ DoPan(envpan,aout->main.panning));
+ else
+ Voice_SetPanning_internal(channel,aout->main.panning);
+
+ if (aout->main.period && s->vibdepth)
+ switch (s->vibtype) {
+ case 0:
+ vibval=avibtab[s->avibpos&127];
+ if (aout->avibpos & 0x80) vibval=-vibval;
+ break;
+ case 1:
+ vibval=64;
+ if (aout->avibpos & 0x80) vibval=-vibval;
+ break;
+ case 2:
+ vibval=63-(((aout->avibpos+128)&255)>>1);
+ break;
+ default:
+ vibval=(((aout->avibpos+128)&255)>>1)-64;
+ break;
+ }
+ else
+ vibval=0;
+
+ if (s->vibflags & AV_IT) {
+ if ((aout->aswppos>>8)<s->vibdepth) {
+ aout->aswppos += s->vibsweep;
+ vibdpt=aout->aswppos;
+ } else
+ vibdpt=s->vibdepth<<8;
+ vibval=(vibval*vibdpt)>>16;
+ if (aout->mflag) {
+ if (!(mod->flags&UF_LINEAR)) vibval>>=1;
+ aout->main.period-=vibval;
+ }
+ } else {
+ /* do XM style auto-vibrato */
+ if (!(aout->main.keyoff & KEY_OFF)) {
+ if (aout->aswppos<s->vibsweep) {
+ vibdpt=(aout->aswppos*s->vibdepth)/s->vibsweep;
+ aout->aswppos++;
+ } else
+ vibdpt=s->vibdepth;
+ } else {
+ /* keyoff -> depth becomes 0 if final depth wasn't reached or
+ stays at final level if depth WAS reached */
+ if (aout->aswppos>=s->vibsweep)
+ vibdpt=s->vibdepth;
+ else
+ vibdpt=0;
+ }
+ vibval=(vibval*vibdpt)>>8;
+ aout->main.period-=vibval;
+ }
+
+ /* update vibrato position */
+ aout->avibpos=(aout->avibpos+s->vibrate)&0xff;
+
+ /* process pitch envelope */
+ playperiod=aout->main.period;
+
+ if ((aout->main.pitflg&EF_ON)&&(envpit!=32)) {
+ long p1;
+
+ envpit-=32;
+ if ((aout->main.note<<1)+envpit<=0) envpit=-(aout->main.note<<1);
+
+ p1=GetPeriod(mod->flags, ((UWORD)aout->main.note<<1)+envpit,
+ aout->master->speed)-aout->masterperiod;
+ if (p1>0) {
+ if ((UWORD)(playperiod+p1)<=playperiod) {
+ p1=0;
+ aout->main.keyoff|=KEY_OFF;
+ }
+ } else if (p1<0) {
+ if ((UWORD)(playperiod+p1)>=playperiod) {
+ p1=0;
+ aout->main.keyoff|=KEY_OFF;
+ }
+ }
+ playperiod+=p1;
+ }
+
+ if (!aout->main.fadevol) { /* check for a dead note (fadevol=0) */
+ Voice_Stop_internal(channel);
+ mod->totalchn--;
+ if ((tmpvol)&&(aout->master)&&(aout->master->slave==aout))
+ mod->realchn--;
+ } else {
+ Voice_SetFrequency_internal(channel,
+ getfrequency(mod->flags,playperiod));
+
+ /* if keyfade, start substracting fadeoutspeed from fadevol: */
+ if ((i)&&(aout->main.keyoff&KEY_FADE)) {
+ if (aout->main.fadevol>=i->volfade)
+ aout->main.fadevol-=i->volfade;
+ else
+ aout->main.fadevol=0;
+ }
+ }
+
+ md_bpm=mod->bpm+mod->relspd;
+ if (md_bpm<32)
+ md_bpm=32;
+ else if ((!(mod->flags&UF_HIGHBPM)) && md_bpm>255)
+ md_bpm=255;
+ }
+}
+
+/* Handles new notes or instruments */
+void pt_Notes(MODULE *mod)
+{
+ SWORD channel;
+ MP_CONTROL *a;
+ UBYTE c,inst;
+ int tr,funky; /* funky is set to indicate note or instrument change */
+
+ for (channel=0;channel<mod->numchn;channel++) {
+ a=&mod->control[channel];
+
+ if (mod->sngpos>=mod->numpos) {
+ tr=mod->numtrk;
+ mod->numrow=0;
+ } else {
+ tr=mod->patterns[(mod->positions[mod->sngpos]*mod->numchn)+channel];
+ mod->numrow=mod->pattrows[mod->positions[mod->sngpos]];
+ }
+
+ a->row=(tr<mod->numtrk)?UniFindRow(mod->tracks[tr],mod->patpos):NULL;
+ a->newsamp=0;
+ if (!mod->vbtick) a->main.notedelay=0;
+
+ if (!a->row) continue;
+ UniSetRow(a->row);
+ funky=0;
+
+ while((c=UniGetByte()))
+ switch (c) {
+ case UNI_NOTE:
+ funky|=1;
+ a->oldnote=a->anote,a->anote=UniGetByte();
+ a->main.kick =KICK_NOTE;
+ a->main.start=-1;
+ a->sliding=0;
+
+ /* retrig tremolo and vibrato waves ? */
+ if (!(a->wavecontrol & 0x80)) a->trmpos=0;
+ if (!(a->wavecontrol & 0x08)) a->vibpos=0;
+ if (!a->panbwave) a->panbpos=0;
+ break;
+ case UNI_INSTRUMENT:
+ inst=UniGetByte();
+ if (inst>=mod->numins) break; /* safety valve */
+ funky|=2;
+ a->main.i=(mod->flags & UF_INST)?&mod->instruments[inst]:NULL;
+ a->retrig=0;
+ a->s3mtremor=0;
+ a->ultoffset=0;
+ a->main.sample=inst;
+ break;
+ default:
+ UniSkipOpcode();
+ break;
+ }
+
+ if (funky) {
+ INSTRUMENT *i;
+ SAMPLE *s;
+
+ if ((i=a->main.i)) {
+ if (i->samplenumber[a->anote] >= mod->numsmp) continue;
+ s=&mod->samples[i->samplenumber[a->anote]];
+ a->main.note=i->samplenote[a->anote];
+ } else {
+ a->main.note=a->anote;
+ s=&mod->samples[a->main.sample];
+ }
+
+ if (a->main.s!=s) {
+ a->main.s=s;
+ a->newsamp=a->main.period;
+ }
+
+ /* channel or instrument determined panning ? */
+ a->main.panning=mod->panning[channel];
+ if (s->flags & SF_OWNPAN)
+ a->main.panning=s->panning;
+ else if ((i)&&(i->flags & IF_OWNPAN))
+ a->main.panning=i->panning;
+
+ a->main.handle=s->handle;
+ a->speed=s->speed;
+
+ if (i) {
+ if ((mod->panflag)&&(i->flags & IF_PITCHPAN)
+ &&(a->main.panning!=PAN_SURROUND)){
+ a->main.panning+=
+ ((a->anote-i->pitpancenter)*i->pitpansep)/8;
+ if (a->main.panning<PAN_LEFT)
+ a->main.panning=PAN_LEFT;
+ else if (a->main.panning>PAN_RIGHT)
+ a->main.panning=PAN_RIGHT;
+ }
+ a->main.pitflg=i->pitflg;
+ a->main.volflg=i->volflg;
+ a->main.panflg=i->panflg;
+ a->main.nna=i->nnatype;
+ a->dca=i->dca;
+ a->dct=i->dct;
+ } else {
+ a->main.pitflg=a->main.volflg=a->main.panflg=0;
+ a->main.nna=a->dca=0;
+ a->dct=DCT_OFF;
+ }
+
+ if (funky&2) /* instrument change */ {
+ /* IT random volume variations: 0:8 bit fixed, and one bit for
+ sign. */
+ a->volume=a->tmpvolume=s->volume;
+ if ((s)&&(i)) {
+ if (i->rvolvar) {
+ a->volume=a->tmpvolume=s->volume+
+ ((s->volume*((SLONG)i->rvolvar*(SLONG)getrandom(512)
+ ))/25600);
+ if (a->volume<0)
+ a->volume=a->tmpvolume=0;
+ else if (a->volume>64)
+ a->volume=a->tmpvolume=64;
+ }
+ if ((mod->panflag)&&(a->main.panning!=PAN_SURROUND)) {
+ a->main.panning+=((a->main.panning*((SLONG)i->rpanvar*
+ (SLONG)getrandom(512)))/25600);
+ if (a->main.panning<PAN_LEFT)
+ a->main.panning=PAN_LEFT;
+ else if (a->main.panning>PAN_RIGHT)
+ a->main.panning=PAN_RIGHT;
+ }
+ }
+ }
+
+ a->wantedperiod=a->tmpperiod=
+ GetPeriod(mod->flags, (UWORD)a->main.note<<1,a->speed);
+ a->main.keyoff=KEY_KICK;
+ }
+ }
+}
+
+/* Handles effects */
+void pt_EffectsPass1(MODULE *mod)
+{
+ SWORD channel;
+ MP_CONTROL *a;
+ MP_VOICE *aout;
+ int explicitslides;
+
+ for (channel=0;channel<mod->numchn;channel++) {
+ a=&mod->control[channel];
+
+ if ((aout=a->slave)) {
+ a->main.fadevol=aout->main.fadevol;
+ a->main.period=aout->main.period;
+ if (a->main.kick==KICK_KEYOFF)
+ a->main.keyoff=aout->main.keyoff;
+ }
+
+ if (!a->row) continue;
+ UniSetRow(a->row);
+
+ a->ownper=a->ownvol=0;
+ explicitslides = pt_playeffects(mod, channel, a);
+
+ /* continue volume slide if necessary for XM and IT */
+ if (mod->flags&UF_BGSLIDES) {
+ if (!explicitslides && a->sliding)
+ DoS3MVolSlide(mod->vbtick, mod->flags, a, 0);
+ else if (a->tmpvolume)
+ a->sliding = explicitslides;
+ }
+
+ if (!a->ownper)
+ a->main.period=a->tmpperiod;
+ if (!a->ownvol)
+ a->volume=a->tmpvolume;
+
+ if (a->main.s) {
+ if (a->main.i)
+ a->main.outvolume=
+ (a->volume*a->main.s->globvol*a->main.i->globvol)>>10;
+ else
+ a->main.outvolume=(a->volume*a->main.s->globvol)>>4;
+ if (a->main.outvolume>256)
+ a->main.outvolume=256;
+ else if (a->main.outvolume<0)
+ a->main.outvolume=0;
+ }
+ }
+}
+
+/* NNA management */
+void pt_NNA(MODULE *mod)
+{
+ SWORD channel;
+ MP_CONTROL *a;
+
+ for (channel=0;channel<mod->numchn;channel++) {
+ a=&mod->control[channel];
+
+ if (a->main.kick==KICK_NOTE) {
+ BOOL kill=0;
+
+ if (a->slave) {
+ MP_VOICE *aout;
+
+ aout=a->slave;
+ if (aout->main.nna & NNA_MASK) {
+ /* Make sure the old MP_VOICE channel knows it has no
+ master now ! */
+ a->slave=NULL;
+ /* assume the channel is taken by NNA */
+ aout->mflag=0;
+
+ switch (aout->main.nna) {
+ case NNA_CONTINUE: /* continue note, do nothing */
+ break;
+ case NNA_OFF: /* note off */
+ aout->main.keyoff|=KEY_OFF;
+ if ((!(aout->main.volflg & EF_ON))||
+ (aout->main.volflg & EF_LOOP))
+ aout->main.keyoff=KEY_KILL;
+ break;
+ case NNA_FADE:
+ aout->main.keyoff |= KEY_FADE;
+ break;
+ }
+ }
+ }
+
+ if (a->dct!=DCT_OFF) {
+ int t;
+
+ for (t=0;t<md_sngchn;t++)
+ if ((!Voice_Stopped_internal(t))&&
+ (mod->voice[t].masterchn==channel)&&
+ (a->main.sample==mod->voice[t].main.sample)) {
+ kill=0;
+ switch (a->dct) {
+ case DCT_NOTE:
+ if (a->main.note==mod->voice[t].main.note)
+ kill=1;
+ break;
+ case DCT_SAMPLE:
+ if (a->main.handle==mod->voice[t].main.handle)
+ kill=1;
+ break;
+ case DCT_INST:
+ kill=1;
+ break;
+ }
+ if (kill)
+ switch (a->dca) {
+ case DCA_CUT:
+ mod->voice[t].main.fadevol=0;
+ break;
+ case DCA_OFF:
+ mod->voice[t].main.keyoff|=KEY_OFF;
+ if ((!(mod->voice[t].main.volflg&EF_ON))||
+ (mod->voice[t].main.volflg&EF_LOOP))
+ mod->voice[t].main.keyoff=KEY_KILL;
+ break;
+ case DCA_FADE:
+ mod->voice[t].main.keyoff|=KEY_FADE;
+ break;
+ }
+ }
+ }
+ } /* if (a->main.kick==KICK_NOTE) */
+ }
+}
+
+/* Setup module and NNA voices */
+void pt_SetupVoices(MODULE *mod)
+{
+ SWORD channel;
+ MP_CONTROL *a;
+ MP_VOICE *aout;
+
+ for (channel=0;channel<mod->numchn;channel++) {
+ a=&mod->control[channel];
+
+ if (a->main.notedelay) continue;
+ if (a->main.kick==KICK_NOTE) {
+ /* if no channel was cut above, find an empty or quiet channel
+ here */
+ if (mod->flags&UF_NNA) {
+ if (!a->slave) {
+ int newchn;
+
+ if ((newchn=MP_FindEmptyChannel(mod))!=-1)
+ a->slave=&mod->voice[a->slavechn=newchn];
+ }
+ } else
+ a->slave=&mod->voice[a->slavechn=channel];
+
+ /* assign parts of MP_VOICE only done for a KICK_NOTE */
+ if ((aout=a->slave)) {
+ if (aout->mflag && aout->master) aout->master->slave=NULL;
+ aout->master=a;
+ a->slave=aout;
+ aout->masterchn=channel;
+ aout->mflag=1;
+ }
+ } else
+ aout=a->slave;
+
+ if (aout)
+ aout->main=a->main;
+ a->main.kick=KICK_ABSENT;
+ }
+}
+
+/* second effect pass */
+void pt_EffectsPass2(MODULE *mod)
+{
+ SWORD channel;
+ MP_CONTROL *a;
+ UBYTE c;
+
+ for (channel=0;channel<mod->numchn;channel++) {
+ a=&mod->control[channel];
+
+ if (!a->row) continue;
+ UniSetRow(a->row);
+
+ while((c=UniGetByte()))
+ if (c==UNI_ITEFFECTS0) {
+ c=UniGetByte();
+ if ((c>>4)==SS_S7EFFECTS)
+ DoNNAEffects(mod, a, c&0xf);
+ } else
+ UniSkipOpcode();
+ }
+}
+
+void Player_HandleTick(void)
+{
+ SWORD channel;
+ int max_volume;
+
+#if 0
+ /* don't handle the very first ticks, this allows the other hardware to
+ settle down so we don't loose any starting notes */
+ if (isfirst) {
+ isfirst--;
+ return;
+ }
+#endif
+
+ if ((!pf)||(pf->forbid)||(pf->sngpos>=pf->numpos)) return;
+
+ /* update time counter (sngtime is in milliseconds (in fact 2^-10)) */
+ pf->sngremainder+=(1<<9)*5; /* thus 2.5*(1<<10), since fps=0.4xtempo */
+ pf->sngtime+=pf->sngremainder/pf->bpm;
+ pf->sngremainder%=pf->bpm;
+
+ if (++pf->vbtick>=pf->sngspd) {
+ if (pf->pat_repcrazy)
+ pf->pat_repcrazy=0; /* play 2 times row 0 */
+ else
+ pf->patpos++;
+ pf->vbtick=0;
+
+ /* process pattern-delay. pf->patdly2 is the counter and pf->patdly is
+ the command memory. */
+ if (pf->patdly)
+ pf->patdly2=pf->patdly,pf->patdly=0;
+ if (pf->patdly2) {
+ /* patterndelay active */
+ if (--pf->patdly2)
+ /* so turn back pf->patpos by 1 */
+ if (pf->patpos) pf->patpos--;
+ }
+
+ /* do we have to get a new patternpointer ? (when pf->patpos reaches the
+ pattern size, or when a patternbreak is active) */
+ if (((pf->patpos>=pf->numrow)&&(pf->numrow>0))&&(!pf->posjmp))
+ pf->posjmp=3;
+
+ if (pf->posjmp) {
+ pf->patpos=pf->numrow?(pf->patbrk%pf->numrow):0;
+ pf->pat_repcrazy=0;
+ pf->sngpos+=(pf->posjmp-2);
+ for (channel=0;channel<pf->numchn;channel++)
+ pf->control[channel].pat_reppos=-1;
+
+ pf->patbrk=pf->posjmp=0;
+ /* handle the "---" (end of song) pattern since it can occur
+ *inside* the module in some formats */
+ if ((pf->sngpos>=pf->numpos)||
+ (pf->positions[pf->sngpos]==LAST_PATTERN)) {
+ if (!pf->wrap) return;
+ if (!(pf->sngpos=pf->reppos)) {
+ pf->volume=pf->initvolume>128?128:pf->initvolume;
+ if(pf->initspeed!=0)
+ pf->sngspd=pf->initspeed<32?pf->initspeed:32;
+ else
+ pf->sngspd=6;
+ pf->bpm=pf->inittempo<32?32:pf->inittempo;
+ }
+ }
+ if (pf->sngpos<0) pf->sngpos=pf->numpos-1;
+ }
+
+ if (!pf->patdly2)
+ pt_Notes(pf);
+ }
+
+ /* Fade global volume if enabled and we're playing the last pattern */
+ if (((pf->sngpos==pf->numpos-1)||
+ (pf->positions[pf->sngpos+1]==LAST_PATTERN))&&
+ (pf->fadeout))
+ max_volume=pf->numrow?((pf->numrow-pf->patpos)*128)/pf->numrow:0;
+ else
+ max_volume=128;
+
+ pt_EffectsPass1(pf);
+ if (pf->flags&UF_NNA)
+ pt_NNA(pf);
+ pt_SetupVoices(pf);
+ pt_EffectsPass2(pf);
+
+ /* now set up the actual hardware channel playback information */
+ pt_UpdateVoices(pf, max_volume);
+}
+
+static void Player_Init_internal(MODULE* mod)
+{
+ int t;
+
+ for (t=0;t<mod->numchn;t++) {
+ mod->control[t].main.chanvol=mod->chanvol[t];
+ mod->control[t].main.panning=mod->panning[t];
+ }
+
+ mod->sngtime=0;
+ mod->sngremainder=0;
+
+ mod->pat_repcrazy=0;
+ mod->sngpos=0;
+ if(mod->initspeed!=0)
+ mod->sngspd=mod->initspeed<32?mod->initspeed:32;
+ else
+ mod->sngspd=6;
+ mod->volume=mod->initvolume>128?128:mod->initvolume;
+
+ mod->vbtick=mod->sngspd;
+ mod->patdly=0;
+ mod->patdly2=0;
+ mod->bpm=mod->inittempo<32?32:mod->inittempo;
+ mod->realchn=0;
+
+ mod->patpos=0;
+ mod->posjmp=2; /* make sure the player fetches the first note */
+ mod->numrow=-1;
+ mod->patbrk=0;
+}
+
+BOOL Player_Init(MODULE* mod)
+{
+ mod->extspd=1;
+ mod->panflag=1;
+ mod->wrap=0;
+ mod->loop=1;
+ mod->fadeout=0;
+
+ mod->relspd=0;
+
+ /* make sure the player doesn't start with garbage */
+ if (!(mod->control=(MP_CONTROL*)MikMod_calloc(mod->numchn,sizeof(MP_CONTROL))))
+ return 1;
+ if (!(mod->voice=(MP_VOICE*)MikMod_calloc(md_sngchn,sizeof(MP_VOICE))))
+ return 1;
+
+ Player_Init_internal(mod);
+ return 0;
+}
+
+void Player_Exit_internal(MODULE* mod)
+{
+ if (!mod)
+ return;
+
+ /* Stop playback if necessary */
+ if (mod==pf) {
+ Player_Stop_internal();
+ pf=NULL;
+ }
+
+ if (mod->control)
+ MikMod_free(mod->control);
+ if (mod->voice)
+ MikMod_free(mod->voice);
+ mod->control=NULL;
+ mod->voice=NULL;
+}
+
+void Player_Exit(MODULE* mod)
+{
+ MUTEX_LOCK(vars);
+ Player_Exit_internal(mod);
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI void Player_SetVolume(SWORD volume)
+{
+ MUTEX_LOCK(vars);
+ if (pf)
+ pf->volume=(volume<0)?0:(volume>128)?128:volume;
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI MODULE* Player_GetModule(void)
+{
+ MODULE* result;
+
+ MUTEX_LOCK(vars);
+ result=pf;
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI void Player_Start(MODULE *mod)
+{
+ int t;
+
+ if (!mod)
+ return;
+
+ if (!MikMod_Active())
+ MikMod_EnableOutput();
+
+ mod->forbid=0;
+
+ MUTEX_LOCK(vars);
+ if (pf!=mod) {
+ /* new song is being started, so completely stop out the old one. */
+ if (pf) pf->forbid=1;
+ for (t=0;t<md_sngchn;t++) Voice_Stop_internal(t);
+ }
+ pf=mod;
+ MUTEX_UNLOCK(vars);
+}
+
+void Player_Stop_internal(void)
+{
+ if (!md_sfxchn) MikMod_DisableOutput_internal();
+ if (pf) pf->forbid=1;
+ pf=NULL;
+}
+
+MIKMODAPI void Player_Stop(void)
+{
+ MUTEX_LOCK(vars);
+ Player_Stop_internal();
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI BOOL Player_Active(void)
+{
+ BOOL result=0;
+
+ MUTEX_LOCK(vars);
+ if (pf)
+ result=(!(pf->sngpos>=pf->numpos));
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI void Player_NextPosition(void)
+{
+ MUTEX_LOCK(vars);
+ if (pf) {
+ int t;
+
+ pf->forbid=1;
+ pf->posjmp=3;
+ pf->patbrk=0;
+ pf->vbtick=pf->sngspd;
+
+ for (t=0;t<md_sngchn;t++) {
+ Voice_Stop_internal(t);
+ pf->voice[t].main.i=NULL;
+ pf->voice[t].main.s=NULL;
+ }
+ for (t=0;t<pf->numchn;t++) {
+ pf->control[t].main.i=NULL;
+ pf->control[t].main.s=NULL;
+ }
+ pf->forbid=0;
+ }
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI void Player_PrevPosition(void)
+{
+ MUTEX_LOCK(vars);
+ if (pf) {
+ int t;
+
+ pf->forbid=1;
+ pf->posjmp=1;
+ pf->patbrk=0;
+ pf->vbtick=pf->sngspd;
+
+ for (t=0;t<md_sngchn;t++) {
+ Voice_Stop_internal(t);
+ pf->voice[t].main.i=NULL;
+ pf->voice[t].main.s=NULL;
+ }
+ for (t=0;t<pf->numchn;t++) {
+ pf->control[t].main.i=NULL;
+ pf->control[t].main.s=NULL;
+ }
+ pf->forbid=0;
+ }
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI void Player_SetPosition(UWORD pos)
+{
+ MUTEX_LOCK(vars);
+ if (pf) {
+ int t;
+
+ pf->forbid=1;
+ if (pos>=pf->numpos) pos=pf->numpos;
+ pf->posjmp=2;
+ pf->patbrk=0;
+ pf->sngpos=pos;
+ pf->vbtick=pf->sngspd;
+
+ for (t=0;t<md_sngchn;t++) {
+ Voice_Stop_internal(t);
+ pf->voice[t].main.i=NULL;
+ pf->voice[t].main.s=NULL;
+ }
+ for (t=0;t<pf->numchn;t++) {
+ pf->control[t].main.i=NULL;
+ pf->control[t].main.s=NULL;
+ }
+ pf->forbid=0;
+
+ if (!pos)
+ Player_Init_internal(pf);
+ }
+ MUTEX_UNLOCK(vars);
+}
+
+static void Player_Unmute_internal(SLONG arg1,va_list ap)
+{
+ SLONG t,arg2,arg3=0;
+
+ if (pf) {
+ switch (arg1) {
+ case MUTE_INCLUSIVE:
+ if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
+ (arg2>arg3)||(arg3>=pf->numchn))
+ return;
+ for (;arg2<pf->numchn && arg2<=arg3;arg2++)
+ pf->control[arg2].muted=0;
+ break;
+ case MUTE_EXCLUSIVE:
+ if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
+ (arg2>arg3)||(arg3>=pf->numchn))
+ return;
+ for (t=0;t<pf->numchn;t++) {
+ if ((t>=arg2) && (t<=arg3))
+ continue;
+ pf->control[t].muted=0;
+ }
+ break;
+ default:
+ if (arg1<pf->numchn) pf->control[arg1].muted=0;
+ break;
+ }
+ }
+}
+
+MIKMODAPI void Player_Unmute(SLONG arg1, ...)
+{
+ va_list args;
+
+ va_start(args,arg1);
+ MUTEX_LOCK(vars);
+ Player_Unmute_internal(arg1,args);
+ MUTEX_UNLOCK(vars);
+ va_end(args);
+}
+
+static void Player_Mute_internal(SLONG arg1,va_list ap)
+{
+ SLONG t,arg2,arg3=0;
+
+ if (pf) {
+ switch (arg1) {
+ case MUTE_INCLUSIVE:
+ if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
+ (arg2>arg3)||(arg3>=pf->numchn))
+ return;
+ for (;arg2<pf->numchn && arg2<=arg3;arg2++)
+ pf->control[arg2].muted=1;
+ break;
+ case MUTE_EXCLUSIVE:
+ if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
+ (arg2>arg3)||(arg3>=pf->numchn))
+ return;
+ for (t=0;t<pf->numchn;t++) {
+ if ((t>=arg2) && (t<=arg3))
+ continue;
+ pf->control[t].muted=1;
+ }
+ break;
+ default:
+ if (arg1<pf->numchn)
+ pf->control[arg1].muted=1;
+ break;
+ }
+ }
+}
+
+MIKMODAPI void Player_Mute(SLONG arg1,...)
+{
+ va_list args;
+
+ va_start(args,arg1);
+ MUTEX_LOCK(vars);
+ Player_Mute_internal(arg1,args);
+ MUTEX_UNLOCK(vars);
+ va_end(args);
+}
+
+static void Player_ToggleMute_internal(SLONG arg1,va_list ap)
+{
+ SLONG arg2,arg3=0;
+ SLONG t;
+
+ if (pf) {
+ switch (arg1) {
+ case MUTE_INCLUSIVE:
+ if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
+ (arg2>arg3)||(arg3>=pf->numchn))
+ return;
+ for (;arg2<pf->numchn && arg2<=arg3;arg2++)
+ pf->control[arg2].muted=1-pf->control[arg2].muted;
+ break;
+ case MUTE_EXCLUSIVE:
+ if (((!(arg2=va_arg(ap,SLONG)))&&(!(arg3=va_arg(ap,SLONG))))||
+ (arg2>arg3)||(arg3>=pf->numchn))
+ return;
+ for (t=0;t<pf->numchn;t++) {
+ if ((t>=arg2) && (t<=arg3))
+ continue;
+ pf->control[t].muted=1-pf->control[t].muted;
+ }
+ break;
+ default:
+ if (arg1<pf->numchn)
+ pf->control[arg1].muted=1-pf->control[arg1].muted;
+ break;
+ }
+ }
+}
+
+MIKMODAPI void Player_ToggleMute(SLONG arg1,...)
+{
+ va_list args;
+
+ va_start(args,arg1);
+ MUTEX_LOCK(vars);
+ Player_ToggleMute_internal(arg1,args);
+ MUTEX_UNLOCK(vars);
+ va_end(args);
+}
+
+MIKMODAPI BOOL Player_Muted(UBYTE chan)
+{
+ BOOL result=1;
+
+ MUTEX_LOCK(vars);
+ if (pf)
+ result=(chan<pf->numchn)?pf->control[chan].muted:1;
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI int Player_GetChannelVoice(UBYTE chan)
+{
+ int result=0;
+
+ MUTEX_LOCK(vars);
+ if (pf)
+ result=(chan<pf->numchn)?pf->control[chan].slavechn:-1;
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI UWORD Player_GetChannelPeriod(UBYTE chan)
+{
+ UWORD result=0;
+
+ MUTEX_LOCK(vars);
+ if (pf)
+ result=(chan<pf->numchn)?pf->control[chan].main.period:0;
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+BOOL Player_Paused_internal(void)
+{
+ return pf?pf->forbid:1;
+}
+
+MIKMODAPI BOOL Player_Paused(void)
+{
+ BOOL result;
+
+ MUTEX_LOCK(vars);
+ result=Player_Paused_internal();
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI void Player_TogglePause(void)
+{
+ MUTEX_LOCK(vars);
+ if (pf)
+ pf->forbid=1-pf->forbid;
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI void Player_SetSpeed(UWORD speed)
+{
+ MUTEX_LOCK(vars);
+ if (pf)
+ pf->sngspd=speed?(speed<32?speed:32):1;
+ MUTEX_UNLOCK(vars);
+}
+
+MIKMODAPI void Player_SetTempo(UWORD tempo)
+{
+ if (tempo<32) tempo=32;
+ MUTEX_LOCK(vars);
+ if (pf) {
+ if ((!(pf->flags&UF_HIGHBPM))&&(tempo>255)) tempo=255;
+ pf->bpm=tempo;
+ }
+ MUTEX_UNLOCK(vars);
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/munitrk.c b/src/libs/mikmod/munitrk.c
new file mode 100644
index 0000000..2818969
--- /dev/null
+++ b/src/libs/mikmod/munitrk.c
@@ -0,0 +1,303 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ All routines dealing with the manipulation of UNITRK streams
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mikmod_internals.h"
+
+#include <string.h>
+
+/* Unibuffer chunk size */
+#define BUFPAGE 128
+
+UWORD unioperands[UNI_LAST]={
+ 0, /* not used */
+ 1, /* UNI_NOTE */
+ 1, /* UNI_INSTRUMENT */
+ 1, /* UNI_PTEFFECT0 */
+ 1, /* UNI_PTEFFECT1 */
+ 1, /* UNI_PTEFFECT2 */
+ 1, /* UNI_PTEFFECT3 */
+ 1, /* UNI_PTEFFECT4 */
+ 1, /* UNI_PTEFFECT5 */
+ 1, /* UNI_PTEFFECT6 */
+ 1, /* UNI_PTEFFECT7 */
+ 1, /* UNI_PTEFFECT8 */
+ 1, /* UNI_PTEFFECT9 */
+ 1, /* UNI_PTEFFECTA */
+ 1, /* UNI_PTEFFECTB */
+ 1, /* UNI_PTEFFECTC */
+ 1, /* UNI_PTEFFECTD */
+ 1, /* UNI_PTEFFECTE */
+ 1, /* UNI_PTEFFECTF */
+ 1, /* UNI_S3MEFFECTA */
+ 1, /* UNI_S3MEFFECTD */
+ 1, /* UNI_S3MEFFECTE */
+ 1, /* UNI_S3MEFFECTF */
+ 1, /* UNI_S3MEFFECTI */
+ 1, /* UNI_S3MEFFECTQ */
+ 1, /* UNI_S3MEFFECTR */
+ 1, /* UNI_S3MEFFECTT */
+ 1, /* UNI_S3MEFFECTU */
+ 0, /* UNI_KEYOFF */
+ 1, /* UNI_KEYFADE */
+ 2, /* UNI_VOLEFFECTS */
+ 1, /* UNI_XMEFFECT4 */
+ 1, /* UNI_XMEFFECT6 */
+ 1, /* UNI_XMEFFECTA */
+ 1, /* UNI_XMEFFECTE1 */
+ 1, /* UNI_XMEFFECTE2 */
+ 1, /* UNI_XMEFFECTEA */
+ 1, /* UNI_XMEFFECTEB */
+ 1, /* UNI_XMEFFECTG */
+ 1, /* UNI_XMEFFECTH */
+ 1, /* UNI_XMEFFECTL */
+ 1, /* UNI_XMEFFECTP */
+ 1, /* UNI_XMEFFECTX1 */
+ 1, /* UNI_XMEFFECTX2 */
+ 1, /* UNI_ITEFFECTG */
+ 1, /* UNI_ITEFFECTH */
+ 1, /* UNI_ITEFFECTI */
+ 1, /* UNI_ITEFFECTM */
+ 1, /* UNI_ITEFFECTN */
+ 1, /* UNI_ITEFFECTP */
+ 1, /* UNI_ITEFFECTT */
+ 1, /* UNI_ITEFFECTU */
+ 1, /* UNI_ITEFFECTW */
+ 1, /* UNI_ITEFFECTY */
+ 2, /* UNI_ITEFFECTZ */
+ 1, /* UNI_ITEFFECTS0 */
+ 2, /* UNI_ULTEFFECT9 */
+ 2, /* UNI_MEDSPEED */
+ 0, /* UNI_MEDEFFECTF1 */
+ 0, /* UNI_MEDEFFECTF2 */
+ 0, /* UNI_MEDEFFECTF3 */
+ 2, /* UNI_OKTARP */
+};
+
+/* Sparse description of the internal module format
+ ------------------------------------------------
+
+ A UNITRK stream is an array of bytes representing a single track of a pattern.
+It's made up of 'repeat/length' bytes, opcodes and operands (sort of a assembly
+language):
+
+rrrlllll
+[REP/LEN][OPCODE][OPERAND][OPCODE][OPERAND] [REP/LEN][OPCODE][OPERAND]..
+^ ^ ^
+|-------ROWS 0 - 0+REP of a track---------| |-------ROWS xx - xx+REP of a track...
+
+ The rep/len byte contains the number of bytes in the current row, _including_
+the length byte itself (So the LENGTH byte of row 0 in the previous example
+would have a value of 5). This makes it easy to search through a stream for a
+particular row. A track is concluded by a 0-value length byte.
+
+ The upper 3 bits of the rep/len byte contain the number of times -1 this row
+is repeated for this track. (so a value of 7 means this row is repeated 8 times)
+
+ Opcodes can range from 1 to 255 but currently only opcodes 1 to 62 are being
+used. Each opcode can have a different number of operands. You can find the
+number of operands to a particular opcode by using the opcode as an index into
+the 'unioperands' table.
+
+*/
+
+/*========== Reading routines */
+
+static UBYTE *rowstart; /* startadress of a row */
+static UBYTE *rowend; /* endaddress of a row (exclusive) */
+static UBYTE *rowpc; /* current unimod(tm) programcounter */
+
+static UBYTE lastbyte; /* for UniSkipOpcode() */
+
+void UniSetRow(UBYTE* t)
+{
+ rowstart = t;
+ rowpc = rowstart;
+ rowend = t?rowstart+(*(rowpc++)&0x1f):t;
+}
+
+UBYTE UniGetByte(void)
+{
+ return lastbyte = (rowpc<rowend)?*(rowpc++):0;
+}
+
+UWORD UniGetWord(void)
+{
+ return ((UWORD)UniGetByte()<<8)|UniGetByte();
+}
+
+void UniSkipOpcode(void)
+{
+ if (lastbyte < UNI_LAST) {
+ UWORD t = unioperands[lastbyte];
+
+ while (t--)
+ UniGetByte();
+ }
+}
+
+/* Finds the address of row number 'row' in the UniMod(tm) stream 't' returns
+ NULL if the row can't be found. */
+UBYTE *UniFindRow(UBYTE* t,UWORD row)
+{
+ UBYTE c,l;
+
+ if(t)
+ while(1) {
+ c = *t; /* get rep/len byte */
+ if(!c) return NULL; /* zero ? -> end of track.. */
+ l = (c>>5)+1; /* extract repeat value */
+ if(l>row) break; /* reached wanted row? -> return pointer */
+ row -= l; /* haven't reached row yet.. update row */
+ t += c&0x1f; /* point t to the next row */
+ }
+ return t;
+}
+
+/*========== Writing routines */
+
+static UBYTE *unibuf; /* pointer to the temporary unitrk buffer */
+static UWORD unimax; /* buffer size */
+
+static UWORD unipc; /* buffer cursor */
+static UWORD unitt; /* current row index */
+static UWORD lastp; /* previous row index */
+
+/* Resets index-pointers to create a new track. */
+void UniReset(void)
+{
+ unitt = 0; /* reset index to rep/len byte */
+ unipc = 1; /* first opcode will be written to index 1 */
+ lastp = 0; /* no previous row yet */
+ unibuf[0] = 0; /* clear rep/len byte */
+}
+
+/* Expands the buffer */
+static BOOL UniExpand(int wanted)
+{
+ if ((unipc+wanted)>=unimax) {
+ UBYTE *newbuf;
+
+ /* Expand the buffer by BUFPAGE bytes */
+ newbuf=(UBYTE*)MikMod_realloc(unibuf,(unimax+BUFPAGE)*sizeof(UBYTE));
+
+ /* Check if realloc succeeded */
+ if(newbuf) {
+ unibuf = newbuf;
+ unimax+=BUFPAGE;
+ return 1;
+ } else
+ return 0;
+ }
+ return 1;
+}
+
+/* Appends one byte of data to the current row of a track. */
+void UniWriteByte(UBYTE data)
+{
+ if (UniExpand(1))
+ /* write byte to current position and update */
+ unibuf[unipc++]=data;
+}
+
+void UniWriteWord(UWORD data)
+{
+ if (UniExpand(2)) {
+ unibuf[unipc++]=data>>8;
+ unibuf[unipc++]=data&0xff;
+ }
+}
+
+static BOOL MyCmp(UBYTE* a,UBYTE* b,UWORD l)
+{
+ UWORD t;
+
+ for(t=0;t<l;t++)
+ if(*(a++)!=*(b++)) return 0;
+ return 1;
+}
+
+/* Closes the current row of a unitrk stream (updates the rep/len byte) and sets
+ pointers to start a new row. */
+void UniNewline(void)
+{
+ UWORD n,l,len;
+
+ n = (unibuf[lastp]>>5)+1; /* repeat of previous row */
+ l = (unibuf[lastp]&0x1f); /* length of previous row */
+
+ len = unipc-unitt; /* length of current row */
+
+ /* Now, check if the previous and the current row are identical.. when they
+ are, just increase the repeat field of the previous row */
+ if(n<8 && len==l && MyCmp(&unibuf[lastp+1],&unibuf[unitt+1],len-1)) {
+ unibuf[lastp]+=0x20;
+ unipc = unitt+1;
+ } else {
+ if (UniExpand(unitt-unipc)) {
+ /* current and previous row aren't equal... update the pointers */
+ unibuf[unitt] = len;
+ lastp = unitt;
+ unitt = unipc++;
+ }
+ }
+}
+
+/* Terminates the current unitrk stream and returns a pointer to a copy of the
+ stream. */
+UBYTE* UniDup(void)
+{
+ UBYTE *d;
+
+ if (!UniExpand(unitt-unipc)) return NULL;
+ unibuf[unitt] = 0;
+
+ if(!(d=(UBYTE *)MikMod_malloc(unipc))) return NULL;
+ memcpy(d,unibuf,unipc);
+
+ return d;
+}
+
+BOOL UniInit(void)
+{
+ unimax = BUFPAGE;
+
+ if(!(unibuf=(UBYTE*)MikMod_malloc(unimax*sizeof(UBYTE)))) return 0;
+ return 1;
+}
+
+void UniCleanup(void)
+{
+ if(unibuf) MikMod_free(unibuf);
+ unibuf = NULL;
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/mwav.c b/src/libs/mikmod/mwav.c
new file mode 100644
index 0000000..b629985
--- /dev/null
+++ b/src/libs/mikmod/mwav.c
@@ -0,0 +1,210 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ WAV sample loader
+
+==============================================================================*/
+
+/*
+ FIXME: Stereo .WAV files are not yet supported as samples.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+#ifdef SUNOS
+extern int fprintf(FILE *, const char *, ...);
+#endif
+
+typedef struct WAV {
+ CHAR rID[4];
+ ULONG rLen;
+ CHAR wID[4];
+ CHAR fID[4];
+ ULONG fLen;
+ UWORD wFormatTag;
+ UWORD nChannels;
+ ULONG nSamplesPerSec;
+ ULONG nAvgBytesPerSec;
+ UWORD nBlockAlign;
+ UWORD nFormatSpecific;
+} WAV;
+
+SAMPLE* Sample_LoadGeneric_internal(MREADER* reader)
+{
+ SAMPLE *si=NULL;
+ WAV wh;
+ BOOL have_fmt=0;
+
+ /* read wav header */
+ _mm_read_string(wh.rID,4,reader);
+ wh.rLen = _mm_read_I_ULONG(reader);
+ _mm_read_string(wh.wID,4,reader);
+
+ /* check for correct header */
+ if(_mm_eof(reader)|| memcmp(wh.rID,"RIFF",4) || memcmp(wh.wID,"WAVE",4)) {
+ _mm_errno = MMERR_UNKNOWN_WAVE_TYPE;
+ return NULL;
+ }
+
+ /* scan all RIFF blocks until we find the sample data */
+ for(;;) {
+ CHAR dID[4];
+ ULONG len,start;
+
+ _mm_read_string(dID,4,reader);
+ len = _mm_read_I_ULONG(reader);
+ /* truncated file ? */
+ if (_mm_eof(reader)) {
+ _mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
+ return NULL;
+ }
+ start = _mm_ftell(reader);
+
+ /* sample format block
+ should be present only once and before a data block */
+ if(!memcmp(dID,"fmt ",4)) {
+ wh.wFormatTag = _mm_read_I_UWORD(reader);
+ wh.nChannels = _mm_read_I_UWORD(reader);
+ wh.nSamplesPerSec = _mm_read_I_ULONG(reader);
+ wh.nAvgBytesPerSec = _mm_read_I_ULONG(reader);
+ wh.nBlockAlign = _mm_read_I_UWORD(reader);
+ wh.nFormatSpecific = _mm_read_I_UWORD(reader);
+
+#ifdef MIKMOD_DEBUG
+ fprintf(stderr,"\rwavloader : wFormatTag=%04x blockalign=%04x nFormatSpc=%04x\n",
+ wh.wFormatTag,wh.nBlockAlign,wh.nFormatSpecific);
+#endif
+
+ if((have_fmt)||(wh.nChannels>1)) {
+ _mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
+ return NULL;
+ }
+ have_fmt=1;
+ } else
+ /* sample data block
+ should be present only once and after a format block */
+ if(!memcmp(dID,"data",4)) {
+ if(!have_fmt) {
+ _mm_errno=MMERR_UNKNOWN_WAVE_TYPE;
+ return NULL;
+ }
+ if(!(si=(SAMPLE*)MikMod_malloc(sizeof(SAMPLE)))) return NULL;
+ si->speed = wh.nSamplesPerSec/wh.nChannels;
+ si->volume = 64;
+ si->length = len;
+ if(wh.nBlockAlign == 2) {
+ si->flags = SF_16BITS | SF_SIGNED;
+ si->length >>= 1;
+ }
+ si->inflags = si->flags;
+ SL_RegisterSample(si,MD_SNDFX,reader);
+ SL_LoadSamples();
+
+ /* skip any other remaining blocks - so in case of repeated sample
+ fragments, we'll return the first anyway instead of an error */
+ break;
+ }
+ /* onto next block */
+ _mm_fseek(reader,start+len,SEEK_SET);
+ if (_mm_eof(reader))
+ break;
+ }
+
+ return si;
+}
+
+MIKMODAPI SAMPLE* Sample_LoadGeneric(MREADER* reader)
+{
+ SAMPLE* result;
+
+ MUTEX_LOCK(vars);
+ result=Sample_LoadGeneric_internal(reader);
+ MUTEX_UNLOCK(vars);
+
+ return result;
+}
+
+MIKMODAPI extern SAMPLE *Sample_LoadMem(const char *buf, int len)
+{
+ SAMPLE* result=NULL;
+ MREADER* reader;
+
+ if ((reader=_mm_new_mem_reader(buf, len))) {
+ result=Sample_LoadGeneric(reader);
+ _mm_delete_mem_reader(reader);
+ }
+ return result;
+}
+
+MIKMODAPI SAMPLE* Sample_LoadFP(FILE *fp)
+{
+ SAMPLE* result=NULL;
+ MREADER* reader;
+
+ if((reader=_mm_new_file_reader(fp))) {
+ result=Sample_LoadGeneric(reader);
+ _mm_delete_file_reader(reader);
+ }
+ return result;
+}
+
+MIKMODAPI SAMPLE* Sample_Load(CHAR* filename)
+{
+ FILE *fp;
+ SAMPLE *si=NULL;
+
+ if(!(md_mode & DMODE_SOFT_SNDFX)) return NULL;
+ if((fp=_mm_fopen(filename,"rb"))) {
+ si = Sample_LoadFP(fp);
+ _mm_fclose(fp);
+ }
+ return si;
+}
+
+MIKMODAPI void Sample_Free(SAMPLE* si)
+{
+ if(si) {
+ MD_SampleUnload(si->handle);
+ MikMod_free(si);
+ }
+}
+
+void Sample_Free_internal(SAMPLE *si)
+{
+ MUTEX_LOCK(vars);
+ Sample_Free(si);
+ MUTEX_UNLOCK(vars);
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/npertab.c b/src/libs/mikmod/npertab.c
new file mode 100644
index 0000000..c6659e8
--- /dev/null
+++ b/src/libs/mikmod/npertab.c
@@ -0,0 +1,48 @@
+/* MikMod sound library
+ (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ MOD format period table. Used by both the MOD and M15 (15-inst mod) Loaders.
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "mikmod_internals.h"
+
+UWORD npertab[7 * OCTAVE] = {
+ /* Octaves 6 -> 0 */
+ /* C C# D D# E F F# G G# A A# B */
+ 0x6b0,0x650,0x5f4,0x5a0,0x54c,0x500,0x4b8,0x474,0x434,0x3f8,0x3c0,0x38a,
+ 0x358,0x328,0x2fa,0x2d0,0x2a6,0x280,0x25c,0x23a,0x21a,0x1fc,0x1e0,0x1c5,
+ 0x1ac,0x194,0x17d,0x168,0x153,0x140,0x12e,0x11d,0x10d,0x0fe,0x0f0,0x0e2,
+ 0x0d6,0x0ca,0x0be,0x0b4,0x0aa,0x0a0,0x097,0x08f,0x087,0x07f,0x078,0x071,
+ 0x06b,0x065,0x05f,0x05a,0x055,0x050,0x04b,0x047,0x043,0x03f,0x03c,0x038,
+ 0x035,0x032,0x02f,0x02d,0x02a,0x028,0x025,0x023,0x021,0x01f,0x01e,0x01c,
+ 0x01b,0x019,0x018,0x016,0x015,0x014,0x013,0x012,0x011,0x010,0x00f,0x00e
+};
+
+/* ex:set ts=4: */
+
diff --git a/src/libs/mikmod/sloader.c b/src/libs/mikmod/sloader.c
new file mode 100644
index 0000000..0b8c685
--- /dev/null
+++ b/src/libs/mikmod/sloader.c
@@ -0,0 +1,519 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Routines for loading samples. The sample loader utilizes the routines
+ provided by the "registered" sample loader.
+
+==============================================================================*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "mikmod_internals.h"
+
+static int sl_rlength;
+static SWORD sl_old;
+static SWORD *sl_buffer=NULL;
+static SAMPLOAD *musiclist=NULL,*sndfxlist=NULL;
+
+/* size of the loader buffer in words */
+#define SLBUFSIZE 2048
+
+/* IT-Compressed status structure */
+typedef struct ITPACK {
+ UWORD bits; /* current number of bits */
+ UWORD bufbits; /* bits in buffer */
+ SWORD last; /* last output */
+ UBYTE buf; /* bit buffer */
+} ITPACK;
+
+BOOL SL_Init(SAMPLOAD* s)
+{
+ if(!sl_buffer)
+ if(!(sl_buffer=MikMod_malloc(SLBUFSIZE*sizeof(SWORD)))) return 0;
+
+ sl_rlength = s->length;
+ if(s->infmt & SF_16BITS) sl_rlength>>=1;
+ sl_old = 0;
+
+ return 1;
+}
+
+void SL_Exit(SAMPLOAD *s)
+{
+ if(sl_rlength>0) _mm_fseek(s->reader,sl_rlength,SEEK_CUR);
+ if(sl_buffer) {
+ MikMod_free(sl_buffer);
+ sl_buffer=NULL;
+ }
+}
+
+/* unpack a 8bit IT packed sample */
+static BOOL read_itcompr8(ITPACK* status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt)
+{
+ SWORD *dest=sl_buffer,*end=sl_buffer+count;
+ UWORD x,y,needbits,havebits,new_count=0;
+ UWORD bits = status->bits;
+ UWORD bufbits = status->bufbits;
+ SBYTE last = status->last;
+ UBYTE buf = status->buf;
+
+ while (dest<end) {
+ needbits=new_count?3:bits;
+ x=havebits=0;
+ while (needbits) {
+ /* feed buffer */
+ if (!bufbits) {
+ if((*incnt)--)
+ buf=_mm_read_UBYTE(reader);
+ else
+ buf=0;
+ bufbits=8;
+ }
+ /* get as many bits as necessary */
+ y = needbits<bufbits?needbits:bufbits;
+ x|= (buf & ((1<<y)- 1))<<havebits;
+ buf>>=y;
+ bufbits-=y;
+ needbits-=y;
+ havebits+=y;
+ }
+ if (new_count) {
+ new_count = 0;
+ if (++x >= bits)
+ x++;
+ bits = x;
+ continue;
+ }
+ if (bits<7) {
+ if (x==(1<<(bits-1))) {
+ new_count = 1;
+ continue;
+ }
+ }
+ else if (bits<9) {
+ y = (0xff >> (9-bits)) - 4;
+ if ((x>y)&&(x<=y+8)) {
+ if ((x-=y)>=bits)
+ x++;
+ bits = x;
+ continue;
+ }
+ }
+ else if (bits<10) {
+ if (x>=0x100) {
+ bits=x-0x100+1;
+ continue;
+ }
+ } else {
+ /* error in compressed data... */
+ _mm_errno=MMERR_ITPACK_INVALID_DATA;
+ return 0;
+ }
+
+ if (bits<8) /* extend sign */
+ x = ((SBYTE)(x <<(8-bits))) >> (8-bits);
+ *(dest++)= (last+=x) << 8; /* convert to 16 bit */
+ }
+ status->bits = bits;
+ status->bufbits = bufbits;
+ status->last = last;
+ status->buf = buf;
+ return !!(dest-sl_buffer);
+}
+
+/* unpack a 16bit IT packed sample */
+static BOOL read_itcompr16(ITPACK *status,MREADER *reader,SWORD *sl_buffer,UWORD count,UWORD* incnt)
+{
+ SWORD *dest=sl_buffer,*end=sl_buffer+count;
+ SLONG x,y,needbits,havebits,new_count=0;
+ UWORD bits = status->bits;
+ UWORD bufbits = status->bufbits;
+ SWORD last = status->last;
+ UBYTE buf = status->buf;
+
+ while (dest<end) {
+ needbits=new_count?4:bits;
+ x=havebits=0;
+ while (needbits) {
+ /* feed buffer */
+ if (!bufbits) {
+ if((*incnt)--)
+ buf=_mm_read_UBYTE(reader);
+ else
+ buf=0;
+ bufbits=8;
+ }
+ /* get as many bits as necessary */
+ y=needbits<bufbits?needbits:bufbits;
+ x|=(buf &((1<<y)-1))<<havebits;
+ buf>>=y;
+ bufbits-=y;
+ needbits-=y;
+ havebits+=y;
+ }
+ if (new_count) {
+ new_count = 0;
+ if (++x >= bits)
+ x++;
+ bits = x;
+ continue;
+ }
+ if (bits<7) {
+ if (x==(1<<(bits-1))) {
+ new_count=1;
+ continue;
+ }
+ }
+ else if (bits<17) {
+ y=(0xffff>>(17-bits))-8;
+ if ((x>y)&&(x<=y+16)) {
+ if ((x-=y)>=bits)
+ x++;
+ bits = x;
+ continue;
+ }
+ }
+ else if (bits<18) {
+ if (x>=0x10000) {
+ bits=x-0x10000+1;
+ continue;
+ }
+ } else {
+ /* error in compressed data... */
+ _mm_errno=MMERR_ITPACK_INVALID_DATA;
+ return 0;
+ }
+
+ if (bits<16) /* extend sign */
+ x = ((SWORD)(x<<(16-bits)))>>(16-bits);
+ *(dest++)=(last+=x);
+ }
+ status->bits = bits;
+ status->bufbits = bufbits;
+ status->last = last;
+ status->buf = buf;
+ return !!(dest-sl_buffer);
+}
+
+static BOOL SL_LoadInternal(void* buffer,UWORD infmt,UWORD outfmt,int scalefactor,ULONG length,MREADER* reader,BOOL dither)
+{
+ SBYTE *bptr = (SBYTE*)buffer;
+ SWORD *wptr = (SWORD*)buffer;
+ int stodo,t,u;
+
+ int result,c_block=0; /* compression bytes until next block */
+ ITPACK status;
+ UWORD incnt;
+
+ while(length) {
+ stodo=(length<SLBUFSIZE)?length:SLBUFSIZE;
+
+ if(infmt&SF_ITPACKED) {
+ sl_rlength=0;
+ if (!c_block) {
+ status.bits = (infmt & SF_16BITS) ? 17 : 9;
+ status.last = status.bufbits = 0;
+ incnt=_mm_read_I_UWORD(reader);
+ c_block = (infmt & SF_16BITS) ? 0x4000 : 0x8000;
+ if(infmt&SF_DELTA) sl_old=0;
+ }
+ if (infmt & SF_16BITS) {
+ if(!(result=read_itcompr16(&status,reader,sl_buffer,stodo,&incnt)))
+ return 1;
+ } else {
+ if(!(result=read_itcompr8(&status,reader,sl_buffer,stodo,&incnt)))
+ return 1;
+ }
+ if(result!=stodo) {
+ _mm_errno=MMERR_ITPACK_INVALID_DATA;
+ return 1;
+ }
+ c_block -= stodo;
+ } else {
+ if(infmt&SF_16BITS) {
+ if(infmt&SF_BIG_ENDIAN)
+ _mm_read_M_SWORDS(sl_buffer,stodo,reader);
+ else
+ _mm_read_I_SWORDS(sl_buffer,stodo,reader);
+ } else {
+ SBYTE *src;
+ SWORD *dest;
+
+ reader->Read(reader,sl_buffer,sizeof(SBYTE)*stodo);
+ src = (SBYTE*)sl_buffer;
+ dest = sl_buffer;
+ src += stodo;dest += stodo;
+
+ for(t=0;t<stodo;t++) {
+ src--;dest--;
+ *dest = (*src)<<8;
+ }
+ }
+ sl_rlength-=stodo;
+ }
+
+ if(infmt & SF_DELTA)
+ for(t=0;t<stodo;t++) {
+ sl_buffer[t] += sl_old;
+ sl_old = sl_buffer[t];
+ }
+
+ if((infmt^outfmt) & SF_SIGNED)
+ for(t=0;t<stodo;t++)
+ sl_buffer[t]^= 0x8000;
+
+ if(scalefactor) {
+ int idx = 0;
+ SLONG scaleval;
+
+ /* Sample Scaling... average values for better results. */
+ t= 0;
+ while(t<stodo && length) {
+ scaleval = 0;
+ for(u=scalefactor;u && t<stodo;u--,t++)
+ scaleval+=sl_buffer[t];
+ sl_buffer[idx++]=scaleval/(scalefactor-u);
+ length--;
+ }
+ stodo = idx;
+ } else
+ length -= stodo;
+
+ if (dither) {
+ if((infmt & SF_STEREO) && !(outfmt & SF_STEREO)) {
+ /* dither stereo to mono, average together every two samples */
+ SLONG avgval;
+ int idx = 0;
+
+ t=0;
+ while(t<stodo && length) {
+ avgval=sl_buffer[t++];
+ avgval+=sl_buffer[t++];
+ sl_buffer[idx++]=avgval>>1;
+ length-=2;
+ }
+ stodo = idx;
+ }
+ }
+
+ if(outfmt & SF_16BITS) {
+ for(t=0;t<stodo;t++)
+ *(wptr++)=sl_buffer[t];
+ } else {
+ for(t=0;t<stodo;t++)
+ *(bptr++)=sl_buffer[t]>>8;
+ }
+ }
+ return 0;
+}
+
+BOOL SL_Load(void* buffer,SAMPLOAD *smp,ULONG length)
+{
+ return SL_LoadInternal(buffer,smp->infmt,smp->outfmt,smp->scalefactor,
+ length,smp->reader,0);
+}
+
+/* Registers a sample for loading when SL_LoadSamples() is called. */
+SAMPLOAD* SL_RegisterSample(SAMPLE* s,int type,MREADER* reader)
+{
+ SAMPLOAD *news,**samplist,*cruise;
+
+ if(type==MD_MUSIC) {
+ samplist = &musiclist;
+ cruise = musiclist;
+ } else
+ if (type==MD_SNDFX) {
+ samplist = &sndfxlist;
+ cruise = sndfxlist;
+ } else
+ return NULL;
+
+ /* Allocate and add structure to the END of the list */
+ if(!(news=(SAMPLOAD*)MikMod_malloc(sizeof(SAMPLOAD)))) return NULL;
+
+ if(cruise) {
+ while(cruise->next) cruise=cruise->next;
+ cruise->next = news;
+ } else
+ *samplist = news;
+
+ news->infmt = s->flags & SF_FORMATMASK;
+ news->outfmt = news->infmt;
+ news->reader = reader;
+ news->sample = s;
+ news->length = s->length;
+ news->loopstart = s->loopstart;
+ news->loopend = s->loopend;
+
+ return news;
+}
+
+static void FreeSampleList(SAMPLOAD* s)
+{
+ SAMPLOAD *old;
+
+ while(s) {
+ old = s;
+ s = s->next;
+ MikMod_free(old);
+ }
+}
+
+/* Returns the total amount of memory required by the samplelist queue. */
+static ULONG SampleTotal(SAMPLOAD* samplist,int type)
+{
+ int total = 0;
+
+ while(samplist) {
+ samplist->sample->flags=
+ (samplist->sample->flags&~SF_FORMATMASK)|samplist->outfmt;
+ total += MD_SampleLength(type,samplist->sample);
+ samplist=samplist->next;
+ }
+
+ return total;
+}
+
+static ULONG RealSpeed(SAMPLOAD *s)
+{
+ return(s->sample->speed/(s->scalefactor?s->scalefactor:1));
+}
+
+static BOOL DitherSamples(SAMPLOAD* samplist,int type)
+{
+ SAMPLOAD *c2smp=NULL;
+ ULONG maxsize, speed;
+ SAMPLOAD *s;
+
+ if(!samplist) return 0;
+
+ if((maxsize=MD_SampleSpace(type)*1024))
+ while(SampleTotal(samplist,type)>maxsize) {
+ /* First Pass - check for any 16 bit samples */
+ s = samplist;
+ while(s) {
+ if(s->outfmt & SF_16BITS) {
+ SL_Sample16to8(s);
+ break;
+ }
+ s=s->next;
+ }
+ /* Second pass (if no 16bits found above) is to take the sample with
+ the highest speed and dither it by half. */
+ if(!s) {
+ s = samplist;
+ speed = 0;
+ while(s) {
+ if((s->sample->length) && (RealSpeed(s)>speed)) {
+ speed=RealSpeed(s);
+ c2smp=s;
+ }
+ s=s->next;
+ }
+ if (c2smp)
+ SL_HalveSample(c2smp,2);
+ }
+ }
+
+ /* Samples dithered, now load them ! */
+ s = samplist;
+ while(s) {
+ /* sample has to be loaded ? -> increase number of samples, allocate
+ memory and load sample. */
+ if(s->sample->length) {
+ if(s->sample->seekpos)
+ _mm_fseek(s->reader, s->sample->seekpos, SEEK_SET);
+
+ /* Call the sample load routine of the driver module. It has to
+ return a 'handle' (>=0) that identifies the sample. */
+ s->sample->handle = MD_SampleLoad(s, type);
+ s->sample->flags = (s->sample->flags & ~SF_FORMATMASK) | s->outfmt;
+ if(s->sample->handle<0) {
+ FreeSampleList(samplist);
+ if(_mm_errorhandler) _mm_errorhandler();
+ return 1;
+ }
+ }
+ s = s->next;
+ }
+
+ FreeSampleList(samplist);
+ return 0;
+}
+
+BOOL SL_LoadSamples(void)
+{
+ BOOL ok;
+
+ _mm_critical = 0;
+
+ if((!musiclist)&&(!sndfxlist)) return 0;
+ ok=DitherSamples(musiclist,MD_MUSIC)||DitherSamples(sndfxlist,MD_SNDFX);
+ musiclist=sndfxlist=NULL;
+
+ return ok;
+}
+
+void SL_Sample16to8(SAMPLOAD* s)
+{
+ s->outfmt &= ~SF_16BITS;
+ s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt;
+}
+
+void SL_Sample8to16(SAMPLOAD* s)
+{
+ s->outfmt |= SF_16BITS;
+ s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt;
+}
+
+void SL_SampleSigned(SAMPLOAD* s)
+{
+ s->outfmt |= SF_SIGNED;
+ s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt;
+}
+
+void SL_SampleUnsigned(SAMPLOAD* s)
+{
+ s->outfmt &= ~SF_SIGNED;
+ s->sample->flags = (s->sample->flags&~SF_FORMATMASK) | s->outfmt;
+}
+
+void SL_HalveSample(SAMPLOAD* s,int factor)
+{
+ s->scalefactor=factor>0?factor:2;
+
+ s->sample->divfactor = s->scalefactor;
+ s->sample->length = s->length / s->scalefactor;
+ s->sample->loopstart = s->loopstart / s->scalefactor;
+ s->sample->loopend = s->loopend / s->scalefactor;
+}
+
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/virtch.c b/src/libs/mikmod/virtch.c
new file mode 100644
index 0000000..0bbb470
--- /dev/null
+++ b/src/libs/mikmod/virtch.c
@@ -0,0 +1,935 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
+ AUTHORS for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Sample mixing routines, using a 32 bits mixing buffer.
+
+==============================================================================*/
+
+/*
+
+ Optional features include:
+ (a) 4-step reverb (for 16 bit output only)
+ (b) Interpolation of sample data during mixing
+ (c) Dolby Surround Sound
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stddef.h>
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+/*
+ Constant definitions
+ ====================
+
+ BITSHIFT
+ Controls the maximum volume of the sound output. All data is shifted
+ right by BITSHIFT after being mixed. Higher values result in quieter
+ sound and less chance of distortion.
+
+ REVERBERATION
+ Controls the duration of the reverb. Larger values represent a shorter
+ reverb loop. Smaller values extend the reverb but can result in more of
+ an echo-ish sound.
+
+*/
+
+#define BITSHIFT 9
+#define REVERBERATION 110000L
+
+#define FRACBITS 11
+#define FRACMASK ((1L<<FRACBITS)-1L)
+
+#define TICKLSIZE 8192
+#define TICKWSIZE (TICKLSIZE<<1)
+#define TICKBSIZE (TICKWSIZE<<1)
+
+#define CLICK_SHIFT 6
+#define CLICK_BUFFER (1L<<CLICK_SHIFT)
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+typedef struct VINFO {
+ UBYTE kick; /* =1 -> sample has to be restarted */
+ UBYTE active; /* =1 -> sample is playing */
+ UWORD flags; /* 16/8 bits looping/one-shot */
+ SWORD handle; /* identifies the sample */
+ ULONG start; /* start index */
+ ULONG size; /* samplesize */
+ ULONG reppos; /* loop start */
+ ULONG repend; /* loop end */
+ ULONG frq; /* current frequency */
+ int vol; /* current volume */
+ int pan; /* current panning position */
+
+ int rampvol;
+ int lvolsel,rvolsel; /* Volume factor in range 0-255 */
+ int oldlvol,oldrvol;
+
+ SLONGLONG current; /* current index in the sample */
+ SLONGLONG increment; /* increment value */
+} VINFO;
+
+static SWORD **Samples;
+static VINFO *vinf=NULL,*vnf;
+static long tickleft,samplesthatfit,vc_memory=0;
+static int vc_softchn;
+static SLONGLONG idxsize,idxlpos,idxlend;
+static SLONG *vc_tickbuf=NULL;
+static UWORD vc_mode;
+
+/* Reverb control variables */
+
+static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8;
+static ULONG RVRindex;
+
+/* For Mono or Left Channel */
+static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL,
+ *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL;
+
+/* For Stereo only (Right Channel) */
+static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL,
+ *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL;
+
+#ifdef NATIVE_64BIT_INT
+#define NATIVE SLONGLONG
+#else
+#define NATIVE SLONG
+#endif
+
+/*========== 32 bit sample mixers - only for 32 bit platforms */
+#ifndef NATIVE_64BIT_INT
+
+static SLONG Mix32MonoNormal(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo)
+{
+ SWORD sample;
+ SLONG lvolsel = vnf->lvolsel;
+
+ while(todo--) {
+ sample = srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ }
+ return index;
+}
+
+static SLONG Mix32StereoNormal(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo)
+{
+ SWORD sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+
+ while(todo--) {
+ sample=srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ *dest++ += rvolsel * sample;
+ }
+ return index;
+}
+
+static SLONG Mix32SurroundNormal(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo)
+{
+ SWORD sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+
+ if (lvolsel>=rvolsel) {
+ while(todo--) {
+ sample = srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ += lvolsel*sample;
+ *dest++ -= lvolsel*sample;
+ }
+ } else {
+ while(todo--) {
+ sample = srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ -= rvolsel*sample;
+ *dest++ += rvolsel*sample;
+ }
+ }
+ return index;
+}
+
+static SLONG Mix32MonoInterp(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo)
+{
+ SLONG sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rampvol = vnf->rampvol;
+
+ if (rampvol) {
+ SLONG oldlvol = vnf->oldlvol - lvolsel;
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ if (!--rampvol)
+ break;
+ }
+ vnf->rampvol = rampvol;
+ if (todo < 0)
+ return index;
+ }
+
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ }
+ return index;
+}
+
+static SLONG Mix32StereoInterp(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo)
+{
+ SLONG sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+ SLONG rampvol = vnf->rampvol;
+
+ if (rampvol) {
+ SLONG oldlvol = vnf->oldlvol - lvolsel;
+ SLONG oldrvol = vnf->oldrvol - rvolsel;
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ *dest++ += ((rvolsel << CLICK_SHIFT) + oldrvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ if (!--rampvol)
+ break;
+ }
+ vnf->rampvol = rampvol;
+ if (todo < 0)
+ return index;
+ }
+
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ *dest++ += rvolsel * sample;
+ }
+ return index;
+}
+
+static SLONG Mix32SurroundInterp(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo)
+{
+ SLONG sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+ SLONG rampvol = vnf->rampvol;
+ SLONG oldvol, vol;
+
+ if (lvolsel >= rvolsel) {
+ vol = lvolsel;
+ oldvol = vnf->oldlvol;
+ } else {
+ vol = rvolsel;
+ oldvol = vnf->oldrvol;
+ }
+
+ if (rampvol) {
+ oldvol -= vol;
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ sample=((vol << CLICK_SHIFT) + oldvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ *dest++ += sample;
+ *dest++ -= sample;
+
+ if (!--rampvol)
+ break;
+ }
+ vnf->rampvol = rampvol;
+ if (todo < 0)
+ return index;
+ }
+
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += vol*sample;
+ *dest++ -= vol*sample;
+ }
+ return index;
+}
+#endif
+
+/*========== 64 bit sample mixers - all platforms */
+
+static SLONGLONG MixMonoNormal(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo)
+{
+ SWORD sample;
+ SLONG lvolsel = vnf->lvolsel;
+
+ while(todo--) {
+ sample = srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ }
+ return index;
+}
+
+static SLONGLONG MixStereoNormal(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo)
+{
+ SWORD sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+
+ while(todo--) {
+ sample=srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ *dest++ += rvolsel * sample;
+ }
+ return index;
+}
+
+static SLONGLONG MixSurroundNormal(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo)
+{
+ SWORD sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+
+ if(vnf->lvolsel>=vnf->rvolsel) {
+ while(todo--) {
+ sample = srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ += lvolsel*sample;
+ *dest++ -= lvolsel*sample;
+ }
+ } else {
+ while(todo--) {
+ sample = srce[index >> FRACBITS];
+ index += increment;
+
+ *dest++ -= rvolsel*sample;
+ *dest++ += rvolsel*sample;
+ }
+ }
+ return index;
+}
+
+static SLONGLONG MixMonoInterp(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo)
+{
+ SLONG sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rampvol = vnf->rampvol;
+
+ if (rampvol) {
+ SLONG oldlvol = vnf->oldlvol - lvolsel;
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += ((lvolsel << CLICK_SHIFT) + oldlvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ if (!--rampvol)
+ break;
+ }
+ vnf->rampvol = rampvol;
+ if (todo < 0)
+ return index;
+ }
+
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ }
+ return index;
+}
+
+static SLONGLONG MixStereoInterp(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo)
+{
+ SLONG sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+ SLONG rampvol = vnf->rampvol;
+
+ if (rampvol) {
+ SLONG oldlvol = vnf->oldlvol - lvolsel;
+ SLONG oldrvol = vnf->oldrvol - rvolsel;
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ +=((lvolsel << CLICK_SHIFT) + oldlvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ *dest++ +=((rvolsel << CLICK_SHIFT) + oldrvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ if (!--rampvol)
+ break;
+ }
+ vnf->rampvol = rampvol;
+ if (todo < 0)
+ return index;
+ }
+
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += lvolsel * sample;
+ *dest++ += rvolsel * sample;
+ }
+ return index;
+}
+
+static SLONGLONG MixSurroundInterp(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo)
+{
+ SLONG sample;
+ SLONG lvolsel = vnf->lvolsel;
+ SLONG rvolsel = vnf->rvolsel;
+ SLONG rampvol = vnf->rampvol;
+ SLONG oldvol, vol;
+
+ if (lvolsel >= rvolsel) {
+ vol = lvolsel;
+ oldvol = vnf->oldlvol;
+ } else {
+ vol = rvolsel;
+ oldvol = vnf->oldrvol;
+ }
+
+ if (rampvol) {
+ oldvol -= vol;
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ sample=((vol << CLICK_SHIFT) + oldvol * rampvol)
+ * sample >> CLICK_SHIFT;
+ *dest++ += sample;
+ *dest++ -= sample;
+ if (!--rampvol)
+ break;
+ }
+ vnf->rampvol = rampvol;
+ if (todo < 0)
+ return index;
+ }
+
+ while(todo--) {
+ sample=(SLONG)srce[index>>FRACBITS]+
+ ((SLONG)(srce[(index>>FRACBITS)+1]-srce[index>>FRACBITS])
+ *(index&FRACMASK)>>FRACBITS);
+ index += increment;
+
+ *dest++ += vol*sample;
+ *dest++ -= vol*sample;
+ }
+ return index;
+}
+
+static void (*MixReverb)(SLONG* srce,NATIVE count);
+
+/* Reverb macros */
+#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n
+#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7)
+#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7)
+
+static void MixReverb_Normal(SLONG* srce,NATIVE count)
+{
+ unsigned int speedup;
+ int ReverbPct;
+ unsigned int loc1,loc2,loc3,loc4;
+ unsigned int loc5,loc6,loc7,loc8;
+
+ ReverbPct=58+(md_reverb<<2);
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ while(count--) {
+ /* Compute the left channel echo buffers */
+ speedup = *srce >> 3;
+
+ COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4);
+ COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8);
+
+ /* Prepare to compute actual finalized data */
+ RVRindex++;
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ /* left channel */
+ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+
+ RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8];
+ }
+}
+
+static void MixReverb_Stereo(SLONG* srce,NATIVE count)
+{
+ unsigned int speedup;
+ int ReverbPct;
+ unsigned int loc1, loc2, loc3, loc4;
+ unsigned int loc5, loc6, loc7, loc8;
+
+ ReverbPct = 92+(md_reverb<<1);
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ while(count--) {
+ /* Compute the left channel echo buffers */
+ speedup = *srce >> 3;
+
+ COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4);
+ COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8);
+
+ /* Compute the right channel echo buffers */
+ speedup = srce[1] >> 3;
+
+ COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4);
+ COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8);
+
+ /* Prepare to compute actual finalized data */
+ RVRindex++;
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ /* left channel then right channel */
+ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+
+ RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8];
+
+ *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+
+ RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8];
+ }
+}
+
+/* Mixing macros */
+#define EXTRACT_SAMPLE(var,size) var=*srce++>>(BITSHIFT+16-size)
+#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var
+#define PUT_SAMPLE(var) *dste++=var
+
+static void Mix32To16(SWORD* dste,const SLONG* srce,NATIVE count)
+{
+ SLONG x1,x2,x3,x4;
+ int remain;
+
+ remain=count&3;
+ for(count>>=2;count;count--) {
+ EXTRACT_SAMPLE(x1,16); EXTRACT_SAMPLE(x2,16);
+ EXTRACT_SAMPLE(x3,16); EXTRACT_SAMPLE(x4,16);
+
+ CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768);
+ CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768);
+
+ PUT_SAMPLE(x1); PUT_SAMPLE(x2); PUT_SAMPLE(x3); PUT_SAMPLE(x4);
+ }
+ while(remain--) {
+ EXTRACT_SAMPLE(x1,16);
+ CHECK_SAMPLE(x1,32768);
+ PUT_SAMPLE(x1);
+ }
+}
+
+static void Mix32To8(SBYTE* dste,const SLONG* srce,NATIVE count)
+{
+ SWORD x1,x2,x3,x4;
+ int remain;
+
+ remain=count&3;
+ for(count>>=2;count;count--) {
+ EXTRACT_SAMPLE(x1,8); EXTRACT_SAMPLE(x2,8);
+ EXTRACT_SAMPLE(x3,8); EXTRACT_SAMPLE(x4,8);
+
+ CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128);
+ CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128);
+
+ PUT_SAMPLE(x1+128); PUT_SAMPLE(x2+128);
+ PUT_SAMPLE(x3+128); PUT_SAMPLE(x4+128);
+ }
+ while(remain--) {
+ EXTRACT_SAMPLE(x1,8);
+ CHECK_SAMPLE(x1,128);
+ PUT_SAMPLE(x1+128);
+ }
+}
+
+static void AddChannel(SLONG* ptr,NATIVE todo)
+{
+ SLONGLONG end,done;
+ SWORD *s;
+
+ if(!(s=Samples[vnf->handle])) {
+ vnf->current = vnf->active = 0;
+ return;
+ }
+
+ /* update the 'current' index so the sample loops, or stops playing if it
+ reached the end of the sample */
+ while(todo>0) {
+ SLONGLONG endpos;
+
+ if(vnf->flags & SF_REVERSE) {
+ /* The sample is playing in reverse */
+ if((vnf->flags&SF_LOOP)&&(vnf->current<idxlpos)) {
+ /* the sample is looping and has reached the loopstart index */
+ if(vnf->flags & SF_BIDI) {
+ /* sample is doing bidirectional loops, so 'bounce' the
+ current index against the idxlpos */
+ vnf->current = idxlpos+(idxlpos-vnf->current);
+ vnf->flags &= ~SF_REVERSE;
+ vnf->increment = -vnf->increment;
+ } else
+ /* normal backwards looping, so set the current position to
+ loopend index */
+ vnf->current=idxlend-(idxlpos-vnf->current);
+ } else {
+ /* the sample is not looping, so check if it reached index 0 */
+ if(vnf->current < 0) {
+ /* playing index reached 0, so stop playing this sample */
+ vnf->current = vnf->active = 0;
+ break;
+ }
+ }
+ } else {
+ /* The sample is playing forward */
+ if((vnf->flags & SF_LOOP) &&
+ (vnf->current >= idxlend)) {
+ /* the sample is looping, check the loopend index */
+ if(vnf->flags & SF_BIDI) {
+ /* sample is doing bidirectional loops, so 'bounce' the
+ current index against the idxlend */
+ vnf->flags |= SF_REVERSE;
+ vnf->increment = -vnf->increment;
+ vnf->current = idxlend-(vnf->current-idxlend);
+ } else
+ /* normal backwards looping, so set the current position
+ to loopend index */
+ vnf->current=idxlpos+(vnf->current-idxlend);
+ } else {
+ /* sample is not looping, so check if it reached the last
+ position */
+ if(vnf->current >= idxsize) {
+ /* yes, so stop playing this sample */
+ vnf->current = vnf->active = 0;
+ break;
+ }
+ }
+ }
+
+ end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0:
+ (vnf->flags&SF_LOOP)?idxlend:idxsize;
+
+ /* if the sample is not blocked... */
+ if((end==vnf->current)||(!vnf->increment))
+ done=0;
+ else {
+ done=MIN((end-vnf->current)/vnf->increment+1,todo);
+ if(done<0) done=0;
+ }
+
+ if(!done) {
+ vnf->active = 0;
+ break;
+ }
+
+ endpos=vnf->current+done*vnf->increment;
+
+ if(vnf->vol) {
+#ifndef NATIVE_64BIT_INT
+ /* use the 32 bit mixers as often as we can (they're much faster) */
+ if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) {
+ if((md_mode & DMODE_INTERP)) {
+ if(vc_mode & DMODE_STEREO) {
+ if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND))
+ vnf->current=Mix32SurroundInterp
+ (s,ptr,vnf->current,vnf->increment,done);
+ else
+ vnf->current=Mix32StereoInterp
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+ vnf->current=Mix32MonoInterp
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else if(vc_mode & DMODE_STEREO) {
+ if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND))
+ vnf->current=Mix32SurroundNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ else
+ vnf->current=Mix32StereoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+ vnf->current=Mix32MonoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+#endif
+ {
+ if((md_mode & DMODE_INTERP)) {
+ if(vc_mode & DMODE_STEREO) {
+ if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND))
+ vnf->current=MixSurroundInterp
+ (s,ptr,vnf->current,vnf->increment,done);
+ else
+ vnf->current=MixStereoInterp
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+ vnf->current=MixMonoInterp
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else if(vc_mode & DMODE_STEREO) {
+ if((vnf->pan==PAN_SURROUND)&&(md_mode&DMODE_SURROUND))
+ vnf->current=MixSurroundNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ else
+ vnf->current=MixStereoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+ vnf->current=MixMonoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ }
+ } else
+ /* update sample position */
+ vnf->current=endpos;
+
+ todo-=done;
+ ptr +=(vc_mode & DMODE_STEREO)?(done<<1):done;
+ }
+}
+
+#define _IN_VIRTCH_
+#include "virtch_common.c"
+#undef _IN_VIRTCH_
+
+void VC1_WriteSamples(SBYTE* buf,ULONG todo)
+{
+ int left,portion=0,count;
+ SBYTE *buffer;
+ int t, pan, vol;
+
+ while(todo) {
+ if(!tickleft) {
+ if(vc_mode & DMODE_SOFT_MUSIC) md_player();
+ tickleft=(md_mixfreq*125L)/(md_bpm*50L);
+ }
+ left = MIN(tickleft, (long)todo);
+ buffer = buf;
+ tickleft -= left;
+ todo -= left;
+ buf += samples2bytes(left);
+
+ while(left) {
+ portion = MIN(left, samplesthatfit);
+ count = (vc_mode & DMODE_STEREO)?(portion<<1):portion;
+ memset(vc_tickbuf, 0, count<<2);
+ for(t=0;t<vc_softchn;t++) {
+ vnf = &vinf[t];
+
+ if(vnf->kick) {
+ vnf->current=((SLONGLONG)vnf->start)<<FRACBITS;
+ vnf->kick =0;
+ vnf->active =1;
+ }
+
+ if(!vnf->frq) vnf->active = 0;
+
+ if(vnf->active) {
+ vnf->increment=((SLONGLONG)(vnf->frq<<FRACBITS))/md_mixfreq;
+ if(vnf->flags&SF_REVERSE) vnf->increment=-vnf->increment;
+ vol = vnf->vol; pan = vnf->pan;
+
+ vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel;
+ if(vc_mode & DMODE_STEREO) {
+ if(pan != PAN_SURROUND) {
+ vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8;
+ vnf->rvolsel=(vol*pan)>>8;
+ } else
+ vnf->lvolsel=vnf->rvolsel=vol/2;
+ } else
+ vnf->lvolsel=vol;
+
+ idxsize = (vnf->size)? ((SLONGLONG)vnf->size << FRACBITS)-1 : 0;
+ idxlend = (vnf->repend)? ((SLONGLONG)vnf->repend << FRACBITS)-1 : 0;
+ idxlpos = (SLONGLONG)vnf->reppos << FRACBITS;
+ AddChannel(vc_tickbuf, portion);
+ }
+ }
+
+ if(md_reverb) {
+ if(md_reverb>15) md_reverb=15;
+ MixReverb(vc_tickbuf, portion);
+ }
+
+ if(vc_mode & DMODE_16BITS)
+ Mix32To16((SWORD*) buffer, vc_tickbuf, count);
+ else
+ Mix32To8((SBYTE*) buffer, vc_tickbuf, count);
+
+ buffer += samples2bytes(portion);
+ left -= portion;
+ }
+ }
+}
+
+BOOL VC1_Init(void)
+{
+ VC_SetupPointers();
+
+ if (md_mode&DMODE_HQMIXER)
+ return VC2_Init();
+
+ if(!(Samples=(SWORD**)MikMod_calloc(MAXSAMPLEHANDLES,sizeof(SWORD*)))) {
+ _mm_errno = MMERR_INITIALIZING_MIXER;
+ return 1;
+ }
+ if(!vc_tickbuf)
+ if(!(vc_tickbuf=(SLONG*)MikMod_malloc((TICKLSIZE+32)*sizeof(SLONG)))) {
+ _mm_errno = MMERR_INITIALIZING_MIXER;
+ return 1;
+ }
+
+ MixReverb=(md_mode&DMODE_STEREO)?MixReverb_Stereo:MixReverb_Normal;
+ vc_mode = md_mode;
+ return 0;
+}
+
+BOOL VC1_PlayStart(void)
+{
+ samplesthatfit=TICKLSIZE;
+ if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1;
+ tickleft = 0;
+
+ RVc1 = (5000L * md_mixfreq) / REVERBERATION;
+ RVc2 = (5078L * md_mixfreq) / REVERBERATION;
+ RVc3 = (5313L * md_mixfreq) / REVERBERATION;
+ RVc4 = (5703L * md_mixfreq) / REVERBERATION;
+ RVc5 = (6250L * md_mixfreq) / REVERBERATION;
+ RVc6 = (6953L * md_mixfreq) / REVERBERATION;
+ RVc7 = (7813L * md_mixfreq) / REVERBERATION;
+ RVc8 = (8828L * md_mixfreq) / REVERBERATION;
+
+ if(!(RVbufL1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1;
+
+ if(!(RVbufR1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1;
+
+ RVRindex = 0;
+ return 0;
+}
+
+void VC1_PlayStop(void)
+{
+ if(RVbufL1) MikMod_free(RVbufL1);
+ if(RVbufL2) MikMod_free(RVbufL2);
+ if(RVbufL3) MikMod_free(RVbufL3);
+ if(RVbufL4) MikMod_free(RVbufL4);
+ if(RVbufL5) MikMod_free(RVbufL5);
+ if(RVbufL6) MikMod_free(RVbufL6);
+ if(RVbufL7) MikMod_free(RVbufL7);
+ if(RVbufL8) MikMod_free(RVbufL8);
+ RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL;
+ if(RVbufR1) MikMod_free(RVbufR1);
+ if(RVbufR2) MikMod_free(RVbufR2);
+ if(RVbufR3) MikMod_free(RVbufR3);
+ if(RVbufR4) MikMod_free(RVbufR4);
+ if(RVbufR5) MikMod_free(RVbufR5);
+ if(RVbufR6) MikMod_free(RVbufR6);
+ if(RVbufR7) MikMod_free(RVbufR7);
+ if(RVbufR8) MikMod_free(RVbufR8);
+ RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL;
+}
+
+BOOL VC1_SetNumVoices(void)
+{
+ int t;
+
+ if(!(vc_softchn=md_softchn)) return 0;
+
+ if(vinf) MikMod_free(vinf);
+ if(!(vinf= MikMod_calloc(sizeof(VINFO),vc_softchn))) return 1;
+
+ for(t=0;t<vc_softchn;t++) {
+ vinf[t].frq=10000;
+ vinf[t].pan=(t&1)?PAN_LEFT:PAN_RIGHT;
+ }
+
+ return 0;
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/virtch2.c b/src/libs/mikmod/virtch2.c
new file mode 100644
index 0000000..231f1a6
--- /dev/null
+++ b/src/libs/mikmod/virtch2.c
@@ -0,0 +1,887 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
+ complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ High-quality sample mixing routines, using a 32 bits mixing buffer,
+ interpolation, and sample smoothing to improve sound quality and remove
+ clicks.
+
+==============================================================================*/
+
+/*
+
+ Future Additions:
+ Low-Pass filter to remove annoying staticy buzz.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+#include <string.h>
+
+#include "mikmod_internals.h"
+
+/*
+ Constant Definitions
+ ====================
+
+ MAXVOL_FACTOR (was BITSHIFT in virtch.c)
+ Controls the maximum volume of the output data. All mixed data is
+ divided by this number after mixing, so larger numbers result in
+ quieter mixing. Smaller numbers will increase the likeliness of
+ distortion on loud modules.
+
+ REVERBERATION
+ Larger numbers result in shorter reverb duration. Longer reverb
+ durations can cause unwanted static and make the reverb sound more
+ like a crappy echo.
+
+ SAMPLING_SHIFT
+ Specified the shift multiplier which controls by how much the mixing
+ rate is multiplied while mixing. Higher values can improve quality by
+ smoothing the sound and reducing pops and clicks. Note, this is a shift
+ value, so a value of 2 becomes a mixing-rate multiplier of 4, and a
+ value of 3 = 8, etc.
+
+ FRACBITS
+ The number of bits per integer devoted to the fractional part of the
+ number. Generally, this number should not be changed for any reason.
+
+ !!! IMPORTANT !!! All values below MUST ALWAYS be greater than 0
+
+*/
+
+#define MAXVOL_FACTOR (1<<9)
+#define REVERBERATION 11000L
+
+#define SAMPLING_SHIFT 2
+#define SAMPLING_FACTOR (1UL<<SAMPLING_SHIFT)
+
+#define FRACBITS 28
+#define FRACMASK ((1UL<<FRACBITS)-1UL)
+
+#define TICKLSIZE 8192
+#define TICKWSIZE (TICKLSIZE * 2)
+#define TICKBSIZE (TICKWSIZE * 2)
+
+#define CLICK_SHIFT_BASE 6
+#define CLICK_SHIFT (CLICK_SHIFT_BASE + SAMPLING_SHIFT)
+#define CLICK_BUFFER (1L << CLICK_SHIFT)
+
+#ifndef MIN
+#define MIN(a,b) (((a)<(b)) ? (a) : (b))
+#endif
+
+typedef struct VINFO {
+ UBYTE kick; /* =1 -> sample has to be restarted */
+ UBYTE active; /* =1 -> sample is playing */
+ UWORD flags; /* 16/8 bits looping/one-shot */
+ SWORD handle; /* identifies the sample */
+ ULONG start; /* start index */
+ ULONG size; /* samplesize */
+ ULONG reppos; /* loop start */
+ ULONG repend; /* loop end */
+ ULONG frq; /* current frequency */
+ int vol; /* current volume */
+ int pan; /* current panning position */
+
+ int click;
+ int rampvol;
+ SLONG lastvalL,lastvalR;
+ int lvolsel,rvolsel; /* Volume factor in range 0-255 */
+ int oldlvol,oldrvol;
+
+ SLONGLONG current; /* current index in the sample */
+ SLONGLONG increment; /* increment value */
+} VINFO;
+
+static SWORD **Samples;
+static VINFO *vinf=NULL,*vnf;
+static long tickleft,samplesthatfit,vc_memory=0;
+static int vc_softchn;
+static SLONGLONG idxsize,idxlpos,idxlend;
+static SLONG *vc_tickbuf=NULL;
+static UWORD vc_mode;
+
+/* Reverb control variables */
+
+static int RVc1, RVc2, RVc3, RVc4, RVc5, RVc6, RVc7, RVc8;
+static ULONG RVRindex;
+
+/* For Mono or Left Channel */
+static SLONG *RVbufL1=NULL,*RVbufL2=NULL,*RVbufL3=NULL,*RVbufL4=NULL,
+ *RVbufL5=NULL,*RVbufL6=NULL,*RVbufL7=NULL,*RVbufL8=NULL;
+
+/* For Stereo only (Right Channel) */
+static SLONG *RVbufR1=NULL,*RVbufR2=NULL,*RVbufR3=NULL,*RVbufR4=NULL,
+ *RVbufR5=NULL,*RVbufR6=NULL,*RVbufR7=NULL,*RVbufR8=NULL;
+
+#ifdef NATIVE_64BIT_INT
+#define NATIVE SLONGLONG
+#else
+#define NATIVE SLONG
+#endif
+
+/*========== 32 bit sample mixers - only for 32 bit platforms */
+#ifndef NATIVE_64BIT_INT
+
+static SLONG Mix32MonoNormal(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,SLONG todo)
+{
+ SWORD sample=0;
+ SLONG i,f;
+
+ while(todo--) {
+ i=index>>FRACBITS,f=index&FRACMASK;
+ sample=(((SLONG)(srce[i]*(FRACMASK+1L-f)) +
+ ((SLONG)srce[i+1]*f)) >> FRACBITS);
+ index+=increment;
+
+ if(vnf->rampvol) {
+ *dest++ += (long)(
+ ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) +
+ (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) *
+ (SLONG)sample ) >> CLICK_SHIFT );
+ vnf->rampvol--;
+ } else
+ if(vnf->click) {
+ *dest++ += (long)(
+ ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONG)sample ) +
+ (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT );
+ vnf->click--;
+ } else
+ *dest++ +=vnf->lvolsel*sample;
+ }
+ vnf->lastvalL=vnf->lvolsel * sample;
+
+ return index;
+}
+
+static SLONG Mix32StereoNormal(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,ULONG todo)
+{
+ SWORD sample=0;
+ SLONG i,f;
+
+ while(todo--) {
+ i=index>>FRACBITS,f=index&FRACMASK;
+ sample=((((SLONG)srce[i]*(FRACMASK+1L-f)) +
+ ((SLONG)srce[i+1] * f)) >> FRACBITS);
+ index += increment;
+
+ if(vnf->rampvol) {
+ *dest++ += (long)(
+ ( ( ((SLONG)vnf->oldlvol*vnf->rampvol) +
+ (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol))
+ ) * (SLONG)sample ) >> CLICK_SHIFT );
+ *dest++ += (long)(
+ ( ( ((SLONG)vnf->oldrvol*vnf->rampvol) +
+ (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol))
+ ) * (SLONG)sample ) >> CLICK_SHIFT );
+ vnf->rampvol--;
+ } else
+ if(vnf->click) {
+ *dest++ += (long)(
+ ( ( (SLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONG)sample ) + (vnf->lastvalL * vnf->click) )
+ >> CLICK_SHIFT );
+ *dest++ += (long)(
+ ( ( ((SLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONG)sample ) + (vnf->lastvalR * vnf->click) )
+ >> CLICK_SHIFT );
+ vnf->click--;
+ } else {
+ *dest++ +=vnf->lvolsel*sample;
+ *dest++ +=vnf->rvolsel*sample;
+ }
+ }
+ vnf->lastvalL=vnf->lvolsel*sample;
+ vnf->lastvalR=vnf->rvolsel*sample;
+
+ return index;
+}
+
+static SLONG Mix32StereoSurround(const SWORD* srce,SLONG* dest,SLONG index,SLONG increment,ULONG todo)
+{
+ SWORD sample=0;
+ long whoop;
+ SLONG i, f;
+
+ while(todo--) {
+ i=index>>FRACBITS,f=index&FRACMASK;
+ sample=((((SLONG)srce[i]*(FRACMASK+1L-f)) +
+ ((SLONG)srce[i+1]*f)) >> FRACBITS);
+ index+=increment;
+
+ if(vnf->rampvol) {
+ whoop=(long)(
+ ( ( (SLONG)(vnf->oldlvol*vnf->rampvol) +
+ (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) *
+ (SLONG)sample) >> CLICK_SHIFT );
+ *dest++ +=whoop;
+ *dest++ -=whoop;
+ vnf->rampvol--;
+ } else
+ if(vnf->click) {
+ whoop = (long)(
+ ( ( ((SLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONG)sample) +
+ (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT );
+ *dest++ +=whoop;
+ *dest++ -=whoop;
+ vnf->click--;
+ } else {
+ *dest++ +=vnf->lvolsel*sample;
+ *dest++ -=vnf->lvolsel*sample;
+ }
+ }
+ vnf->lastvalL=vnf->lvolsel*sample;
+ vnf->lastvalR=vnf->lvolsel*sample;
+
+ return index;
+}
+#endif
+
+/*========== 64 bit mixers */
+
+static SLONGLONG MixMonoNormal(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,SLONG todo)
+{
+ SWORD sample=0;
+ SLONGLONG i,f;
+
+ while(todo--) {
+ i=index>>FRACBITS,f=index&FRACMASK;
+ sample=(((SLONGLONG)(srce[i]*(FRACMASK+1L-f)) +
+ ((SLONGLONG)srce[i+1]*f)) >> FRACBITS);
+ index+=increment;
+
+ if(vnf->rampvol) {
+ *dest++ += (long)(
+ ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) +
+ (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) *
+ (SLONGLONG)sample ) >> CLICK_SHIFT );
+ vnf->rampvol--;
+ } else
+ if(vnf->click) {
+ *dest++ += (long)(
+ ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONGLONG)sample ) +
+ (vnf->lastvalL*vnf->click) ) >> CLICK_SHIFT );
+ vnf->click--;
+ } else
+ *dest++ +=vnf->lvolsel*sample;
+ }
+ vnf->lastvalL=vnf->lvolsel * sample;
+
+ return index;
+}
+
+static SLONGLONG MixStereoNormal(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,ULONG todo)
+{
+ SWORD sample=0;
+ SLONGLONG i,f;
+
+ while(todo--) {
+ i=index>>FRACBITS,f=index&FRACMASK;
+ sample=((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) +
+ ((SLONGLONG)srce[i+1] * f)) >> FRACBITS);
+ index += increment;
+
+ if(vnf->rampvol) {
+ *dest++ += (long)(
+ ( ( ((SLONGLONG)vnf->oldlvol*vnf->rampvol) +
+ (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol))
+ ) * (SLONGLONG)sample ) >> CLICK_SHIFT );
+ *dest++ += (long)(
+ ( ( ((SLONGLONG)vnf->oldrvol*vnf->rampvol) +
+ (vnf->rvolsel*(CLICK_BUFFER-vnf->rampvol))
+ ) * (SLONGLONG)sample ) >> CLICK_SHIFT );
+ vnf->rampvol--;
+ } else
+ if(vnf->click) {
+ *dest++ += (long)(
+ ( ( (SLONGLONG)(vnf->lvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONGLONG)sample ) + (vnf->lastvalL * vnf->click) )
+ >> CLICK_SHIFT );
+ *dest++ += (long)(
+ ( ( ((SLONGLONG)vnf->rvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONGLONG)sample ) + (vnf->lastvalR * vnf->click) )
+ >> CLICK_SHIFT );
+ vnf->click--;
+ } else {
+ *dest++ +=vnf->lvolsel*sample;
+ *dest++ +=vnf->rvolsel*sample;
+ }
+ }
+ vnf->lastvalL=vnf->lvolsel*sample;
+ vnf->lastvalR=vnf->rvolsel*sample;
+
+ return index;
+}
+
+static SLONGLONG MixStereoSurround(const SWORD* srce,SLONG* dest,SLONGLONG index,SLONGLONG increment,ULONG todo)
+{
+ SWORD sample=0;
+ long whoop;
+ SLONGLONG i, f;
+
+ while(todo--) {
+ i=index>>FRACBITS,f=index&FRACMASK;
+ sample=((((SLONGLONG)srce[i]*(FRACMASK+1L-f)) +
+ ((SLONGLONG)srce[i+1]*f)) >> FRACBITS);
+ index+=increment;
+
+ if(vnf->rampvol) {
+ whoop=(long)(
+ ( ( (SLONGLONG)(vnf->oldlvol*vnf->rampvol) +
+ (vnf->lvolsel*(CLICK_BUFFER-vnf->rampvol)) ) *
+ (SLONGLONG)sample) >> CLICK_SHIFT );
+ *dest++ +=whoop;
+ *dest++ -=whoop;
+ vnf->rampvol--;
+ } else
+ if(vnf->click) {
+ whoop = (long)(
+ ( ( ((SLONGLONG)vnf->lvolsel*(CLICK_BUFFER-vnf->click)) *
+ (SLONGLONG)sample) +
+ (vnf->lastvalL * vnf->click) ) >> CLICK_SHIFT );
+ *dest++ +=whoop;
+ *dest++ -=whoop;
+ vnf->click--;
+ } else {
+ *dest++ +=vnf->lvolsel*sample;
+ *dest++ -=vnf->lvolsel*sample;
+ }
+ }
+ vnf->lastvalL=vnf->lvolsel*sample;
+ vnf->lastvalR=vnf->lvolsel*sample;
+
+ return index;
+}
+
+static void(*Mix32to16)(SWORD* dste,const SLONG* srce,NATIVE count);
+static void(*Mix32to8)(SBYTE* dste,const SLONG* srce,NATIVE count);
+static void(*MixReverb)(SLONG* srce,NATIVE count);
+
+/* Reverb macros */
+#define COMPUTE_LOC(n) loc##n = RVRindex % RVc##n
+#define COMPUTE_LECHO(n) RVbufL##n [loc##n ]=speedup+((ReverbPct*RVbufL##n [loc##n ])>>7)
+#define COMPUTE_RECHO(n) RVbufR##n [loc##n ]=speedup+((ReverbPct*RVbufR##n [loc##n ])>>7)
+
+static void MixReverb_Normal(SLONG* srce,NATIVE count)
+{
+ NATIVE speedup;
+ int ReverbPct;
+ unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8;
+
+ ReverbPct=58+(md_reverb*4);
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ while(count--) {
+ /* Compute the left channel echo buffers */
+ speedup = *srce >> 3;
+
+ COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4);
+ COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8);
+
+ /* Prepare to compute actual finalized data */
+ RVRindex++;
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ /* left channel */
+ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+
+ RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8];
+ }
+}
+
+static void MixReverb_Stereo(SLONG *srce,NATIVE count)
+{
+ NATIVE speedup;
+ int ReverbPct;
+ unsigned int loc1,loc2,loc3,loc4,loc5,loc6,loc7,loc8;
+
+ ReverbPct=58+(md_reverb*4);
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ while(count--) {
+ /* Compute the left channel echo buffers */
+ speedup = *srce >> 3;
+
+ COMPUTE_LECHO(1); COMPUTE_LECHO(2); COMPUTE_LECHO(3); COMPUTE_LECHO(4);
+ COMPUTE_LECHO(5); COMPUTE_LECHO(6); COMPUTE_LECHO(7); COMPUTE_LECHO(8);
+
+ /* Compute the right channel echo buffers */
+ speedup = srce[1] >> 3;
+
+ COMPUTE_RECHO(1); COMPUTE_RECHO(2); COMPUTE_RECHO(3); COMPUTE_RECHO(4);
+ COMPUTE_RECHO(5); COMPUTE_RECHO(6); COMPUTE_RECHO(7); COMPUTE_RECHO(8);
+
+ /* Prepare to compute actual finalized data */
+ RVRindex++;
+
+ COMPUTE_LOC(1); COMPUTE_LOC(2); COMPUTE_LOC(3); COMPUTE_LOC(4);
+ COMPUTE_LOC(5); COMPUTE_LOC(6); COMPUTE_LOC(7); COMPUTE_LOC(8);
+
+ /* left channel */
+ *srce++ +=RVbufL1[loc1]-RVbufL2[loc2]+RVbufL3[loc3]-RVbufL4[loc4]+
+ RVbufL5[loc5]-RVbufL6[loc6]+RVbufL7[loc7]-RVbufL8[loc8];
+
+ /* right channel */
+ *srce++ +=RVbufR1[loc1]-RVbufR2[loc2]+RVbufR3[loc3]-RVbufR4[loc4]+
+ RVbufR5[loc5]-RVbufR6[loc6]+RVbufR7[loc7]-RVbufR8[loc8];
+ }
+}
+
+/* Mixing macros */
+#define EXTRACT_SAMPLE(var,attenuation) var=*srce++/(MAXVOL_FACTOR*attenuation)
+#define CHECK_SAMPLE(var,bound) var=(var>=bound)?bound-1:(var<-bound)?-bound:var
+
+static void Mix32To16_Normal(SWORD* dste,const SLONG* srce,NATIVE count)
+{
+ NATIVE x1,x2,tmpx;
+ int i;
+
+ for(count/=SAMPLING_FACTOR;count;count--) {
+ tmpx=0;
+
+ for(i=SAMPLING_FACTOR/2;i;i--) {
+ EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1);
+
+ CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768);
+
+ tmpx+=x1+x2;
+ }
+ *dste++ =tmpx/SAMPLING_FACTOR;
+ }
+}
+
+static void Mix32To16_Stereo(SWORD* dste,const SLONG* srce,NATIVE count)
+{
+ NATIVE x1,x2,x3,x4,tmpx,tmpy;
+ int i;
+
+ for(count/=SAMPLING_FACTOR;count;count--) {
+ tmpx=tmpy=0;
+
+ for(i=SAMPLING_FACTOR/2;i;i--) {
+ EXTRACT_SAMPLE(x1,1); EXTRACT_SAMPLE(x2,1);
+ EXTRACT_SAMPLE(x3,1); EXTRACT_SAMPLE(x4,1);
+
+ CHECK_SAMPLE(x1,32768); CHECK_SAMPLE(x2,32768);
+ CHECK_SAMPLE(x3,32768); CHECK_SAMPLE(x4,32768);
+
+ tmpx+=x1+x3;
+ tmpy+=x2+x4;
+ }
+ *dste++ =tmpx/SAMPLING_FACTOR;
+ *dste++ =tmpy/SAMPLING_FACTOR;
+ }
+}
+
+static void Mix32To8_Normal(SBYTE* dste,const SLONG* srce,NATIVE count)
+{
+ NATIVE x1,x2,tmpx;
+ int i;
+
+ for(count/=SAMPLING_FACTOR;count;count--) {
+ tmpx = 0;
+
+ for(i=SAMPLING_FACTOR/2;i;i--) {
+ EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256);
+
+ CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128);
+
+ tmpx+=x1+x2;
+ }
+ *dste++ =(tmpx/SAMPLING_FACTOR)+128;
+ }
+}
+
+static void Mix32To8_Stereo(SBYTE* dste,const SLONG* srce,NATIVE count)
+{
+ NATIVE x1,x2,x3,x4,tmpx,tmpy;
+ int i;
+
+ for(count/=SAMPLING_FACTOR;count;count--) {
+ tmpx=tmpy=0;
+
+ for(i=SAMPLING_FACTOR/2;i;i--) {
+ EXTRACT_SAMPLE(x1,256); EXTRACT_SAMPLE(x2,256);
+ EXTRACT_SAMPLE(x3,256); EXTRACT_SAMPLE(x4,256);
+
+ CHECK_SAMPLE(x1,128); CHECK_SAMPLE(x2,128);
+ CHECK_SAMPLE(x3,128); CHECK_SAMPLE(x4,128);
+
+ tmpx+=x1+x3;
+ tmpy+=x2+x4;
+ }
+ *dste++ =(tmpx/SAMPLING_FACTOR)+128;
+ *dste++ =(tmpy/SAMPLING_FACTOR)+128;
+ }
+}
+
+static void AddChannel(SLONG* ptr,NATIVE todo)
+{
+ SLONGLONG end,done;
+ SWORD *s;
+
+ if(!(s=Samples[vnf->handle])) {
+ vnf->current = vnf->active = 0;
+ vnf->lastvalL = vnf->lastvalR = 0;
+ return;
+ }
+
+ /* update the 'current' index so the sample loops, or stops playing if it
+ reached the end of the sample */
+ while(todo>0) {
+ SLONGLONG endpos;
+
+ if(vnf->flags & SF_REVERSE) {
+ /* The sample is playing in reverse */
+ if((vnf->flags&SF_LOOP)&&(vnf->current<idxlpos)) {
+ /* the sample is looping and has reached the loopstart index */
+ if(vnf->flags & SF_BIDI) {
+ /* sample is doing bidirectional loops, so 'bounce' the
+ current index against the idxlpos */
+ vnf->current = idxlpos+(idxlpos-vnf->current);
+ vnf->flags &= ~SF_REVERSE;
+ vnf->increment = -vnf->increment;
+ } else
+ /* normal backwards looping, so set the current position to
+ loopend index */
+ vnf->current=idxlend-(idxlpos-vnf->current);
+ } else {
+ /* the sample is not looping, so check if it reached index 0 */
+ if(vnf->current < 0) {
+ /* playing index reached 0, so stop playing this sample */
+ vnf->current = vnf->active = 0;
+ break;
+ }
+ }
+ } else {
+ /* The sample is playing forward */
+ if((vnf->flags & SF_LOOP) &&
+ (vnf->current >= idxlend)) {
+ /* the sample is looping, check the loopend index */
+ if(vnf->flags & SF_BIDI) {
+ /* sample is doing bidirectional loops, so 'bounce' the
+ current index against the idxlend */
+ vnf->flags |= SF_REVERSE;
+ vnf->increment = -vnf->increment;
+ vnf->current = idxlend-(vnf->current-idxlend);
+ } else
+ /* normal backwards looping, so set the current position
+ to loopend index */
+ vnf->current=idxlpos+(vnf->current-idxlend);
+ } else {
+ /* sample is not looping, so check if it reached the last
+ position */
+ if(vnf->current >= idxsize) {
+ /* yes, so stop playing this sample */
+ vnf->current = vnf->active = 0;
+ break;
+ }
+ }
+ }
+
+ end=(vnf->flags&SF_REVERSE)?(vnf->flags&SF_LOOP)?idxlpos:0:
+ (vnf->flags&SF_LOOP)?idxlend:idxsize;
+
+ /* if the sample is not blocked... */
+ if((end==vnf->current)||(!vnf->increment))
+ done=0;
+ else {
+ done=MIN((end-vnf->current)/vnf->increment+1,todo);
+ if(done<0) done=0;
+ }
+
+ if(!done) {
+ vnf->active = 0;
+ break;
+ }
+
+ endpos=vnf->current+done*vnf->increment;
+
+ if(vnf->vol || vnf->rampvol) {
+#ifndef NATIVE_64BIT_INT
+ /* use the 32 bit mixers as often as we can (they're much faster) */
+ if((vnf->current<0x7fffffff)&&(endpos<0x7fffffff)) {
+ if(vc_mode & DMODE_STEREO) {
+ if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND))
+ vnf->current=Mix32StereoSurround
+ (s,ptr,vnf->current,vnf->increment,done);
+ else
+ vnf->current=Mix32StereoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+ vnf->current=Mix32MonoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+#endif
+ {
+ if(vc_mode & DMODE_STEREO) {
+ if((vnf->pan==PAN_SURROUND)&&(vc_mode&DMODE_SURROUND))
+ vnf->current=MixStereoSurround
+ (s,ptr,vnf->current,vnf->increment,done);
+ else
+ vnf->current=MixStereoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ } else
+ vnf->current=MixMonoNormal
+ (s,ptr,vnf->current,vnf->increment,done);
+ }
+ } else {
+ vnf->lastvalL = vnf->lastvalR = 0;
+ /* update sample position */
+ vnf->current=endpos;
+ }
+
+ todo -= done;
+ ptr +=(vc_mode & DMODE_STEREO)?(done<<1):done;
+ }
+}
+
+#define _IN_VIRTCH_
+
+#define VC1_SilenceBytes VC2_SilenceBytes
+#define VC1_WriteSamples VC2_WriteSamples
+#define VC1_WriteBytes VC2_WriteBytes
+#define VC1_Exit VC2_Exit
+#define VC1_VoiceSetVolume VC2_VoiceSetVolume
+#define VC1_VoiceGetVolume VC2_VoiceGetVolume
+#define VC1_VoiceSetPanning VC2_VoiceSetPanning
+#define VC1_VoiceGetPanning VC2_VoiceGetPanning
+#define VC1_VoiceSetFrequency VC2_VoiceSetFrequency
+#define VC1_VoiceGetFrequency VC2_VoiceGetFrequency
+#define VC1_VoicePlay VC2_VoicePlay
+#define VC1_VoiceStop VC2_VoiceStop
+#define VC1_VoiceStopped VC2_VoiceStopped
+#define VC1_VoiceGetPosition VC2_VoiceGetPosition
+#define VC1_SampleUnload VC2_SampleUnload
+#define VC1_SampleLoad VC2_SampleLoad
+#define VC1_SampleSpace VC2_SampleSpace
+#define VC1_SampleLength VC2_SampleLength
+#define VC1_VoiceRealVolume VC2_VoiceRealVolume
+
+#include "virtch_common.c"
+#undef _IN_VIRTCH_
+
+void VC2_WriteSamples(SBYTE* buf,ULONG todo)
+{
+ int left,portion=0;
+ SBYTE *buffer;
+ int t,pan,vol;
+
+ todo*=SAMPLING_FACTOR;
+
+ while(todo) {
+ if(!tickleft) {
+ if(vc_mode & DMODE_SOFT_MUSIC) md_player();
+ tickleft=(md_mixfreq*125L*SAMPLING_FACTOR)/(md_bpm*50L);
+ tickleft&=~(SAMPLING_FACTOR-1);
+ }
+ left = MIN(tickleft, (long)todo);
+ buffer = buf;
+ tickleft -= left;
+ todo -= left;
+ buf += samples2bytes(left)/SAMPLING_FACTOR;
+
+ while(left) {
+ portion = MIN(left, samplesthatfit);
+ memset(vc_tickbuf,0,portion<<((vc_mode&DMODE_STEREO)?3:2));
+ for(t=0;t<vc_softchn;t++) {
+ vnf = &vinf[t];
+
+ if(vnf->kick) {
+ vnf->current=((SLONGLONG)(vnf->start))<<FRACBITS;
+ vnf->kick = 0;
+ vnf->active = 1;
+ vnf->click = CLICK_BUFFER;
+ vnf->rampvol = 0;
+ }
+
+ if(!vnf->frq) vnf->active = 0;
+
+ if(vnf->active) {
+ vnf->increment=((SLONGLONG)(vnf->frq)<<(FRACBITS-SAMPLING_SHIFT))
+ /md_mixfreq;
+ if(vnf->flags&SF_REVERSE) vnf->increment=-vnf->increment;
+ vol = vnf->vol; pan = vnf->pan;
+
+ vnf->oldlvol=vnf->lvolsel;vnf->oldrvol=vnf->rvolsel;
+ if(vc_mode & DMODE_STEREO) {
+ if(pan!=PAN_SURROUND) {
+ vnf->lvolsel=(vol*(PAN_RIGHT-pan))>>8;
+ vnf->rvolsel=(vol*pan)>>8;
+ } else {
+ vnf->lvolsel=vnf->rvolsel=(vol * 256L) / 480;
+ }
+ } else
+ vnf->lvolsel=vol;
+
+ idxsize=(vnf->size)?((SLONGLONG)(vnf->size)<<FRACBITS)-1:0;
+ idxlend=(vnf->repend)?((SLONGLONG)(vnf->repend)<<FRACBITS)-1:0;
+ idxlpos=(SLONGLONG)(vnf->reppos)<<FRACBITS;
+ AddChannel(vc_tickbuf,portion);
+ }
+ }
+
+ if(md_reverb) {
+ if(md_reverb>15) md_reverb=15;
+ MixReverb(vc_tickbuf,portion);
+ }
+
+ if(vc_mode & DMODE_16BITS)
+ Mix32to16((SWORD*)buffer,vc_tickbuf,portion);
+ else
+ Mix32to8((SBYTE*)buffer,vc_tickbuf,portion);
+
+ buffer += samples2bytes(portion) / SAMPLING_FACTOR;
+ left -= portion;
+ }
+ }
+}
+
+BOOL VC2_Init(void)
+{
+ VC_SetupPointers();
+
+ if (!(md_mode&DMODE_HQMIXER))
+ return VC1_Init();
+
+ if(!(Samples=(SWORD**)MikMod_calloc(MAXSAMPLEHANDLES,sizeof(SWORD*)))) {
+ _mm_errno = MMERR_INITIALIZING_MIXER;
+ return 1;
+ }
+ if(!vc_tickbuf)
+ if(!(vc_tickbuf=(SLONG*)MikMod_malloc((TICKLSIZE+32)*sizeof(SLONG)))) {
+ _mm_errno = MMERR_INITIALIZING_MIXER;
+ return 1;
+ }
+
+ if(md_mode & DMODE_STEREO) {
+ Mix32to16 = Mix32To16_Stereo;
+ Mix32to8 = Mix32To8_Stereo;
+ MixReverb = MixReverb_Stereo;
+ } else {
+ Mix32to16 = Mix32To16_Normal;
+ Mix32to8 = Mix32To8_Normal;
+ MixReverb = MixReverb_Normal;
+ }
+ md_mode |= DMODE_INTERP;
+ vc_mode = md_mode;
+ return 0;
+}
+
+BOOL VC2_PlayStart(void)
+{
+ md_mode|=DMODE_INTERP;
+
+ samplesthatfit = TICKLSIZE;
+ if(vc_mode & DMODE_STEREO) samplesthatfit >>= 1;
+ tickleft = 0;
+
+ RVc1 = (5000L * md_mixfreq) / (REVERBERATION * 10);
+ RVc2 = (5078L * md_mixfreq) / (REVERBERATION * 10);
+ RVc3 = (5313L * md_mixfreq) / (REVERBERATION * 10);
+ RVc4 = (5703L * md_mixfreq) / (REVERBERATION * 10);
+ RVc5 = (6250L * md_mixfreq) / (REVERBERATION * 10);
+ RVc6 = (6953L * md_mixfreq) / (REVERBERATION * 10);
+ RVc7 = (7813L * md_mixfreq) / (REVERBERATION * 10);
+ RVc8 = (8828L * md_mixfreq) / (REVERBERATION * 10);
+
+ if(!(RVbufL1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufL8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1;
+
+ if(!(RVbufR1=(SLONG*)MikMod_calloc((RVc1+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR2=(SLONG*)MikMod_calloc((RVc2+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR3=(SLONG*)MikMod_calloc((RVc3+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR4=(SLONG*)MikMod_calloc((RVc4+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR5=(SLONG*)MikMod_calloc((RVc5+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR6=(SLONG*)MikMod_calloc((RVc6+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR7=(SLONG*)MikMod_calloc((RVc7+1),sizeof(SLONG)))) return 1;
+ if(!(RVbufR8=(SLONG*)MikMod_calloc((RVc8+1),sizeof(SLONG)))) return 1;
+
+ RVRindex = 0;
+ return 0;
+}
+
+void VC2_PlayStop(void)
+{
+ if(RVbufL1) MikMod_free(RVbufL1);
+ if(RVbufL2) MikMod_free(RVbufL2);
+ if(RVbufL3) MikMod_free(RVbufL3);
+ if(RVbufL4) MikMod_free(RVbufL4);
+ if(RVbufL5) MikMod_free(RVbufL5);
+ if(RVbufL6) MikMod_free(RVbufL6);
+ if(RVbufL7) MikMod_free(RVbufL7);
+ if(RVbufL8) MikMod_free(RVbufL8);
+ if(RVbufR1) MikMod_free(RVbufR1);
+ if(RVbufR2) MikMod_free(RVbufR2);
+ if(RVbufR3) MikMod_free(RVbufR3);
+ if(RVbufR4) MikMod_free(RVbufR4);
+ if(RVbufR5) MikMod_free(RVbufR5);
+ if(RVbufR6) MikMod_free(RVbufR6);
+ if(RVbufR7) MikMod_free(RVbufR7);
+ if(RVbufR8) MikMod_free(RVbufR8);
+
+ RVbufL1=RVbufL2=RVbufL3=RVbufL4=RVbufL5=RVbufL6=RVbufL7=RVbufL8=NULL;
+ RVbufR1=RVbufR2=RVbufR3=RVbufR4=RVbufR5=RVbufR6=RVbufR7=RVbufR8=NULL;
+}
+
+BOOL VC2_SetNumVoices(void)
+{
+ int t;
+
+ md_mode|=DMODE_INTERP;
+
+ if(!(vc_softchn=md_softchn)) return 0;
+
+ if(vinf) MikMod_free(vinf);
+ if(!(vinf=MikMod_calloc(sizeof(VINFO),vc_softchn))) return 1;
+
+ for(t=0;t<vc_softchn;t++) {
+ vinf[t].frq=10000;
+ vinf[t].pan=(t&1)?PAN_LEFT:PAN_RIGHT;
+ }
+
+ return 0;
+}
+
+/* ex:set ts=4: */
diff --git a/src/libs/mikmod/virtch_common.c b/src/libs/mikmod/virtch_common.c
new file mode 100644
index 0000000..b0b81ee
--- /dev/null
+++ b/src/libs/mikmod/virtch_common.c
@@ -0,0 +1,459 @@
+/* MikMod sound library
+ (c) 1998, 1999, 2000, 2001 Miodrag Vallat and others - see file AUTHORS
+ for complete list.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/*==============================================================================
+
+ $Id$
+
+ Common source parts between the two software mixers.
+ This file is probably the ugliest part of libmikmod...
+
+==============================================================================*/
+
+#ifndef _IN_VIRTCH_
+
+#include "mikmod_internals.h"
+
+extern BOOL VC1_Init(void);
+extern BOOL VC2_Init(void);
+static BOOL (*VC_Init_ptr)(void)=VC1_Init;
+extern void VC1_Exit(void);
+extern void VC2_Exit(void);
+static void (*VC_Exit_ptr)(void)=VC1_Exit;
+extern BOOL VC1_SetNumVoices(void);
+extern BOOL VC2_SetNumVoices(void);
+static BOOL (*VC_SetNumVoices_ptr)(void);
+extern ULONG VC1_SampleSpace(int);
+extern ULONG VC2_SampleSpace(int);
+static ULONG (*VC_SampleSpace_ptr)(int);
+extern ULONG VC1_SampleLength(int,SAMPLE*);
+extern ULONG VC2_SampleLength(int,SAMPLE*);
+static ULONG (*VC_SampleLength_ptr)(int,SAMPLE*);
+
+extern BOOL VC1_PlayStart(void);
+extern BOOL VC2_PlayStart(void);
+static BOOL (*VC_PlayStart_ptr)(void);
+extern void VC1_PlayStop(void);
+extern void VC2_PlayStop(void);
+static void (*VC_PlayStop_ptr)(void);
+
+extern SWORD VC1_SampleLoad(struct SAMPLOAD*,int);
+extern SWORD VC2_SampleLoad(struct SAMPLOAD*,int);
+static SWORD (*VC_SampleLoad_ptr)(struct SAMPLOAD*,int);
+extern void VC1_SampleUnload(SWORD);
+extern void VC2_SampleUnload(SWORD);
+static void (*VC_SampleUnload_ptr)(SWORD);
+
+extern ULONG VC1_WriteBytes(SBYTE*,ULONG);
+extern ULONG VC2_WriteBytes(SBYTE*,ULONG);
+static ULONG (*VC_WriteBytes_ptr)(SBYTE*,ULONG);
+extern ULONG VC1_SilenceBytes(SBYTE*,ULONG);
+extern ULONG VC2_SilenceBytes(SBYTE*,ULONG);
+static ULONG (*VC_SilenceBytes_ptr)(SBYTE*,ULONG);
+
+extern void VC1_VoiceSetVolume(UBYTE,UWORD);
+extern void VC2_VoiceSetVolume(UBYTE,UWORD);
+static void (*VC_VoiceSetVolume_ptr)(UBYTE,UWORD);
+extern UWORD VC1_VoiceGetVolume(UBYTE);
+extern UWORD VC2_VoiceGetVolume(UBYTE);
+static UWORD (*VC_VoiceGetVolume_ptr)(UBYTE);
+extern void VC1_VoiceSetFrequency(UBYTE,ULONG);
+extern void VC2_VoiceSetFrequency(UBYTE,ULONG);
+static void (*VC_VoiceSetFrequency_ptr)(UBYTE,ULONG);
+extern ULONG VC1_VoiceGetFrequency(UBYTE);
+extern ULONG VC2_VoiceGetFrequency(UBYTE);
+static ULONG (*VC_VoiceGetFrequency_ptr)(UBYTE);
+extern void VC1_VoiceSetPanning(UBYTE,ULONG);
+extern void VC2_VoiceSetPanning(UBYTE,ULONG);
+static void (*VC_VoiceSetPanning_ptr)(UBYTE,ULONG);
+extern ULONG VC1_VoiceGetPanning(UBYTE);
+extern ULONG VC2_VoiceGetPanning(UBYTE);
+static ULONG (*VC_VoiceGetPanning_ptr)(UBYTE);
+extern void VC1_VoicePlay(UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD);
+extern void VC2_VoicePlay(UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD);
+static void (*VC_VoicePlay_ptr)(UBYTE,SWORD,ULONG,ULONG,ULONG,ULONG,UWORD);
+
+extern void VC1_VoiceStop(UBYTE);
+extern void VC2_VoiceStop(UBYTE);
+static void (*VC_VoiceStop_ptr)(UBYTE);
+extern BOOL VC1_VoiceStopped(UBYTE);
+extern BOOL VC2_VoiceStopped(UBYTE);
+static BOOL (*VC_VoiceStopped_ptr)(UBYTE);
+extern SLONG VC1_VoiceGetPosition(UBYTE);
+extern SLONG VC2_VoiceGetPosition(UBYTE);
+static SLONG (*VC_VoiceGetPosition_ptr)(UBYTE);
+extern ULONG VC1_VoiceRealVolume(UBYTE);
+extern ULONG VC2_VoiceRealVolume(UBYTE);
+static ULONG (*VC_VoiceRealVolume_ptr)(UBYTE);
+
+#if defined __STDC__ || defined _MSC_VER || defined MPW_C
+#define VC_PROC0(suffix) \
+MIKMODAPI void VC_##suffix (void) { VC_##suffix##_ptr(); }
+
+#define VC_FUNC0(suffix,ret) \
+MIKMODAPI ret VC_##suffix (void) { return VC_##suffix##_ptr(); }
+
+#define VC_PROC1(suffix,typ1) \
+MIKMODAPI void VC_##suffix (typ1 a) { VC_##suffix##_ptr(a); }
+
+#define VC_FUNC1(suffix,ret,typ1) \
+MIKMODAPI ret VC_##suffix (typ1 a) { return VC_##suffix##_ptr(a); }
+
+#define VC_PROC2(suffix,typ1,typ2) \
+MIKMODAPI void VC_##suffix (typ1 a,typ2 b) { VC_##suffix##_ptr(a,b); }
+
+#define VC_FUNC2(suffix,ret,typ1,typ2) \
+MIKMODAPI ret VC_##suffix (typ1 a,typ2 b) { return VC_##suffix##_ptr(a,b); }
+#else
+#define VC_PROC0(suffix) \
+MIKMODAPI void VC_/**/suffix (void) { VC_/**/suffix/**/_ptr(); }
+
+#define VC_FUNC0(suffix,ret) \
+MIKMODAPI ret VC_/**/suffix (void) { return VC_/**/suffix/**/_ptr(); }
+
+#define VC_PROC1(suffix,typ1) \
+MIKMODAPI void VC_/**/suffix (typ1 a) { VC_/**/suffix/**/_ptr(a); }
+
+#define VC_FUNC1(suffix,ret,typ1) \
+MIKMODAPI ret VC_/**/suffix (typ1 a) { return VC_/**/suffix/**/_ptr(a); }
+
+#define VC_PROC2(suffix,typ1,typ2) \
+MIKMODAPI void VC_/**/suffix (typ1 a,typ2 b) { VC_/**/suffix/**/_ptr(a,b); }
+
+#define VC_FUNC2(suffix,ret,typ1,typ2) \
+MIKMODAPI ret VC_/**/suffix (typ1 a,typ2 b) { return VC_/**/suffix/**/_ptr(a,b); }
+#endif
+
+VC_FUNC0(Init,BOOL)
+VC_PROC0(Exit)
+VC_FUNC0(SetNumVoices,BOOL)
+VC_FUNC1(SampleSpace,ULONG,int)
+VC_FUNC2(SampleLength,ULONG,int,SAMPLE*)
+VC_FUNC0(PlayStart,BOOL)
+VC_PROC0(PlayStop)
+VC_FUNC2(SampleLoad,SWORD,struct SAMPLOAD*,int)
+VC_PROC1(SampleUnload,SWORD)
+VC_FUNC2(WriteBytes,ULONG,SBYTE*,ULONG)
+VC_FUNC2(SilenceBytes,ULONG,SBYTE*,ULONG)
+VC_PROC2(VoiceSetVolume,UBYTE,UWORD)
+VC_FUNC1(VoiceGetVolume,UWORD,UBYTE)
+VC_PROC2(VoiceSetFrequency,UBYTE,ULONG)
+VC_FUNC1(VoiceGetFrequency,ULONG,UBYTE)
+VC_PROC2(VoiceSetPanning,UBYTE,ULONG)
+VC_FUNC1(VoiceGetPanning,ULONG,UBYTE)
+
+void VC_VoicePlay(UBYTE a,SWORD b,ULONG c,ULONG d,ULONG e,ULONG f,UWORD g)
+{ VC_VoicePlay_ptr(a,b,c,d,e,f,g); }
+
+VC_PROC1(VoiceStop,UBYTE)
+VC_FUNC1(VoiceStopped,BOOL,UBYTE)
+VC_FUNC1(VoiceGetPosition,SLONG,UBYTE)
+VC_FUNC1(VoiceRealVolume,ULONG,UBYTE)
+
+void VC_SetupPointers(void)
+{
+ if (md_mode&DMODE_HQMIXER) {
+ VC_Init_ptr=VC2_Init;
+ VC_Exit_ptr=VC2_Exit;
+ VC_SetNumVoices_ptr=VC2_SetNumVoices;
+ VC_SampleSpace_ptr=VC2_SampleSpace;
+ VC_SampleLength_ptr=VC2_SampleLength;
+ VC_PlayStart_ptr=VC2_PlayStart;
+ VC_PlayStop_ptr=VC2_PlayStop;
+ VC_SampleLoad_ptr=VC2_SampleLoad;
+ VC_SampleUnload_ptr=VC2_SampleUnload;
+ VC_WriteBytes_ptr=VC2_WriteBytes;
+ VC_SilenceBytes_ptr=VC2_SilenceBytes;
+ VC_VoiceSetVolume_ptr=VC2_VoiceSetVolume;
+ VC_VoiceGetVolume_ptr=VC2_VoiceGetVolume;
+ VC_VoiceSetFrequency_ptr=VC2_VoiceSetFrequency;
+ VC_VoiceGetFrequency_ptr=VC2_VoiceGetFrequency;
+ VC_VoiceSetPanning_ptr=VC2_VoiceSetPanning;
+ VC_VoiceGetPanning_ptr=VC2_VoiceGetPanning;
+ VC_VoicePlay_ptr=VC2_VoicePlay;
+ VC_VoiceStop_ptr=VC2_VoiceStop;
+ VC_VoiceStopped_ptr=VC2_VoiceStopped;
+ VC_VoiceGetPosition_ptr=VC2_VoiceGetPosition;
+ VC_VoiceRealVolume_ptr=VC2_VoiceRealVolume;
+ } else {
+ VC_Init_ptr=VC1_Init;
+ VC_Exit_ptr=VC1_Exit;
+ VC_SetNumVoices_ptr=VC1_SetNumVoices;
+ VC_SampleSpace_ptr=VC1_SampleSpace;
+ VC_SampleLength_ptr=VC1_SampleLength;
+ VC_PlayStart_ptr=VC1_PlayStart;
+ VC_PlayStop_ptr=VC1_PlayStop;
+ VC_SampleLoad_ptr=VC1_SampleLoad;
+ VC_SampleUnload_ptr=VC1_SampleUnload;
+ VC_WriteBytes_ptr=VC1_WriteBytes;
+ VC_SilenceBytes_ptr=VC1_SilenceBytes;
+ VC_VoiceSetVolume_ptr=VC1_VoiceSetVolume;
+ VC_VoiceGetVolume_ptr=VC1_VoiceGetVolume;
+ VC_VoiceSetFrequency_ptr=VC1_VoiceSetFrequency;
+ VC_VoiceGetFrequency_ptr=VC1_VoiceGetFrequency;
+ VC_VoiceSetPanning_ptr=VC1_VoiceSetPanning;
+ VC_VoiceGetPanning_ptr=VC1_VoiceGetPanning;
+ VC_VoicePlay_ptr=VC1_VoicePlay;
+ VC_VoiceStop_ptr=VC1_VoiceStop;
+ VC_VoiceStopped_ptr=VC1_VoiceStopped;
+ VC_VoiceGetPosition_ptr=VC1_VoiceGetPosition;
+ VC_VoiceRealVolume_ptr=VC1_VoiceRealVolume;
+ }
+}
+
+#else
+
+#ifndef _VIRTCH_COMMON_
+#define _VIRTCH_COMMON_
+
+static ULONG samples2bytes(ULONG samples)
+{
+ if(vc_mode & DMODE_16BITS) samples <<= 1;
+ if(vc_mode & DMODE_STEREO) samples <<= 1;
+ return samples;
+}
+
+static ULONG bytes2samples(ULONG bytes)
+{
+ if(vc_mode & DMODE_16BITS) bytes >>= 1;
+ if(vc_mode & DMODE_STEREO) bytes >>= 1;
+ return bytes;
+}
+
+/* Fill the buffer with 'todo' bytes of silence (it depends on the mixing mode
+ how the buffer is filled) */
+ULONG VC1_SilenceBytes(SBYTE* buf,ULONG todo)
+{
+ todo=samples2bytes(bytes2samples(todo));
+
+ /* clear the buffer to zero (16 bits signed) or 0x80 (8 bits unsigned) */
+ if(vc_mode & DMODE_16BITS)
+ memset(buf,0,todo);
+ else
+ memset(buf,0x80,todo);
+
+ return todo;
+}
+
+void VC1_WriteSamples(SBYTE*,ULONG);
+
+/* Writes 'todo' mixed SBYTES (!!) to 'buf'. It returns the number of SBYTES
+ actually written to 'buf' (which is rounded to number of samples that fit
+ into 'todo' bytes). */
+ULONG VC1_WriteBytes(SBYTE* buf,ULONG todo)
+{
+ if(!vc_softchn)
+ return VC1_SilenceBytes(buf,todo);
+
+ todo = bytes2samples(todo);
+ VC1_WriteSamples(buf,todo);
+
+ return samples2bytes(todo);
+}
+
+void VC1_Exit(void)
+{
+ if(vc_tickbuf) MikMod_free(vc_tickbuf);
+ if(vinf) MikMod_free(vinf);
+ if(Samples) MikMod_free(Samples);
+
+ vc_tickbuf = NULL;
+ vinf = NULL;
+ Samples = NULL;
+
+ VC_SetupPointers();
+}
+
+UWORD VC1_VoiceGetVolume(UBYTE voice)
+{
+ return vinf[voice].vol;
+}
+
+ULONG VC1_VoiceGetPanning(UBYTE voice)
+{
+ return vinf[voice].pan;
+}
+
+void VC1_VoiceSetFrequency(UBYTE voice,ULONG frq)
+{
+ vinf[voice].frq=frq;
+}
+
+ULONG VC1_VoiceGetFrequency(UBYTE voice)
+{
+ return vinf[voice].frq;
+}
+
+void VC1_VoicePlay(UBYTE voice,SWORD handle,ULONG start,ULONG size,ULONG reppos,ULONG repend,UWORD flags)
+{
+ vinf[voice].flags = flags;
+ vinf[voice].handle = handle;
+ vinf[voice].start = start;
+ vinf[voice].size = size;
+ vinf[voice].reppos = reppos;
+ vinf[voice].repend = repend;
+ vinf[voice].kick = 1;
+}
+
+void VC1_VoiceStop(UBYTE voice)
+{
+ vinf[voice].active = 0;
+}
+
+BOOL VC1_VoiceStopped(UBYTE voice)
+{
+ return(vinf[voice].active==0);
+}
+
+SLONG VC1_VoiceGetPosition(UBYTE voice)
+{
+ return(vinf[voice].current>>FRACBITS);
+}
+
+void VC1_VoiceSetVolume(UBYTE voice,UWORD vol)
+{
+ /* protect against clicks if volume variation is too high */
+ if(abs((int)vinf[voice].vol-(int)vol)>32)
+ vinf[voice].rampvol=CLICK_BUFFER;
+ vinf[voice].vol=vol;
+}
+
+void VC1_VoiceSetPanning(UBYTE voice,ULONG pan)
+{
+ /* protect against clicks if panning variation is too high */
+ if(abs((int)vinf[voice].pan-(int)pan)>48)
+ vinf[voice].rampvol=CLICK_BUFFER;
+ vinf[voice].pan=pan;
+}
+
+/*========== External mixer interface */
+
+void VC1_SampleUnload(SWORD handle)
+{
+ if (handle<MAXSAMPLEHANDLES) {
+ if (Samples[handle])
+ MikMod_free(Samples[handle]);
+ Samples[handle]=NULL;
+ }
+}
+
+SWORD VC1_SampleLoad(struct SAMPLOAD* sload,int type)
+{
+ SAMPLE *s = sload->sample;
+ int handle;
+ ULONG t, length,loopstart,loopend;
+
+ if(type==MD_HARDWARE) return -1;
+
+ /* Find empty slot to put sample address in */
+ for(handle=0;handle<MAXSAMPLEHANDLES;handle++)
+ if(!Samples[handle]) break;
+
+ if(handle==MAXSAMPLEHANDLES) {
+ _mm_errno = MMERR_OUT_OF_HANDLES;
+ return -1;
+ }
+
+ /* Reality check for loop settings */
+ if (s->loopend > s->length)
+ s->loopend = s->length;
+ if (s->loopstart >= s->loopend)
+ s->flags &= ~SF_LOOP;
+
+ length = s->length;
+ loopstart = s->loopstart;
+ loopend = s->loopend;
+
+ SL_SampleSigned(sload);
+ SL_Sample8to16(sload);
+
+ if(!(Samples[handle]=(SWORD*)MikMod_malloc((length+20)<<1))) {
+ _mm_errno = MMERR_SAMPLE_TOO_BIG;
+ return -1;
+ }
+
+ /* read sample into buffer */
+ if (SL_Load(Samples[handle],sload,length))
+ return -1;
+
+ /* Unclick sample */
+ if(s->flags & SF_LOOP) {
+ if(s->flags & SF_BIDI)
+ for(t=0;t<16;t++)
+ Samples[handle][loopend+t]=Samples[handle][(loopend-t)-1];
+ else
+ for(t=0;t<16;t++)
+ Samples[handle][loopend+t]=Samples[handle][t+loopstart];
+ } else
+ for(t=0;t<16;t++)
+ Samples[handle][t+length]=0;
+
+ return handle;
+}
+
+ULONG VC1_SampleSpace(int type)
+{
+ (void)type; /* unused arg */
+
+ return vc_memory;
+}
+
+ULONG VC1_SampleLength(int type,SAMPLE* s)
+{
+ (void)type; /* unused arg */
+
+ if (!s) return 0;
+
+ return (s->length*((s->flags&SF_16BITS)?2:1))+16;
+}
+
+ULONG VC1_VoiceRealVolume(UBYTE voice)
+{
+ ULONG i,s,size;
+ int k,j;
+ SWORD *smp;
+ SLONG t;
+
+ t = vinf[voice].current>>FRACBITS;
+ if(!vinf[voice].active) return 0;
+
+ s = vinf[voice].handle;
+ size = vinf[voice].size;
+
+ i=64; t-=64; k=0; j=0;
+ if(i>size) i = size;
+ if(t<0) t = 0;
+ if(t+i > size) t = size-i;
+
+ i &= ~1; /* make sure it's EVEN. */
+
+ smp = &Samples[s][t];
+ for(;i;i--,smp++) {
+ if(k<*smp) k = *smp;
+ if(j>*smp) j = *smp;
+ }
+ return abs(k-j);
+}
+
+#endif
+
+#endif
+
+/* ex:set ts=4: */
diff --git a/src/libs/misc.h b/src/libs/misc.h
new file mode 100644
index 0000000..39333a2
--- /dev/null
+++ b/src/libs/misc.h
@@ -0,0 +1,66 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// This file includes some misc things, previously in SDL_wrapper.h
+// before modularization. -Mika
+
+#ifndef MISC_H
+#define MISC_H
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include "port.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+extern int TFB_DEBUG_HALT;
+
+static inline void explode (void) _NORETURN;
+
+static inline void explode (void)
+{
+#ifdef DEBUG
+ // give debugger a chance to hook
+ abort ();
+#else
+ exit (EXIT_FAILURE);
+#endif
+}
+
+/* Sometimes you just have to remove a 'const'.
+ * (for instance, when implementing a function like strchr)
+ */
+static inline void *
+unconst(const void *arg) {
+ union {
+ void *c;
+ const void *cc;
+ } u;
+ u.cc = arg;
+ return u.c;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/src/libs/net.h b/src/libs/net.h
new file mode 100644
index 0000000..ca5931e
--- /dev/null
+++ b/src/libs/net.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NET_H_
+#define LIBS_NET_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "network/network.h"
+#include "network/netmanager/netmanager.h"
+#include "network/connect/connect.h"
+#include "network/connect/listen.h"
+#include "network/connect/resolve.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_NET_H_ */
diff --git a/src/libs/network/FILES b/src/libs/network/FILES
new file mode 100644
index 0000000..ddcd685
--- /dev/null
+++ b/src/libs/network/FILES
@@ -0,0 +1,26 @@
+In libs/network/:
+netport.{c,h} Portability definitions.
+bytesex.h Functions for endianness conversions.
+network.h Functions for initialising the network subsystem.
+network_bsd.c Network subsystem functions for BSD sockets.
+network_win.c Network subsystem functions for Winsock sockets.
+wspiapiwrap.{c,h} Hack to make sure <wspiapi.h> is #included in exactly
+ one file.
+
+In libs/network/netmanager/:
+ndesc.{c,h} Defines network descriptors.
+netmanager.h Handles callbacks for network activity.
+netmanager_bsd.{c,h} NetManager for systems with BSD sockets.
+netmanager_win.{c,h} NetManager for Winsock systems.
+
+In libs/network/socket/:
+socket.{c,h} Platform-independant socket layer.
+socket_bsd.{c,h} Socket operations for BSD sockets.
+socket_win.{c,h} Socket operations for Winsock sockets.
+
+In libs/network/connect/:
+connect.{c,h} Routines for establishing outgoing connections.
+listen.{c,h} Routines for receiving incoming connections.
+resolve.{c,h} Routines for hostname lookups.
+
+
diff --git a/src/libs/network/Makeinfo b/src/libs/network/Makeinfo
new file mode 100644
index 0000000..40171fa
--- /dev/null
+++ b/src/libs/network/Makeinfo
@@ -0,0 +1,14 @@
+uqm_SUBDIRS="connect netmanager socket"
+uqm_CFILES="netport.c"
+uqm_HFILES="bytesex.h netport.h network.h"
+
+if [ -n "$uqm_USE_WINSOCK" ]; then
+ uqm_CFILES="$uqm_CFILES network_win.c"
+ if [ -n "$MACRO___MINGW32__" ]; then
+ uqm_CFILES="$uqm_CFILES wspiapiwrap.c"
+ uqm_HFILES="$uqm_HFILES wspiapiwrap.h"
+ fi
+else
+ uqm_CFILES="$uqm_CFILES network_bsd.c"
+fi
+
diff --git a/src/libs/network/bytesex.h b/src/libs/network/bytesex.h
new file mode 100644
index 0000000..4ee89d7
--- /dev/null
+++ b/src/libs/network/bytesex.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Routines for changing the endianness of values.
+// I'm not using ntohs() etc. as those would require include files that may
+// have conflicting definitions. This is a problem on Windows, where these
+// functions are in winsock2.h, which includes windows.h, which includes
+// pretty much Microsoft's complete collection of .h files.
+
+#ifndef LIBS_NETWORK_BYTESEX_H_
+#define LIBS_NETWORK_BYTESEX_H_
+
+#include "port.h"
+ // for inline
+#include "endian_uqm.h"
+ // for WORDS_BIGENDIAN
+#include "types.h"
+
+static inline uint16
+swapBytes16(uint16 x) {
+ return (x << 8) | (x >> 8);
+}
+
+static inline uint32
+swapBytes32(uint32 x) {
+ return (x << 24)
+ | ((x & 0x0000ff00) << 8)
+ | ((x & 0x00ff0000) >> 8)
+ | (x >> 24);
+}
+
+#ifdef WORDS_BIGENDIAN
+// Already in network order.
+
+static inline uint16
+hton16(uint16 x) {
+ return x;
+}
+
+static inline uint32
+hton32(uint32 x) {
+ return x;
+}
+
+static inline uint16
+ntoh16(uint16 x) {
+ return x;
+}
+
+static inline uint32
+ntoh32(uint32 x) {
+ return x;
+}
+
+#else /* !defined(WORDS_BIGENDIAN) */
+// Need to swap bytes
+
+static inline uint16
+hton16(uint16 x) {
+ return swapBytes16(x);
+}
+
+static inline uint32
+hton32(uint32 x) {
+ return swapBytes32(x);
+}
+
+static inline uint16
+ntoh16(uint16 x) {
+ return swapBytes16(x);
+}
+
+static inline uint32
+ntoh32(uint32 x) {
+ return swapBytes32(x);
+}
+
+#endif /* defined(WORDS_BIGENDIAN) */
+
+#endif /* LIBS_NETWORK_BYTESEX_H_ */
+
diff --git a/src/libs/network/connect/Makeinfo b/src/libs/network/connect/Makeinfo
new file mode 100644
index 0000000..5595270
--- /dev/null
+++ b/src/libs/network/connect/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="connect.c listen.c resolve.c"
+uqm_HFILES="connect.h listen.h resolve.h"
diff --git a/src/libs/network/connect/connect.c b/src/libs/network/connect/connect.c
new file mode 100644
index 0000000..4599b18
--- /dev/null
+++ b/src/libs/network/connect/connect.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+
+#define CONNECT_INTERNAL
+#define SOCKET_INTERNAL
+#include "connect.h"
+
+#include "resolve.h"
+#include "libs/alarm.h"
+#include "../socket/socket.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include "../wspiapiwrap.h"
+#else
+# include <netdb.h>
+#endif
+
+#define DEBUG_CONNECT_REF
+#ifdef DEBUG_CONNECT_REF
+# include "types.h"
+#endif
+
+
+static void connectHostNext(ConnectState *connectState);
+static void doConnectCallback(ConnectState *connectState, NetDescriptor *nd,
+ const struct sockaddr *addr, socklen_t addrLen);
+static void doConnectErrorCallback(ConnectState *connectState,
+ const ConnectError *error);
+
+
+static ConnectState *
+ConnectState_alloc(void) {
+ return (ConnectState *) malloc(sizeof (ConnectState));
+}
+
+static void
+ConnectState_free(ConnectState *connectState) {
+ free(connectState);
+}
+
+static void
+ConnectState_delete(ConnectState *connectState) {
+ assert(connectState->nd == NULL);
+ assert(connectState->alarm == NULL);
+ assert(connectState->info == NULL);
+ assert(connectState->infoPtr == NULL);
+ ConnectState_free(connectState);
+}
+
+void
+ConnectState_incRef(ConnectState *connectState) {
+ assert(connectState->refCount < REFCOUNT_MAX);
+ connectState->refCount++;
+#ifdef DEBUG_CONNECT_REF
+ log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) connectState, connectState->refCount);
+#endif
+}
+
+bool
+ConnectState_decRef(ConnectState *connectState) {
+ assert(connectState->refCount > 0);
+ connectState->refCount--;
+#ifdef DEBUG_CONNECT_REF
+ log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) connectState, connectState->refCount);
+#endif
+ if (connectState->refCount == 0) {
+ ConnectState_delete(connectState);
+ return true;
+ }
+ return false;
+}
+
+// decrements ref count by 1
+void
+ConnectState_close(ConnectState *connectState) {
+ if (connectState->resolveState != NULL) {
+ Resolve_close(connectState->resolveState);
+ connectState->resolveState = NULL;
+ }
+ if (connectState->alarm != NULL) {
+ Alarm_remove(connectState->alarm);
+ connectState->alarm = NULL;
+ }
+ if (connectState->nd != NULL) {
+ NetDescriptor_close(connectState->nd);
+ connectState->nd = NULL;
+ }
+ if (connectState->info != NULL) {
+ freeaddrinfo(connectState->info);
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ }
+ connectState->state = Connect_closed;
+ ConnectState_decRef(connectState);
+}
+
+void
+ConnectState_setExtra(ConnectState *connectState, void *extra) {
+ connectState->extra = extra;
+}
+
+void *
+ConnectState_getExtra(ConnectState *connectState) {
+ return connectState->extra;
+}
+
+static void
+connectCallback(NetDescriptor *nd) {
+ // Called by the NetManager when a connection has been established.
+ ConnectState *connectState =
+ (ConnectState *) NetDescriptor_getExtra(nd);
+ int err;
+
+ if (connectState->alarm != NULL) {
+ Alarm_remove(connectState->alarm);
+ connectState->alarm = NULL;
+ }
+
+ if (connectState->state == Connect_closed) {
+ // The connection attempt has been aborted.
+#ifdef DEBUG
+ log_add(log_Debug, "Connection attempt was aborted.");
+#endif
+ ConnectState_decRef(connectState);
+ return;
+ }
+
+ if (Socket_getError(NetDescriptor_getSocket(nd), &err) == -1) {
+ log_add(log_Fatal, "Socket_getError() failed: %s.",
+ strerror(errno));
+ explode();
+ }
+ if (err != 0) {
+#ifdef DEBUG
+ log_add(log_Debug, "connect() failed: %s.", strerror(err));
+#endif
+ NetDescriptor_close(nd);
+ connectState->nd = NULL;
+ connectState->infoPtr = connectState->infoPtr->ai_next;
+ connectHostNext(connectState);
+ return;
+ }
+
+#ifdef DEBUG
+ log_add(log_Debug, "Connection established.");
+#endif
+
+ // Notify the higher layer.
+ connectState->nd = NULL;
+ // The callback function takes over ownership of the
+ // NetDescriptor.
+ NetDescriptor_setWriteCallback(nd, NULL);
+ // Note that connectState->info and connectState->infoPtr are cleaned up
+ // when ConnectState_close() is called by the callback function.
+
+ ConnectState_incRef(connectState);
+ doConnectCallback(connectState, nd, connectState->infoPtr->ai_addr,
+ connectState->infoPtr->ai_addrlen);
+ {
+ // The callback called should release the last reference to
+ // the connectState, by calling ConnectState_close().
+ bool released = ConnectState_decRef(connectState);
+ assert(released);
+ (void) released; // In case assert() evaluates to nothing.
+ }
+}
+
+static void
+connectTimeoutCallback(ConnectState *connectState) {
+ connectState->alarm = NULL;
+
+ NetDescriptor_close(connectState->nd);
+ connectState->nd = NULL;
+
+ connectState->infoPtr = connectState->infoPtr->ai_next;
+ connectHostNext(connectState);
+}
+
+static void
+setConnectTimeout(ConnectState *connectState) {
+ assert(connectState->alarm == NULL);
+
+ connectState->alarm =
+ Alarm_addRelativeMs(connectState->flags.timeout,
+ (AlarmCallback) connectTimeoutCallback, connectState);
+}
+
+// Try connecting to the next address.
+static Socket *
+tryConnectHostNext(ConnectState *connectState) {
+ struct addrinfo *info;
+ Socket *sock;
+ int connectResult;
+
+ assert(connectState->nd == NULL);
+
+ info = connectState->infoPtr;
+
+ sock = Socket_openNative(info->ai_family, info->ai_socktype,
+ info->ai_protocol);
+ if (sock == Socket_noSocket) {
+ int savedErrno = errno;
+ log_add(log_Error, "socket() failed: %s.", strerror(errno));
+ errno = savedErrno;
+ return Socket_noSocket;
+ }
+
+ if (Socket_setNonBlocking(sock) == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Could not make socket non-blocking: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return Socket_noSocket;
+ }
+
+ (void) Socket_setReuseAddr(sock);
+ // Ignore errors; it's not a big deal.
+ (void) Socket_setInlineOOB(sock);
+ // Ignore errors; it's not a big deal as the other party is not
+ // not supposed to send any OOB data.
+ (void) Socket_setKeepAlive(sock);
+ // Ignore errors; it's not a big deal.
+
+ connectResult = Socket_connect(sock, info->ai_addr, info->ai_addrlen);
+ if (connectResult == 0) {
+ // Connection has already succeeded.
+ // We just wait for the writability callback anyhow, so that
+ // we can use one code path.
+ return sock;
+ }
+
+ switch (errno) {
+ case EINPROGRESS:
+ // Connection in progress; wait for the write callback.
+ return sock;
+ }
+
+ // Connection failed immediately. This is just for one of the addresses,
+ // so this does not have to be final.
+ // Note that as the socket is non-blocking, most failed connection
+ // errors will usually not be reported immediately.
+ {
+ int savedErrno = errno;
+ Socket_close(sock);
+#ifdef DEBUG
+ log_add(log_Debug, "connect() immediately failed for one address: "
+ "%s.", strerror(errno));
+ // TODO: add the address in the status message.
+#endif
+ errno = savedErrno;
+ }
+ return Socket_noSocket;
+}
+
+static void
+connectRetryCallback(ConnectState *connectState) {
+ connectState->alarm = NULL;
+
+ connectState->infoPtr = connectState->info;
+ connectHostNext(connectState);
+}
+
+static void
+setConnectRetryAlarm(ConnectState *connectState) {
+ assert(connectState->alarm == NULL);
+ assert(connectState->flags.retryDelayMs != Connect_noRetry);
+
+ connectState->alarm =
+ Alarm_addRelativeMs(connectState->flags.retryDelayMs,
+ (AlarmCallback) connectRetryCallback, connectState);
+}
+
+static void
+connectHostReportAllFailed(ConnectState *connectState) {
+ // Could not connect to any host.
+ ConnectError error;
+ freeaddrinfo(connectState->info);
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ connectState->state = Connect_closed;
+ error.state = Connect_connecting;
+ error.err = ETIMEDOUT;
+ // No errno code is exactly suitable. We have been unable
+ // to connect to any host, but the reasons may vary
+ // (unreachable, refused, ...).
+ // ETIMEDOUT is the least specific portable errno code that
+ // seems appropriate.
+ doConnectErrorCallback(connectState, &error);
+}
+
+static void
+connectHostNext(ConnectState *connectState) {
+ Socket *sock;
+
+ while (connectState->infoPtr != NULL) {
+ sock = tryConnectHostNext(connectState);
+
+ if (sock != Socket_noSocket) {
+ // Connection succeeded or connection in progress
+ connectState->nd =
+ NetDescriptor_new(sock, (void *) connectState);
+ if (connectState->nd == NULL) {
+ ConnectError error;
+ int savedErrno = errno;
+
+ log_add(log_Error, "NetDescriptor_new() failed: %s.",
+ strerror(errno));
+ Socket_close(sock);
+ freeaddrinfo(connectState->info);
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ connectState->state = Connect_closed;
+ error.state = Connect_connecting;
+ error.err = savedErrno;
+ doConnectErrorCallback(connectState, &error);
+ return;
+ }
+
+ NetDescriptor_setWriteCallback(connectState->nd, connectCallback);
+ setConnectTimeout(connectState);
+ return;
+ }
+
+ connectState->infoPtr = connectState->infoPtr->ai_next;
+ }
+
+ // Connect failed to all addresses.
+
+ if (connectState->flags.retryDelayMs == Connect_noRetry) {
+ connectHostReportAllFailed(connectState);
+ return;
+ }
+
+ setConnectRetryAlarm(connectState);
+}
+
+static void
+connectHostResolveCallback(ResolveState *resolveState,
+ struct addrinfo *info) {
+ ConnectState *connectState =
+ (ConnectState *) ResolveState_getExtra(resolveState);
+
+ connectState->state = Connect_connecting;
+
+ Resolve_close(resolveState);
+ connectState->resolveState = NULL;
+
+ if (connectState->flags.familyPrefer != PF_UNSPEC) {
+ // Reorganise the 'info' list to put the structures of the
+ // prefered family in front.
+ struct addrinfo *preferred;
+ struct addrinfo **preferredEnd;
+ struct addrinfo *rest;
+ struct addrinfo **restEnd;
+ splitAddrInfoOnFamily(info, connectState->flags.familyPrefer,
+ &preferred, &preferredEnd, &rest, &restEnd);
+ info = preferred;
+ *preferredEnd = rest;
+ }
+
+ connectState->info = info;
+ connectState->infoPtr = info;
+
+ connectHostNext(connectState);
+}
+
+static void
+connectHostResolveErrorCallback(ResolveState *resolveState,
+ const ResolveError *resolveError) {
+ ConnectState *connectState =
+ (ConnectState *) ResolveState_getExtra(resolveState);
+ ConnectError connectError;
+
+ assert(resolveError->gaiRes != 0);
+
+ Resolve_close(resolveState);
+ connectState->resolveState = NULL;
+
+ connectError.state = Connect_resolving;
+ connectError.resolveError = resolveError;
+ connectError.err = resolveError->err;
+ doConnectErrorCallback(connectState, &connectError);
+}
+
+ConnectState *
+connectHostByName(const char *host, const char *service, Protocol proto,
+ const ConnectFlags *flags, ConnectConnectCallback connectCallback,
+ ConnectErrorCallback errorCallback, void *extra) {
+ struct addrinfo hints;
+ ConnectState *connectState;
+ ResolveFlags resolveFlags;
+ // Structure is empty (for now).
+
+ assert(flags->familyDemand == PF_inet ||
+ flags->familyDemand == PF_inet6 ||
+ flags->familyDemand == PF_unspec);
+ assert(flags->familyPrefer == PF_inet ||
+ flags->familyPrefer == PF_inet6 ||
+ flags->familyPrefer == PF_unspec);
+ assert(proto == IPProto_tcp || proto == IPProto_udp);
+
+ memset(&hints, '\0', sizeof hints);
+ hints.ai_family = protocolFamilyTranslation[flags->familyDemand];
+ hints.ai_protocol = protocolTranslation[proto];
+
+ if (proto == IPProto_tcp) {
+ hints.ai_socktype = SOCK_STREAM;
+ } else {
+ assert(proto == IPProto_udp);
+ hints.ai_socktype = SOCK_DGRAM;
+ }
+ hints.ai_flags = 0;
+
+ connectState = ConnectState_alloc();
+ connectState->refCount = 1;
+#ifdef DEBUG_CONNECT_REF
+ log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) connectState, connectState->refCount);
+#endif
+ connectState->state = Connect_resolving;
+ connectState->flags = *flags;
+ connectState->connectCallback = connectCallback;
+ connectState->errorCallback = errorCallback;
+ connectState->extra = extra;
+ connectState->info = NULL;
+ connectState->infoPtr = NULL;
+ connectState->nd = NULL;
+ connectState->alarm = NULL;
+
+ connectState->resolveState = getaddrinfoAsync(
+ host, service, &hints, &resolveFlags,
+ (ResolveCallback) connectHostResolveCallback,
+ (ResolveErrorCallback) connectHostResolveErrorCallback,
+ (ResolveCallbackArg) connectState);
+
+ return connectState;
+}
+
+// NB: The callback function becomes the owner of nd
+static void
+doConnectCallback(ConnectState *connectState, NetDescriptor *nd,
+ const struct sockaddr *addr, socklen_t addrLen) {
+ assert(connectState->connectCallback != NULL);
+
+ ConnectState_incRef(connectState);
+ // No need to increment nd as the callback function takes over ownership.
+ (*connectState->connectCallback)(connectState, nd, addr, addrLen);
+ ConnectState_decRef(connectState);
+}
+
+static void
+doConnectErrorCallback(ConnectState *connectState,
+ const ConnectError *error) {
+ assert(connectState->errorCallback != NULL);
+
+ ConnectState_incRef(connectState);
+ (*connectState->errorCallback)(connectState, error);
+ ConnectState_decRef(connectState);
+}
+
+
+
diff --git a/src/libs/network/connect/connect.h b/src/libs/network/connect/connect.h
new file mode 100644
index 0000000..77d7a44
--- /dev/null
+++ b/src/libs/network/connect/connect.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_CONNECT_CONNECT_H_
+#define LIBS_NETWORK_CONNECT_CONNECT_H_
+
+
+typedef struct ConnectFlags ConnectFlags;
+typedef struct ConnectError ConnectError;
+typedef struct ConnectState ConnectState;
+
+typedef enum {
+ Connect_closed,
+ Connect_resolving,
+ Connect_connecting
+} ConnectStateState;
+
+
+#include "../netmanager/netmanager.h"
+#include "../socket/socket.h"
+#include "resolve.h"
+
+
+// For connectHost()
+struct ConnectFlags {
+ ProtocolFamily familyDemand;
+ // Only accept a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ ProtocolFamily familyPrefer;
+ // Prefer a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ int timeout;
+ /* Number of milliseconds before timing out a connection attempt.
+ * Note that if a host has multiple addresses, a connect to that
+ * host will have this timeout *per address*. */
+ int retryDelayMs;
+ /* Retry connecting this many ms after connecting to the last
+ * address for the specified host fails. Set to Connect_noRetry
+ * to give up after one try. */
+#define Connect_noRetry -1
+};
+
+struct ConnectError {
+ ConnectStateState state;
+ // State where the error occured.
+ int err;
+ // errno value. Not relevant if state == resolving unless
+ // gaiRes == EAI_SYSTEM.
+ const ResolveError *resolveError;
+ // Only relevant if state == resolving.
+};
+
+typedef void (*ConnectConnectCallback)(ConnectState *connectState,
+ NetDescriptor *nd, const struct sockaddr *addr, socklen_t addrLen);
+typedef void (*ConnectErrorCallback)(ConnectState *connectState,
+ const ConnectError *error);
+
+#ifdef CONNECT_INTERNAL
+
+#include "libs/alarm.h"
+
+struct ConnectState {
+ RefCount refCount;
+
+ ConnectStateState state;
+ ConnectFlags flags;
+
+ ConnectConnectCallback connectCallback;
+ ConnectErrorCallback errorCallback;
+ void *extra;
+
+ struct addrinfo *info;
+ struct addrinfo *infoPtr;
+
+ ResolveState *resolveState;
+
+ NetDescriptor *nd;
+ Alarm *alarm;
+ // Used for both the timeout for a connection attempt
+ // and to retry after all addresses have been tried.
+};
+#endif /* CONNECT_INTERNAL */
+
+ConnectState *connectHostByName(const char *host, const char *service,
+ Protocol proto, const ConnectFlags *flags,
+ ConnectConnectCallback connectCallback,
+ ConnectErrorCallback errorCallback, void *extra);
+void ConnectState_incRef(ConnectState *connectState);
+bool ConnectState_decRef(ConnectState *connectState);
+void ConnectState_close(ConnectState *connectState);
+void ConnectState_setExtra(ConnectState *connectState, void *extra);
+void *ConnectState_getExtra(ConnectState *connectState);
+
+#endif /* LIBS_NETWORK_CONNECT_CONNECT_H_ */
+
+
diff --git a/src/libs/network/connect/listen.c b/src/libs/network/connect/listen.c
new file mode 100644
index 0000000..4a7a65c
--- /dev/null
+++ b/src/libs/network/connect/listen.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "../netport.h"
+
+#define LISTEN_INTERNAL
+#define SOCKET_INTERNAL
+#include "listen.h"
+
+#include "resolve.h"
+#include "../socket/socket.h"
+#include "../netmanager/netmanager.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include "../wspiapiwrap.h"
+#else
+# include <netdb.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+
+#define DEBUG_LISTEN_REF
+#ifdef DEBUG_LISTEN_REF
+# include "types.h"
+#endif
+
+
+static void acceptCallback(NetDescriptor *nd);
+static void doListenErrorCallback(ListenState *listenState,
+ const ListenError *error);
+
+
+static ListenState *
+ListenState_alloc(void) {
+ return (ListenState *) malloc(sizeof (ListenState));
+}
+
+static void
+ListenState_free(ListenState *listenState) {
+ free(listenState);
+}
+
+static void
+ListenState_delete(ListenState *listenState) {
+ assert(listenState->nds == NULL);
+ ListenState_free(listenState);
+}
+
+void
+ListenState_incRef(ListenState *listenState) {
+ assert(listenState->refCount < REFCOUNT_MAX);
+ listenState->refCount++;
+#ifdef DEBUG_LISTEN_REF
+ log_add(log_Debug, "ListenState %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) listenState, listenState->refCount);
+#endif
+}
+
+bool
+ListenState_decRef(ListenState *listenState) {
+ assert(listenState->refCount > 0);
+ listenState->refCount--;
+#ifdef DEBUG_LISTEN_REF
+ log_add(log_Debug, "ListenState %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) listenState, listenState->refCount);
+#endif
+ if (listenState->refCount == 0) {
+ ListenState_delete(listenState);
+ return true;
+ }
+ return false;
+}
+
+// Decrements ref count byh 1
+void
+ListenState_close(ListenState *listenState) {
+ if (listenState->resolveState != NULL) {
+ Resolve_close(listenState->resolveState);
+ listenState->resolveState = NULL;
+ }
+ if (listenState->nds != NULL) {
+ while (listenState->numNd > 0) {
+ listenState->numNd--;
+ NetDescriptor_close(listenState->nds[listenState->numNd]);
+ }
+ free(listenState->nds);
+ listenState->nds = NULL;
+ }
+ listenState->state = Listen_closed;
+ ListenState_decRef(listenState);
+}
+
+void
+ListenState_setExtra(ListenState *listenState, void *extra) {
+ listenState->extra = extra;
+}
+
+void *
+ListenState_getExtra(ListenState *listenState) {
+ return listenState->extra;
+}
+
+static NetDescriptor *
+listenPortSingle(struct ListenState *listenState, struct addrinfo *info) {
+ Socket *sock;
+ int bindResult;
+ int listenResult;
+ NetDescriptor *nd;
+
+ sock = Socket_openNative(info->ai_family, info->ai_socktype,
+ info->ai_protocol);
+ if (sock == Socket_noSocket) {
+ int savedErrno = errno;
+ log_add(log_Error, "socket() failed: %s.", strerror(errno));
+ errno = savedErrno;
+ return NULL;
+ }
+
+ (void) Socket_setReuseAddr(sock);
+ // Ignore errors; it's not a big deal.
+ if (Socket_setNonBlocking(sock) == -1) {
+ int savedErrno = errno;
+ // Error message is already printed.
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ bindResult = Socket_bind(sock, info->ai_addr, info->ai_addrlen);
+ if (bindResult == -1) {
+ int savedErrno = errno;
+ if (errno == EADDRINUSE) {
+#ifdef DEBUG
+ log_add(log_Warning, "bind() failed: %s.", strerror(errno));
+#endif
+ } else
+ log_add(log_Error, "bind() failed: %s.", strerror(errno));
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ listenResult = Socket_listen(sock, listenState->flags.backlog);
+ if (listenResult == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "listen() failed: %s.", strerror(errno));
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ nd = NetDescriptor_new(sock, (void *) listenState);
+ if (nd == NULL) {
+ int savedErrno = errno;
+ log_add(log_Error, "NetDescriptor_new() failed: %s.",
+ strerror(errno));
+ Socket_close(sock);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ NetDescriptor_setReadCallback(nd, acceptCallback);
+
+ return nd;
+}
+
+static void
+listenPortMulti(struct ListenState *listenState, struct addrinfo *info) {
+ struct addrinfo *addrPtr;
+ size_t addrCount;
+ size_t addrOkCount;
+ NetDescriptor **nds;
+
+ // Count how many addresses we've got.
+ addrCount = 0;
+ for (addrPtr = info; addrPtr != NULL; addrPtr = addrPtr->ai_next)
+ addrCount++;
+
+ // This is where we intend to store the file descriptors of the
+ // listening sockets.
+ nds = malloc(addrCount * sizeof listenState->nds[0]);
+
+ // Bind to each address.
+ addrOkCount = 0;
+ for (addrPtr = info; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
+ NetDescriptor *nd;
+ nd = listenPortSingle(listenState, addrPtr);
+ if (nd == NULL) {
+ // Failed. On to the next.
+ // An error message is already printed for serious errors.
+ // If the address is already in use, we here also print
+ // a message when we are not already listening on one of
+ // the other addresses.
+ // This is because on some IPv6 capabable systems (like Linux),
+ // IPv6 sockets also handle IPv4 connections, which means
+ // that a separate IPv4 socket won't be able to bind to the
+ // port.
+ // BUG: if the IPv4 socket is in the list before the
+ // IPv6 socket, it will be the IPv6 which will fail to bind,
+ // so only IPv4 connections will be handled, as v4 sockets can't
+ // accept v6 connections.
+ // In practice, on Linux, I haven't seen it happen, but
+ // it's a real possibility.
+ if (errno == EADDRINUSE && addrOkCount == 0) {
+ log_add(log_Error, "Error while preparing a network socket "
+ "for incoming connections: %s", strerror(errno));
+ }
+ continue;
+ }
+
+ nds[addrOkCount] = nd;
+ addrOkCount++;
+ }
+
+ freeaddrinfo(info);
+
+ listenState->nds =
+ realloc(nds, addrOkCount * sizeof listenState->nds[0]);
+ listenState->numNd = addrOkCount;
+
+ if (addrOkCount == 0) {
+ // Could not listen on any port.
+ ListenError error;
+ error.state = Listen_listening;
+ error.err = EIO;
+ // Nothing better to offer.
+ doListenErrorCallback(listenState, &error);
+ return;
+ }
+}
+
+static void
+listenPortResolveCallback(ResolveState *resolveState,
+ struct addrinfo *result) {
+ ListenState *listenState =
+ (ListenState *) ResolveState_getExtra(resolveState);
+ Resolve_close(listenState->resolveState);
+ listenState->resolveState = NULL;
+ listenState->state = Listen_listening;
+ listenPortMulti(listenState, result);
+}
+
+static void
+listenPortResolveErrorCallback(ResolveState *resolveState,
+ const ResolveError *resolveError) {
+ ListenState *listenState =
+ (ListenState *) ResolveState_getExtra(resolveState);
+ ListenError listenError;
+
+ assert(resolveError->gaiRes != 0);
+
+ listenError.state = Listen_resolving;
+ listenError.resolveError = resolveError;
+ listenError.err = resolveError->err;
+ doListenErrorCallback(listenState, &listenError);
+}
+
+// 'proto' is one of IPProto_tcp or IPProto_udp.
+ListenState *
+listenPort(const char *service, Protocol proto, const ListenFlags *flags,
+ ListenConnectCallback connectCallback,
+ ListenErrorCallback errorCallback, void *extra) {
+ struct addrinfo hints;
+ ListenState *listenState;
+ ResolveFlags resolveFlags;
+ // Structure is empty (for now).
+
+ assert(flags->familyDemand == PF_inet ||
+ flags->familyDemand == PF_inet6 ||
+ flags->familyDemand == PF_unspec);
+ assert(flags->familyPrefer == PF_inet ||
+ flags->familyPrefer == PF_inet6 ||
+ flags->familyPrefer == PF_unspec);
+ assert(proto == IPProto_tcp || proto == IPProto_udp);
+
+ // Acquire a list of addresses to bind to.
+ memset(&hints, '\0', sizeof hints);
+ hints.ai_family = protocolFamilyTranslation[flags->familyDemand];
+ hints.ai_protocol = protocolTranslation[proto];
+
+ if (proto == IPProto_tcp) {
+ hints.ai_socktype = SOCK_STREAM;
+ } else {
+ assert(proto == IPProto_udp);
+ hints.ai_socktype = SOCK_DGRAM;
+ }
+ hints.ai_flags = AI_PASSIVE;
+
+ listenState = ListenState_alloc();
+ listenState->refCount = 1;
+#ifdef DEBUG_LISTEN_REF
+ log_add(log_Debug, "ListenState %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) listenState, listenState->refCount);
+#endif
+ listenState->state = Listen_resolving;
+ listenState->flags = *flags;
+ listenState->connectCallback = connectCallback;
+ listenState->errorCallback = errorCallback;
+ listenState->extra = extra;
+ listenState->nds = NULL;
+ listenState->numNd = 0;
+
+ listenState->resolveState = getaddrinfoAsync(NULL, service, &hints,
+ &resolveFlags, listenPortResolveCallback,
+ listenPortResolveErrorCallback,
+ (ResolveCallbackArg) listenState);
+
+ return listenState;
+}
+
+// NB: The callback function becomes the owner of newNd.
+static void
+doListenConnectCallback(ListenState *listenState, NetDescriptor *listenNd,
+ NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen) {
+ assert(listenState->connectCallback != NULL);
+
+ ListenState_incRef(listenState);
+ // No need to increment listenNd, as there's guaranteed to be one
+ // reference from listenState. And no need to increment newNd,
+ // as the callback function takes over ownership.
+ (*listenState->connectCallback)(listenState, listenNd, newNd,
+ addr, (socklen_t) addrLen);
+ ListenState_decRef(listenState);
+}
+
+static void
+doListenErrorCallback(ListenState *listenState, const ListenError *error) {
+ assert(listenState->errorCallback != NULL);
+
+ ListenState_incRef(listenState);
+ (*listenState->errorCallback)(listenState, error);
+ ListenState_decRef(listenState);
+}
+
+static void
+acceptSingleConnection(ListenState *listenState, NetDescriptor *nd) {
+ Socket *sock;
+ Socket *acceptResult;
+ struct sockaddr_storage addr;
+ socklen_t addrLen;
+ NetDescriptor *newNd;
+
+ sock = NetDescriptor_getSocket(nd);
+ addrLen = sizeof (addr);
+ acceptResult = Socket_accept(sock, (struct sockaddr *) &addr, &addrLen);
+ if (acceptResult == Socket_noSocket) {
+ switch (errno) {
+ case EWOULDBLOCK:
+ case ECONNABORTED:
+ // Nothing serious. Keep listening.
+ return;
+ case EMFILE:
+ case ENFILE:
+ case ENOBUFS:
+ case ENOMEM:
+#ifdef ENOSR
+ case ENOSR:
+#endif
+ // Serious problems, but future connections may still
+ // be possible.
+ log_add(log_Warning, "accept() reported '%s'",
+ strerror(errno));
+ return;
+ default:
+ // Should not happen.
+ log_add(log_Fatal, "Internal error: accept() reported "
+ "'%s'", strerror(errno));
+ explode();
+ }
+ }
+
+ (void) Socket_setReuseAddr(acceptResult);
+ // Ignore errors; it's not a big deal.
+ if (Socket_setNonBlocking(acceptResult) == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Could not make socket non-blocking: %s.",
+ strerror(errno));
+ Socket_close(acceptResult);
+ errno = savedErrno;
+ return;
+ }
+ (void) Socket_setInlineOOB(acceptResult);
+ // Ignore errors; it's not a big deal as the other
+ // party is not not supposed to send any OOB data.
+ (void) Socket_setKeepAlive(sock);
+ // Ignore errors; it's not a big deal.
+
+#ifdef DEBUG
+ {
+ char hostname[NI_MAXHOST];
+ int gniRes;
+
+ gniRes = getnameinfo((struct sockaddr *) &addr, addrLen,
+ hostname, sizeof hostname, NULL, 0, 0);
+ if (gniRes != 0) {
+ log_add(log_Error, "Error while performing hostname "
+ "lookup for incoming connection: %s",
+ (gniRes == EAI_SYSTEM) ? strerror(errno) :
+ gai_strerror(gniRes));
+ } else {
+ log_add(log_Debug, "Accepted incoming connection from '%s'.",
+ hostname);
+ }
+ }
+#endif
+
+ newNd = NetDescriptor_new(acceptResult, NULL);
+ if (newNd == NULL) {
+ int savedErrno = errno;
+ log_add(log_Error, "NetDescriptor_new() failed: %s.",
+ strerror(errno));
+ Socket_close(acceptResult);
+ errno = savedErrno;
+ return;
+ }
+
+ doListenConnectCallback(listenState, nd, newNd,
+ (struct sockaddr *) &addr, addrLen);
+ // NB: newNd is now handed over to the callback function, and should
+ // no longer be referenced from here.
+}
+
+// Called when select() has indicated readability on a listening socket,
+// i.e. when a connection is in the queue.
+static void
+acceptCallback(NetDescriptor *nd) {
+ ListenState *listenState = (ListenState *) NetDescriptor_getExtra(nd);
+
+ acceptSingleConnection(listenState, nd);
+}
+
+
diff --git a/src/libs/network/connect/listen.h b/src/libs/network/connect/listen.h
new file mode 100644
index 0000000..e44ef53
--- /dev/null
+++ b/src/libs/network/connect/listen.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_CONNECT_LISTEN_H_
+#define LIBS_NETWORK_CONNECT_LISTEN_H_
+
+typedef struct ListenFlags ListenFlags;
+typedef enum {
+ Listen_closed,
+ Listen_resolving,
+ Listen_listening
+} ListenStateState;
+typedef struct ListenError ListenError;
+typedef struct ListenState ListenState;
+
+#include "port.h"
+
+#ifdef USE_WINSOCK
+// I do not want to include winsock2.h, because of possible conflicts with
+// code that includes this file.
+// Note that listen.c itself includes winsock2.h; SOCKLEN_T is used there
+// only where necessary to keep the API consistent.
+# define SOCKLEN_T size_t
+struct sockaddr;
+#else
+# include <netinet/in.h>
+# define SOCKLEN_T socklen_t
+#endif
+
+#include "resolve.h"
+#include "../socket/socket.h"
+
+#include "../netmanager/netmanager.h"
+
+// For listenPort()
+struct ListenFlags {
+ ProtocolFamily familyDemand;
+ // Only accept a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ ProtocolFamily familyPrefer;
+ // Prefer a protocol family of this type.
+ // One of PF_inet, PF_inet6, or PF_unspec.
+ int backlog;
+ // As the 2rd parameter to listen();
+};
+
+struct ListenError {
+ ListenStateState state;
+ // State where the error occured.
+ int err;
+ // errno value. Not relevant if state == resolving unless
+ // gaiRes == EAI_SYSTEM.
+ const ResolveError *resolveError;
+ // Only relevant if state == resolving.
+};
+
+typedef void (*ListenConnectCallback)(ListenState *listenState,
+ NetDescriptor *listenNd, NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen);
+typedef void (*ListenErrorCallback)(ListenState *listenState,
+ const ListenError *error);
+
+#ifdef LISTEN_INTERNAL
+struct ListenState {
+ RefCount refCount;
+
+ ListenStateState state;
+ ListenFlags flags;
+
+ ListenConnectCallback connectCallback;
+ ListenErrorCallback errorCallback;
+ void *extra;
+
+ ResolveState *resolveState;
+ NetDescriptor **nds;
+ size_t numNd;
+ // INV: (numNd == NULL) == (nds == NULL)
+};
+#endif /* defined(LISTEN_INTERNAL) */
+
+ListenState *listenPort(const char *service, Protocol proto,
+ const ListenFlags *flags, ListenConnectCallback connectCallback,
+ ListenErrorCallback errorCallback, void *extra);
+void ListenState_close(ListenState *listenState);
+void ListenState_incRef(ListenState *listenState);
+bool ListenState_decRef(ListenState *listenState);
+void ListenState_setExtra(ListenState *listenState, void *extra);
+void *ListenState_getExtra(ListenState *listenState);
+
+#endif /* LIBS_NETWORK_CONNECT_LISTEN_H_ */
+
diff --git a/src/libs/network/connect/resolve.c b/src/libs/network/connect/resolve.c
new file mode 100644
index 0000000..646e437
--- /dev/null
+++ b/src/libs/network/connect/resolve.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define RESOLVE_INTERNAL
+#include "resolve.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#define DEBUG_RESOLVE_REF
+#ifdef DEBUG_RESOLVE_REF
+# include "types.h"
+# include "libs/log.h"
+# include <string.h>
+#endif
+
+static ResolveState *
+ResolveState_new(void) {
+ return (ResolveState *) malloc(sizeof (ResolveState));
+}
+
+static void
+ResolveState_free(ResolveState *resolveState) {
+ free(resolveState);
+}
+
+static void
+ResolveState_delete(ResolveState *resolveState) {
+ assert(resolveState->callbackID == CallbackID_invalid);
+ ResolveState_free(resolveState);
+}
+
+void
+ResolveState_incRef(ResolveState *resolveState) {
+ assert(resolveState->refCount < REFCOUNT_MAX);
+ resolveState->refCount++;
+#ifdef DEBUG_RESOLVE_REF
+ log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) resolveState, resolveState->refCount);
+#endif
+}
+
+bool
+ResolveState_decRef(ResolveState *resolveState) {
+ assert(resolveState->refCount > 0);
+ resolveState->refCount--;
+#ifdef DEBUG_RESOLVE_REF
+ log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) resolveState, resolveState->refCount);
+#endif
+ if (resolveState->refCount == 0) {
+ ResolveState_delete(resolveState);
+ return true;
+ }
+ return false;
+}
+
+void
+ResolveState_setExtra(ResolveState *resolveState, void *extra) {
+ resolveState->extra = extra;
+}
+
+void *
+ResolveState_getExtra(ResolveState *resolveState) {
+ return resolveState->extra;
+}
+
+static void
+doResolveCallback(ResolveState *resolveState) {
+ ResolveState_incRef(resolveState);
+ (*resolveState->callback)(resolveState, resolveState->result);
+ {
+ bool released = ResolveState_decRef(resolveState);
+ assert(released);
+ (void) released; // In case assert() evaluates to nothing.
+ }
+}
+
+static void
+doResolveErrorCallback(ResolveState *resolveState) {
+ ResolveState_incRef(resolveState);
+ resolveState->errorCallback(resolveState, &resolveState->error);
+ {
+ bool released = ResolveState_decRef(resolveState);
+ assert(released);
+ (void) released; // In case assert() evaluates to nothing.
+ }
+}
+
+static void
+resolveCallback(ResolveState *resolveState) {
+ resolveState->callbackID = CallbackID_invalid;
+ if (resolveState->error.gaiRes == 0) {
+ // Successful lookup.
+ doResolveCallback(resolveState);
+ } else {
+ // Lookup failed.
+ doResolveErrorCallback(resolveState);
+ }
+}
+
+// Function that does getaddrinfo() and calls the callback function when
+// the result is available.
+// TODO: actually make this function asynchronous. Right now it just calls
+// getaddrinfo() (which blocks) and then schedules the callback.
+ResolveState *
+getaddrinfoAsync(const char *node, const char *service,
+ const struct addrinfo *hints, ResolveFlags *flags,
+ ResolveCallback callback, ResolveErrorCallback errorCallback,
+ ResolveCallbackArg extra) {
+ ResolveState *resolveState;
+
+ resolveState = ResolveState_new();
+ resolveState->refCount = 1;
+#ifdef DEBUG_RESOLVE_REF
+ log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) resolveState, resolveState->refCount);
+#endif
+ resolveState->state = Resolve_resolving;
+ resolveState->flags = *flags;
+ resolveState->callback = callback;
+ resolveState->errorCallback = errorCallback;
+ resolveState->extra = extra;
+ resolveState->result = NULL;
+
+ resolveState->error.gaiRes =
+ getaddrinfo(node, service, hints, &resolveState->result);
+ resolveState->error.err = errno;
+
+ resolveState->callbackID = Callback_add(
+ (CallbackFunction) resolveCallback, (CallbackArg) resolveState);
+
+ return resolveState;
+}
+
+void
+Resolve_close(ResolveState *resolveState) {
+ if (resolveState->callbackID != CallbackID_invalid) {
+ Callback_remove(resolveState->callbackID);
+ resolveState->callbackID = CallbackID_invalid;
+ }
+ resolveState->state = Resolve_closed;
+ ResolveState_decRef(resolveState);
+}
+
+// Split an addrinfo list into two separate lists, one with structures with
+// the specified value for the ai_family field, the other with the other
+// structures. The order of entries in the resulting lists will remain
+// the same as in the original list.
+// info - the original list
+// family - the family for the first list
+// selected - pointer to where the list of selected structures should be
+// stored
+// selectedEnd - pointer to where the end of 'selected' should be stored
+// rest - pointer to where the list of not-selected structures should be
+// stored
+// restEnd - pointer to where the end of 'rest' should be stored
+//
+// Yes, it is allowed to modify the ai_next field of addrinfo structures.
+// Or at least, it's not disallowed by RFC 3493, and the following
+// text from that RFC seems to suggest it should be possible:
+// ] The freeaddrinfo() function must support the freeing of arbitrary
+// ] sublists of an addrinfo list originally returned by getaddrinfo().
+void
+splitAddrInfoOnFamily(struct addrinfo *info, int family,
+ struct addrinfo **selected, struct addrinfo ***selectedEnd,
+ struct addrinfo **rest, struct addrinfo ***restEnd) {
+ struct addrinfo *selectedFirst;
+ struct addrinfo **selectedNext;
+ struct addrinfo *restFirst;
+ struct addrinfo **restNext;
+
+ selectedNext = &selectedFirst;
+ restNext = &restFirst;
+ while (info != NULL) {
+ if (info->ai_family == family) {
+ *selectedNext = info;
+ selectedNext = &(*selectedNext)->ai_next;
+ } else {
+ *restNext = info;
+ restNext = &(*restNext)->ai_next;
+ }
+ info = info->ai_next;
+ }
+ *selectedNext = NULL;
+ *restNext = NULL;
+
+ // Fill in the result parameters.
+ *selected = selectedFirst;
+ *selectedEnd = selectedNext;
+ *rest = restFirst;
+ *restEnd = restNext;
+}
+
+
diff --git a/src/libs/network/connect/resolve.h b/src/libs/network/connect/resolve.h
new file mode 100644
index 0000000..509c3b9
--- /dev/null
+++ b/src/libs/network/connect/resolve.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_CONNECT_RESOLVE_H_
+#define LIBS_NETWORK_CONNECT_RESOLVE_H_
+
+
+typedef struct ResolveFlags ResolveFlags;
+typedef struct ResolveError ResolveError;
+typedef enum {
+ Resolve_closed,
+ Resolve_resolving
+} ResolveStateState;
+typedef struct ResolveState ResolveState;
+
+#include "port.h"
+#include "../netport.h"
+
+// For addrinfo.
+#ifdef USE_WINSOCK
+// Not including <winsock2.h> because of possible conflicts with files
+// including this file.
+struct addrinfo;
+#else
+# include <netdb.h>
+#endif
+
+#include "libs/callback.h"
+#include "../netmanager/netmanager.h"
+
+
+struct ResolveFlags {
+ // Nothing yet.
+
+ int dummy; // empty struct declarations are not allowed by C'99.
+};
+
+struct ResolveError {
+ int gaiRes;
+ int err;
+ // errno value. Only relevant if gaiRes == EAI_SYSTEM.
+};
+
+typedef void *ResolveCallbackArg;
+typedef void (*ResolveCallback)(ResolveState *resolveState,
+ struct addrinfo *result);
+ // The receiver of the callback is owner of 'result' and
+ // should call freeaddrinfo().
+typedef void (*ResolveErrorCallback)(ResolveState *resolveState,
+ const ResolveError *error);
+
+#ifdef RESOLVE_INTERNAL
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include "../wspiapiwrap.h"
+#else /* !defined(USE_WINSOCK) */
+# include <netdb.h>
+#endif /* !defined(USE_WINSOCK) */
+
+struct ResolveState {
+ RefCount refCount;
+
+ ResolveStateState state;
+ ResolveFlags flags;
+
+ ResolveCallback callback;
+ ResolveErrorCallback errorCallback;
+ void *extra;
+
+ CallbackID callbackID;
+ ResolveError error;
+ struct addrinfo *result;
+};
+#endif /* RESOLVE_INTERNAL */
+
+
+void ResolveState_incRef(ResolveState *resolveState);
+bool ResolveState_decRef(ResolveState *resolveState);
+void ResolveState_setExtra(ResolveState *resolveState, void *extra);
+void *ResolveState_getExtra(ResolveState *resolveState);
+ResolveState *getaddrinfoAsync(const char *node, const char *service,
+ const struct addrinfo *hints, ResolveFlags *flags,
+ ResolveCallback callback, ResolveErrorCallback errorCallback,
+ ResolveCallbackArg extra);
+void Resolve_close(ResolveState *resolveState);
+
+void splitAddrInfoOnFamily(struct addrinfo *info, int family,
+ struct addrinfo **selected, struct addrinfo ***selectedEnd,
+ struct addrinfo **rest, struct addrinfo ***restEnd);
+
+
+#endif /* LIBS_NETWORK_CONNECT_RESOLVE_H_ */
+
diff --git a/src/libs/network/netmanager/Makeinfo b/src/libs/network/netmanager/Makeinfo
new file mode 100644
index 0000000..ddf28b3
--- /dev/null
+++ b/src/libs/network/netmanager/Makeinfo
@@ -0,0 +1,11 @@
+uqm_CFILES="ndesc.c"
+uqm_HFILES="ndesc.h netmanager.h"
+
+if [ -n "$uqm_USE_WINSOCK" ]; then
+ uqm_CFILES="$uqm_CFILES netmanager_win.c"
+ uqm_HFILES="$uqm_HFILES netmanager_win.h"
+else
+ uqm_CFILES="$uqm_CFILES netmanager_bsd.c"
+ uqm_HFILES="$uqm_HFILES netmanager_bsd.h"
+fi
+
diff --git a/src/libs/network/netmanager/ndesc.c b/src/libs/network/netmanager/ndesc.c
new file mode 100644
index 0000000..e407ff9
--- /dev/null
+++ b/src/libs/network/netmanager/ndesc.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NETDESCRIPTOR_INTERNAL
+#include "ndesc.h"
+
+#include "netmanager.h"
+#include "libs/callback.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#undef DEBUG_NETDESCRIPTOR_REF
+#ifdef DEBUG_NETDESCRIPTOR_REF
+# include "libs/log.h"
+# include <inttypes.h>
+#endif
+
+
+static NetDescriptor *
+NetDescriptor_alloc(void) {
+ return malloc(sizeof (NetDescriptor));
+}
+
+static void
+NetDescriptor_free(NetDescriptor *nd) {
+ free(nd);
+}
+
+// Sets the ref count to 1.
+NetDescriptor *
+NetDescriptor_new(Socket *socket, void *extra) {
+ NetDescriptor *nd;
+
+ nd = NetDescriptor_alloc();
+ nd->refCount = 1;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref=1 (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+
+ nd->flags.closed = false;
+ nd->readCallback = NULL;
+ nd->writeCallback = NULL;
+ nd->exceptionCallback = NULL;
+ nd->closeCallback = NULL;
+ nd->socket = socket;
+ nd->smd = NULL;
+ nd->extra = extra;
+
+ if (NetManager_addDesc(nd) == -1) {
+ int savedErrno = errno;
+ NetDescriptor_free(nd);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ return nd;
+}
+
+static void
+NetDescriptor_delete(NetDescriptor *nd) {
+ assert(nd->socket == Socket_noSocket);
+ assert(nd->smd == NULL);
+
+ NetDescriptor_free(nd);
+}
+
+// Called from the callback handler.
+static void
+NetDescriptor_closeCallback(NetDescriptor *nd) {
+ if (nd->closeCallback != NULL) {
+ // The check is necessary because the close callback may have
+ // been removed before it is triggered.
+ (*nd->closeCallback)(nd);
+ }
+ NetDescriptor_decRef(nd);
+}
+
+void
+NetDescriptor_close(NetDescriptor *nd) {
+ assert(!nd->flags.closed);
+ assert(nd->socket != Socket_noSocket);
+
+ NetManager_removeDesc(nd);
+ (void) Socket_close(nd->socket);
+ nd->socket = Socket_noSocket;
+ nd->flags.closed = true;
+ if (nd->closeCallback != NULL) {
+ // Keep one reference around until the close callback has been
+ // called.
+ (void) Callback_add(
+ (CallbackFunction) NetDescriptor_closeCallback,
+ (CallbackArg) nd);
+ } else
+ NetDescriptor_decRef(nd);
+}
+
+void
+NetDescriptor_incRef(NetDescriptor *nd) {
+ assert(nd->refCount < REFCOUNT_MAX);
+ nd->refCount++;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref++ (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+}
+
+// returns true iff the ref counter has reached 0.
+bool
+NetDescriptor_decRef(NetDescriptor *nd) {
+ assert(nd->refCount > 0);
+ nd->refCount--;
+#ifdef DEBUG_NETDESCRIPTOR_REF
+ log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref-- (%d)",
+ (uintptr_t) nd, nd->refCount);
+#endif
+ if (nd->refCount == 0) {
+ NetDescriptor_delete(nd);
+ return true;
+ }
+ return false;
+}
+
+// The socket will no longer be managed by the NetManager.
+void
+NetDescriptor_detach(NetDescriptor *nd) {
+ NetManager_removeDesc(nd);
+ nd->socket = Socket_noSocket;
+ nd->flags.closed = true;
+ NetDescriptor_decRef(nd);
+}
+
+Socket *
+NetDescriptor_getSocket(NetDescriptor *nd) {
+ return nd->socket;
+}
+
+void
+NetDescriptor_setExtra(NetDescriptor *nd, void *extra) {
+ nd->extra = extra;
+}
+
+void *
+NetDescriptor_getExtra(const NetDescriptor *nd) {
+ return nd->extra;
+}
+
+void
+NetDescriptor_setReadCallback(NetDescriptor *nd,
+ NetDescriptor_ReadCallback callback) {
+ nd->readCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->readCallback != NULL) {
+ NetManager_activateReadCallback(nd);
+ } else
+ NetManager_deactivateReadCallback(nd);
+ }
+}
+
+void
+NetDescriptor_setWriteCallback(NetDescriptor *nd,
+ NetDescriptor_WriteCallback callback) {
+ nd->writeCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->writeCallback != NULL) {
+ NetManager_activateWriteCallback(nd);
+ } else
+ NetManager_deactivateWriteCallback(nd);
+ }
+}
+
+void
+NetDescriptor_setExceptionCallback(NetDescriptor *nd,
+ NetDescriptor_ExceptionCallback callback) {
+ nd->exceptionCallback = callback;
+ if (!nd->flags.closed) {
+ if (nd->exceptionCallback != NULL) {
+ NetManager_activateExceptionCallback(nd);
+ } else
+ NetManager_deactivateExceptionCallback(nd);
+ }
+}
+
+// The close callback is called as a result of a socket being closed, either
+// because of a local command or a remote disconnect.
+// The close callback will only be scheduled when this happens. The
+// callback will not be called until the Callback_process() is called.
+void
+NetDescriptor_setCloseCallback(NetDescriptor *nd,
+ NetDescriptor_CloseCallback callback) {
+ nd->closeCallback = callback;
+}
+
+
diff --git a/src/libs/network/netmanager/ndesc.h b/src/libs/network/netmanager/ndesc.h
new file mode 100644
index 0000000..8834db9
--- /dev/null
+++ b/src/libs/network/netmanager/ndesc.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NDESC_H_
+#define LIBS_NETWORK_NETMANAGER_NDESC_H_
+
+#include "types.h"
+
+
+typedef struct NetDescriptor NetDescriptor;
+
+typedef void (*NetDescriptor_ReadCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_WriteCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_ExceptionCallback)(NetDescriptor *nd);
+typedef void (*NetDescriptor_CloseCallback)(NetDescriptor *nd);
+
+typedef uint32 RefCount;
+#define REFCOUNT_MAX UINT32_MAX
+
+#include "../socket/socket.h"
+#include "netmanager.h"
+
+#ifdef NETDESCRIPTOR_INTERNAL
+// All created NetDescriptors are registered to the NetManager.
+// They are unregisted when the NetDescriptor is closed.
+// On creation the ref count is set to 1. On close it is decremented by 1.
+struct NetDescriptor {
+ struct {
+ bool closed: 1;
+ } flags;
+
+ RefCount refCount;
+
+ NetDescriptor_ReadCallback readCallback;
+ NetDescriptor_WriteCallback writeCallback;
+ NetDescriptor_ExceptionCallback exceptionCallback;
+ NetDescriptor_CloseCallback closeCallback;
+
+ Socket *socket;
+ SocketManagementData *smd;
+
+ // Extra state-dependant information for the user.
+ void *extra;
+};
+#endif
+
+NetDescriptor *NetDescriptor_new(Socket *socket, void *extra);
+void NetDescriptor_close(NetDescriptor *nd);
+void NetDescriptor_incRef(NetDescriptor *nd);
+bool NetDescriptor_decRef(NetDescriptor *nd);
+void NetDescriptor_detach(NetDescriptor *nd);
+Socket *NetDescriptor_getSocket(NetDescriptor *nd);
+void NetDescriptor_setExtra(NetDescriptor *nd, void *extra);
+void *NetDescriptor_getExtra(const NetDescriptor *nd);
+void NetDescriptor_setReadCallback(NetDescriptor *nd,
+ NetDescriptor_ReadCallback callback);
+void NetDescriptor_setWriteCallback(NetDescriptor *nd,
+ NetDescriptor_WriteCallback callback);
+void NetDescriptor_setExceptionCallback(NetDescriptor *nd,
+ NetDescriptor_ExceptionCallback callback);
+void NetDescriptor_setCloseCallback(NetDescriptor *nd,
+ NetDescriptor_CloseCallback callback);
+
+
+#endif /* LIBS_NETWORK_NETMANAGER_NDESC_H_ */
+
+
diff --git a/src/libs/network/netmanager/ndindex.ci b/src/libs/network/netmanager/ndindex.ci
new file mode 100644
index 0000000..f922d8b
--- /dev/null
+++ b/src/libs/network/netmanager/ndindex.ci
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// This file is part of netmanager_bsd.c, from where it is #included.
+// Only used for BSD sockets.
+
+// This file provides a mapping of Sockets to NetDescriptors.
+
+
+static NetDescriptor *netDescriptors[FD_SETSIZE];
+ // INV: flags.closed is not set for entries in netDescriptors.
+static size_t maxND;
+ // One past the largest used ND in netDescriptors, as used in
+ // the first argument of select();
+
+
+static inline void
+NDIndex_init(void) {
+ size_t i;
+ size_t numND = sizeof (netDescriptors) / sizeof (netDescriptors[0]);
+
+ for (i = 0; i < numND; i++)
+ netDescriptors[i] = NULL;
+}
+
+static inline void
+NDIndex_uninit(void) {
+ // Nothing to do.
+}
+
+static inline int
+NDIndex_registerNDWithSocket(Socket *sock, NetDescriptor *nd) {
+ if ((unsigned int) sock->fd >= FD_SETSIZE) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ netDescriptors[sock->fd] = nd;
+
+ if ((size_t) sock->fd >= maxND)
+ maxND = (size_t) sock->fd + 1;
+
+ return 0;
+}
+
+static inline void
+NDIndex_unregisterNDForSocket(Socket *sock) {
+ NetDescriptor **last;
+
+ netDescriptors[sock->fd] = NULL;
+
+ last = &netDescriptors[sock->fd];
+
+ if ((size_t) sock->fd + 1 == maxND) {
+ do {
+ maxND--;
+ if (last == &netDescriptors[0])
+ break;
+ last--;
+ } while (*last == NULL);
+ }
+}
+
+static inline NetDescriptor *
+NDIndex_getNDForSocket(Socket *sock) {
+ assert((size_t) sock->fd < maxND);
+ return netDescriptors[sock->fd];
+}
+
+static inline NetDescriptor *
+NDIndex_getNDForSocketFd(int fd) {
+ assert((size_t) fd < maxND);
+ return netDescriptors[fd];
+}
+
+static inline bool
+NDIndex_socketRegistered(Socket *sock) {
+ return ((size_t) sock->fd < maxND)
+ && (NDIndex_getNDForSocket(sock) != NULL);
+}
+
+// Get the first argument to be used in select().
+static inline size_t
+NDIndex_getSelectNumND(void) {
+ return maxND;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager.h b/src/libs/network/netmanager/netmanager.h
new file mode 100644
index 0000000..42be572
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_H_
+
+#include "port.h"
+#include "types.h"
+
+#ifdef USE_WINSOCK
+# include "netmanager_win.h"
+#else
+# include "netmanager_bsd.h"
+#endif
+
+#include "ndesc.h"
+
+void NetManager_init(void);
+void NetManager_uninit(void);
+int NetManager_process(uint32 *timeoutMs);
+
+// Only for internal use by the NetManager:
+int NetManager_addDesc(NetDescriptor *nd);
+void NetManager_removeDesc(NetDescriptor *nd);
+void NetManager_activateReadCallback(NetDescriptor *nd);
+void NetManager_deactivateReadCallback(NetDescriptor *nd);
+void NetManager_activateWriteCallback(NetDescriptor *nd);
+void NetManager_deactivateWriteCallback(NetDescriptor *nd);
+void NetManager_activateExceptionCallback(NetDescriptor *nd);
+void NetManager_deactivateExceptionCallback(NetDescriptor *nd);
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_H_ */
+
diff --git a/src/libs/network/netmanager/netmanager_bsd.c b/src/libs/network/netmanager/netmanager_bsd.c
new file mode 100644
index 0000000..29159f8
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_bsd.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define SOCKET_INTERNAL
+#define NETDESCRIPTOR_INTERNAL
+#include "netmanager_bsd.h"
+#include "ndesc.h"
+#include "../socket/socket.h"
+
+#include "ndesc.h"
+#include "types.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "netmanager_common.ci"
+#include "ndindex.ci"
+
+
+// INV: The following sets only contain sockets present in the netDescriptor
+// array.
+static fd_set readSet;
+static fd_set writeSet;
+static fd_set exceptionSet;
+
+
+void
+NetManager_init(void) {
+ NDIndex_init();
+
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&exceptionSet);
+}
+
+void
+NetManager_uninit(void) {
+ NDIndex_uninit();
+}
+
+// Register the NetDescriptor with the NetManager.
+int
+NetManager_addDesc(NetDescriptor *nd) {
+ int fd;
+ assert(nd->socket != Socket_noSocket);
+ assert(!NDIndex_socketRegistered(nd->socket));
+
+ if (NDIndex_registerNDWithSocket(nd->socket, nd) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ fd = nd->socket->fd;
+ if (nd->readCallback != NULL)
+ FD_SET(fd, &readSet);
+ if (nd->writeCallback != NULL)
+ FD_SET(fd, &writeSet);
+ if (nd->exceptionCallback != NULL)
+ FD_SET(fd, &exceptionSet);
+ return 0;
+}
+
+void
+NetManager_removeDesc(NetDescriptor *nd) {
+ int fd;
+
+ assert(nd->socket != Socket_noSocket);
+ assert(NDIndex_getNDForSocket(nd->socket) == nd);
+
+ fd = nd->socket->fd;
+ FD_CLR(fd, &readSet);
+ FD_CLR(fd, &writeSet);
+ FD_CLR(fd, &exceptionSet);
+
+ NDIndex_unregisterNDForSocket(nd->socket);
+}
+
+void
+NetManager_activateReadCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &readSet);
+}
+
+void
+NetManager_deactivateReadCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &readSet);
+}
+
+void
+NetManager_activateWriteCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &writeSet);
+}
+
+void
+NetManager_deactivateWriteCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &writeSet);
+}
+
+void
+NetManager_activateExceptionCallback(NetDescriptor *nd) {
+ FD_SET(nd->socket->fd, &exceptionSet);
+}
+
+void
+NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
+ FD_CLR(nd->socket->fd, &exceptionSet);
+}
+
+// This function may be called again from inside a callback function
+// triggered by this function. BUG: This may result in callbacks being
+// called multiple times.
+// This function should however not be called from multiple threads at once.
+int
+NetManager_process(uint32 *timeoutMs) {
+ struct timeval timeout;
+ size_t i;
+ int selectResult;
+ fd_set newReadSet;
+ fd_set newWriteSet;
+ fd_set newExceptionSet;
+ bool bitSet;
+
+ timeout.tv_sec = *timeoutMs / 1000;
+ timeout.tv_usec = (*timeoutMs % 1000) * 1000;
+
+ // Structure assignment:
+ newReadSet = readSet;
+ newWriteSet = writeSet;
+ newExceptionSet = exceptionSet;
+
+ do {
+ selectResult = select(NDIndex_getSelectNumND(),
+ &newReadSet, &newWriteSet, &newExceptionSet, &timeout);
+ // BUG: If select() is restarted because of EINTR, the timeout
+ // may start over. (Linux changes 'timeout' to the time left,
+ // but most other platforms don't.)
+ } while (selectResult == -1 && errno == EINTR);
+ if (selectResult == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "select() failed: %s.", strerror(errno));
+ errno = savedErrno;
+ *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
+ // XXX: rounding microseconds down. Is that the correct
+ // thing to do?
+ return -1;
+ }
+
+ for (i = 0; i < maxND; i++) {
+ NetDescriptor *nd;
+
+ if (selectResult == 0) {
+ // No more bits set in the fd_sets
+ break;
+ }
+
+ nd = NDIndex_getNDForSocketFd(i);
+ if (nd == NULL)
+ continue;
+
+ bitSet = false;
+ // Is one of the bits in the fd_sets set?
+
+ // A callback may cause a NetDescriptor to be closed. The deletion
+ // of the structure will be scheduled, but will still be
+ // available at least until this function returns.
+
+ if (FD_ISSET(i, &newExceptionSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doExceptionCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+ if (FD_ISSET(i, &newWriteSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doWriteCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+ if (FD_ISSET(i, &newReadSet))
+ {
+ bool closed;
+ bitSet = true;
+ closed = NetManager_doReadCallback(nd);
+ if (closed)
+ goto next;
+ }
+
+next:
+ if (bitSet)
+ selectResult--;
+ }
+
+ *timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
+ // XXX: rounding microseconds down. Is that the correct
+ // thing to do?
+ return 0;
+}
+
+
+
diff --git a/src/libs/network/netmanager/netmanager_bsd.h b/src/libs/network/netmanager/netmanager_bsd.h
new file mode 100644
index 0000000..b94dd69
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_bsd.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_
+
+typedef struct SocketManagementDataBsd SocketManagementDataBsd;
+typedef SocketManagementDataBsd SocketManagementData;
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_BSD_H_ */
+
diff --git a/src/libs/network/netmanager/netmanager_common.ci b/src/libs/network/netmanager/netmanager_common.ci
new file mode 100644
index 0000000..058e739
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_common.ci
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// This file is part of netmanager_bsd.ci and netmanager_win.ci,
+// from where it is #included.
+
+static bool
+NetManager_doReadCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->readCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->readCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+static bool
+NetManager_doWriteCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->writeCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->writeCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+static bool
+NetManager_doExceptionCallback(NetDescriptor *nd) {
+ bool closed;
+
+ assert(nd->exceptionCallback != NULL);
+ NetDescriptor_incRef(nd);
+ (*nd->exceptionCallback)(nd);
+ closed = nd->flags.closed;
+ NetDescriptor_decRef(nd);
+ return closed;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager_win.c b/src/libs/network/netmanager/netmanager_win.c
new file mode 100644
index 0000000..f146732
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_win.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "../netport.h"
+
+#define NETMANAGER_INTERNAL
+#define NETDESCRIPTOR_INTERNAL
+#define SOCKET_INTERNAL
+#include "netmanager_win.h"
+#include "../socket/socket.h"
+
+#include "ndesc.h"
+#include "types.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <assert.h>
+#include <winsock2.h>
+
+#include "netmanager_common.ci"
+
+int closeWSAEvent(WSAEVENT event);
+
+
+// The elements of the following arrays with the same index belong to
+// eachother.
+#define MAX_SOCKETS WSA_MAXIMUM_WAIT_EVENTS
+ // We cannot have more sockets than we can have events, as
+ // all may need an event at some point.
+static NetDescriptor *netDescriptors[MAX_SOCKETS];
+static WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS];
+static size_t numSockets;
+static size_t numActiveEvents;
+// For each NetDescriptor registered with the NetManager, an event
+// is created. Only the first numActiveEvents will be processed though.
+// Inv: numActiveEvents <= numSockets
+// Inv: for all i: 0 <= i < numActiveEvents: events[i] is active
+// (events[i] being active also means netDescriptor[i]->smd->eventMask
+// != 0)
+// Inv: for all i: 0 <= i < numSockets: netDescriptor[i]->smd->index == i
+
+void
+NetManager_init(void) {
+ numActiveEvents = 0;
+}
+
+void
+NetManager_uninit(void) {
+ assert(numActiveEvents == 0);
+}
+
+static inline SocketManagementDataWin *
+SocketManagementData_alloc(void) {
+ return malloc(sizeof (SocketManagementDataWin));
+}
+
+static inline void
+SocketManagementData_free(SocketManagementDataWin *smd) {
+ free(smd);
+}
+
+// XXX: This function should be moved to some file with generic network
+// functions.
+int
+closeWSAEvent(WSAEVENT event) {
+ for (;;) {
+ int error;
+
+ if (WSACloseEvent(event))
+ break;
+
+ error = WSAGetLastError();
+ if (error != WSAEINPROGRESS) {
+ log_add(log_Error,
+ "WSACloseEvent() failed with error code %d.", error);
+ errno = winsockErrorToErrno(error);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+// Register the NetDescriptor with the NetManager.
+int
+NetManager_addDesc(NetDescriptor *nd) {
+ long eventMask = 0;
+ WSAEVENT event;
+
+ if (numSockets >= WSA_MAXIMUM_WAIT_EVENTS) {
+ errno = EMFILE;
+ return -1;
+ }
+
+ if (nd->readCallback != NULL)
+ eventMask |= FD_READ | FD_ACCEPT;
+
+ if (nd->writeCallback != NULL)
+ eventMask |= FD_WRITE /* | FD_CONNECT */;
+
+ if (nd->exceptionCallback != NULL)
+ eventMask |= FD_OOB;
+
+ eventMask |= FD_CLOSE;
+
+ event = WSACreateEvent();
+ if (event == WSA_INVALID_EVENT) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ nd->smd = SocketManagementData_alloc();
+ if (eventMask != 0) {
+ // XXX: This guard is now always true, because of FD_CLOSE.
+ // This means that numActiveEvents will always be equal to
+ // numEvents.
+ // Once I'm convinced this is the right way to go,
+ // I can remove some unnecessary code.
+ if (WSAEventSelect(nd->socket->sock, event, eventMask) ==
+ SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ int closeStatus = closeWSAEvent(event);
+ if (closeStatus == -1) {
+ log_add(log_Fatal, "closeWSAEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ }
+ SocketManagementData_free(nd->smd);
+ errno = savedErrno;
+ return -1;
+ }
+
+ // Move existing socket for which there exists no event, so
+ // so that all sockets for which there exists an event are at
+ // the front of the array of netdescriptors.
+ if (numActiveEvents < numSockets) {
+ netDescriptors[numSockets] = netDescriptors[numActiveEvents];
+ netDescriptors[numSockets]->smd->index = numSockets;
+ }
+
+ nd->smd->index = numActiveEvents;
+ numActiveEvents++;
+ } else {
+ nd->smd->index = numSockets;
+ }
+ nd->smd->eventMask = eventMask;
+
+ netDescriptors[nd->smd->index] = nd;
+ events[nd->smd->index] = event;
+ numSockets++;
+
+ return 0;
+}
+
+void
+NetManager_removeDesc(NetDescriptor *nd) {
+ assert(nd->smd != NULL);
+ assert(nd->smd->index < numSockets);
+ assert(nd == netDescriptors[nd->smd->index]);
+
+ {
+ int closeStatus = closeWSAEvent(events[nd->smd->index]);
+ if (closeStatus == -1)
+ explode();
+ }
+
+ if (nd->smd->index < numActiveEvents) {
+ size_t index = nd->smd->index;
+ if (index + 1 != numActiveEvents) {
+ // Keep the list of active events consecutive by filling
+ // the new hole with the last active event.
+ events[index] = events[numActiveEvents - 1];
+ netDescriptors[index] = netDescriptors[numActiveEvents - 1];
+ netDescriptors[index]->smd->index = index;
+ }
+ numActiveEvents--;
+ }
+
+ SocketManagementData_free(nd->smd);
+ nd->smd = NULL;
+
+ numSockets--;
+}
+
+static void
+swapSockets(int index1, int index2) {
+ NetDescriptor *tempNd;
+ WSAEVENT tempEvent;
+
+ tempNd = netDescriptors[index2];
+ tempEvent = events[index2];
+
+ netDescriptors[index2] = netDescriptors[index1];
+ events[index2] = events[index1];
+ netDescriptors[index2]->smd->index = index2;
+
+ netDescriptors[index1] = tempNd;
+ events[index1] = tempEvent;
+ netDescriptors[index1]->smd->index = index1;
+}
+
+static int
+NetManager_updateEvent(NetDescriptor *nd) {
+ assert(nd == netDescriptors[nd->smd->index]);
+
+ if (WSAEventSelect(nd->socket->sock,
+ events[nd->smd->index], nd->smd->eventMask) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ int closeStatus = closeWSAEvent(events[nd->smd->index]);
+ if (closeStatus == -1) {
+ log_add(log_Fatal, "closeWSAEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ }
+ errno = savedErrno;
+ return -1;
+ }
+
+ if (nd->smd->eventMask != 0) {
+ // There are some events that we are interested in.
+ if (nd->smd->index >= numActiveEvents) {
+ // Event was not yet active.
+ if (nd->smd->index != numActiveEvents) {
+ // Need to keep the active nds and events in the front of
+ // their arrays.
+ swapSockets(nd->smd->index, numActiveEvents);
+ }
+ numActiveEvents++;
+ }
+ } else {
+ // There are no events that we are interested in.
+ if (nd->smd->index < numActiveEvents) {
+ // Event was active.
+ if (nd->smd->index != numActiveEvents - 1) {
+ // Need to keep the active nds and events in the front of
+ // their arrays.
+ swapSockets(nd->smd->index, numActiveEvents - 1);
+ }
+ }
+ numActiveEvents--;
+ }
+
+ return 0;
+}
+
+static void
+activateSomeCallback(NetDescriptor *nd, long eventMask) {
+ nd->smd->eventMask |= eventMask;
+ {
+ int status = NetManager_updateEvent(nd);
+ if (status == -1) {
+ log_add(log_Fatal, "NetManager_updateEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ // TODO: better error handling.
+ }
+ }
+}
+
+static void
+deactivateSomeCallback(NetDescriptor *nd, long eventMask) {
+ nd->smd->eventMask &= ~eventMask;
+ {
+ int status = NetManager_updateEvent(nd);
+ if (status == -1) {
+ log_add(log_Fatal, "NetManager_updateEvent() failed: %s.",
+ strerror(errno));
+ explode();
+ // TODO: better error handling
+ }
+ }
+}
+
+void
+NetManager_activateReadCallback(NetDescriptor *nd) {
+ activateSomeCallback(nd, FD_READ | FD_ACCEPT);
+}
+
+void
+NetManager_deactivateReadCallback(NetDescriptor *nd) {
+ deactivateSomeCallback(nd, FD_READ | FD_ACCEPT);
+}
+
+void
+NetManager_activateWriteCallback(NetDescriptor *nd) {
+ activateSomeCallback(nd, FD_WRITE /* | FD_CONNECT */);
+}
+
+void
+NetManager_deactivateWriteCallback(NetDescriptor *nd) {
+ deactivateSomeCallback(nd, FD_WRITE /* | FD_CONNECT */);
+}
+
+void
+NetManager_activateExceptionCallback(NetDescriptor *nd) {
+ activateSomeCallback(nd, FD_OOB);
+}
+
+void
+NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
+ deactivateSomeCallback(nd, FD_OOB);
+}
+
+static inline int
+NetManager_processEvent(size_t index) {
+ WSANETWORKEVENTS networkEvents;
+ int enumRes;
+
+ enumRes = WSAEnumNetworkEvents(netDescriptors[index]->socket->sock,
+ events[index], &networkEvents);
+ if (enumRes == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ if (networkEvents.lNetworkEvents & FD_READ) {
+ bool closed;
+ if (networkEvents.iErrorCode[FD_READ_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will try to do a recv() and will get the error then.
+ }
+
+ closed = NetManager_doReadCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+ if (networkEvents.lNetworkEvents & FD_WRITE) {
+ bool closed;
+ if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will try to do a send() and will get the error then.
+ }
+
+ closed = NetManager_doWriteCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+ if (networkEvents.lNetworkEvents & FD_OOB) {
+ bool closed;
+ if (networkEvents.iErrorCode[FD_OOB_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will get the error then when it tries to do a recv().
+ }
+
+ closed = NetManager_doExceptionCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+ if (networkEvents.lNetworkEvents & FD_ACCEPT) {
+ // There is no specific accept callback (because the BSD sockets
+ // don't work with specific notification for accept); we use
+ // the read callback instead.
+ bool closed;
+ if (networkEvents.iErrorCode[FD_READ_BIT] != 0) {
+ // No special handling is required; the callback function
+ // will try to do an accept() and will get the error then.
+ }
+
+ closed = NetManager_doReadCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+#if 0
+ // No need for this. Windows also sets FD_WRITE in this case, and
+ // writability is what we check for anyhow.
+ if (networkEvents.lNetworkEvents & FD_CONNECT) {
+ // There is no specific connect callback (because the BSD sockets
+ // don't work with specific notification for connect); we use
+ // the write callback instead.
+ bool closed;
+ if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) {
+ // No special handling is required; the callback function
+ // should do getsockopt() with SO_ERROR to get the error.
+ }
+
+ closed = NetManager_doWriteCallback(netDescriptors[index]);
+ if (closed)
+ goto closed;
+ }
+#endif
+ if (networkEvents.lNetworkEvents & FD_CLOSE) {
+ // The close event is handled last, in case there was still
+ // data in the buffers which could be processed.
+ NetDescriptor_close(netDescriptors[index]);
+ goto closed;
+ }
+
+closed: /* No special actions required for now. */
+
+ return 0;
+}
+
+// This function may be called again from inside a callback function
+// triggered by this function. BUG: This may result in callbacks being
+// called multiple times.
+// This function should however not be called from multiple threads at once.
+int
+NetManager_process(uint32 *timeoutMs) {
+ DWORD timeoutTemp;
+ DWORD waitResult;
+ DWORD startEvent;
+
+ timeoutTemp = (DWORD) *timeoutMs;
+
+ // WSAWaitForMultipleEvents only reports events for one socket at a
+ // time. In order to have each socket checked once, we call it
+ // again after it has reported an event, but passing only the
+ // events not yet processed. The second time, the timeout will be set
+ // to 0, so it won't wait.
+ startEvent = 0;
+ while (startEvent < numActiveEvents) {
+ waitResult = WSAWaitForMultipleEvents(numActiveEvents - startEvent,
+ &events[startEvent], FALSE, timeoutTemp, FALSE);
+
+ if (waitResult == WSA_WAIT_IO_COMPLETION)
+ continue;
+
+ if (waitResult == WSA_WAIT_TIMEOUT) {
+ // No events waiting.
+ *timeoutMs = 0;
+ return 0;
+ }
+
+ if (waitResult == WSA_WAIT_FAILED) {
+ errno = getWinsockErrno();
+ *timeoutMs = timeoutTemp;
+ return -1;
+ }
+
+ {
+ DWORD eventIndex = waitResult - WSA_WAIT_EVENT_0;
+ if (NetManager_processEvent((size_t) eventIndex) == -1) {
+ // errno is set
+ *timeoutMs = timeoutTemp;
+ return -1;
+ }
+
+ // Check the rest of the sockets, but don't wait anymore.
+ startEvent += eventIndex + 1;
+ timeoutTemp = 0;
+ }
+ }
+
+ *timeoutMs = timeoutTemp;
+ return 0;
+}
+
+
diff --git a/src/libs/network/netmanager/netmanager_win.h b/src/libs/network/netmanager/netmanager_win.h
new file mode 100644
index 0000000..03d65e4
--- /dev/null
+++ b/src/libs/network/netmanager/netmanager_win.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_
+#define LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_
+
+typedef struct SocketManagementDataWin SocketManagementDataWin;
+typedef SocketManagementDataWin SocketManagementData;
+
+#ifdef NETMANAGER_INTERNAL
+struct SocketManagementDataWin {
+ size_t index;
+ long eventMask;
+};
+#endif /* NETMANAGER_INTERNAL */
+
+
+#endif /* LIBS_NETWORK_NETMANAGER_NETMANAGER_WIN_H_ */
+
+
diff --git a/src/libs/network/netport.c b/src/libs/network/netport.c
new file mode 100644
index 0000000..bfd478c
--- /dev/null
+++ b/src/libs/network/netport.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "netport.h"
+
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+
+int
+winsockErrorToErrno(int winsockError) {
+ switch (winsockError) {
+ case WSAEINTR: return EINTR;
+ case WSAEACCES: return EACCES;
+ case WSAEFAULT: return EFAULT;
+ case WSAEINVAL: return EINVAL;
+ case WSAEMFILE: return EMFILE;
+ case WSAEWOULDBLOCK: return EWOULDBLOCK;
+ case WSAEINPROGRESS: return EINPROGRESS;
+ case WSAEALREADY: return EALREADY;
+ case WSAENOTSOCK: return ENOTSOCK;
+ case WSAEDESTADDRREQ: return EDESTADDRREQ;
+ case WSAEMSGSIZE: return EMSGSIZE;
+ case WSAEPROTOTYPE: return EPROTOTYPE;
+ case WSAENOPROTOOPT: return ENOPROTOOPT;
+ case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
+ case WSAESOCKTNOSUPPORT: return ESOCKTNOSUPPORT;
+ case WSAEOPNOTSUPP: return EOPNOTSUPP;
+ case WSAEPFNOSUPPORT: return EPFNOSUPPORT;
+ case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
+ case WSAEADDRINUSE: return EADDRINUSE;
+ case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
+ case WSAENETDOWN: return ENETDOWN;
+ case WSAENETUNREACH: return ENETUNREACH;
+ case WSAENETRESET: return ENETRESET;
+ case WSAECONNABORTED: return ECONNABORTED;
+ case WSAECONNRESET: return ECONNRESET;
+ case WSAENOBUFS: return ENOBUFS;
+ case WSAEISCONN: return EISCONN;
+ case WSAENOTCONN: return ENOTCONN;
+ case WSAESHUTDOWN: return ESHUTDOWN;
+ case WSAETIMEDOUT: return ETIMEDOUT;
+ case WSAECONNREFUSED: return ECONNREFUSED;
+ case WSAEHOSTDOWN: return EHOSTDOWN;
+ case WSAEHOSTUNREACH: return EHOSTUNREACH;
+ case WSAEPROCLIM: return EPROCLIM;
+ case WSASYSNOTREADY: return ENOSYS;
+ case WSAVERNOTSUPPORTED: return ENOSYS;
+ case WSANOTINITIALISED: return ENOSYS;
+ case WSAEDISCON: return ECONNRESET;
+ case WSATYPE_NOT_FOUND: return ENODATA;
+ case WSAHOST_NOT_FOUND: return ENODATA;
+ case WSATRY_AGAIN: return EAGAIN;
+ case WSANO_RECOVERY: return EIO;
+ case WSANO_DATA: return ENODATA;
+ case WSA_INVALID_HANDLE: return EBADF;
+ case WSA_INVALID_PARAMETER: return EINVAL;
+ case WSA_IO_INCOMPLETE: return EAGAIN;
+ case WSA_IO_PENDING: return EINPROGRESS;
+ case WSA_NOT_ENOUGH_MEMORY: return ENOMEM;
+ case WSA_OPERATION_ABORTED: return EINTR;
+ case WSAEINVALIDPROCTABLE: return ENOSYS;
+ case WSAEINVALIDPROVIDER: return ENOSYS;
+ case WSAEPROVIDERFAILEDINIT: return ENOSYS;
+ case WSASYSCALLFAILURE: return EIO;
+ default: return EIO;
+ }
+}
+
+int
+getWinsockErrno(void) {
+ return winsockErrorToErrno(WSAGetLastError());
+}
+#endif /* defined (USE_WINSOCK) */
+
diff --git a/src/libs/network/netport.h b/src/libs/network/netport.h
new file mode 100644
index 0000000..ee99699
--- /dev/null
+++ b/src/libs/network/netport.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETPORT_H_
+#define LIBS_NETWORK_NETPORT_H_
+
+#include "port.h"
+
+#ifdef USE_WINSOCK
+int winsockErrorToErrno(int winsockError);
+int getWinsockErrno(void);
+# define EAI_SYSTEM 0x02000001
+ // Any value will do that doesn't conflict with an existing value.
+
+#ifdef __MINGW32__
+// MinGW does not have a working gai_strerror() yet.
+static inline const char *
+gai_strerror(int err) {
+ (void) err;
+ return "[gai_strerror() is not available on MinGW]";
+}
+#endif /* defined(__MINGW32__) */
+
+#endif
+
+#endif /* LIBS_NETWORK_NETPORT_H_ */
+
+
diff --git a/src/libs/network/network.h b/src/libs/network/network.h
new file mode 100644
index 0000000..d8c596e
--- /dev/null
+++ b/src/libs/network/network.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_NETWORK_H_
+#define LIBS_NETWORK_NETWORK_H_
+
+void Network_init(void);
+void Network_uninit(void);
+
+#endif /* LIBS_NETWORK_NETWORK_H_ */
+
+
diff --git a/src/libs/network/network_bsd.c b/src/libs/network/network_bsd.c
new file mode 100644
index 0000000..a213c4e
--- /dev/null
+++ b/src/libs/network/network_bsd.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "network.h"
+
+void
+Network_init(void) {
+ // Nothing to do.
+}
+
+void
+Network_uninit(void) {
+ // Nothing to do.
+}
+
diff --git a/src/libs/network/network_win.c b/src/libs/network/network_win.c
new file mode 100644
index 0000000..a5c6abf
--- /dev/null
+++ b/src/libs/network/network_win.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netport.h"
+
+#include "network.h"
+
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#include <errno.h>
+#include <winsock2.h>
+
+void
+Network_init(void) {
+ WSADATA data;
+ int startupResult;
+ WORD requestVersion = MAKEWORD(2, 2);
+
+ startupResult = WSAStartup(requestVersion, &data);
+ if (startupResult != 0) {
+ int savedErrno = winsockErrorToErrno(startupResult);
+ log_add(log_Fatal, "WSAStartup failed.");
+ errno = savedErrno;
+ explode();
+ }
+
+#ifdef DEBUG
+ log_add(log_Debug, "Winsock version %d.%d found: \"%s\".",
+ LOBYTE(data.wHighVersion), HIBYTE(data.wHighVersion),
+ data.szDescription);
+ log_add(log_Debug, "Requesting to use Winsock version %d.%d, got "
+ "version %d.%d.",
+ LOBYTE(requestVersion), HIBYTE(requestVersion),
+ LOBYTE(data.wVersion), HIBYTE(data.wVersion));
+#endif
+ if (data.wVersion != requestVersion) {
+ log_add(log_Fatal, "Winsock version %d.%d presented, requested "
+ "%d.%d.", LOBYTE(data.wVersion), HIBYTE(data.wVersion),
+ LOBYTE(requestVersion), HIBYTE(requestVersion));
+ (void) WSACleanup();
+ // Ignoring errors; we're going to abort anyhow.
+ explode();
+ }
+}
+
+void
+Network_uninit(void) {
+ int cleanupResult;
+
+ cleanupResult = WSACleanup();
+ if (cleanupResult == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Fatal, "WSACleanup failed.");
+ errno = savedErrno;
+ explode();
+ }
+}
+
+
diff --git a/src/libs/network/socket/Makeinfo b/src/libs/network/socket/Makeinfo
new file mode 100644
index 0000000..19e1637
--- /dev/null
+++ b/src/libs/network/socket/Makeinfo
@@ -0,0 +1,11 @@
+uqm_CFILES="socket.c"
+uqm_HFILES="socket.h"
+
+if [ -n "$uqm_USE_WINSOCK" ]; then
+ uqm_CFILES="$uqm_CFILES socket_win.c"
+ uqm_HFILES="$uqm_HFILES socket_win.h"
+else
+ uqm_CFILES="$uqm_CFILES socket_bsd.c"
+ uqm_HFILES="$uqm_CFILES socket_bsd.h"
+fi
+
diff --git a/src/libs/network/socket/socket.c b/src/libs/network/socket/socket.c
new file mode 100644
index 0000000..be7d601
--- /dev/null
+++ b/src/libs/network/socket/socket.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "port.h"
+
+#define SOCKET_INTERNAL
+#include "socket.h"
+
+#ifdef USE_WINSOCK
+# include <winsock2.h>
+#else
+# include <sys/socket.h>
+# include <netinet/in.h>
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+const int protocolFamilyTranslation[] = {
+ /* .[PF_unspec] = */ PF_UNSPEC,
+ /* .[PF_inet] = */ PF_INET,
+ /* .[PF_inet6] = */ PF_INET6,
+};
+
+const int protocolTranslation[] = {
+ /* .[IPProto_tcp] = */ IPPROTO_TCP,
+ /* .[IPProto_udp] = */ IPPROTO_UDP,
+};
+
+const int socketTypeTranslation[] = {
+ /* .[Sock_stream] = */ SOCK_STREAM,
+ /* .[Sock_dgram] = */ SOCK_DGRAM,
+};
+
+
+Socket *
+Socket_open(ProtocolFamily domain, SocketType type, Protocol protocol) {
+ return Socket_openNative(protocolFamilyTranslation[domain],
+ socketTypeTranslation[type], protocolTranslation[protocol]);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
diff --git a/src/libs/network/socket/socket.h b/src/libs/network/socket/socket.h
new file mode 100644
index 0000000..5c75467
--- /dev/null
+++ b/src/libs/network/socket/socket.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_SOCKET_SOCKET_H_
+#define LIBS_NETWORK_SOCKET_SOCKET_H_
+
+typedef struct Socket Socket;
+#define Socket_noSocket ((Socket *) NULL)
+
+#include "port.h"
+
+#ifdef USE_WINSOCK
+# include "socket_win.h"
+#else
+# include "socket_bsd.h"
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+// Defining our own types for protocol families and protocols instead of
+// using the system defines, so that the layer using this API does not have
+// to have anything to do with the system layer.
+
+typedef enum {
+ PF_unspec,
+ PF_inet,
+ PF_inet6,
+} ProtocolFamily;
+typedef ProtocolFamily AddressFamily;
+
+typedef enum {
+ IPProto_tcp,
+ IPProto_udp,
+} Protocol;
+
+typedef enum {
+ Sock_stream,
+ Sock_dgram,
+} SocketType;
+
+#ifdef SOCKET_INTERNAL
+extern const int protocolFamilyTranslation[];
+#define addressFamilyTranslation protocolFamilyTranslation;
+extern const int protocolTranslation[];
+extern const int socketTypeTranslation[];
+#endif
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+Socket *Socket_open(ProtocolFamily domain, SocketType type,
+ Protocol protocol);
+#ifdef SOCKET_INTERNAL
+Socket *Socket_openNative(int domain, int type, int protocol);
+#endif
+int Socket_close(Socket *sock);
+
+int Socket_connect(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen);
+int Socket_bind(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen);
+int Socket_listen(Socket *sock, int backlog);
+Socket *Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen);
+ssize_t Socket_send(Socket *sock, const void *buf, size_t len, int flags);
+ssize_t Socket_sendto(Socket *sock, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t addrLen);
+ssize_t Socket_recv(Socket *sock, void *buf, size_t len, int flags);
+ssize_t Socket_recvfrom(Socket *sock, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromLen);
+
+int Socket_setNonBlocking(Socket *sock);
+int Socket_setReuseAddr(Socket *sock);
+int Socket_setNodelay(Socket *sock);
+int Socket_setTOS(Socket *sock, int tos);
+int Socket_setInteractive(Socket *sock);
+int Socket_setInlineOOB(Socket *sock);
+int Socket_setKeepAlive(Socket *sock);
+int Socket_getError(Socket *sock, int *err);
+
+#endif /* LIBS_NETWORK_SOCKET_SOCKET_H_ */
+
diff --git a/src/libs/network/socket/socket_bsd.c b/src/libs/network/socket/socket_bsd.c
new file mode 100644
index 0000000..2bf25b8
--- /dev/null
+++ b/src/libs/network/socket/socket_bsd.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Socket functions for BSD sockets.
+
+#define SOCKET_INTERNAL
+#include "socket.h"
+
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+# include <netinet/in_systm.h>
+# include <netinet/in.h>
+#endif
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+static Socket *
+Socket_alloc(void) {
+ return malloc(sizeof (Socket));
+}
+
+static void
+Socket_free(Socket *sock) {
+ free(sock);
+}
+
+Socket *
+Socket_openNative(int domain, int type, int protocol) {
+ Socket *result;
+ int fd;
+
+ fd = socket(domain, type, protocol);
+ if (fd == -1) {
+ // errno is set
+ return Socket_noSocket;
+ }
+
+ result = Socket_alloc();
+ result->fd = fd;
+ return result;
+}
+
+int
+Socket_close(Socket *sock) {
+ int closeResult;
+
+ do {
+ closeResult = close(sock->fd);
+ if (closeResult == 0) {
+ Socket_free(sock);
+ return 0;
+ }
+ } while (errno == EINTR);
+
+ return -1;
+}
+
+int
+Socket_connect(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen) {
+ int connectResult;
+
+ do {
+ connectResult = connect(sock->fd, addr, addrLen);
+ } while (connectResult == -1 && errno == EINTR);
+
+ return connectResult;
+}
+
+int
+Socket_bind(Socket *sock, const struct sockaddr *addr, socklen_t addrLen) {
+ return bind(sock->fd, addr, addrLen);
+}
+
+int
+Socket_listen(Socket *sock, int backlog) {
+ return listen(sock->fd, backlog);
+}
+
+Socket *
+Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen) {
+ int acceptResult;
+ socklen_t tempAddrLen;
+
+ do {
+ tempAddrLen = *addrLen;
+ acceptResult = accept(sock->fd, addr, &tempAddrLen);
+ if (acceptResult != -1) {
+ Socket *result = Socket_alloc();
+ result->fd = acceptResult;
+ *addrLen = tempAddrLen;
+ return result;
+ }
+
+ } while (errno == EINTR);
+
+ // errno is set
+ return Socket_noSocket;
+}
+
+ssize_t
+Socket_send(Socket *sock, const void *buf, size_t len, int flags) {
+ return send(sock->fd, buf, len, flags);
+}
+
+ssize_t
+Socket_sendto(Socket *sock, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t addrLen) {
+ return sendto(sock->fd, buf, len, flags, addr, addrLen);
+}
+
+ssize_t
+Socket_recv(Socket *sock, void *buf, size_t len, int flags) {
+ return recv(sock->fd, buf, len, flags);
+}
+
+ssize_t
+Socket_recvfrom(Socket *sock, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromLen) {
+ return recvfrom(sock->fd, buf, len, flags, from, fromLen);
+}
+
+int
+Socket_setNonBlocking(Socket *sock) {
+ int flags;
+
+ flags = fcntl(sock->fd, F_GETFL);
+ if (flags == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Getting file descriptor flags of socket failed: "
+ "%s.", strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+
+ if (fcntl(sock->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting non-blocking mode on socket failed: "
+ "%s.", strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+Socket_setReuseAddr(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof flag)
+ == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting socket reuse failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+// Send data as soon as it is available. Do not collect data to send at
+// once.
+int
+Socket_setNodelay(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof flag)
+ == -1) {
+#ifdef DEBUG
+ int savedErrno = errno;
+ log_add(log_Warning, "Disabling Nagle algorithm failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+// 'tos' should be IPTOS_LOWDELAY, IPTOS_THROUGHPUT, IPTOS_THROUGHPUT,
+// IPTOS_RELIABILITY, or IPTOS_MINCOST.
+int
+Socket_setTOS(Socket *sock, int tos) {
+ if (setsockopt(sock->fd, IPPROTO_IP, IP_TOS, &tos, sizeof tos) == -1) {
+#ifdef DEBUG
+ int savedErrno = errno;
+ log_add(log_Warning, "Setting socket type-of-service failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+// This function setups the socket for optimal configuration for an
+// interactive connection.
+int
+Socket_setInteractive(Socket *sock) {
+ if (Socket_setNodelay(sock) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (Socket_setTOS(sock, IPTOS_LOWDELAY) == -1) {
+ // errno is set
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setInlineOOB(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, SOL_SOCKET, SO_OOBINLINE, &flag, sizeof flag)
+ == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting inline OOB on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setKeepAlive(Socket *sock) {
+ int flag = 1;
+
+ if (setsockopt(sock->fd, IPPROTO_TCP, SO_KEEPALIVE, &flag, sizeof flag)
+ == -1) {
+ int savedErrno = errno;
+ log_add(log_Error, "Setting keep-alive on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_getError(Socket *sock, int *err) {
+ socklen_t errLen = sizeof(*err);
+
+ if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, err, &errLen) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ assert(errLen == sizeof(*err));
+ // err is set
+ return 0;
+}
+
+
diff --git a/src/libs/network/socket/socket_bsd.h b/src/libs/network/socket/socket_bsd.h
new file mode 100644
index 0000000..8019c01
--- /dev/null
+++ b/src/libs/network/socket/socket_bsd.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_SOCKET_SOCKET_BSD_H_
+#define LIBS_NETWORK_SOCKET_SOCKET_BSD_H_
+
+#include "types.h"
+#include <sys/socket.h>
+
+#ifdef SOCKET_INTERNAL
+struct Socket {
+ int fd;
+};
+#endif /* SOCKET_INTERNAL */
+
+
+#endif /* LIBS_NETWORK_SOCKET_SOCKET_BSD_H_ */
+
diff --git a/src/libs/network/socket/socket_win.c b/src/libs/network/socket/socket_win.c
new file mode 100644
index 0000000..e1ed002
--- /dev/null
+++ b/src/libs/network/socket/socket_win.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Socket functions for Winsock sockets.
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "../netport.h"
+
+#define SOCKET_INTERNAL
+#include "socket.h"
+
+#include "libs/log.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <winsock2.h>
+
+
+Socket *
+Socket_alloc(void) {
+ return malloc(sizeof (Socket));
+}
+
+void
+Socket_free(Socket *sock) {
+ free(sock);
+}
+
+Socket *
+Socket_openNative(int domain, int type, int protocol) {
+ Socket *result;
+ SOCKET sock;
+
+ sock = socket(domain, type, protocol);
+ if (sock == INVALID_SOCKET) {
+ errno = getWinsockErrno();
+ return Socket_noSocket;
+ }
+
+ result = Socket_alloc();
+ result->sock = sock;
+ return result;
+}
+
+int
+Socket_close(Socket *sock) {
+ int closeResult;
+
+ do {
+ closeResult = closesocket(sock->sock);
+ if (closeResult != SOCKET_ERROR) {
+ Socket_free(sock);
+ return 0;
+ }
+
+ errno = getWinsockErrno();
+ } while (errno == EINTR);
+
+ return -1;
+}
+
+int
+Socket_connect(Socket *sock, const struct sockaddr *addr,
+ socklen_t addrLen) {
+ int connectResult;
+
+ do {
+ connectResult = connect(sock->sock, addr, addrLen);
+ if (connectResult == 0)
+ return 0;
+
+ errno = getWinsockErrno();
+ } while (errno == EINTR);
+
+ if (errno == EWOULDBLOCK) {
+ // Windows returns (WSA)EWOULDBLOCK when a connection is being
+ // initiated on a non-blocking socket, while other platforms
+ // use EINPROGRESS in such cases.
+ errno = EINPROGRESS;
+ }
+
+ return -1;
+}
+
+int
+Socket_bind(Socket *sock, const struct sockaddr *addr, socklen_t addrLen) {
+ int bindResult;
+
+ bindResult = bind(sock->sock, addr, addrLen);
+ if (bindResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+Socket_listen(Socket *sock, int backlog) {
+ int listenResult;
+
+ listenResult = listen(sock->sock, backlog);
+ if (listenResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return 0;
+}
+
+Socket *
+Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen) {
+ SOCKET acceptResult;
+ socklen_t tempAddrLen;
+
+ do {
+ tempAddrLen = *addrLen;
+ acceptResult = accept(sock->sock, addr, &tempAddrLen);
+ if (acceptResult != INVALID_SOCKET) {
+ Socket *result = Socket_alloc();
+ result->sock = acceptResult;
+ *addrLen = tempAddrLen;
+ return result;
+ }
+
+ errno = getWinsockErrno();
+ } while (errno == EINTR);
+
+ // errno is set
+ return Socket_noSocket;
+}
+
+ssize_t
+Socket_send(Socket *sock, const void *buf, size_t len, int flags) {
+ int sendResult;
+
+ sendResult = send(sock->sock, buf, len, flags);
+ if (sendResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return sendResult;
+}
+
+ssize_t
+Socket_sendto(Socket *sock, const void *buf, size_t len, int flags,
+ const struct sockaddr *addr, socklen_t addrLen) {
+ int sendResult;
+
+ sendResult = sendto(sock->sock, buf, len, flags, addr, addrLen);
+ if (sendResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return sendResult;
+}
+
+ssize_t
+Socket_recv(Socket *sock, void *buf, size_t len, int flags) {
+ int recvResult;
+
+ recvResult = recv(sock->sock, buf, len, flags);
+ if (recvResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return recvResult;
+}
+
+ssize_t
+Socket_recvfrom(Socket *sock, void *buf, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromLen) {
+ int recvResult;
+
+ recvResult = recvfrom(sock->sock, buf, len, flags, from, fromLen);
+ if (recvResult == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ return recvResult;
+}
+
+int
+Socket_setNonBlocking(Socket *sock) {
+ unsigned long flag = 1;
+
+ if (ioctlsocket(sock->sock, FIONBIO, &flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting non-block mode on socket failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setReuseAddr(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char *) &flag, sizeof flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting socket reuse failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+// Send data as soon as it is available. Do not collect data to send at
+// once.
+int
+Socket_setNodelay(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char *) &flag, sizeof flag) == SOCKET_ERROR) {
+#ifdef DEBUG
+ int savedErrno = getWinsockErrno();
+ log_add(log_Warning, "Disabling Nagle algorithm failed: %s.",
+ strerror(errno));
+ errno = savedErrno;
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+// This function setups the socket for optimal configuration for an
+// interactive connection.
+int
+Socket_setInteractive(Socket *sock) {
+ if (Socket_setNodelay(sock) == -1) {
+ // errno is set
+ return -1;
+ }
+
+#if 0
+ if (Socket_setTOS(sock, IPTOS_LOWDELAY) == -1) {
+ // errno is set
+ return -1;
+ }
+#endif
+ return 0;
+}
+
+int
+Socket_setInlineOOB(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, SOL_SOCKET, SO_OOBINLINE, (const char *) &flag,
+ sizeof flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting inline OOB on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_setKeepAlive(Socket *sock) {
+ BOOL flag = 1;
+
+ if (setsockopt(sock->sock, IPPROTO_TCP, SO_KEEPALIVE,
+ (const char *) &flag, sizeof flag) == SOCKET_ERROR) {
+ int savedErrno = getWinsockErrno();
+ log_add(log_Error, "Setting keep-alive on socket failed: %s",
+ strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ return 0;
+}
+
+int
+Socket_getError(Socket *sock, int *err) {
+ int errLen = sizeof(*err);
+
+ if (getsockopt(sock->sock, SOL_SOCKET, SO_ERROR, (char *) err, &errLen)
+ == SOCKET_ERROR) {
+ errno = getWinsockErrno();
+ return -1;
+ }
+
+ assert(errLen == sizeof(*err));
+ // err is set
+ return 0;
+}
+
+
diff --git a/src/libs/network/socket/socket_win.h b/src/libs/network/socket/socket_win.h
new file mode 100644
index 0000000..ed1674c
--- /dev/null
+++ b/src/libs/network/socket/socket_win.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_SOCKET_SOCKET_WIN_H_
+#define LIBS_NETWORK_SOCKET_SOCKET_WIN_H_
+
+#ifdef SOCKET_INTERNAL
+#include <winsock2.h>
+struct Socket {
+ SOCKET sock;
+};
+#endif /* SOCKET_INTERNAL */
+
+typedef int socklen_t;
+struct sockaddr;
+
+#endif /* LIBS_NETWORK_SOCKET_SOCKET_WIN_H_ */
+
+
diff --git a/src/libs/network/wspiapiwrap.c b/src/libs/network/wspiapiwrap.c
new file mode 100644
index 0000000..2eddeb4
--- /dev/null
+++ b/src/libs/network/wspiapiwrap.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// Only used for MinGW
+
+// HACK. MinGW misses some functionality, so we're #including
+// the actual Windows wspiapi.h file. Because that file includes
+// inline functions that aren't static, it can only be included
+// once per executable when using gcc (for MSVC this is apparently
+// not a problem), so this file is it. The prototypes of these
+// functions are added to wspiapiwrap.h
+
+#include "netport.h"
+
+#if defined(USE_WINSOCK) && defined(__MINGW32__)
+# include <ws2tcpip.h>
+# include <wspiapi.h>
+#endif
+
diff --git a/src/libs/network/wspiapiwrap.h b/src/libs/network/wspiapiwrap.h
new file mode 100644
index 0000000..23361a4
--- /dev/null
+++ b/src/libs/network/wspiapiwrap.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef LIBS_NETWORK_WSPIAPIWRAP_H_
+#define LIBS_NETWORK_WSPIAPIWRAP_H_
+
+// HACK. See wspiapiwrap.c
+# define getaddrinfo WspiapiGetAddrInfo
+# define getnameinfo WspiapiGetNameInfo
+# define freeaddrinfo WspiapiFreeAddrInfo
+void WINAPI WspiapiFreeAddrInfo (struct addrinfo *ai);
+int WINAPI WspiapiGetAddrInfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res);
+int WINAPI WspiapiGetNameInfo (const struct sockaddr *sa, socklen_t salen,
+ char *host, size_t hostlen, char *serv, size_t servlen, int flags);
+
+#endif /* LIBS_NETWORK_WSPIAPIWRAP_H_ */
+
diff --git a/src/libs/platform.h b/src/libs/platform.h
new file mode 100644
index 0000000..f17674d
--- /dev/null
+++ b/src/libs/platform.h
@@ -0,0 +1,57 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PLATFORM_H_
+#define PLATFORM_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(USE_PLATFORM_ACCEL)
+# if defined(__GNUC__) && (defined(i386) || defined(__x86_64__))
+# define MMX_ASM
+# define GCC_ASM
+# elif (_MSC_VER >= 1100) && defined(_M_IX86)
+// All 32bit AMD chips are IX86 equivalents
+// We do not enable MSVC/MMX_ASM for _M_AMD64 as of now
+# define MMX_ASM
+# define MSVC_ASM
+# endif
+// other realistic possibilities for MSVC compiler are
+// _M_AMD64 (AMD x86-64), _M_IA64 (Intel Arch 64)
+#endif
+
+typedef enum
+{
+ PLATFORM_NULL = 0,
+ PLATFORM_C,
+ PLATFORM_MMX,
+ PLATFORM_SSE,
+ PLATFORM_3DNOW,
+ PLATFORM_ALTIVEC,
+
+ PLATFORM_LAST = PLATFORM_ALTIVEC
+
+} PLATFORM_TYPE;
+
+extern PLATFORM_TYPE force_platform;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PLATFORM_H_ */
diff --git a/src/libs/reslib.h b/src/libs/reslib.h
new file mode 100644
index 0000000..0b56262
--- /dev/null
+++ b/src/libs/reslib.h
@@ -0,0 +1,140 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_RESLIB_H_
+#define LIBS_RESLIB_H_
+
+//#include <stdio.h>
+#include "libs/compiler.h"
+#include "port.h"
+#include "libs/memlib.h"
+#include "libs/uio.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct resource_index_desc RESOURCE_INDEX_DESC;
+typedef RESOURCE_INDEX_DESC *RESOURCE_INDEX;
+
+typedef const char *RESOURCE;
+
+typedef union {
+ DWORD num;
+ void *ptr;
+ const char *str;
+} RESOURCE_DATA;
+
+#define NULL_RESOURCE NULL
+
+extern const char *_cur_resfile_name;
+
+typedef void (ResourceLoadFun) (const char *pathname, RESOURCE_DATA *resdata);
+typedef BOOLEAN (ResourceFreeFun) (void *handle);
+typedef void (ResourceStringFun) (RESOURCE_DATA *handle, char *buf, unsigned int size);
+
+typedef void *(ResourceLoadFileFun) (uio_Stream *fp, DWORD len);
+
+void *LoadResourceFromPath(const char *pathname, ResourceLoadFileFun fn);
+
+uio_Stream *res_OpenResFile (uio_DirHandle *dir, const char *filename, const char *mode);
+size_t ReadResFile (void *lpBuf, size_t size, size_t count, uio_Stream *fp);
+size_t WriteResFile (const void *lpBuf, size_t size, size_t count, uio_Stream *fp);
+int GetResFileChar (uio_Stream *fp);
+int PutResFileChar (char ch, uio_Stream *fp);
+int PutResFileNewline (uio_Stream *fp);
+long SeekResFile (uio_Stream *fp, long offset, int whence);
+long TellResFile (uio_Stream *fp);
+size_t LengthResFile (uio_Stream *fp);
+BOOLEAN res_CloseResFile (uio_Stream *fp);
+BOOLEAN DeleteResFile (uio_DirHandle *dir, const char *filename);
+
+RESOURCE_INDEX InitResourceSystem (void);
+void UninitResourceSystem (void);
+BOOLEAN InstallResTypeVectors (const char *res_type, ResourceLoadFun *loadFun, ResourceFreeFun *freeFun, ResourceStringFun *stringFun);
+void *res_GetResource (RESOURCE res);
+void *res_DetachResource (RESOURCE res);
+void res_FreeResource (RESOURCE res);
+COUNT CountResourceTypes (void);
+DWORD res_GetIntResource (RESOURCE res);
+BOOLEAN res_GetBooleanResource (RESOURCE res);
+const char *res_GetResourceType (RESOURCE res);
+
+void LoadResourceIndex (uio_DirHandle *dir, const char *filename, const char *prefix);
+void SaveResourceIndex (uio_DirHandle *dir, const char *rmpfile, const char *root, BOOLEAN strip_root);
+
+void *GetResourceData (uio_Stream *fp, DWORD length);
+
+#define AllocResourceData HMalloc
+BOOLEAN FreeResourceData (void *);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "libs/strlib.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ // For Color
+
+typedef STRING_TABLE DIRENTRY_REF;
+typedef STRING DIRENTRY;
+
+extern DIRENTRY_REF LoadDirEntryTable (uio_DirHandle *dirHandle,
+ const char *path, const char *pattern, match_MatchType matchType);
+#define CaptureDirEntryTable CaptureStringTable
+#define ReleaseDirEntryTable ReleaseStringTable
+#define DestroyDirEntryTable DestroyStringTable
+#define GetDirEntryTableRef GetStringTable
+#define GetDirEntryTableCount GetStringTableCount
+#define GetDirEntryTableIndex GetStringTableIndex
+#define SetAbsDirEntryTableIndex SetAbsStringTableIndex
+#define SetRelDirEntryTableIndex SetRelStringTableIndex
+#define GetDirEntryLength GetStringLengthBin
+#define GetDirEntryAddress GetStringAddress
+
+/* Key-Value resources */
+
+BOOLEAN res_HasKey (const char *key);
+
+BOOLEAN res_IsString (const char *key);
+const char *res_GetString (const char *key);
+void res_PutString (const char *key, const char *value);
+
+BOOLEAN res_IsInteger (const char *key);
+int res_GetInteger (const char *key);
+void res_PutInteger (const char *key, int value);
+
+BOOLEAN res_IsBoolean (const char *key);
+BOOLEAN res_GetBoolean (const char *key);
+void res_PutBoolean (const char *key, BOOLEAN value);
+
+BOOLEAN res_IsColor (const char *key);
+Color res_GetColor (const char *key);
+void res_PutColor (const char *key, Color value);
+
+BOOLEAN res_Remove (const char *key);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_RESLIB_H_ */
diff --git a/src/libs/resource/Makeinfo b/src/libs/resource/Makeinfo
new file mode 100644
index 0000000..ddac8e2
--- /dev/null
+++ b/src/libs/resource/Makeinfo
@@ -0,0 +1,3 @@
+uqm_CFILES="direct.c filecntl.c getres.c loadres.c stringbank.c
+ propfile.c resinit.c"
+uqm_HFILES="index.h propfile.h resintrn.h stringbank.h"
diff --git a/src/libs/resource/direct.c b/src/libs/resource/direct.c
new file mode 100644
index 0000000..b3d3541
--- /dev/null
+++ b/src/libs/resource/direct.c
@@ -0,0 +1,101 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/strings/strintrn.h"
+#include "libs/memlib.h"
+#include "port.h"
+#include "libs/uio.h"
+#include <sys/stat.h>
+
+DIRENTRY_REF
+LoadDirEntryTable (uio_DirHandle *dirHandle, const char *path,
+ const char *pattern, match_MatchType matchType)
+{
+ uio_DirList *dirList;
+ COUNT num_entries;
+ COUNT i;
+ uio_DirHandle *dir;
+ STRING_TABLE StringTable;
+ STRING_TABLE_DESC *lpST;
+ STRING lpLastString;
+
+ dir = uio_openDirRelative (dirHandle, path, 0);
+ assert(dir != NULL);
+ dirList = uio_getDirList (dir, "", pattern, matchType);
+ assert(dirList != NULL);
+ num_entries = 0;
+
+ // First, count the amount of space needed
+ for (i = 0; i < dirList->numNames; i++)
+ {
+ struct stat sb;
+
+ if (dirList->names[i][0] == '.')
+ {
+ dirList->names[i] = NULL;
+ continue;
+ }
+ if (uio_stat (dir, dirList->names[i], &sb) == -1)
+ {
+ dirList->names[i] = NULL;
+ continue;
+ }
+ if (!S_ISREG (sb.st_mode))
+ {
+ dirList->names[i] = NULL;
+ continue;
+ }
+ num_entries++;
+ }
+ uio_closeDir (dir);
+
+ if (num_entries == 0) {
+ uio_DirList_free(dirList);
+ return ((DIRENTRY_REF) 0);
+ }
+
+ StringTable = AllocStringTable (num_entries, 0);
+ lpST = StringTable;
+ if (lpST == 0)
+ {
+ FreeStringTable (StringTable);
+ uio_DirList_free(dirList);
+ return ((DIRENTRY_REF) 0);
+ }
+ lpST->size = num_entries;
+ lpLastString = lpST->strings;
+
+ for (i = 0; i < dirList->numNames; i++)
+ {
+ int size;
+ STRINGPTR target;
+ if (dirList->names[i] == NULL)
+ continue;
+ size = strlen (dirList->names[i]) + 1;
+ target = HMalloc (size);
+ memcpy (target, dirList->names[i], size);
+ lpLastString->data = target;
+ lpLastString->length = size;
+ lpLastString++;
+ }
+
+ uio_DirList_free(dirList);
+ return StringTable;
+}
+
+
diff --git a/src/libs/resource/filecntl.c b/src/libs/resource/filecntl.c
new file mode 100644
index 0000000..e2a81d9
--- /dev/null
+++ b/src/libs/resource/filecntl.c
@@ -0,0 +1,146 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef WIN32
+#include <io.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "port.h"
+#include "resintrn.h"
+#include "libs/uio.h"
+
+uio_Stream *
+res_OpenResFile (uio_DirHandle *dir, const char *filename, const char *mode)
+{
+ uio_Stream *fp;
+ struct stat sb;
+
+ if (uio_stat (dir, filename, &sb) == 0 && S_ISDIR(sb.st_mode))
+ return ((uio_Stream *) ~0);
+
+ fp = uio_fopen (dir, filename, mode);
+
+ return (fp);
+}
+
+BOOLEAN
+res_CloseResFile (uio_Stream *fp)
+{
+ if (fp)
+ {
+ if (fp != (uio_Stream *)~0)
+ uio_fclose (fp);
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+BOOLEAN
+DeleteResFile (uio_DirHandle *dir, const char *filename)
+{
+ return (uio_unlink (dir, filename) == 0);
+}
+
+size_t
+ReadResFile (void *lpBuf, size_t size, size_t count, uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_fread (lpBuf, size, count, fp);
+
+ return (retval);
+}
+
+size_t
+WriteResFile (const void *lpBuf, size_t size, size_t count, uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_fwrite (lpBuf, size, count, fp);
+
+ return (retval);
+}
+
+int
+GetResFileChar (uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_getc (fp);
+
+ return (retval);
+}
+
+int
+PutResFileChar (char ch, uio_Stream *fp)
+{
+ int retval;
+
+ retval = uio_putc (ch, fp);
+ return (retval);
+}
+
+int
+PutResFileNewline (uio_Stream *fp)
+{
+ int retval;
+
+#ifdef WIN32
+ PutResFileChar ('\r', fp);
+#endif
+ retval = PutResFileChar ('\n', fp);
+ return (retval);
+}
+
+long
+SeekResFile (uio_Stream *fp, long offset, int whence)
+{
+ long retval;
+
+ retval = uio_fseek (fp, offset, whence);
+
+ return (retval);
+}
+
+long
+TellResFile (uio_Stream *fp)
+{
+ long retval;
+
+ retval = uio_ftell (fp);
+
+ return (retval);
+}
+
+size_t
+LengthResFile (uio_Stream *fp)
+{
+ struct stat sb;
+
+ if (fp == (uio_Stream *)~0)
+ return (1);
+ if (uio_fstat(uio_streamHandle(fp), &sb) == -1)
+ return 1;
+ return sb.st_size;
+}
+
+
diff --git a/src/libs/resource/getres.c b/src/libs/resource/getres.c
new file mode 100644
index 0000000..39e24a9
--- /dev/null
+++ b/src/libs/resource/getres.c
@@ -0,0 +1,257 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "options.h"
+#include "port.h"
+#include "resintrn.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+#include "libs/uio/charhashtable.h"
+
+const char *_cur_resfile_name;
+// When a file is being loaded, _cur_resfile_name is set to its name.
+// At other times, it is NULL.
+
+ResourceDesc *
+lookupResourceDesc (RESOURCE_INDEX idx, RESOURCE res)
+{
+ return (ResourceDesc *) CharHashTable_find (idx->map, res);
+}
+
+void
+loadResourceDesc (ResourceDesc *desc)
+{
+ desc->vtable->loadFun (desc->fname, &desc->resdata);
+}
+
+void *
+LoadResourceFromPath (const char *path, ResourceLoadFileFun *loadFun)
+{
+ uio_Stream *stream;
+ unsigned long dataLen;
+ void *resdata;
+
+ stream = res_OpenResFile (contentDir, path, "rb");
+ if (stream == NULL)
+ {
+ log_add (log_Warning, "Warning: Can't open '%s'", path);
+ return NULL;
+ }
+
+ dataLen = LengthResFile (stream);
+ log_add (log_Info, "\t'%s' -- %lu bytes", path, dataLen);
+
+ if (dataLen == 0)
+ {
+ log_add (log_Warning, "Warning: Trying to load empty file '%s'.", path);
+ goto err;
+ }
+
+ _cur_resfile_name = path;
+ resdata = (*loadFun) (stream, dataLen);
+ _cur_resfile_name = NULL;
+ res_CloseResFile (stream);
+
+ return resdata;
+
+err:
+ res_CloseResFile (stream);
+ return NULL;
+}
+
+const char *
+res_GetResourceType (RESOURCE res)
+{
+ RESOURCE_INDEX resourceIndex;
+ ResourceDesc *desc;
+
+ if (res == NULL_RESOURCE)
+ {
+ log_add (log_Warning, "Trying to get type of null resource");
+ return NULL;
+ }
+
+ resourceIndex = _get_current_index_header ();
+ desc = lookupResourceDesc (resourceIndex, res);
+ if (desc == NULL)
+ {
+ log_add (log_Warning, "Trying to get type of undefined resource '%s'",
+ res);
+ return NULL;
+ }
+
+ return desc->vtable->resType;
+}
+
+
+// Get a resource by its resource ID.
+void *
+res_GetResource (RESOURCE res)
+{
+ RESOURCE_INDEX resourceIndex;
+ ResourceDesc *desc;
+
+ if (res == NULL_RESOURCE)
+ {
+ log_add (log_Warning, "Trying to get null resource");
+ return NULL;
+ }
+
+ resourceIndex = _get_current_index_header ();
+
+ desc = lookupResourceDesc (resourceIndex, res);
+ if (desc == NULL)
+ {
+ log_add (log_Warning, "Trying to get undefined resource '%s'",
+ res);
+ return NULL;
+ }
+
+ if (desc->resdata.ptr == NULL)
+ loadResourceDesc (desc);
+ if (desc->resdata.ptr != NULL)
+ ++desc->refcount;
+
+ return desc->resdata.ptr;
+ // May still be NULL, if the load failed.
+}
+
+DWORD
+res_GetIntResource (RESOURCE res)
+{
+ RESOURCE_INDEX resourceIndex;
+ ResourceDesc *desc;
+
+ if (res == NULL_RESOURCE)
+ {
+ log_add (log_Warning, "Trying to get null resource");
+ return 0;
+ }
+
+ resourceIndex = _get_current_index_header ();
+
+ desc = lookupResourceDesc (resourceIndex, res);
+ if (desc == NULL)
+ {
+ log_add (log_Warning, "Trying to get undefined resource '%s'",
+ res);
+ return 0;
+ }
+
+ return desc->resdata.num;
+}
+
+BOOLEAN
+res_GetBooleanResource (RESOURCE res)
+{
+ return (res_GetIntResource (res) != 0);
+}
+
+// NB: this function appears to be never called!
+void
+res_FreeResource (RESOURCE res)
+{
+ ResourceDesc *desc;
+ ResourceFreeFun *freeFun;
+
+ desc = lookupResourceDesc (_get_current_index_header(), res);
+ if (desc == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to free an unrecognised "
+ "resource.");
+ return;
+ }
+
+ if (desc->refcount > 0)
+ --desc->refcount;
+ else
+ log_add (log_Debug, "Warning: freeing an unreferenced resource.");
+ if (desc->refcount > 0)
+ return; // Still references left
+
+ freeFun = desc->vtable->freeFun;
+ if (freeFun == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to free a non-heap resource.");
+ return;
+ }
+
+ if (desc->resdata.ptr == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to free not loaded "
+ "resource.");
+ return;
+ }
+
+ (*freeFun) (desc->resdata.ptr);
+ desc->resdata.ptr = NULL;
+}
+
+// By calling this function the caller will be responsible of unloading
+// the resource. If res_GetResource() get called again for this
+// resource, a NEW copy will be loaded, regardless of whether a detached
+// copy still exists.
+void *
+res_DetachResource (RESOURCE res)
+{
+ ResourceDesc *desc;
+ ResourceFreeFun *freeFun;
+ void *result;
+
+ desc = lookupResourceDesc (_get_current_index_header(), res);
+ if (desc == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to detach from an unrecognised "
+ "resource.");
+ return NULL;
+ }
+
+ freeFun = desc->vtable->freeFun;
+ if (freeFun == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to detach from a non-heap resource.");
+ return NULL;
+ }
+
+ if (desc->resdata.ptr == NULL)
+ {
+ log_add (log_Debug, "Warning: trying to detach from a not loaded "
+ "resource.");
+ return NULL;
+ }
+
+ if (desc->refcount > 1)
+ {
+ log_add (log_Debug, "Warning: trying to detach a resource referenced "
+ "%u times", desc->refcount);
+ return NULL;
+ }
+
+ result = desc->resdata.ptr;
+ desc->resdata.ptr = NULL;
+ desc->refcount = 0;
+
+ return result;
+}
+
+BOOLEAN
+FreeResourceData (void *data)
+{
+ HFree (data);
+ return TRUE;
+}
diff --git a/src/libs/resource/index.h b/src/libs/resource/index.h
new file mode 100644
index 0000000..bdbb162
--- /dev/null
+++ b/src/libs/resource/index.h
@@ -0,0 +1,54 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_RESOURCE_INDEX_H_
+#define LIBS_RESOURCE_INDEX_H_
+
+typedef struct resource_handlers ResourceHandlers;
+typedef struct resource_desc ResourceDesc;
+
+#include <stdio.h>
+#include "libs/reslib.h"
+#include "libs/uio/charhashtable.h"
+
+struct resource_handlers
+{
+ const char *resType;
+ ResourceLoadFun *loadFun;
+ ResourceFreeFun *freeFun;
+ ResourceStringFun *toString;
+};
+
+struct resource_desc
+{
+ RESOURCE res_id;
+ char *fname;
+ ResourceHandlers *vtable;
+ RESOURCE_DATA resdata;
+ // refcount is rudimentary as nothing really frees the descriptors
+ unsigned refcount;
+};
+
+struct resource_index_desc
+{
+ CharHashTable_HashTable *map;
+ size_t numRes;
+};
+
+#endif /* LIBS_RESOURCE_INDEX_H_ */
+
diff --git a/src/libs/resource/loadres.c b/src/libs/resource/loadres.c
new file mode 100644
index 0000000..a9849e4
--- /dev/null
+++ b/src/libs/resource/loadres.c
@@ -0,0 +1,54 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "resintrn.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+
+
+void *
+GetResourceData (uio_Stream *fp, DWORD length)
+{
+ void *result;
+ DWORD compLen;
+
+ // Resource data used to be prefixed by its length in package files.
+ // A valid length prefix indicated compressed data, and
+ // a length prefix ~0 meant uncompressed.
+ // Currently, .ct and .xlt files still carry a ~0 length prefix.
+ if (ReadResFile (&compLen, sizeof (compLen), 1, fp) != 1)
+ return NULL;
+ if (compLen != ~(DWORD)0)
+ {
+ log_add (log_Warning, "LZ-compressed binary data not supported");
+ return NULL;
+ }
+ length -= sizeof (DWORD);
+
+ result = AllocResourceData (length);
+ if (!result)
+ return NULL;
+
+ if (ReadResFile (result, 1, length, fp) != length)
+ {
+ FreeResourceData (result);
+ result = NULL;
+ }
+
+ return result;
+}
diff --git a/src/libs/resource/propfile.c b/src/libs/resource/propfile.c
new file mode 100644
index 0000000..1784600
--- /dev/null
+++ b/src/libs/resource/propfile.c
@@ -0,0 +1,129 @@
+/* propfile.c, Copyright (c) 2008 Michael C. Martin */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "libs/log.h"
+#include "propfile.h"
+#include "libs/reslib.h"
+
+void
+PropFile_from_string (char *d, PROPERTY_HANDLER handler, const char *prefix)
+{
+ int len, i;
+
+ len = strlen(d);
+ i = 0;
+ while (i < len) {
+ int key_start, key_end, value_start, value_end;
+ /* Starting a line: search for non-whitespace */
+ while ((i < len) && isspace (d[i])) i++;
+ if (i >= len) break; /* Done parsing! */
+ /* If it was a comment, skip to end of comment/file */
+ if (d[i] == '#') {
+ while ((i < len) && (d[i] != '\n')) i++;
+ if (i >= len) break;
+ continue; /* Back to keyword search */
+ }
+ key_start = i;
+ /* Find the = on this line */
+ while ((i < len) && (d[i] != '=') &&
+ (d[i] != '\n') && (d[i] != '#')) i++;
+ if (i >= len) { /* Bare key at EOF */
+ log_add (log_Warning, "Warning: Bare keyword at EOF");
+ break;
+ }
+ /* Comments here mean incomplete line too */
+ if (d[i] != '=') {
+ log_add (log_Warning, "Warning: Key without value");
+ while ((i < len) && (d[i] != '\n')) i++;
+ if (i >= len) break;
+ continue; /* Back to keyword search */
+ }
+ /* Key ends at first whitespace before = , or at key_start*/
+ key_end = i;
+ while ((key_end > key_start) && isspace (d[key_end-1]))
+ key_end--;
+
+ /* Consume the = */
+ i++;
+ /* Value starts at first non-whitespace after = on line... */
+ while ((i < len) && (d[i] != '#') && (d[i] != '\n') &&
+ isspace (d[i]))
+ i++;
+ value_start = i;
+ /* Until first non-whitespace before terminator */
+ while ((i < len) && (d[i] != '#') && (d[i] != '\n'))
+ i++;
+ value_end = i;
+ while ((value_end > value_start) && isspace (d[value_end-1]))
+ value_end--;
+ /* Skip past EOL or EOF */
+ while ((i < len) && (d[i] != '\n'))
+ i++;
+ i++;
+
+ /* We now have start and end values for key and value.
+ We terminate the strings for both by writing \0s, then
+ make a new map entry. */
+ d[key_end] = '\0';
+ d[value_end] = '\0';
+ if (prefix) {
+ char buf[256];
+ snprintf(buf, 255, "%s%s", prefix, d+key_start);
+ buf[255]=0;
+ handler(buf, d+value_start);
+ } else {
+ handler (d+key_start, d+value_start);
+ }
+ }
+}
+
+void
+PropFile_from_file (uio_Stream *f, PROPERTY_HANDLER handler, const char *prefix)
+{
+ size_t flen;
+ char *data;
+
+ flen = LengthResFile (f);
+
+ data = malloc (flen + 1);
+ if (!data) {
+ return;
+ }
+
+ // We may end up with less bytes than we asked for due to the
+ // DOS->Unix newline conversion
+ flen = ReadResFile (data, 1, flen, f);
+ data[flen] = '\0';
+
+ PropFile_from_string (data, handler, prefix);
+ free (data);
+}
+
+void
+PropFile_from_filename (uio_DirHandle *path, const char *fname, PROPERTY_HANDLER handler, const char *prefix)
+{
+ uio_Stream *f = res_OpenResFile (path, fname, "rt");
+ if (!f) {
+ return;
+ }
+ PropFile_from_file (f, handler, prefix);
+ res_CloseResFile(f);
+}
diff --git a/src/libs/resource/propfile.h b/src/libs/resource/propfile.h
new file mode 100644
index 0000000..edc8c36
--- /dev/null
+++ b/src/libs/resource/propfile.h
@@ -0,0 +1,30 @@
+/* propfile.h, Copyright (c) 2008 Michael C. Martin */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PROPFILE_H_
+#define PROPFILE_H_
+
+#include "libs/uio.h"
+
+typedef void (*PROPERTY_HANDLER) (const char *, const char *);
+
+void PropFile_from_string (char *d, PROPERTY_HANDLER handler, const char *prefix);
+void PropFile_from_file (uio_Stream *f, PROPERTY_HANDLER handler, const char *prefix);
+void PropFile_from_filename (uio_DirHandle *path, const char *fname, PROPERTY_HANDLER handler, const char *prefix);
+
+#endif
diff --git a/src/libs/resource/resinit.c b/src/libs/resource/resinit.c
new file mode 100644
index 0000000..dacbee4
--- /dev/null
+++ b/src/libs/resource/resinit.c
@@ -0,0 +1,651 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "resintrn.h"
+#include "libs/memlib.h"
+#include "options.h"
+#include "types.h"
+#include "libs/log.h"
+#include "libs/gfxlib.h"
+#include "libs/reslib.h"
+#include "libs/sndlib.h"
+#include "libs/vidlib.h"
+#include "propfile.h"
+#include <ctype.h>
+#include <stdlib.h>
+// XXX: we should not include anything from uqm/ inside libs/
+#include "uqm/coderes.h"
+
+static RESOURCE_INDEX
+allocResourceIndex (void) {
+ RESOURCE_INDEX ndx = HMalloc (sizeof (RESOURCE_INDEX_DESC));
+ ndx->map = CharHashTable_newHashTable (NULL, NULL, NULL, NULL, NULL,
+ 0, 0.85, 0.9);
+ return ndx;
+}
+
+static void
+freeResourceIndex (RESOURCE_INDEX h) {
+ if (h != NULL)
+ {
+ /* TODO: This leaks the contents of h->map */
+ CharHashTable_deleteHashTable (h->map);
+ HFree (h);
+ }
+}
+
+#define TYPESIZ 32
+
+static ResourceDesc *
+newResourceDesc (const char *res_id, const char *resval)
+{
+ const char *path;
+ int pathlen;
+ ResourceHandlers *vtable;
+ ResourceDesc *result, *handlerdesc;
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ char typestr[TYPESIZ];
+
+ path = strchr (resval, ':');
+ if (path == NULL)
+ {
+ log_add (log_Warning, "Could not find type information for resource '%s'", res_id);
+ strncpy(typestr, "sys.UNKNOWNRES", TYPESIZ);
+ path = resval;
+ }
+ else
+ {
+ int n = path - resval;
+
+ if (n >= TYPESIZ - 4)
+ {
+ n = TYPESIZ - 5;
+ }
+ strncpy (typestr, "sys.", TYPESIZ);
+ strncat (typestr+1, resval, n);
+ typestr[n+4] = '\0';
+ path++;
+ }
+ pathlen = strlen (path);
+
+ handlerdesc = lookupResourceDesc(idx, typestr);
+ if (handlerdesc == NULL) {
+ path = resval;
+ log_add (log_Warning, "Illegal type '%s' for resource '%s'; treating as UNKNOWNRES", typestr, res_id);
+ handlerdesc = lookupResourceDesc(idx, "sys.UNKNOWNRES");
+ }
+
+ vtable = (ResourceHandlers *)handlerdesc->resdata.ptr;
+
+ if (vtable->loadFun == NULL)
+ {
+ log_add (log_Warning, "Warning: Unable to load '%s'; no handler "
+ "for type %s defined.", res_id, typestr);
+ return NULL;
+ }
+
+ result = HMalloc (sizeof (ResourceDesc));
+ if (result == NULL)
+ return NULL;
+
+ result->fname = HMalloc (pathlen + 1);
+ strncpy (result->fname, path, pathlen);
+ result->fname[pathlen] = '\0';
+ result->vtable = vtable;
+ result->refcount = 0;
+
+ if (vtable->freeFun == NULL)
+ {
+ /* Non-heap resources are raw values. Work those out at load time. */
+ vtable->loadFun (result->fname, &result->resdata);
+ }
+ else
+ {
+ result->resdata.ptr = NULL;
+ }
+ return result;
+}
+
+static void
+process_resource_desc (const char *key, const char *value)
+{
+ CharHashTable_HashTable *map = _get_current_index_header ()->map;
+ ResourceDesc *newDesc = newResourceDesc (key, value);
+ if (newDesc != NULL)
+ {
+ if (!CharHashTable_add (map, key, newDesc))
+ {
+ res_Remove (key);
+ CharHashTable_add (map, key, newDesc);
+ }
+ }
+}
+
+static void
+UseDescriptorAsRes (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ resdata->str = descriptor;
+}
+
+static void
+DescriptorToInt (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ resdata->num = atoi (descriptor);
+}
+
+static void
+DescriptorToBoolean (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ if (!strcasecmp (descriptor, "true"))
+ {
+ resdata->num = TRUE;
+ }
+ else
+ {
+ resdata->num = FALSE;
+ }
+}
+
+static inline size_t
+skipWhiteSpace (const char *start)
+{
+ const char *ptr = start;
+ while (isspace (*ptr))
+ ptr++;
+ return (ptr - start);
+}
+
+// On success, resdata->num will be filled with a 32-bits RGBA value.
+static void
+DescriptorToColor (const char *descriptor, RESOURCE_DATA *resdata)
+{
+ int bytesParsed;
+ int componentBits;
+ int maxComponentValue;
+ size_t componentCount;
+ size_t compI;
+ int comps[4];
+ // One element for each of r, g, b, a.
+
+ descriptor += skipWhiteSpace (descriptor);
+
+#if 0
+ // Can't use this; '#' starts a comment.
+ if (*descriptor == '#')
+ {
+ // "#rrggbb"
+ int i;
+ DWORD value = 0;
+
+ descriptor++;
+ for (i = 0; i < 6; i++)
+ {
+ BYTE nibbleValue;
+ if (*descriptor >= '0' && *descriptor <= '9')
+ nibbleValue = *descriptor - '0';
+ else if (*descriptor >= 'a' && *descriptor <= 'f')
+ nibbleValue = 0xa + *descriptor - 'a';
+ else if (*descriptor >= 'A' && *descriptor <= 'F')
+ nibbleValue = 0xa + *descriptor - 'A';
+ else
+ goto fail;
+
+ value = (value * 16) + nibbleValue;
+ descriptor++;
+ }
+
+ descriptor += skipWhiteSpace (descriptor);
+
+ if (*descriptor != '\0')
+ log_add (log_Warning, "Junk after color resource string.");
+
+ resdata->num = (value << 8) | 0xff;
+ return;
+ }
+#endif
+
+ // Color is of the form "rgb(r, g, b)", "rgba(r, g, b, a)",
+ // or "rgb15(r, g, b)".
+
+ if (sscanf (descriptor, "rgb ( %i , %i , %i ) %n",
+ &comps[0], &comps[1], &comps[2], &bytesParsed) >= 3)
+ {
+ componentBits = 8;
+ componentCount = 3;
+ comps[3] = 0xff;
+ }
+ else if (sscanf (descriptor, "rgba ( %i , %i , %i , %i ) %n",
+ &comps[0], &comps[1], &comps[2], &comps[3], &bytesParsed) >= 4)
+ {
+ componentBits = 8;
+ componentCount = 4;
+ }
+ else if (sscanf (descriptor, "rgb15 ( %i , %i , %i ) %n",
+ &comps[0], &comps[1], &comps[2], &bytesParsed) >= 3)
+ {
+ componentBits = 5;
+ componentCount = 3;
+ comps[3] = 0xff;
+ }
+ else
+ goto fail;
+
+ if (descriptor[bytesParsed] != '\0')
+ log_add (log_Warning, "Junk after color resource string.");
+
+ maxComponentValue = (1 << componentBits) - 1;
+
+ // Check the range of the components.
+ for (compI = 0; compI < componentCount; compI++)
+ {
+ if (comps[compI] < 0)
+ {
+ comps[compI] = 0;
+ log_add (log_Warning, "Color component value too small; "
+ "value clipped.");
+ }
+
+ if (comps[compI] > (long) maxComponentValue)
+ {
+ comps[compI] = maxComponentValue;
+ log_add (log_Warning, "Color component value too large; "
+ "value clipped.");
+ }
+ }
+
+ if (componentBits == 5)
+ resdata->num = ((CC5TO8 (comps[0]) << 24) |
+ (CC5TO8 (comps[1]) << 16) | (CC5TO8 (comps[2]) << 8) |
+ comps[3]);
+ else
+ resdata->num = ((comps[0] << 24) | (comps[1] << 16) |
+ (comps[2] << 8) | comps[3]);
+
+ return;
+
+fail:
+ log_add (log_Error, "Invalid color description string for resource.\n");
+ resdata->num = 0x00000000;
+}
+
+static void
+RawDescriptor (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ snprintf (buf, size, "%s", resdata->str);
+}
+
+static void
+IntToString (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ snprintf (buf, size, "%d", resdata->num);
+}
+
+
+static void
+BooleanToString (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ snprintf (buf, size, "%s", resdata->num ? "true" : "false");
+}
+
+static void
+ColorToString (RESOURCE_DATA *resdata, char *buf, unsigned int size)
+{
+ if ((resdata->num & 0xff) == 0xff)
+ {
+ // Opaque color, save as "rgb".
+ snprintf (buf, size, "rgb(0x%02x, 0x%02x, 0x%02x)",
+ (resdata->num >> 24), (resdata->num >> 16) & 0xff,
+ (resdata->num >> 8) & 0xff);
+ }
+ else
+ {
+ // (Partially) transparent color, save as "rgba".
+ snprintf (buf, size, "rgba(0x%02x, 0x%02x, 0x%02x, 0x%02x)",
+ (resdata->num >> 24), (resdata->num >> 16) & 0xff,
+ (resdata->num >> 8) & 0xff, resdata->num & 0xff);
+ }
+}
+
+static RESOURCE_INDEX curResourceIndex;
+
+void
+_set_current_index_header (RESOURCE_INDEX newResourceIndex)
+{
+ curResourceIndex = newResourceIndex;
+}
+
+RESOURCE_INDEX
+InitResourceSystem (void)
+{
+ RESOURCE_INDEX ndx;
+ if (curResourceIndex) {
+ return curResourceIndex;
+ }
+ ndx = allocResourceIndex ();
+
+ _set_current_index_header (ndx);
+
+ InstallResTypeVectors ("UNKNOWNRES", UseDescriptorAsRes, NULL, NULL);
+ InstallResTypeVectors ("STRING", UseDescriptorAsRes, NULL, RawDescriptor);
+ InstallResTypeVectors ("INT32", DescriptorToInt, NULL, IntToString);
+ InstallResTypeVectors ("BOOLEAN", DescriptorToBoolean, NULL,
+ BooleanToString);
+ InstallResTypeVectors ("COLOR", DescriptorToColor, NULL, ColorToString);
+ InstallGraphicResTypes ();
+ InstallStringTableResType ();
+ InstallAudioResTypes ();
+ InstallVideoResType ();
+ InstallCodeResType ();
+
+ return ndx;
+}
+
+RESOURCE_INDEX
+_get_current_index_header (void)
+{
+ if (!curResourceIndex) {
+ InitResourceSystem ();
+ }
+ return curResourceIndex;
+}
+
+void
+LoadResourceIndex (uio_DirHandle *dir, const char *rmpfile, const char *prefix)
+{
+ PropFile_from_filename (dir, rmpfile, process_resource_desc, prefix);
+}
+
+void
+SaveResourceIndex (uio_DirHandle *dir, const char *rmpfile, const char *root, BOOLEAN strip_root)
+{
+ uio_Stream *f;
+ CharHashTable_Iterator *it;
+ unsigned int prefix_len;
+
+ f = res_OpenResFile (dir, rmpfile, "wb");
+ if (!f) {
+ /* TODO: Warning message */
+ return;
+ }
+ prefix_len = root ? strlen (root) : 0;
+ for (it = CharHashTable_getIterator (_get_current_index_header ()->map);
+ !CharHashTable_iteratorDone (it);
+ it = CharHashTable_iteratorNext (it)) {
+ char *key = CharHashTable_iteratorKey (it);
+ if (!root || !strncmp (root, key, prefix_len)) {
+ ResourceDesc *value = CharHashTable_iteratorValue (it);
+ if (!value) {
+ log_add(log_Warning, "Resource %s had no value", key);
+ } else if (!value->vtable) {
+ log_add(log_Warning, "Resource %s had no type", key);
+ } else if (value->vtable->toString) {
+ char buf[256];
+ value->vtable->toString (&value->resdata, buf, 256);
+ buf[255]=0;
+ if (root && strip_root) {
+ WriteResFile (key+prefix_len, 1, strlen (key) - prefix_len, f);
+ } else {
+ WriteResFile (key, 1, strlen (key), f);
+ }
+ PutResFileChar(' ', f);
+ PutResFileChar('=', f);
+ PutResFileChar(' ', f);
+ WriteResFile (value->vtable->resType, 1, strlen (value->vtable->resType), f);
+ PutResFileChar(':', f);
+ WriteResFile (buf, 1, strlen (buf), f);
+ PutResFileNewline(f);
+ }
+ }
+ }
+ res_CloseResFile (f);
+ CharHashTable_freeIterator (it);
+}
+
+void
+UninitResourceSystem (void)
+{
+ freeResourceIndex (_get_current_index_header ());
+ _set_current_index_header (NULL);
+}
+
+BOOLEAN
+InstallResTypeVectors (const char *resType, ResourceLoadFun *loadFun,
+ ResourceFreeFun *freeFun, ResourceStringFun *stringFun)
+{
+ ResourceHandlers *handlers;
+ ResourceDesc *result;
+ char key[TYPESIZ];
+ int typelen;
+ CharHashTable_HashTable *map;
+
+ snprintf(key, TYPESIZ, "sys.%s", resType);
+ key[TYPESIZ-1] = '\0';
+ typelen = strlen(resType);
+
+ handlers = HMalloc (sizeof (ResourceHandlers));
+ if (handlers == NULL)
+ {
+ return FALSE;
+ }
+ handlers->loadFun = loadFun;
+ handlers->freeFun = freeFun;
+ handlers->toString = stringFun;
+ handlers->resType = resType;
+
+ result = HMalloc (sizeof (ResourceDesc));
+ if (result == NULL)
+ return FALSE;
+
+ result->fname = HMalloc (strlen(resType) + 1);
+ strncpy (result->fname, resType, typelen);
+ result->fname[typelen] = '\0';
+ result->vtable = NULL;
+ result->resdata.ptr = handlers;
+
+ map = _get_current_index_header ()->map;
+ return CharHashTable_add (map, key, result) != 0;
+}
+
+/* These replace the mapres.c calls and probably should be split out at some point. */
+BOOLEAN
+res_IsString (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "STRING");
+}
+
+const char *
+res_GetString (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || !desc->resdata.str || strcmp(desc->vtable->resType, "STRING"))
+ return "";
+ /* TODO: Work out exact STRING semantics, specifically, the lifetime of
+ * the returned value. If caller is allowed to reference the returned
+ * value forever, STRING has to be ref-counted. */
+ return desc->resdata.str;
+}
+
+void
+res_PutString (const char *key, const char *value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ int srclen, dstlen;
+ if (!desc || !desc->resdata.str || strcmp(desc->vtable->resType, "STRING"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring newResourceDesc */
+ process_resource_desc(key, "STRING:undefined");
+ desc = lookupResourceDesc (idx, key);
+ }
+ srclen = strlen (value);
+ dstlen = strlen (desc->fname);
+ if (srclen > dstlen) {
+ char *newValue = HMalloc(srclen + 1);
+ char *oldValue = desc->fname;
+ log_add(log_Warning, "Reallocating string space for '%s'", key);
+ strncpy (newValue, value, srclen + 1);
+ desc->resdata.str = newValue;
+ desc->fname = newValue;
+ HFree (oldValue);
+ } else {
+ strncpy (desc->fname, value, dstlen + 1);
+ }
+}
+
+BOOLEAN
+res_IsInteger (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "INT32");
+}
+
+int
+res_GetInteger (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "INT32"))
+ {
+ // TODO: Better error handling
+ return 0;
+ }
+ return desc->resdata.num;
+}
+
+void
+res_PutInteger (const char *key, int value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "INT32"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring newResourceDesc */
+ process_resource_desc(key, "INT32:0");
+ desc = lookupResourceDesc (idx, key);
+ }
+ desc->resdata.num = value;
+}
+
+BOOLEAN
+res_IsBoolean (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "BOOLEAN");
+}
+
+BOOLEAN
+res_GetBoolean (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "BOOLEAN"))
+ {
+ // TODO: Better error handling
+ return FALSE;
+ }
+ return desc->resdata.num ? TRUE : FALSE;
+}
+
+void
+res_PutBoolean (const char *key, BOOLEAN value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "BOOLEAN"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring newResourceDesc */
+ process_resource_desc(key, "BOOLEAN:false");
+ desc = lookupResourceDesc (idx, key);
+ }
+ desc->resdata.num = value;
+}
+
+BOOLEAN
+res_IsColor (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ return desc && !strcmp(desc->vtable->resType, "COLOR");
+}
+
+Color
+res_GetColor (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ DWORD num;
+ if (!desc || strcmp(desc->vtable->resType, "COLOR"))
+ {
+ // TODO: Better error handling
+ return buildColorRgba (0, 0, 0, 0);
+ }
+
+ num = desc->resdata.num;
+ return buildColorRgba (num >> 24, (num >> 16) & 0xff,
+ (desc->resdata.num >> 8) & 0xff, num & 0xff);
+}
+
+void
+res_PutColor (const char *key, Color value)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ ResourceDesc *desc = lookupResourceDesc (idx, key);
+ if (!desc || strcmp(desc->vtable->resType, "COLOR"))
+ {
+ /* TODO: This is kind of roundabout. We can do better by refactoring
+ * newResourceDesc */
+ process_resource_desc(key, "COLOR:rgb(0, 0, 0)");
+ desc = lookupResourceDesc (idx, key);
+ }
+ desc->resdata.num =
+ (value.r << 24) | (value.g << 16) | (value.b << 8) | value.a;
+}
+
+BOOLEAN
+res_HasKey (const char *key)
+{
+ RESOURCE_INDEX idx = _get_current_index_header ();
+ return (lookupResourceDesc(idx, key) != NULL);
+}
+
+BOOLEAN
+res_Remove (const char *key)
+{
+ CharHashTable_HashTable *map = _get_current_index_header ()->map;
+ ResourceDesc *oldDesc = (ResourceDesc *)CharHashTable_find (map, key);
+ if (oldDesc != NULL)
+ {
+ if (oldDesc->resdata.ptr != NULL)
+ {
+ if (oldDesc->refcount > 0)
+ log_add (log_Warning, "WARNING: Replacing '%s' while it is live", key);
+ if (oldDesc->vtable && oldDesc->vtable->freeFun)
+ {
+ oldDesc->vtable->freeFun(oldDesc->resdata.ptr);
+ }
+ }
+ HFree (oldDesc->fname);
+ HFree (oldDesc);
+ }
+ return CharHashTable_remove (map, key);
+}
diff --git a/src/libs/resource/resintrn.h b/src/libs/resource/resintrn.h
new file mode 100644
index 0000000..e2255ea
--- /dev/null
+++ b/src/libs/resource/resintrn.h
@@ -0,0 +1,34 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_RESOURCE_RESINTRN_H_
+#define LIBS_RESOURCE_RESINTRN_H_
+
+#include <string.h>
+#include "libs/reslib.h"
+#include "index.h"
+
+ResourceDesc *lookupResourceDesc (RESOURCE_INDEX idx, RESOURCE res);
+void loadResourceDesc (ResourceDesc *desc);
+
+void _set_current_index_header (RESOURCE_INDEX newResourceIndex);
+RESOURCE_INDEX _get_current_index_header (void);
+
+
+#endif /* LIBS_RESOURCE_RESINTRN_H_ */
+
diff --git a/src/libs/resource/stringbank.c b/src/libs/resource/stringbank.c
new file mode 100644
index 0000000..a1b9576
--- /dev/null
+++ b/src/libs/resource/stringbank.c
@@ -0,0 +1,181 @@
+/* stringbank.c, Copyright (c) 2005 Michael C. Martin */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "stringbank.h"
+
+typedef stringbank chunk;
+
+static stringbank *
+add_chunk (stringbank *bank)
+{
+ stringbank *n = malloc (sizeof (stringbank));
+ n->len = 0;
+ n->next = NULL;
+ if (bank)
+ {
+ while (bank->next)
+ bank = bank->next;
+ bank->next = n;
+ }
+ return n;
+}
+
+stringbank *
+StringBank_Create (void)
+{
+ return add_chunk (NULL);
+}
+
+void
+StringBank_Free (stringbank *bank)
+{
+ if (bank)
+ {
+ StringBank_Free (bank->next);
+ free (bank);
+ }
+}
+
+const char *
+StringBank_AddString (stringbank *bank, const char *str)
+{
+ unsigned int len = strlen (str) + 1;
+ stringbank *x = bank;
+ if (len > STRBANK_CHUNK_SIZE)
+ return NULL;
+ while (x) {
+ unsigned int remaining = STRBANK_CHUNK_SIZE - x->len;
+ if (len < remaining) {
+ char *result = x->data + x->len;
+ strcpy (result, str);
+ x->len += len;
+ return result;
+ }
+ x = x->next;
+ }
+ /* No room in any currently existing chunk */
+ x = add_chunk (bank);
+ strcpy (x->data, str);
+ x->len += len;
+ return x->data;
+}
+
+const char *
+StringBank_AddOrFindString (stringbank *bank, const char *str)
+{
+ unsigned int len = strlen (str) + 1;
+ stringbank *x = bank;
+ if (len > STRBANK_CHUNK_SIZE)
+ return NULL;
+ while (x) {
+ int i = 0;
+ while (i < x->len) {
+ if (!strcmp (x->data + i, str))
+ return x->data + i;
+ while (x->data[i]) i++;
+ i++;
+ }
+ x = x->next;
+ }
+ /* We didn't find it, so add it */
+ return StringBank_AddString (bank, str);
+}
+
+static char buffer[STRBANK_CHUNK_SIZE];
+
+const char *
+StringBank_AddSubstring (stringbank *bank, const char *str, unsigned int n)
+{
+ unsigned int len = strlen (str);
+ if (n > len)
+ {
+ return StringBank_AddString (bank, str);
+ }
+ if (n >= STRBANK_CHUNK_SIZE)
+ {
+ return NULL;
+ }
+ strncpy (buffer, str, n);
+ buffer[n] = '\0';
+ return StringBank_AddString(bank, buffer);
+}
+
+const char *
+StringBank_AddOrFindSubstring (stringbank *bank, const char *str, unsigned int n)
+{
+ unsigned int len = strlen (str);
+ if (n > len)
+ {
+ return StringBank_AddOrFindString (bank, str);
+ }
+ if (n >= STRBANK_CHUNK_SIZE)
+ {
+ return NULL;
+ }
+ strncpy (buffer, str, n);
+ buffer[n] = '\0';
+ return StringBank_AddOrFindString(bank, buffer);
+}
+
+int
+SplitString (const char *s, char splitchar, int n, const char **result, stringbank *bank)
+{
+ int i;
+ const char *index = s;
+
+ for (i = 0; i < n-1; i++)
+ {
+ const char *next;
+ int len;
+
+ next = strchr (index, splitchar);
+ if (!next)
+ {
+ break;
+ }
+
+ len = next - index;
+ result[i] = StringBank_AddOrFindSubstring (bank, index, len);
+ index = next+1;
+ }
+ result[i] = StringBank_AddOrFindString (bank, index);
+ return i+1;
+}
+
+#ifdef SB_DEBUG
+
+void
+StringBank_Dump (stringbank *bank, FILE *s)
+{
+ stringbank *x = bank;
+ while (x) {
+ int i = 0;
+ while (i < x->len) {
+ fprintf (s, "\"%s\"\n", x->data + i);
+ while (x->data[i]) i++;
+ i++;
+ }
+ x = x->next;
+ }
+}
+
+#endif
diff --git a/src/libs/resource/stringbank.h b/src/libs/resource/stringbank.h
new file mode 100644
index 0000000..e77105b
--- /dev/null
+++ b/src/libs/resource/stringbank.h
@@ -0,0 +1,57 @@
+/* stringbank.h, Copyright (c) 2005 Michael C. Martin */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope thta it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Se the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_RESOURCE_STRINGBANK_H_
+#define LIBS_RESOURCE_STRINGBANK_H_
+
+#ifdef SB_DEBUG
+#include <stdio.h>
+#endif
+
+#define STRBANK_CHUNK_SIZE (1024 - sizeof (void *) - sizeof (int))
+
+typedef struct _stringbank_chunk {
+ char data[STRBANK_CHUNK_SIZE];
+ int len;
+ struct _stringbank_chunk *next;
+} stringbank;
+
+/* Constructors and destructors */
+stringbank *StringBank_Create (void);
+void StringBank_Free (stringbank *bank);
+
+/* Put str or n chars after str into the string bank. */
+const char *StringBank_AddString (stringbank *bank, const char *str);
+const char *StringBank_AddSubstring (stringbank *bank, const char *str, unsigned int n);
+
+/* Put str or n chars after str into the string bank if it's not already
+ there. Much slower. */
+const char *StringBank_AddOrFindString (stringbank *bank, const char *str);
+const char *StringBank_AddOrFindSubstring (stringbank *bank, const char *str, unsigned int n);
+
+/* Split a string s into at most n substrings, separated by splitchar.
+ Pointers to these substrings will be stored in result; the
+ substrings themselves will be filed in the specified stringbank. */
+int SplitString (const char *s, char splitchar, int n, const char **result, stringbank *bank);
+
+#ifdef SB_DEBUG
+/* Print out a list of the contents of the string bank to the named stream. */
+void StringBank_Dump (stringbank *bank, FILE *s);
+#endif /* SB_DEBUG */
+
+#endif /* LIBS_RESOURCE_STRINGBANK_H_ */
diff --git a/src/libs/sndlib.h b/src/libs/sndlib.h
new file mode 100644
index 0000000..e900707
--- /dev/null
+++ b/src/libs/sndlib.h
@@ -0,0 +1,107 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_SNDLIB_H_
+#define LIBS_SNDLIB_H_
+
+#include "port.h"
+#include "libs/strlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef STRING_TABLE SOUND_REF;
+typedef STRING SOUND;
+// SOUNDPTR is really a TFB_SoundSample**
+typedef void *SOUNDPTR;
+
+typedef struct soundposition
+{
+ BOOLEAN positional;
+ int x, y;
+} SoundPosition;
+
+#define InitSoundResources InitStringTableResources
+#define CaptureSound CaptureStringTable
+#define ReleaseSound ReleaseStringTable
+#define GetSoundRef GetStringTable
+#define GetSoundCount GetStringTableCount
+#define GetSoundIndex GetStringTableIndex
+#define SetAbsSoundIndex SetAbsStringTableIndex
+#define SetRelSoundIndex SetRelStringTableIndex
+
+extern SOUNDPTR GetSoundAddress (SOUND sound);
+
+typedef struct tfb_soundsample TFB_SoundSample;
+typedef TFB_SoundSample **MUSIC_REF;
+
+extern BOOLEAN InitSound (int argc, char *argv[]);
+extern void UninitSound (void);
+extern SOUND_REF LoadSoundFile (const char *pStr);
+extern MUSIC_REF LoadMusicFile (const char *pStr);
+extern BOOLEAN InstallAudioResTypes (void);
+extern SOUND_REF LoadSoundInstance (RESOURCE res);
+extern MUSIC_REF LoadMusicInstance (RESOURCE res);
+extern BOOLEAN DestroySound (SOUND_REF SoundRef);
+extern BOOLEAN DestroyMusic (MUSIC_REF MusicRef);
+
+#define MAX_CHANNELS 8
+#define MAX_VOLUME 255
+#define NORMAL_VOLUME 160
+
+#define FIRST_SFX_CHANNEL 0
+#define MIN_FX_CHANNEL 1
+#define NUM_FX_CHANNELS 4
+#define LAST_SFX_CHANNEL (MIN_FX_CHANNEL + NUM_FX_CHANNELS - 1)
+#define NUM_SFX_CHANNELS (MIN_FX_CHANNEL + NUM_FX_CHANNELS)
+
+extern void PLRPlaySong (MUSIC_REF MusicRef, BOOLEAN Continuous, BYTE
+ Priority);
+extern void PLRStop (MUSIC_REF MusicRef);
+extern BOOLEAN PLRPlaying (MUSIC_REF MusicRef);
+extern void PLRSeek (MUSIC_REF MusicRef, DWORD pos);
+extern void PLRPause (MUSIC_REF MusicRef);
+extern void PLRResume (MUSIC_REF MusicRef);
+extern void snd_PlaySpeech (MUSIC_REF SpeechRef);
+extern void snd_StopSpeech (void);
+extern void PlayChannel (COUNT channel, SOUND snd, SoundPosition pos,
+ void *positional_object, unsigned char priority);
+extern BOOLEAN ChannelPlaying (COUNT Channel);
+extern void * GetPositionalObject (COUNT channel);
+extern void SetPositionalObject (COUNT channel, void *positional_object);
+extern void UpdateSoundPosition (COUNT channel, SoundPosition pos);
+extern void StopChannel (COUNT Channel, BYTE Priority);
+extern void SetMusicVolume (COUNT Volume);
+extern void SetChannelVolume (COUNT Channel, COUNT Volume, BYTE
+ Priority);
+
+extern void StopSound (void);
+extern BOOLEAN SoundPlaying (void);
+
+extern void WaitForSoundEnd (COUNT Channel);
+#define TFBSOUND_WAIT_ALL ((COUNT)~0)
+
+extern DWORD FadeMusic (BYTE end_vol, SIZE TimeInterval);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_SNDLIB_H_ */
+
diff --git a/src/libs/sound/Makeinfo b/src/libs/sound/Makeinfo
new file mode 100644
index 0000000..28ee5cc
--- /dev/null
+++ b/src/libs/sound/Makeinfo
@@ -0,0 +1,9 @@
+if [ "$uqm_SOUNDMODULE" = "openal" ]; then
+ uqm_SUBDIRS="openal mixer decoders"
+ uqm_CFLAGS="$uqm_CFLAGS -DHAVE_OPENAL"
+else
+ uqm_SUBDIRS="mixer decoders"
+fi
+
+uqm_CFILES="audiocore.c fileinst.c resinst.c sound.c sfx.c music.c stream.c trackplayer.c"
+uqm_HFILES="audiocore.h sndintrn.h sound.h stream.h trackint.h trackplayer.h"
diff --git a/src/libs/sound/audiocore.c b/src/libs/sound/audiocore.c
new file mode 100644
index 0000000..440f63f
--- /dev/null
+++ b/src/libs/sound/audiocore.c
@@ -0,0 +1,272 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Audio Core API (derived from OpenAL)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "audiocore.h"
+#include "sound.h"
+#include "libs/log.h"
+
+static audio_Driver audiodrv;
+
+/* The globals that control the sound drivers. */
+int snddriver, soundflags;
+
+volatile bool audio_inited = false;
+
+/*
+ * Declarations for driver init funcs
+ */
+
+#ifdef HAVE_OPENAL
+sint32 openAL_Init (audio_Driver *driver, sint32 flags);
+#endif
+sint32 mixSDL_Init (audio_Driver *driver, sint32 flags);
+sint32 noSound_Init (audio_Driver *driver, sint32 flags);
+
+
+/*
+ * Initialization
+ */
+
+sint32
+initAudio (sint32 driver, sint32 flags)
+{
+ sint32 ret;
+
+#ifdef HAVE_OPENAL
+ if (driver == audio_DRIVER_MIXSDL)
+ ret = mixSDL_Init (&audiodrv, flags);
+ else if (driver == audio_DRIVER_OPENAL)
+ ret = openAL_Init (&audiodrv, flags);
+ else
+ ret = noSound_Init (&audiodrv, flags);
+#else
+ if (driver == audio_DRIVER_OPENAL)
+ {
+ log_add (log_Warning, "OpenAL driver not compiled in, so using MixSDL");
+ driver = audio_DRIVER_MIXSDL;
+ }
+ if (driver == audio_DRIVER_MIXSDL)
+ ret = mixSDL_Init (&audiodrv, flags);
+ else
+ ret = noSound_Init (&audiodrv, flags);
+#endif
+
+ if (ret != 0)
+ {
+ log_add (log_Fatal, "Sound driver initialization failed.\n"
+ "This may happen when a soundcard is "
+ "not present or not available.\n"
+ "NOTICE: Try running UQM with '--sound=none' option");
+ exit (EXIT_FAILURE);
+ }
+
+ SetSFXVolume (sfxVolumeScale);
+ SetSpeechVolume (speechVolumeScale);
+ SetMusicVolume (musicVolume);
+
+ audio_inited = true;
+
+ return ret;
+}
+
+void
+unInitAudio (void)
+{
+ if (!audio_inited)
+ return;
+
+ audio_inited = false;
+ audiodrv.Uninitialize ();
+}
+
+
+/*
+ * General
+ */
+
+sint32
+audio_GetError (void)
+{
+ return audiodrv.GetError ();
+}
+
+
+/*
+ * Sources
+ */
+
+void
+audio_GenSources (uint32 n, audio_Object *psrcobj)
+{
+ audiodrv.GenSources (n, psrcobj);
+}
+
+void
+audio_DeleteSources (uint32 n, audio_Object *psrcobj)
+{
+ audiodrv.DeleteSources (n, psrcobj);
+}
+
+bool
+audio_IsSource (audio_Object srcobj)
+{
+ return audiodrv.IsSource (srcobj);
+}
+
+void
+audio_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value)
+
+{
+ audiodrv.Sourcei (srcobj, audiodrv.EnumLookup[pname], value);
+}
+
+void
+audio_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value)
+{
+ audiodrv.Sourcef (srcobj, audiodrv.EnumLookup[pname], value);
+}
+
+void
+audio_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ audiodrv.Sourcefv (srcobj, audiodrv.EnumLookup[pname], value);
+}
+
+void
+audio_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value)
+{
+ audiodrv.GetSourcei (srcobj, audiodrv.EnumLookup[pname], value);
+}
+
+void
+audio_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ audiodrv.GetSourcef (srcobj, audiodrv.EnumLookup[pname], value);
+}
+
+void
+audio_SourceRewind (audio_Object srcobj)
+{
+ audiodrv.SourceRewind (srcobj);
+}
+
+void
+audio_SourcePlay (audio_Object srcobj)
+{
+ audiodrv.SourcePlay (srcobj);
+}
+
+void
+audio_SourcePause (audio_Object srcobj)
+{
+ audiodrv.SourcePause (srcobj);
+}
+
+void
+audio_SourceStop (audio_Object srcobj)
+{
+ audiodrv.SourceStop (srcobj);
+}
+
+void
+audio_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ audiodrv.SourceQueueBuffers (srcobj, n, pbufobj);
+}
+
+void
+audio_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ audiodrv.SourceUnqueueBuffers (srcobj, n, pbufobj);
+}
+
+
+/*
+ * Buffers
+ */
+
+void
+audio_GenBuffers (uint32 n, audio_Object *pbufobj)
+{
+ audiodrv.GenBuffers (n, pbufobj);
+}
+
+void
+audio_DeleteBuffers (uint32 n, audio_Object *pbufobj)
+{
+ audiodrv.DeleteBuffers (n, pbufobj);
+}
+
+bool
+audio_IsBuffer (audio_Object bufobj)
+{
+ return audiodrv.IsBuffer (bufobj);
+}
+
+void
+audio_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value)
+{
+ audiodrv.GetBufferi (bufobj, audiodrv.EnumLookup[pname], value);
+}
+
+void
+audio_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ audiodrv.BufferData (bufobj, audiodrv.EnumLookup[format], data, size,
+ freq);
+}
+
+bool
+audio_GetFormatInfo (uint32 format, int *channels, int *sample_size)
+{
+ switch (format)
+ {
+ case audio_FORMAT_MONO8:
+ *channels = 1;
+ *sample_size = sizeof (uint8);
+ return true;
+
+ case audio_FORMAT_STEREO8:
+ *channels = 2;
+ *sample_size = sizeof (uint8);
+ return true;
+
+ case audio_FORMAT_MONO16:
+ *channels = 1;
+ *sample_size = sizeof (sint16);
+ return true;
+
+ case audio_FORMAT_STEREO16:
+ *channels = 2;
+ *sample_size = sizeof (sint16);
+ return true;
+ }
+ return false;
+}
diff --git a/src/libs/sound/audiocore.h b/src/libs/sound/audiocore.h
new file mode 100644
index 0000000..6f48b26
--- /dev/null
+++ b/src/libs/sound/audiocore.h
@@ -0,0 +1,169 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Audio Core API (derived from OpenAL)
+ */
+
+#ifndef LIBS_SOUND_AUDIOCORE_H_
+#define LIBS_SOUND_AUDIOCORE_H_
+
+#include "config.h"
+#include "types.h"
+
+
+/* Available drivers */
+enum
+{
+ audio_DRIVER_MIXSDL,
+ audio_DRIVER_NOSOUND,
+ audio_DRIVER_OPENAL
+};
+
+/* Initialization flags */
+#define audio_QUALITY_HIGH (1 << 0)
+#define audio_QUALITY_MEDIUM (1 << 1)
+#define audio_QUALITY_LOW (1 << 2)
+
+
+/* Interface Types */
+typedef uintptr_t audio_Object;
+typedef intptr_t audio_IntVal;
+typedef const sint32 audio_SourceProp;
+typedef const sint32 audio_BufferProp;
+
+enum
+{
+ /* Errors */
+ audio_NO_ERROR = 0,
+ audio_INVALID_NAME,
+ audio_INVALID_ENUM,
+ audio_INVALID_VALUE,
+ audio_INVALID_OPERATION,
+ audio_OUT_OF_MEMORY,
+ audio_DRIVER_FAILURE,
+
+ /* Source properties */
+ audio_POSITION,
+ audio_LOOPING,
+ audio_BUFFER,
+ audio_GAIN,
+ audio_SOURCE_STATE,
+ audio_BUFFERS_QUEUED,
+ audio_BUFFERS_PROCESSED,
+
+ /* Source state information */
+ audio_INITIAL,
+ audio_STOPPED,
+ audio_PLAYING,
+ audio_PAUSED,
+
+ /* Sound buffer properties */
+ audio_FREQUENCY,
+ audio_BITS,
+ audio_CHANNELS,
+ audio_SIZE,
+ audio_FORMAT_MONO16,
+ audio_FORMAT_STEREO16,
+ audio_FORMAT_MONO8,
+ audio_FORMAT_STEREO8,
+ audio_ENUM_SIZE
+};
+
+extern int snddriver, soundflags;
+
+typedef struct {
+ /* General */
+ void (* Uninitialize) (void);
+ sint32 (* GetError) (void);
+ sint32 driverID;
+ sint32 EnumLookup[audio_ENUM_SIZE];
+
+ /* Sources */
+ void (* GenSources) (uint32 n, audio_Object *psrcobj);
+ void (* DeleteSources) (uint32 n, audio_Object *psrcobj);
+ bool (* IsSource) (audio_Object srcobj);
+ void (* Sourcei) (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value);
+ void (* Sourcef) (audio_Object srcobj, audio_SourceProp pname,
+ float value);
+ void (* Sourcefv) (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+ void (* GetSourcei) (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value);
+ void (* GetSourcef) (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+ void (* SourceRewind) (audio_Object srcobj);
+ void (* SourcePlay) (audio_Object srcobj);
+ void (* SourcePause) (audio_Object srcobj);
+ void (* SourceStop) (audio_Object srcobj);
+ void (* SourceQueueBuffers) (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+ void (* SourceUnqueueBuffers) (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+
+ /* Buffers */
+ void (* GenBuffers) (uint32 n, audio_Object *pbufobj);
+ void (* DeleteBuffers) (uint32 n, audio_Object *pbufobj);
+ bool (* IsBuffer) (audio_Object bufobj);
+ void (* GetBufferi) (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value);
+ void (* BufferData) (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+} audio_Driver;
+
+
+/* Initialization */
+sint32 initAudio (sint32 driver, sint32 flags);
+void unInitAudio (void);
+
+/* General */
+sint32 audio_GetError (void);
+
+/* Sources */
+void audio_GenSources (uint32 n, audio_Object *psrcobj);
+void audio_DeleteSources (uint32 n, audio_Object *psrcobj);
+bool audio_IsSource (audio_Object srcobj);
+void audio_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value);
+void audio_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value);
+void audio_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void audio_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value);
+void audio_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void audio_SourceRewind (audio_Object srcobj);
+void audio_SourcePlay (audio_Object srcobj);
+void audio_SourcePause (audio_Object srcobj);
+void audio_SourceStop (audio_Object srcobj);
+void audio_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+void audio_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+
+/* Buffers */
+void audio_GenBuffers (uint32 n, audio_Object *pbufobj);
+void audio_DeleteBuffers (uint32 n, audio_Object *pbufobj);
+bool audio_IsBuffer (audio_Object bufobj);
+void audio_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value);
+void audio_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+bool audio_GetFormatInfo (uint32 format, int *channels, int *sample_size);
+
+#endif /* LIBS_SOUND_AUDIOCORE_H_ */
diff --git a/src/libs/sound/decoders/Makeinfo b/src/libs/sound/decoders/Makeinfo
new file mode 100644
index 0000000..e1735a1
--- /dev/null
+++ b/src/libs/sound/decoders/Makeinfo
@@ -0,0 +1,8 @@
+uqm_CFILES="decoder.c aiffaud.c wav.c dukaud.c modaud.c"
+uqm_HFILES="aiffaud.h decoder.h dukaud.h modaud.h wav.h"
+
+if [ "$uqm_OGGVORBIS" '!=' "none" ]; then
+ uqm_CFILES="$uqm_CFILES oggaud.c"
+ uqm_HFILES="$uqm_HFILES oggaud.h"
+fi
+
diff --git a/src/libs/sound/decoders/aiffaud.c b/src/libs/sound/decoders/aiffaud.c
new file mode 100644
index 0000000..102a78e
--- /dev/null
+++ b/src/libs/sound/decoders/aiffaud.c
@@ -0,0 +1,650 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Portions (C) Serge van den Boom (svdb at stack.nl) */
+/* Portions (C) Alex Volkov (codepro at usa.net) */
+
+/* AIFF decoder (.aif)
+ *
+ * Doesn't work on *all* aiff files in general, only 8/16 PCM and
+ * 16-bit AIFF-C SDX2-compressed.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+ // for abs()
+#include <errno.h>
+#ifndef _WIN32_WCE
+# include <memory.h>
+#endif
+#include <string.h>
+#include "port.h"
+#include "types.h"
+#include "libs/uio.h"
+#include "endian_uqm.h"
+#include "libs/log.h"
+#include "aiffaud.h"
+
+typedef uint32 aiff_ID;
+
+#define aiff_MAKE_ID(x1, x2, x3, x4) \
+ (((x1) << 24) | ((x2) << 16) | ((x3) << 8) | (x4))
+
+#define aiff_FormID aiff_MAKE_ID('F', 'O', 'R', 'M')
+#define aiff_FormVersionID aiff_MAKE_ID('F', 'V', 'E', 'R')
+#define aiff_CommonID aiff_MAKE_ID('C', 'O', 'M', 'M')
+#define aiff_SoundDataID aiff_MAKE_ID('S', 'S', 'N', 'D')
+
+#define aiff_FormTypeAIFF aiff_MAKE_ID('A', 'I', 'F', 'F')
+#define aiff_FormTypeAIFC aiff_MAKE_ID('A', 'I', 'F', 'C')
+
+#define aiff_CompressionTypeSDX2 aiff_MAKE_ID('S', 'D', 'X', '2')
+
+
+typedef struct
+{
+ aiff_ID id;
+ uint32 size;
+} aiff_ChunkHeader;
+
+#define AIFF_CHUNK_HDR_SIZE (4+4)
+
+typedef struct
+{
+ aiff_ChunkHeader chunk;
+ aiff_ID type;
+} aiff_FileHeader;
+
+typedef struct
+{
+ uint32 version; /* format version, in Mac format */
+} aiff_FormatVersionChunk;
+
+typedef struct
+{
+ uint16 channels; /* number of channels */
+ uint32 sampleFrames; /* number of sample frames */
+ uint16 sampleSize; /* number of bits per sample */
+ sint32 sampleRate; /* number of frames per second */
+ /* this is actually stored as IEEE-754 80bit in files */
+} aiff_CommonChunk;
+
+#define AIFF_COMM_SIZE (2+4+2+10)
+
+typedef struct
+{
+ uint16 channels; /* number of channels */
+ uint32 sampleFrames; /* number of sample frames */
+ uint16 sampleSize; /* number of bits per sample */
+ sint32 sampleRate; /* number of frames per second */
+ aiff_ID extTypeID; /* compression type ID */
+ char extName[32]; /* compression type name */
+} aiff_ExtCommonChunk;
+
+#define AIFF_EXT_COMM_SIZE (AIFF_COMM_SIZE+4)
+
+typedef struct
+{
+ uint32 offset; /* offset to sound data */
+ uint32 blockSize; /* size of alignment blocks */
+} aiff_SoundDataChunk;
+
+#define AIFF_SSND_SIZE (4+4)
+
+
+#define THIS_PTR TFB_SoundDecoder* This
+
+static const char* aifa_GetName (void);
+static bool aifa_InitModule (int flags, const TFB_DecoderFormats*);
+static void aifa_TermModule (void);
+static uint32 aifa_GetStructSize (void);
+static int aifa_GetError (THIS_PTR);
+static bool aifa_Init (THIS_PTR);
+static void aifa_Term (THIS_PTR);
+static bool aifa_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void aifa_Close (THIS_PTR);
+static int aifa_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 aifa_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 aifa_GetFrame (THIS_PTR);
+
+TFB_SoundDecoderFuncs aifa_DecoderVtbl =
+{
+ aifa_GetName,
+ aifa_InitModule,
+ aifa_TermModule,
+ aifa_GetStructSize,
+ aifa_GetError,
+ aifa_Init,
+ aifa_Term,
+ aifa_Open,
+ aifa_Close,
+ aifa_Decode,
+ aifa_Seek,
+ aifa_GetFrame,
+};
+
+
+typedef enum
+{
+ aifc_None,
+ aifc_Sdx2,
+} aiff_CompressionType;
+
+#define MAX_CHANNELS 4
+
+typedef struct tfb_wavesounddecoder
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // private
+ sint32 last_error;
+ uio_Stream *fp;
+ aiff_ExtCommonChunk fmtHdr;
+ aiff_CompressionType comp_type;
+ unsigned bits_per_sample;
+ unsigned block_align;
+ unsigned file_block;
+ uint32 data_ofs;
+ uint32 data_size;
+ uint32 max_pcm;
+ uint32 cur_pcm;
+ sint32 prev_val[MAX_CHANNELS];
+
+} TFB_AiffSoundDecoder;
+
+static const TFB_DecoderFormats* aifa_formats = NULL;
+
+static int aifa_DecodePCM (TFB_AiffSoundDecoder*, void* buf, sint32 bufsize);
+static int aifa_DecodeSDX2 (TFB_AiffSoundDecoder*, void* buf, sint32 bufsize);
+
+
+static const char*
+aifa_GetName (void)
+{
+ return "AIFF";
+}
+
+static bool
+aifa_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ aifa_formats = fmts;
+ return true;
+
+ (void)flags; // laugh at compiler warning
+}
+
+static void
+aifa_TermModule (void)
+{
+ // no specific module term
+}
+
+static uint32
+aifa_GetStructSize (void)
+{
+ return sizeof (TFB_AiffSoundDecoder);
+}
+
+static int
+aifa_GetError (THIS_PTR)
+{
+ TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+ int ret = aifa->last_error;
+ aifa->last_error = 0;
+ return ret;
+}
+
+static bool
+aifa_Init (THIS_PTR)
+{
+ //TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+ This->need_swap = !aifa_formats->want_big_endian;
+ return true;
+}
+
+static void
+aifa_Term (THIS_PTR)
+{
+ //TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+ aifa_Close (This); // ensure cleanup
+}
+
+static bool
+read_be_16 (uio_Stream *fp, uint16 *v)
+{
+ if (!uio_fread (v, sizeof(*v), 1, fp))
+ return false;
+ *v = UQM_SwapBE16 (*v);
+ return true;
+}
+
+static bool
+read_be_32 (uio_Stream *fp, uint32 *v)
+{
+ if (!uio_fread (v, sizeof(*v), 1, fp))
+ return false;
+ *v = UQM_SwapBE32 (*v);
+ return true;
+}
+
+// Read 80-bit IEEE 754 floating point number.
+// We are only interested in values that we can work with,
+// so using an sint32 here is fine.
+static bool
+read_be_f80 (uio_Stream *fp, sint32 *v)
+{
+ int sign, exp;
+ int shift;
+ uint16 se;
+ uint32 mant, mant_low;
+ if (!read_be_16 (fp, &se) ||
+ !read_be_32 (fp, &mant) || !read_be_32 (fp, &mant_low))
+ return false;
+
+ sign = (se >> 15) & 1; // sign is the highest bit
+ exp = (se & ((1 << 15) - 1)); // exponent is next highest 15 bits
+#if 0 // XXX: 80bit IEEE 754 used in AIFF uses explicit mantissa MS bit
+ // mantissa has an implied leading bit which is typically 1
+ mant >>= 1;
+ if (exp != 0)
+ mant |= 0x80000000;
+#endif
+ mant >>= 1; // we also need space for sign
+ exp -= (1 << 14) - 1; // exponent is biased by (2^(e-1) - 1)
+ shift = exp - 31 + 1; // mantissa is already 31 bits before decimal pt.
+ if (shift > 0)
+ mant = 0x7fffffff; // already too big
+ else if (shift < 0)
+ mant >>= -shift;
+
+ *v = sign ? -(sint32)mant : (sint32)mant;
+
+ return true;
+}
+
+static bool
+aifa_readFileHeader (TFB_AiffSoundDecoder* aifa, aiff_FileHeader* hdr)
+{
+ if (!read_be_32 (aifa->fp, &hdr->chunk.id) ||
+ !read_be_32 (aifa->fp, &hdr->chunk.size) ||
+ !read_be_32 (aifa->fp, &hdr->type))
+ {
+ aifa->last_error = errno;
+ return false;
+ }
+ return true;
+}
+
+static bool
+aifa_readChunkHeader (TFB_AiffSoundDecoder* aifa, aiff_ChunkHeader* hdr)
+{
+ if (!read_be_32 (aifa->fp, &hdr->id) ||
+ !read_be_32 (aifa->fp, &hdr->size))
+ {
+ aifa->last_error = errno;
+ return false;
+ }
+ return true;
+}
+
+static int
+aifa_readCommonChunk (TFB_AiffSoundDecoder* aifa, uint32 size,
+ aiff_ExtCommonChunk* fmt)
+{
+ int bytes;
+
+ memset(fmt, 0, sizeof(*fmt));
+ if (size < AIFF_COMM_SIZE)
+ {
+ aifa->last_error = aifae_BadFile;
+ return 0;
+ }
+
+ if (!read_be_16 (aifa->fp, &fmt->channels) ||
+ !read_be_32 (aifa->fp, &fmt->sampleFrames) ||
+ !read_be_16 (aifa->fp, &fmt->sampleSize) ||
+ !read_be_f80 (aifa->fp, &fmt->sampleRate))
+ {
+ aifa->last_error = errno;
+ return 0;
+ }
+ bytes = AIFF_COMM_SIZE;
+
+ if (size >= AIFF_EXT_COMM_SIZE)
+ {
+ if (!read_be_32 (aifa->fp, &fmt->extTypeID))
+ {
+ aifa->last_error = errno;
+ return 0;
+ }
+ bytes += sizeof(fmt->extTypeID);
+ }
+
+ return bytes;
+}
+
+static bool
+aifa_readSoundDataChunk (TFB_AiffSoundDecoder* aifa,
+ aiff_SoundDataChunk* data)
+{
+ if (!read_be_32 (aifa->fp, &data->offset) ||
+ !read_be_32 (aifa->fp, &data->blockSize))
+ {
+ aifa->last_error = errno;
+ return false;
+ }
+ return true;
+}
+
+static bool
+aifa_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+ aiff_FileHeader fileHdr;
+ aiff_ChunkHeader chunkHdr;
+ sint32 remSize;
+
+ aifa->fp = uio_fopen (dir, filename, "rb");
+ if (!aifa->fp)
+ {
+ aifa->last_error = errno;
+ return false;
+ }
+
+ aifa->data_size = 0;
+ aifa->max_pcm = 0;
+ aifa->data_ofs = 0;
+ memset(&aifa->fmtHdr, 0, sizeof(aifa->fmtHdr));
+ memset(aifa->prev_val, 0, sizeof(aifa->prev_val));
+
+ // read wave header
+ if (!aifa_readFileHeader (aifa, &fileHdr))
+ {
+ aifa->last_error = errno;
+ aifa_Close (This);
+ return false;
+ }
+ if (fileHdr.chunk.id != aiff_FormID)
+ {
+ log_add (log_Warning, "aifa_Open(): not an aiff file, ID 0x%08x",
+ fileHdr.chunk.id);
+ aifa_Close (This);
+ return false;
+ }
+ if (fileHdr.type != aiff_FormTypeAIFF && fileHdr.type != aiff_FormTypeAIFC)
+ {
+ log_add (log_Warning, "aifa_Open(): unsupported aiff file"
+ ", Type 0x%08x", fileHdr.type);
+ aifa_Close (This);
+ return false;
+ }
+
+ for (remSize = fileHdr.chunk.size - sizeof(aiff_ID); remSize > 0;
+ remSize -= ((chunkHdr.size + 1) & ~1) + AIFF_CHUNK_HDR_SIZE)
+ {
+ if (!aifa_readChunkHeader (aifa, &chunkHdr))
+ {
+ aifa_Close (This);
+ return false;
+ }
+
+ if (chunkHdr.id == aiff_CommonID)
+ {
+ int read = aifa_readCommonChunk (aifa, chunkHdr.size, &aifa->fmtHdr);
+ if (!read)
+ {
+ aifa_Close (This);
+ return false;
+ }
+ uio_fseek (aifa->fp, chunkHdr.size - read, SEEK_CUR);
+ }
+ else if (chunkHdr.id == aiff_SoundDataID)
+ {
+ aiff_SoundDataChunk data;
+ if (!aifa_readSoundDataChunk (aifa, &data))
+ {
+ aifa_Close (This);
+ return false;
+ }
+ aifa->data_ofs = uio_ftell (aifa->fp) + data.offset;
+ uio_fseek (aifa->fp, chunkHdr.size - AIFF_SSND_SIZE, SEEK_CUR);
+ }
+ else
+ { // skip uninteresting chunk
+ uio_fseek (aifa->fp, chunkHdr.size, SEEK_CUR);
+ }
+
+ // 2-align the file ptr
+ uio_fseek (aifa->fp, chunkHdr.size & 1, SEEK_CUR);
+ }
+
+ if (aifa->fmtHdr.sampleFrames == 0)
+ {
+ log_add (log_Warning, "aifa_Open(): aiff file has no sound data");
+ aifa_Close (This);
+ return false;
+ }
+
+ // make bits-per-sample a multiple of 8
+ aifa->bits_per_sample = (aifa->fmtHdr.sampleSize + 7) & ~7;
+ if (aifa->bits_per_sample == 0 || aifa->bits_per_sample > 16)
+ { // XXX: for now we do not support 24 and 32 bps
+ log_add (log_Warning, "aifa_Open(): unsupported sample size %u",
+ aifa->bits_per_sample);
+ aifa_Close (This);
+ return false;
+ }
+ if (aifa->fmtHdr.channels != 1 && aifa->fmtHdr.channels != 2)
+ {
+ log_add (log_Warning, "aifa_Open(): unsupported number of channels %u",
+ (unsigned)aifa->fmtHdr.channels);
+ aifa_Close (This);
+ return false;
+ }
+ if (aifa->fmtHdr.sampleRate < 300 || aifa->fmtHdr.sampleRate > 128000)
+ {
+ log_add (log_Warning, "aifa_Open(): unsupported sampling rate %ld",
+ (long)aifa->fmtHdr.sampleRate);
+ aifa_Close (This);
+ return false;
+ }
+
+ aifa->block_align = aifa->bits_per_sample / 8 * aifa->fmtHdr.channels;
+ aifa->file_block = aifa->block_align;
+ if (!aifa->data_ofs)
+ {
+ log_add (log_Warning, "aifa_Open(): bad aiff file,"
+ " no SSND chunk found");
+ aifa_Close (This);
+ return false;
+ }
+
+ if (fileHdr.type == aiff_FormTypeAIFF)
+ {
+ if (aifa->fmtHdr.extTypeID != 0)
+ {
+ log_add (log_Warning, "aifa_Open(): unsupported extension 0x%08x",
+ aifa->fmtHdr.extTypeID);
+ aifa_Close (This);
+ return false;
+ }
+ aifa->comp_type = aifc_None;
+ }
+ else if (fileHdr.type == aiff_FormTypeAIFC)
+ {
+ if (aifa->fmtHdr.extTypeID != aiff_CompressionTypeSDX2)
+ {
+ log_add (log_Warning, "aifa_Open(): unsupported compression 0x%08x",
+ aifa->fmtHdr.extTypeID);
+ aifa_Close (This);
+ return false;
+ }
+ aifa->comp_type = aifc_Sdx2;
+ aifa->file_block /= 2;
+ assert(aifa->fmtHdr.channels <= MAX_CHANNELS);
+ // after decompression, we will get samples in machine byte order
+ This->need_swap = (aifa_formats->big_endian
+ != aifa_formats->want_big_endian);
+ }
+
+ aifa->data_size = aifa->fmtHdr.sampleFrames * aifa->file_block;
+
+ if (aifa->comp_type == aifc_Sdx2 && aifa->bits_per_sample != 16)
+ {
+ log_add (log_Warning, "aifa_Open(): unsupported sample size %u for SDX2",
+ (unsigned)aifa->fmtHdr.sampleSize);
+ aifa_Close (This);
+ return false;
+ }
+
+ This->format = (aifa->fmtHdr.channels == 1 ?
+ (aifa->bits_per_sample == 8 ?
+ aifa_formats->mono8 : aifa_formats->mono16)
+ :
+ (aifa->bits_per_sample == 8 ?
+ aifa_formats->stereo8 : aifa_formats->stereo16)
+ );
+ This->frequency = aifa->fmtHdr.sampleRate;
+
+ uio_fseek (aifa->fp, aifa->data_ofs, SEEK_SET);
+ aifa->max_pcm = aifa->fmtHdr.sampleFrames;
+ aifa->cur_pcm = 0;
+ This->length = (float) aifa->max_pcm / aifa->fmtHdr.sampleRate;
+ aifa->last_error = 0;
+
+ return true;
+}
+
+static void
+aifa_Close (THIS_PTR)
+{
+ TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+
+ if (aifa->fp)
+ {
+ uio_fclose (aifa->fp);
+ aifa->fp = NULL;
+ }
+}
+
+static int
+aifa_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+ switch (aifa->comp_type)
+ {
+ case aifc_None:
+ return aifa_DecodePCM (aifa, buf, bufsize);
+ case aifc_Sdx2:
+ return aifa_DecodeSDX2 (aifa, buf, bufsize);
+ default:
+ assert(false && "Unknown comp_type");
+ return 0;
+ }
+}
+
+static int
+aifa_DecodePCM (TFB_AiffSoundDecoder* aifa, void* buf, sint32 bufsize)
+{
+ uint32 dec_pcm;
+ uint32 size;
+
+ dec_pcm = bufsize / aifa->block_align;
+ if (dec_pcm > aifa->max_pcm - aifa->cur_pcm)
+ dec_pcm = aifa->max_pcm - aifa->cur_pcm;
+
+ dec_pcm = uio_fread (buf, aifa->file_block, dec_pcm, aifa->fp);
+ aifa->cur_pcm += dec_pcm;
+ size = dec_pcm * aifa->block_align;
+
+ if (aifa->bits_per_sample == 8)
+ { // AIFF files store 8-bit data as signed
+ // and we need it unsigned
+ uint8* ptr = (uint8*)buf;
+ uint32 left;
+ for (left = size; left > 0; --left, ++ptr)
+ *ptr += 128;
+ }
+
+ return size;
+}
+
+static int
+aifa_DecodeSDX2 (TFB_AiffSoundDecoder* aifa, void* buf, sint32 bufsize)
+{
+ uint32 dec_pcm;
+ sint8 *src;
+ sint16 *dst = buf;
+ uint32 left;
+
+ dec_pcm = bufsize / aifa->block_align;
+ if (dec_pcm > aifa->max_pcm - aifa->cur_pcm)
+ dec_pcm = aifa->max_pcm - aifa->cur_pcm;
+
+ src = (sint8*)buf + bufsize - (dec_pcm * aifa->file_block);
+ dec_pcm = uio_fread (src, aifa->file_block, dec_pcm, aifa->fp);
+ aifa->cur_pcm += dec_pcm;
+
+ for (left = dec_pcm; left > 0; --left)
+ {
+ int i;
+ sint32 *prev = aifa->prev_val;
+ for (i = aifa->fmtHdr.channels; i > 0; --i, ++prev, ++src, ++dst)
+ {
+ sint32 v = (*src * abs(*src)) << 1;
+ if (*src & 1)
+ v += *prev;
+ // saturate the value
+ if (v > 32767)
+ v = 32767;
+ else if (v < -32768)
+ v = -32768;
+ *prev = v;
+ *dst = v;
+ }
+ }
+
+ return dec_pcm * aifa->block_align;
+}
+
+static uint32
+aifa_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+
+ if (pcm_pos > aifa->max_pcm)
+ pcm_pos = aifa->max_pcm;
+ aifa->cur_pcm = pcm_pos;
+ uio_fseek (aifa->fp,
+ aifa->data_ofs + pcm_pos * aifa->file_block,
+ SEEK_SET);
+
+ // reset previous values for SDX2 on seek ops
+ // the delta will recover faster with reset
+ memset(aifa->prev_val, 0, sizeof(aifa->prev_val));
+
+ return pcm_pos;
+}
+
+static uint32
+aifa_GetFrame (THIS_PTR)
+{
+ //TFB_AiffSoundDecoder* aifa = (TFB_AiffSoundDecoder*) This;
+ return 0; // only 1 frame for now
+
+ (void)This; // laugh at compiler warning
+}
diff --git a/src/libs/sound/decoders/aiffaud.h b/src/libs/sound/decoders/aiffaud.h
new file mode 100644
index 0000000..36c6679
--- /dev/null
+++ b/src/libs/sound/decoders/aiffaud.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* AIFF decoder */
+
+#ifndef AIFFAUD_H
+#define AIFFAUD_H
+
+#include "decoder.h"
+
+extern TFB_SoundDecoderFuncs aifa_DecoderVtbl;
+
+typedef enum
+{
+ // positive values are the same as in errno
+ aifae_None = 0,
+ aifae_Unknown = -1,
+ aifae_BadFile = -2,
+ aifae_BadArg = -3,
+ aifae_Other = -1000,
+} aifa_Error;
+
+#endif /* AIFFAUD_H */
diff --git a/src/libs/sound/decoders/decoder.c b/src/libs/sound/decoders/decoder.c
new file mode 100644
index 0000000..8c20877
--- /dev/null
+++ b/src/libs/sound/decoders/decoder.c
@@ -0,0 +1,936 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Sound file decoder for .wav, .mod, .ogg (to be used with OpenAL)
+ * API is heavily influenced by SDL_sound.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "port.h"
+#include "libs/memlib.h"
+#include "libs/file.h"
+#include "libs/log.h"
+#include "decoder.h"
+#include "wav.h"
+#include "dukaud.h"
+#include "modaud.h"
+#ifndef OVCODEC_NONE
+# include "oggaud.h"
+#endif /* OVCODEC_NONE */
+#include "aiffaud.h"
+
+
+#define MAX_REG_DECODERS 31
+
+#define THIS_PTR TFB_SoundDecoder*
+
+static const char* bufa_GetName (void);
+static bool bufa_InitModule (int flags, const TFB_DecoderFormats*);
+static void bufa_TermModule (void);
+static uint32 bufa_GetStructSize (void);
+static int bufa_GetError (THIS_PTR);
+static bool bufa_Init (THIS_PTR);
+static void bufa_Term (THIS_PTR);
+static bool bufa_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void bufa_Close (THIS_PTR);
+static int bufa_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 bufa_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 bufa_GetFrame (THIS_PTR);
+
+TFB_SoundDecoderFuncs bufa_DecoderVtbl =
+{
+ bufa_GetName,
+ bufa_InitModule,
+ bufa_TermModule,
+ bufa_GetStructSize,
+ bufa_GetError,
+ bufa_Init,
+ bufa_Term,
+ bufa_Open,
+ bufa_Close,
+ bufa_Decode,
+ bufa_Seek,
+ bufa_GetFrame,
+};
+
+typedef struct tfb_bufsounddecoder
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // private
+ void* data;
+ uint32 max_pcm;
+ uint32 cur_pcm;
+
+} TFB_BufSoundDecoder;
+
+#define SD_MIN_SIZE (sizeof (TFB_BufSoundDecoder))
+
+static const char* nula_GetName (void);
+static bool nula_InitModule (int flags, const TFB_DecoderFormats*);
+static void nula_TermModule (void);
+static uint32 nula_GetStructSize (void);
+static int nula_GetError (THIS_PTR);
+static bool nula_Init (THIS_PTR);
+static void nula_Term (THIS_PTR);
+static bool nula_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void nula_Close (THIS_PTR);
+static int nula_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 nula_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 nula_GetFrame (THIS_PTR);
+
+TFB_SoundDecoderFuncs nula_DecoderVtbl =
+{
+ nula_GetName,
+ nula_InitModule,
+ nula_TermModule,
+ nula_GetStructSize,
+ nula_GetError,
+ nula_Init,
+ nula_Term,
+ nula_Open,
+ nula_Close,
+ nula_Decode,
+ nula_Seek,
+ nula_GetFrame,
+};
+
+typedef struct tfb_nullsounddecoder
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // private
+ uint32 cur_pcm;
+
+} TFB_NullSoundDecoder;
+
+#undef THIS_PTR
+
+
+struct TFB_RegSoundDecoder
+{
+ bool builtin;
+ bool used; // ever used indicator
+ const char* ext;
+ const TFB_SoundDecoderFuncs* funcs;
+};
+static TFB_RegSoundDecoder sd_decoders[MAX_REG_DECODERS + 1] =
+{
+ {true, true, "wav", &wava_DecoderVtbl},
+ {true, true, "mod", &moda_DecoderVtbl},
+#ifndef OVCODEC_NONE
+ {true, true, "ogg", &ova_DecoderVtbl},
+#endif /* OVCODEC_NONE */
+ {true, true, "duk", &duka_DecoderVtbl},
+ {true, true, "aif", &aifa_DecoderVtbl},
+ {false, false, NULL, NULL}, // null term
+};
+
+static TFB_DecoderFormats decoder_formats;
+static int sd_flags = 0;
+
+/* change endianness of 16bit words
+ * Only works optimal when 'data' is aligned on a 32 bits boundary.
+ */
+void
+SoundDecoder_SwapWords (uint16* data, uint32 size)
+{
+ uint32 fsize = size & (~3U);
+
+ size -= fsize;
+ fsize >>= 2;
+ for (; fsize; fsize--, data += 2)
+ {
+ uint32 v = *(uint32*)data;
+ *(uint32*)data = ((v & 0x00ff00ff) << 8)
+ | ((v & 0xff00ff00) >> 8);
+ }
+ if (size)
+ {
+ /* leftover word */
+ *data = ((*data & 0x00ff) << 8) | ((*data & 0xff00) >> 8);
+ }
+}
+
+const char*
+SoundDecoder_GetName (TFB_SoundDecoder *decoder)
+{
+ if (!decoder || !decoder->funcs)
+ return "(Null)";
+ return decoder->funcs->GetName ();
+}
+
+sint32
+SoundDecoder_Init (int flags, TFB_DecoderFormats *formats)
+{
+ TFB_RegSoundDecoder* info;
+ sint32 ret = 0;
+
+ if (!formats)
+ {
+ log_add (log_Error, "SoundDecoder_Init(): missing decoder formats");
+ return 1;
+ }
+ decoder_formats = *formats;
+
+ // init built-in decoders
+ for (info = sd_decoders; info->ext; info++)
+ {
+ if (!info->funcs->InitModule (flags, &decoder_formats))
+ {
+ log_add (log_Error, "SoundDecoder_Init(): "
+ "%s audio decoder init failed",
+ info->funcs->GetName ());
+ ret = 1;
+ }
+ }
+
+ sd_flags = flags;
+
+ return ret;
+}
+
+void
+SoundDecoder_Uninit (void)
+{
+ TFB_RegSoundDecoder* info;
+
+ // uninit all decoders
+ // and unregister loaded decoders
+ for (info = sd_decoders; info->used; info++)
+ {
+ if (info->ext) // check if present
+ info->funcs->TermModule ();
+
+ if (!info->builtin)
+ {
+ info->used = false;
+ info->ext = NULL;
+ }
+ }
+}
+
+TFB_RegSoundDecoder*
+SoundDecoder_Register (const char* fileext, TFB_SoundDecoderFuncs* decvtbl)
+{
+ TFB_RegSoundDecoder* info;
+ TFB_RegSoundDecoder* newslot = NULL;
+
+ if (!decvtbl)
+ {
+ log_add (log_Warning, "SoundDecoder_Register(): Null decoder table");
+ return NULL;
+ }
+ if (!fileext)
+ {
+ log_add (log_Warning, "SoundDecoder_Register(): Bad file type for %s",
+ decvtbl->GetName ());
+ return NULL;
+ }
+
+ // check if extension already registered
+ for (info = sd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, fileext) != 0);
+ ++info)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !info->ext)
+ newslot = info;
+ }
+
+ if (info >= sd_decoders + MAX_REG_DECODERS)
+ {
+ log_add (log_Warning, "SoundDecoder_Register(): Decoders limit reached");
+ return NULL;
+ }
+ else if (info->ext)
+ {
+ log_add (log_Warning, "SoundDecoder_Register(): "
+ "'%s' decoder already registered (%s denied)",
+ fileext, decvtbl->GetName ());
+ return NULL;
+ }
+
+ if (!decvtbl->InitModule (sd_flags, &decoder_formats))
+ {
+ log_add (log_Warning, "SoundDecoder_Register(): %s decoder init failed",
+ decvtbl->GetName ());
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = info;
+ newslot->used = true;
+ // make next one a term
+ info[1].builtin = false;
+ info[1].used = false;
+ info[1].ext = NULL;
+ }
+
+ newslot->ext = fileext;
+ newslot->funcs = decvtbl;
+
+ return newslot;
+}
+
+void
+SoundDecoder_Unregister (TFB_RegSoundDecoder* regdec)
+{
+ if (regdec < sd_decoders || regdec >= sd_decoders + MAX_REG_DECODERS ||
+ !regdec->ext || !regdec->funcs)
+ {
+ log_add (log_Warning, "SoundDecoder_Unregister(): "
+ "Invalid or expired decoder passed");
+ return;
+ }
+
+ regdec->funcs->TermModule ();
+ regdec->ext = NULL;
+ regdec->funcs = NULL;
+}
+
+const TFB_SoundDecoderFuncs*
+SoundDecoder_Lookup (const char* fileext)
+{
+ TFB_RegSoundDecoder* info;
+
+ for (info = sd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, fileext) != 0);
+ ++info)
+ ;
+ return info->ext ? info->funcs : NULL;
+}
+
+TFB_SoundDecoder*
+SoundDecoder_Load (uio_DirHandle *dir, char *filename,
+ uint32 buffer_size, uint32 startTime, sint32 runTime)
+ // runTime < 0 specifies a default length for a nul decoder
+{
+ const char* pext;
+ TFB_RegSoundDecoder* info;
+ const TFB_SoundDecoderFuncs* funcs;
+ TFB_SoundDecoder* decoder;
+ uint32 struct_size;
+
+ pext = strrchr (filename, '.');
+ if (!pext)
+ {
+ log_add (log_Warning, "SoundDecoder_Load(): Unknown file type (%s)",
+ filename);
+ return NULL;
+ }
+ ++pext;
+
+ for (info = sd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, pext) != 0);
+ ++info)
+ ;
+ if (!info->ext)
+ {
+ log_add (log_Warning, "SoundDecoder_Load(): Unsupported file type (%s)",
+ filename);
+
+ if (runTime)
+ {
+ runTime = abs (runTime);
+ startTime = 0;
+ funcs = &nula_DecoderVtbl;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ funcs = info->funcs;
+ }
+
+ if (!fileExists2 (dir, filename))
+ {
+ if (runTime)
+ {
+ runTime = abs (runTime);
+ startTime = 0;
+ funcs = &nula_DecoderVtbl;
+ }
+ else
+ {
+ log_add (log_Warning, "SoundDecoder_Load(): %s does not exist",
+ filename);
+ return NULL;
+ }
+ }
+
+ struct_size = funcs->GetStructSize ();
+ if (struct_size < SD_MIN_SIZE)
+ struct_size = SD_MIN_SIZE;
+
+ decoder = (TFB_SoundDecoder*) HCalloc (struct_size);
+ decoder->funcs = funcs;
+ if (!decoder->funcs->Init (decoder))
+ {
+ log_add (log_Warning, "SoundDecoder_Load(): "
+ "%s decoder instance failed init",
+ decoder->funcs->GetName ());
+ HFree (decoder);
+ return NULL;
+ }
+
+ if (!decoder->funcs->Open (decoder, dir, filename))
+ {
+ log_add (log_Warning, "SoundDecoder_Load(): "
+ "%s decoder could not load %s",
+ decoder->funcs->GetName (), filename);
+ decoder->funcs->Term (decoder);
+ HFree (decoder);
+ return NULL;
+ }
+
+ decoder->buffer = HMalloc (buffer_size);
+ decoder->buffer_size = buffer_size;
+ decoder->looping = false;
+ decoder->error = SOUNDDECODER_OK;
+ decoder->dir = dir;
+ decoder->filename = (char *) HMalloc (strlen (filename) + 1);
+ strcpy (decoder->filename, filename);
+
+ if (decoder->is_null)
+ { // fake decoder, keeps voiceovers and etc. going
+ decoder->length = (float) (runTime / 1000.0);
+ }
+
+ decoder->length -= startTime / 1000.0f;
+ if (decoder->length < 0)
+ decoder->length = 0;
+ else if (runTime > 0 && runTime / 1000.0 < decoder->length)
+ decoder->length = (float)(runTime / 1000.0);
+
+ decoder->start_sample = (uint32)(startTime / 1000.0f * decoder->frequency);
+ decoder->end_sample = decoder->start_sample +
+ (unsigned long)(decoder->length * decoder->frequency);
+ if (decoder->start_sample != 0)
+ decoder->funcs->Seek (decoder, decoder->start_sample);
+
+ if (decoder->format == decoder_formats.mono8)
+ decoder->bytes_per_samp = 1;
+ else if (decoder->format == decoder_formats.mono16)
+ decoder->bytes_per_samp = 2;
+ else if (decoder->format == decoder_formats.stereo8)
+ decoder->bytes_per_samp = 2;
+ else if (decoder->format == decoder_formats.stereo16)
+ decoder->bytes_per_samp = 4;
+
+ decoder->pos = decoder->start_sample * decoder->bytes_per_samp;
+
+ return decoder;
+}
+
+uint32
+SoundDecoder_Decode (TFB_SoundDecoder *decoder)
+{
+ long decoded_bytes;
+ long rc;
+ long buffer_size;
+ uint32 max_bytes = UINT32_MAX;
+ uint8 *buffer;
+
+ if (!decoder || !decoder->funcs)
+ {
+ log_add (log_Warning, "SoundDecoder_Decode(): null or bad decoder");
+ return 0;
+ }
+
+ buffer = (uint8*) decoder->buffer;
+ buffer_size = decoder->buffer_size;
+ if (!decoder->looping && decoder->end_sample > 0)
+ {
+ max_bytes = decoder->end_sample * decoder->bytes_per_samp;
+ if (max_bytes - decoder->pos < decoder->buffer_size)
+ buffer_size = max_bytes - decoder->pos;
+ }
+
+ if (buffer_size == 0)
+ { // nothing more to decode
+ decoder->error = SOUNDDECODER_EOF;
+ return 0;
+ }
+
+ for (decoded_bytes = 0, rc = 1; rc > 0 && decoded_bytes < buffer_size; )
+ {
+ rc = decoder->funcs->Decode (decoder, buffer + decoded_bytes,
+ buffer_size - decoded_bytes);
+ if (rc < 0)
+ {
+ log_add (log_Warning, "SoundDecoder_Decode(): "
+ "error decoding %s, code %ld",
+ decoder->filename, rc);
+ }
+ else if (rc == 0)
+ { // probably EOF
+ if (decoder->looping)
+ {
+ SoundDecoder_Rewind (decoder);
+ if (decoder->error)
+ {
+ log_add (log_Warning, "SoundDecoder_Decode(): "
+ "tried to loop %s but couldn't rewind, "
+ "error code %d",
+ decoder->filename, decoder->error);
+ }
+ else
+ {
+ log_add (log_Info, "SoundDecoder_Decode(): "
+ "looping %s", decoder->filename);
+ rc = 1; // prime the loop again
+ }
+ }
+ else
+ {
+ log_add (log_Info, "SoundDecoder_Decode(): eof for %s",
+ decoder->filename);
+ }
+ }
+ else
+ { // some bytes decoded
+ decoded_bytes += rc;
+ }
+ }
+ decoder->pos += decoded_bytes;
+ if (rc < 0)
+ decoder->error = SOUNDDECODER_ERROR;
+ else if (rc == 0 || decoder->pos >= max_bytes)
+ decoder->error = SOUNDDECODER_EOF;
+ else
+ decoder->error = SOUNDDECODER_OK;
+
+ if (decoder->need_swap && decoded_bytes > 0 &&
+ (decoder->format == decoder_formats.stereo16 ||
+ decoder->format == decoder_formats.mono16))
+ {
+ SoundDecoder_SwapWords (
+ decoder->buffer, decoded_bytes);
+ }
+
+ return decoded_bytes;
+}
+
+uint32
+SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder)
+{
+ uint32 decoded_bytes;
+ long rc;
+ uint32 reqbufsize;
+
+ if (!decoder || !decoder->funcs)
+ {
+ log_add (log_Warning, "SoundDecoder_DecodeAll(): null or bad decoder");
+ return 0;
+ }
+
+ reqbufsize = decoder->buffer_size;
+
+ if (decoder->looping)
+ {
+ log_add (log_Warning, "SoundDecoder_DecodeAll(): "
+ "called for %s with looping", decoder->filename);
+ return 0;
+ }
+
+ if (reqbufsize < 4096)
+ reqbufsize = 4096;
+
+ for (decoded_bytes = 0, rc = 1; rc > 0; )
+ {
+ if (decoded_bytes >= decoder->buffer_size)
+ { // need to grow buffer
+ decoder->buffer_size += reqbufsize;
+ decoder->buffer = HRealloc (
+ decoder->buffer, decoder->buffer_size);
+ }
+
+ rc = decoder->funcs->Decode (decoder,
+ (uint8*) decoder->buffer + decoded_bytes,
+ decoder->buffer_size - decoded_bytes);
+
+ if (rc > 0)
+ decoded_bytes += rc;
+ }
+ decoder->buffer_size = decoded_bytes;
+ decoder->pos += decoded_bytes;
+ // Free up some unused memory
+ decoder->buffer = HRealloc (decoder->buffer, decoded_bytes);
+
+ if (decoder->need_swap && decoded_bytes > 0 &&
+ (decoder->format == decoder_formats.stereo16 ||
+ decoder->format == decoder_formats.mono16))
+ {
+ SoundDecoder_SwapWords (
+ decoder->buffer, decoded_bytes);
+ }
+
+ if (rc < 0)
+ {
+ decoder->error = SOUNDDECODER_ERROR;
+ log_add (log_Warning, "SoundDecoder_DecodeAll(): "
+ "error decoding %s, code %ld",
+ decoder->filename, rc);
+ return decoded_bytes;
+ }
+
+ // switch to Buffer decoder
+ decoder->funcs->Close (decoder);
+ decoder->funcs->Term (decoder);
+
+ decoder->funcs = &bufa_DecoderVtbl;
+ decoder->funcs->Init (decoder);
+ decoder->pos = 0;
+ decoder->start_sample = 0;
+ decoder->error = SOUNDDECODER_OK;
+
+ return decoded_bytes;
+}
+
+void
+SoundDecoder_Rewind (TFB_SoundDecoder *decoder)
+{
+ SoundDecoder_Seek (decoder, 0);
+}
+
+// seekTime is specified in mili-seconds
+void
+SoundDecoder_Seek (TFB_SoundDecoder *decoder, uint32 seekTime)
+{
+ uint32 pcm_pos;
+
+ if (!decoder)
+ return;
+ if (!decoder->funcs)
+ {
+ log_add (log_Warning, "SoundDecoder_Seek(): bad decoder passed");
+ return;
+ }
+
+ pcm_pos = (uint32) (seekTime / 1000.0f * decoder->frequency);
+ pcm_pos = decoder->funcs->Seek (decoder,
+ decoder->start_sample + pcm_pos);
+ decoder->pos = pcm_pos * decoder->bytes_per_samp;
+ decoder->error = SOUNDDECODER_OK;
+}
+
+void
+SoundDecoder_Free (TFB_SoundDecoder *decoder)
+{
+ if (!decoder)
+ return;
+ if (!decoder->funcs)
+ {
+ log_add (log_Warning, "SoundDecoder_Free(): bad decoder passed");
+ return;
+ }
+
+ decoder->funcs->Close (decoder);
+ decoder->funcs->Term (decoder);
+
+ HFree (decoder->buffer);
+ HFree (decoder->filename);
+ HFree (decoder);
+}
+
+float
+SoundDecoder_GetTime (TFB_SoundDecoder *decoder)
+{
+ if (!decoder)
+ return 0.0f;
+ if (!decoder->funcs)
+ {
+ log_add (log_Warning, "SoundDecoder_GetTime(): bad decoder passed");
+ return 0.0f;
+ }
+
+ return (float)
+ ((decoder->pos / decoder->bytes_per_samp)
+ - decoder->start_sample
+ ) / decoder->frequency;
+}
+
+uint32
+SoundDecoder_GetFrame (TFB_SoundDecoder *decoder)
+{
+ if (!decoder)
+ return 0;
+ if (!decoder->funcs)
+ {
+ log_add (log_Warning, "SoundDecoder_GetFrame(): bad decoder passed");
+ return 0;
+ }
+
+ return decoder->funcs->GetFrame (decoder);
+}
+
+
+#define THIS_PTR TFB_SoundDecoder* This
+
+static const char*
+bufa_GetName (void)
+{
+ return "Buffer";
+}
+
+static bool
+bufa_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ // this should never be called
+ log_add (log_Debug, "bufa_InitModule(): dead function called");
+ return false;
+
+ (void)flags; (void)fmts; // laugh at compiler warning
+}
+
+static void
+bufa_TermModule (void)
+{
+ // this should never be called
+ log_add (log_Debug, "bufa_TermModule(): dead function called");
+}
+
+static uint32
+bufa_GetStructSize (void)
+{
+ return sizeof (TFB_BufSoundDecoder);
+}
+
+static int
+bufa_GetError (THIS_PTR)
+{
+ return 0; // error? what error?!
+
+ (void)This; // laugh at compiler warning
+}
+
+static bool
+bufa_Init (THIS_PTR)
+{
+ TFB_BufSoundDecoder* bufa = (TFB_BufSoundDecoder*) This;
+
+ This->need_swap = false;
+ // hijack the buffer
+ bufa->data = This->buffer;
+ bufa->max_pcm = This->buffer_size / This->bytes_per_samp;
+ bufa->cur_pcm = bufa->max_pcm;
+
+ return true;
+}
+
+static void
+bufa_Term (THIS_PTR)
+{
+ //TFB_BufSoundDecoder* bufa = (TFB_BufSoundDecoder*) This;
+ bufa_Close (This); // ensure cleanup
+}
+
+static bool
+bufa_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ // this should never be called
+ log_add (log_Debug, "bufa_Open(): dead function called");
+ return false;
+
+ // laugh at compiler warnings
+ (void)This; (void)dir; (void)filename;
+}
+
+static void
+bufa_Close (THIS_PTR)
+{
+ TFB_BufSoundDecoder* bufa = (TFB_BufSoundDecoder*) This;
+
+ // restore the status quo
+ if (bufa->data)
+ {
+ This->buffer = bufa->data;
+ bufa->data = NULL;
+ }
+ bufa->cur_pcm = 0;
+}
+
+static int
+bufa_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ TFB_BufSoundDecoder* bufa = (TFB_BufSoundDecoder*) This;
+ uint32 dec_pcm;
+ uint32 dec_bytes;
+
+ dec_pcm = bufsize / This->bytes_per_samp;
+ if (dec_pcm > bufa->max_pcm - bufa->cur_pcm)
+ dec_pcm = bufa->max_pcm - bufa->cur_pcm;
+ dec_bytes = dec_pcm * This->bytes_per_samp;
+
+ // Buffer decode is a hack
+ This->buffer = (uint8*) bufa->data
+ + bufa->cur_pcm * This->bytes_per_samp;
+
+ if (dec_pcm > 0)
+ bufa->cur_pcm += dec_pcm;
+
+ return dec_bytes;
+
+ (void)buf; // laugh at compiler warning
+}
+
+static uint32
+bufa_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ TFB_BufSoundDecoder* bufa = (TFB_BufSoundDecoder*) This;
+
+ if (pcm_pos > bufa->max_pcm)
+ pcm_pos = bufa->max_pcm;
+ bufa->cur_pcm = pcm_pos;
+
+ return pcm_pos;
+}
+
+static uint32
+bufa_GetFrame (THIS_PTR)
+{
+ return 0; // only 1 frame
+
+ (void)This; // laugh at compiler warning
+}
+
+
+static const char*
+nula_GetName (void)
+{
+ return "Null";
+}
+
+static bool
+nula_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ // this should never be called
+ log_add (log_Debug, "nula_InitModule(): dead function called");
+ return false;
+
+ (void)flags; (void)fmts; // laugh at compiler warning
+}
+
+static void
+nula_TermModule (void)
+{
+ // this should never be called
+ log_add (log_Debug, "nula_TermModule(): dead function called");
+}
+
+static uint32
+nula_GetStructSize (void)
+{
+ return sizeof (TFB_NullSoundDecoder);
+}
+
+static int
+nula_GetError (THIS_PTR)
+{
+ return 0; // error? what error?!
+
+ (void)This; // laugh at compiler warning
+}
+
+static bool
+nula_Init (THIS_PTR)
+{
+ TFB_NullSoundDecoder* nula = (TFB_NullSoundDecoder*) This;
+
+ This->need_swap = false;
+ nula->cur_pcm = 0;
+ return true;
+}
+
+static void
+nula_Term (THIS_PTR)
+{
+ //TFB_NullSoundDecoder* nula = (TFB_NullSoundDecoder*) This;
+ nula_Close (This); // ensure cleanup
+}
+
+static bool
+nula_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ This->frequency = 11025;
+ This->format = decoder_formats.mono16;
+ This->is_null = true;
+ return true;
+
+ // laugh at compiler warnings
+ (void)dir; (void)filename;
+}
+
+static void
+nula_Close (THIS_PTR)
+{
+ TFB_NullSoundDecoder* nula = (TFB_NullSoundDecoder*) This;
+
+ nula->cur_pcm = 0;
+}
+
+static int
+nula_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ TFB_NullSoundDecoder* nula = (TFB_NullSoundDecoder*) This;
+ uint32 max_pcm;
+ uint32 dec_pcm;
+ uint32 dec_bytes;
+
+ max_pcm = (uint32) (This->length * This->frequency);
+ dec_pcm = bufsize / This->bytes_per_samp;
+ if (dec_pcm > max_pcm - nula->cur_pcm)
+ dec_pcm = max_pcm - nula->cur_pcm;
+ dec_bytes = dec_pcm * This->bytes_per_samp;
+
+ if (dec_pcm > 0)
+ {
+ memset (buf, 0, dec_bytes);
+ nula->cur_pcm += dec_pcm;
+ }
+
+ return dec_bytes;
+}
+
+static uint32
+nula_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ TFB_NullSoundDecoder* nula = (TFB_NullSoundDecoder*) This;
+ uint32 max_pcm;
+
+ max_pcm = (uint32) (This->length * This->frequency);
+ if (pcm_pos > max_pcm)
+ pcm_pos = max_pcm;
+ nula->cur_pcm = pcm_pos;
+
+ return pcm_pos;
+}
+
+static uint32
+nula_GetFrame (THIS_PTR)
+{
+ return 0; // only 1 frame
+
+ (void)This; // laugh at compiler warning
+}
diff --git a/src/libs/sound/decoders/decoder.h b/src/libs/sound/decoders/decoder.h
new file mode 100644
index 0000000..2d6983c
--- /dev/null
+++ b/src/libs/sound/decoders/decoder.h
@@ -0,0 +1,129 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Sound file decoder for .wav, .mod, .ogg
+ * API is heavily influenced by SDL_sound.
+ */
+
+#ifndef DECODER_H
+#define DECODER_H
+
+#include "port.h"
+#include "types.h"
+#include "libs/uio.h"
+
+#ifndef OVCODEC_NONE
+# ifdef _MSC_VER
+# pragma comment (lib, "vorbisfile.lib")
+# endif /* _MSC_VER */
+#endif /* OVCODEC_NONE */
+
+typedef struct tfb_decoderformats
+{
+ bool big_endian;
+ bool want_big_endian;
+ uint32 mono8;
+ uint32 stereo8;
+ uint32 mono16;
+ uint32 stereo16;
+} TFB_DecoderFormats;
+
+// forward-declare
+typedef struct tfb_sounddecoder TFB_SoundDecoder;
+
+#define THIS_PTR TFB_SoundDecoder*
+
+typedef struct tfb_sounddecoderfunc
+{
+ const char* (* GetName) (void);
+ bool (* InitModule) (int flags, const TFB_DecoderFormats*);
+ void (* TermModule) (void);
+ uint32 (* GetStructSize) (void);
+ int (* GetError) (THIS_PTR);
+ bool (* Init) (THIS_PTR);
+ void (* Term) (THIS_PTR);
+ bool (* Open) (THIS_PTR, uio_DirHandle *dir, const char *filename);
+ void (* Close) (THIS_PTR);
+ int (* Decode) (THIS_PTR, void* buf, sint32 bufsize);
+ // returns <0 on error, ==0 when no more data, >0 bytes returned
+ uint32 (* Seek) (THIS_PTR, uint32 pcm_pos);
+ // returns the pcm position set
+ uint32 (* GetFrame) (THIS_PTR);
+
+} TFB_SoundDecoderFuncs;
+
+#undef THIS_PTR
+
+struct tfb_sounddecoder
+{
+ // decoder virtual funcs - R/O
+ const TFB_SoundDecoderFuncs *funcs;
+
+ // public R/O, set by decoder
+ uint32 format;
+ uint32 frequency;
+ float length; // total length in seconds
+ bool is_null;
+ bool need_swap;
+
+ // public R/O, set by wrapper
+ void *buffer;
+ uint32 buffer_size;
+ sint32 error;
+ uint32 bytes_per_samp;
+
+ // public R/W
+ bool looping;
+
+ // semi-private
+ uio_DirHandle *dir;
+ char *filename;
+ uint32 pos;
+ uint32 start_sample;
+ uint32 end_sample;
+
+};
+
+// return values
+enum
+{
+ SOUNDDECODER_OK,
+ SOUNDDECODER_ERROR,
+ SOUNDDECODER_EOF,
+};
+
+typedef struct TFB_RegSoundDecoder TFB_RegSoundDecoder;
+
+TFB_RegSoundDecoder* SoundDecoder_Register (const char* fileext,
+ TFB_SoundDecoderFuncs* decvtbl);
+void SoundDecoder_Unregister (TFB_RegSoundDecoder* regdec);
+const TFB_SoundDecoderFuncs* SoundDecoder_Lookup (const char* fileext);
+
+void SoundDecoder_SwapWords (uint16* data, uint32 size);
+sint32 SoundDecoder_Init (int flags, TFB_DecoderFormats* formats);
+void SoundDecoder_Uninit (void);
+TFB_SoundDecoder* SoundDecoder_Load (uio_DirHandle *dir,
+ char *filename, uint32 buffer_size, uint32 startTime, sint32 runTime);
+uint32 SoundDecoder_Decode (TFB_SoundDecoder *decoder);
+uint32 SoundDecoder_DecodeAll (TFB_SoundDecoder *decoder);
+float SoundDecoder_GetTime (TFB_SoundDecoder *decoder);
+uint32 SoundDecoder_GetFrame (TFB_SoundDecoder *decoder);
+void SoundDecoder_Seek (TFB_SoundDecoder *decoder, uint32 msecs);
+void SoundDecoder_Rewind (TFB_SoundDecoder *decoder);
+void SoundDecoder_Free (TFB_SoundDecoder *decoder);
+const char* SoundDecoder_GetName (TFB_SoundDecoder *decoder);
+
+#endif
diff --git a/src/libs/sound/decoders/dukaud.c b/src/libs/sound/decoders/dukaud.c
new file mode 100644
index 0000000..aeff373
--- /dev/null
+++ b/src/libs/sound/decoders/dukaud.c
@@ -0,0 +1,546 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* .duk sound track decoder
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "libs/memlib.h"
+#include "port.h"
+#include "types.h"
+#include "libs/uio.h"
+#include "dukaud.h"
+#include "decoder.h"
+#include "endian_uqm.h"
+
+#define DATA_BUF_SIZE 0x8000
+#define DUCK_GENERAL_FPS 14.622f
+
+
+#define THIS_PTR TFB_SoundDecoder* This
+
+static const char* duka_GetName (void);
+static bool duka_InitModule (int flags, const TFB_DecoderFormats*);
+static void duka_TermModule (void);
+static uint32 duka_GetStructSize (void);
+static int duka_GetError (THIS_PTR);
+static bool duka_Init (THIS_PTR);
+static void duka_Term (THIS_PTR);
+static bool duka_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void duka_Close (THIS_PTR);
+static int duka_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 duka_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 duka_GetFrame (THIS_PTR);
+
+TFB_SoundDecoderFuncs duka_DecoderVtbl =
+{
+ duka_GetName,
+ duka_InitModule,
+ duka_TermModule,
+ duka_GetStructSize,
+ duka_GetError,
+ duka_Init,
+ duka_Term,
+ duka_Open,
+ duka_Close,
+ duka_Decode,
+ duka_Seek,
+ duka_GetFrame,
+};
+
+typedef struct tfb_ducksounddecoder
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // public read-only
+ uint32 iframe; // current frame index
+ uint32 cframes; // total count of frames
+ uint32 channels; // number of channels
+ uint32 pcm_frame; // samples per frame
+
+ // private
+ sint32 last_error;
+ uio_Stream* duk;
+ uint32* frames;
+ // buffer
+ void* data;
+ uint32 maxdata;
+ uint32 cbdata;
+ uint32 dataofs;
+ // decoder stuff
+ sint32 predictors[2];
+
+} TFB_DuckSoundDecoder;
+
+
+typedef struct
+{
+ uint32 audsize;
+ uint32 vidsize;
+} DukAud_FrameHeader;
+
+typedef struct
+{
+ uint16 magic; // always 0xf77f
+ uint16 numsamples;
+ uint16 tag;
+ uint16 indices[2]; // initial indices for channels
+} DukAud_AudSubframe;
+
+static const TFB_DecoderFormats* duka_formats = NULL;
+
+static sint32
+duka_readAudFrameHeader (TFB_DuckSoundDecoder* duka, uint32 iframe,
+ DukAud_AudSubframe* aud)
+{
+ DukAud_FrameHeader hdr;
+
+ uio_fseek (duka->duk, duka->frames[iframe], SEEK_SET);
+ if (uio_fread (&hdr, sizeof(hdr), 1, duka->duk) != 1)
+ {
+ duka->last_error = errno;
+ return dukae_BadFile;
+ }
+ hdr.audsize = UQM_SwapBE32 (hdr.audsize);
+
+ if (uio_fread (aud, sizeof(*aud), 1, duka->duk) != 1)
+ {
+ duka->last_error = errno;
+ return dukae_BadFile;
+ }
+
+ aud->magic = UQM_SwapBE16 (aud->magic);
+ if (aud->magic != 0xf77f)
+ return duka->last_error = dukae_BadFile;
+
+ aud->numsamples = UQM_SwapBE16 (aud->numsamples);
+ aud->tag = UQM_SwapBE16 (aud->tag);
+ aud->indices[0] = UQM_SwapBE16 (aud->indices[0]);
+ aud->indices[1] = UQM_SwapBE16 (aud->indices[1]);
+
+ return 0;
+}
+
+// This table is from one of the files that came with the original 3do source
+// It's slightly different from the data used by MPlayer.
+static int adpcm_step[89] = {
+ 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xF,
+ 0x10, 0x12, 0x13, 0x15, 0x17, 0x1A, 0x1C, 0x1F,
+ 0x22, 0x26, 0x29, 0x2E, 0x32, 0x37, 0x3D, 0x43,
+ 0x4A, 0x51, 0x59, 0x62, 0x6C, 0x76, 0x82, 0x8F,
+ 0x9E, 0xAD, 0xBF, 0xD2, 0xE7, 0xFE, 0x117, 0x133,
+ 0x152, 0x174, 0x199, 0x1C2, 0x1EF, 0x220, 0x256, 0x292,
+ 0x2D4, 0x31D, 0x36C, 0x3C4, 0x424, 0x48E, 0x503, 0x583,
+ 0x610, 0x6AC, 0x756, 0x812, 0x8E1, 0x9C4, 0xABE, 0xBD1,
+ 0xCFF, 0xE4C, 0xFBA, 0x114D, 0x1308, 0x14EF, 0x1707, 0x1954,
+ 0x1BDD, 0x1EA6, 0x21B7, 0x2516,
+ 0x28CB, 0x2CDF, 0x315C, 0x364C,
+ 0x3BBA, 0x41B2, 0x4844, 0x4F7E,
+ 0x5771, 0x6030, 0x69CE, 0x7463,
+ 0x7FFF
+ };
+
+
+// *** BEGIN part copied from MPlayer ***
+// (some little changes)
+
+#if 0
+// pertinent tables for IMA ADPCM
+static int adpcm_step[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+#endif
+
+static int adpcm_index[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+// clamp a number between 0 and 88
+#define CLAMP_0_TO_88(x) \
+ if ((x) < 0) (x) = 0; else if ((x) > 88) (x) = 88;
+
+// clamp a number within a signed 16-bit range
+#define CLAMP_S16(x) \
+ if ((x) < -32768) \
+ (x) = -32768; \
+ else if ((x) > 32767) \
+ (x) = 32767;
+
+static void
+decode_nibbles (sint16 *output, sint32 output_size, sint32 channels,
+ sint32* predictors, uint16* indices)
+{
+ sint32 step[2];
+ sint32 index[2];
+ sint32 diff;
+ sint32 i;
+ int sign;
+ sint32 delta;
+ int channel_number = 0;
+
+ channels -= 1;
+ index[0] = indices[0];
+ index[1] = indices[1];
+ step[0] = adpcm_step[index[0]];
+ step[1] = adpcm_step[index[1]];
+
+ for (i = 0; i < output_size; i++)
+ {
+ delta = output[i];
+
+ index[channel_number] += adpcm_index[delta];
+ CLAMP_0_TO_88(index[channel_number]);
+
+ sign = delta & 8;
+ delta = delta & 7;
+
+#if 0
+ // fast approximation, used in most decoders
+ diff = step[channel_number] >> 3;
+ if (delta & 4) diff += step[channel_number];
+ if (delta & 2) diff += step[channel_number] >> 1;
+ if (delta & 1) diff += step[channel_number] >> 2;
+#else
+ // real thing
+// diff = ((signed)delta + 0.5) * step[channel_number] / 4;
+ diff = (((delta << 1) + 1) * step[channel_number]) >> 3;
+#endif
+
+ if (sign)
+ predictors[channel_number] -= diff;
+ else
+ predictors[channel_number] += diff;
+
+ CLAMP_S16(predictors[channel_number]);
+ output[i] = predictors[channel_number];
+ step[channel_number] = adpcm_step[index[channel_number]];
+
+ // toggle channel
+ channel_number ^= channels;
+ }
+}
+// *** END part copied from MPlayer ***
+
+static sint32
+duka_decodeFrame (TFB_DuckSoundDecoder* duka, DukAud_AudSubframe* header,
+ uint8* input)
+{
+ uint8* inend;
+ sint16* output;
+ sint16* outptr;
+ sint32 outputsize;
+
+ outputsize = header->numsamples * 2 * sizeof (sint16);
+ outptr = output = (sint16*) ((uint8*)duka->data + duka->cbdata);
+
+ for (inend = input + header->numsamples; input < inend; ++input)
+ {
+ *(outptr++) = *input >> 4;
+ *(outptr++) = *input & 0x0f;
+ }
+
+ decode_nibbles (output, header->numsamples * 2, duka->channels,
+ duka->predictors, header->indices);
+
+ duka->cbdata += outputsize;
+
+ return outputsize;
+}
+
+
+static sint32
+duka_readNextFrame (TFB_DuckSoundDecoder* duka)
+{
+ DukAud_FrameHeader hdr;
+ DukAud_AudSubframe* aud;
+ uint8* p;
+
+ uio_fseek (duka->duk, duka->frames[duka->iframe], SEEK_SET);
+ if (uio_fread (&hdr, sizeof(hdr), 1, duka->duk) != 1)
+ {
+ duka->last_error = errno;
+ return dukae_BadFile;
+ }
+ hdr.audsize = UQM_SwapBE32 (hdr.audsize);
+
+ // dump encoded data at the end of the buffer aligned on 8-byte
+ p = ((uint8*)duka->data + duka->maxdata - ((hdr.audsize + 7) & (-8)));
+ if (uio_fread (p, 1, hdr.audsize, duka->duk) != hdr.audsize)
+ {
+ duka->last_error = errno;
+ return dukae_BadFile;
+ }
+ aud = (DukAud_AudSubframe*) p;
+ p += sizeof(DukAud_AudSubframe);
+
+ aud->magic = UQM_SwapBE16 (aud->magic);
+ if (aud->magic != 0xf77f)
+ return duka->last_error = dukae_BadFile;
+
+ aud->numsamples = UQM_SwapBE16 (aud->numsamples);
+ aud->tag = UQM_SwapBE16 (aud->tag);
+ aud->indices[0] = UQM_SwapBE16 (aud->indices[0]);
+ aud->indices[1] = UQM_SwapBE16 (aud->indices[1]);
+
+ duka->iframe++;
+
+ return duka_decodeFrame (duka, aud, p);
+}
+
+static sint32
+duka_stuffBuffer (TFB_DuckSoundDecoder* duka, void* buf, sint32 bufsize)
+{
+ sint32 dataleft;
+
+ dataleft = duka->cbdata - duka->dataofs;
+ if (dataleft > 0)
+ {
+ if (dataleft > bufsize)
+ dataleft = bufsize & (-4);
+ memcpy (buf, (uint8*)duka->data + duka->dataofs, dataleft);
+ duka->dataofs += dataleft;
+ }
+
+ if (duka->cbdata > 0 && duka->dataofs >= duka->cbdata)
+ duka->cbdata = duka->dataofs = 0; // reset for new data
+
+ return dataleft;
+}
+
+
+static const char*
+duka_GetName (void)
+{
+ return "DukAud";
+}
+
+static bool
+duka_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ duka_formats = fmts;
+ return true;
+
+ (void)flags; // laugh at compiler warning
+}
+
+static void
+duka_TermModule (void)
+{
+ // no specific module term
+}
+
+static uint32
+duka_GetStructSize (void)
+{
+ return sizeof (TFB_DuckSoundDecoder);
+}
+
+static int
+duka_GetError (THIS_PTR)
+{
+ TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+ int ret = duka->last_error;
+ duka->last_error = dukae_None;
+ return ret;
+}
+
+static bool
+duka_Init (THIS_PTR)
+{
+ //TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+ This->need_swap =
+ duka_formats->big_endian != duka_formats->want_big_endian;
+ return true;
+}
+
+static void
+duka_Term (THIS_PTR)
+{
+ //TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+ duka_Close (This); // ensure cleanup
+}
+
+static bool
+duka_Open (THIS_PTR, uio_DirHandle *dir, const char *file)
+{
+ TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+ uio_Stream* duk;
+ uio_Stream* frm;
+ DukAud_AudSubframe aud;
+ char filename[256];
+ uint32 filelen;
+ size_t cread;
+ uint32 i;
+
+ filelen = strlen (file);
+ if (filelen > sizeof (filename) - 1)
+ return false;
+ strcpy (filename, file);
+
+ duk = uio_fopen (dir, filename, "rb");
+ if (!duk)
+ {
+ duka->last_error = errno;
+ return false;
+ }
+
+ strcpy (filename + filelen - 3, "frm");
+ frm = uio_fopen (dir, filename, "rb");
+ if (!frm)
+ {
+ duka->last_error = errno;
+ uio_fclose (duk);
+ return false;
+ }
+
+ duka->duk = duk;
+
+ uio_fseek (frm, 0, SEEK_END);
+ duka->cframes = uio_ftell (frm) / sizeof (uint32);
+ uio_fseek (frm, 0, SEEK_SET);
+ if (!duka->cframes)
+ {
+ duka->last_error = dukae_BadFile;
+ uio_fclose (frm);
+ duka_Close (This);
+ return false;
+ }
+
+ duka->frames = (uint32*) HMalloc (duka->cframes * sizeof (uint32));
+ cread = uio_fread (duka->frames, sizeof (uint32), duka->cframes, frm);
+ uio_fclose (frm);
+ if (cread != duka->cframes)
+ {
+ duka->last_error = dukae_BadFile;
+ duka_Close (This);
+ return false;
+ }
+
+ for (i = 0; i < duka->cframes; ++i)
+ duka->frames[i] = UQM_SwapBE32 (duka->frames[i]);
+
+ if (duka_readAudFrameHeader (duka, 0, &aud) < 0)
+ {
+ duka_Close (This);
+ return false;
+ }
+
+ This->frequency = 22050;
+ This->format = duka_formats->stereo16;
+ duka->channels = 2;
+ duka->pcm_frame = aud.numsamples;
+ duka->data = HMalloc (DATA_BUF_SIZE);
+ duka->maxdata = DATA_BUF_SIZE;
+
+ // estimate
+ This->length = (float) duka->cframes / DUCK_GENERAL_FPS;
+
+ duka->last_error = 0;
+
+ return true;
+}
+
+static void
+duka_Close (THIS_PTR)
+{
+ TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+
+ if (duka->data)
+ {
+ HFree (duka->data);
+ duka->data = NULL;
+ }
+ if (duka->frames)
+ {
+ HFree (duka->frames);
+ duka->frames = NULL;
+ }
+ if (duka->duk)
+ {
+ uio_fclose (duka->duk);
+ duka->duk = NULL;
+ }
+ duka->last_error = 0;
+}
+
+static int
+duka_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+ sint32 stuffed;
+ sint32 total = 0;
+
+ if (bufsize <= 0)
+ return duka->last_error = dukae_BadArg;
+
+ do
+ {
+ stuffed = duka_stuffBuffer (duka, buf, bufsize);
+ buf = (uint8*)buf + stuffed;
+ bufsize -= stuffed;
+ total += stuffed;
+
+ if (bufsize > 0 && duka->iframe < duka->cframes)
+ {
+ stuffed = duka_readNextFrame (duka);
+ if (stuffed <= 0)
+ return stuffed;
+ }
+ } while (bufsize > 0 && duka->iframe < duka->cframes);
+
+ return total;
+}
+
+static uint32
+duka_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+ uint32 iframe;
+
+ iframe = pcm_pos / duka->pcm_frame;
+ if (iframe < duka->cframes)
+ {
+ duka->iframe = iframe;
+ duka->cbdata = 0;
+ duka->dataofs = 0;
+ duka->predictors[0] = 0;
+ duka->predictors[1] = 0;
+ }
+ return duka->iframe * duka->pcm_frame;
+}
+
+static uint32
+duka_GetFrame (THIS_PTR)
+{
+ TFB_DuckSoundDecoder* duka = (TFB_DuckSoundDecoder*) This;
+
+ // if there is nothing buffered return the actual current frame
+ // otherwise return previous
+ return duka->dataofs == duka->cbdata ?
+ duka->iframe : duka->iframe - 1;
+}
diff --git a/src/libs/sound/decoders/dukaud.h b/src/libs/sound/decoders/dukaud.h
new file mode 100644
index 0000000..23c4201
--- /dev/null
+++ b/src/libs/sound/decoders/dukaud.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* .duk sound track decoder */
+
+#ifndef DUKAUD_H
+#define DUKAUD_H
+
+#include "decoder.h"
+
+extern TFB_SoundDecoderFuncs duka_DecoderVtbl;
+
+typedef enum
+{
+ // positive values are the same as in errno
+ dukae_None = 0,
+ dukae_Unknown = -1,
+ dukae_BadFile = -2,
+ dukae_BadArg = -3,
+ dukae_Other = -1000,
+} DukAud_Error;
+
+#endif // DUKAUD_H
diff --git a/src/libs/sound/decoders/modaud.c b/src/libs/sound/decoders/modaud.c
new file mode 100644
index 0000000..18c29a2
--- /dev/null
+++ b/src/libs/sound/decoders/modaud.c
@@ -0,0 +1,430 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* MikMod decoder (.mod adapter)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "libs/memlib.h"
+#include "port.h"
+#include "types.h"
+#include "endian_uqm.h"
+#include "libs/uio.h"
+#include "decoder.h"
+#include "libs/sound/audiocore.h"
+#include "libs/log.h"
+#include "modaud.h"
+
+#ifdef USE_INTERNAL_MIKMOD
+# include "libs/mikmod/mikmod.h"
+#else
+# include <mikmod.h>
+#endif
+
+#define THIS_PTR TFB_SoundDecoder* This
+
+static const char* moda_GetName (void);
+static bool moda_InitModule (int flags, const TFB_DecoderFormats*);
+static void moda_TermModule (void);
+static uint32 moda_GetStructSize (void);
+static int moda_GetError (THIS_PTR);
+static bool moda_Init (THIS_PTR);
+static void moda_Term (THIS_PTR);
+static bool moda_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void moda_Close (THIS_PTR);
+static int moda_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 moda_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 moda_GetFrame (THIS_PTR);
+
+TFB_SoundDecoderFuncs moda_DecoderVtbl =
+{
+ moda_GetName,
+ moda_InitModule,
+ moda_TermModule,
+ moda_GetStructSize,
+ moda_GetError,
+ moda_Init,
+ moda_Term,
+ moda_Open,
+ moda_Close,
+ moda_Decode,
+ moda_Seek,
+ moda_GetFrame,
+};
+
+typedef struct tfb_modsounddecoder
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // private
+ sint32 last_error;
+ MODULE* module;
+
+} TFB_ModSoundDecoder;
+
+
+
+// MikMod Output driver
+// we provide our own so that we can use MikMod as
+// generic decoder
+
+static void* buffer;
+static ULONG bufsize;
+static ULONG written;
+
+static ULONG*
+moda_mmout_SetOutputBuffer (void* buf, ULONG size)
+{
+ buffer = buf;
+ bufsize = size;
+ written = 0;
+ return &written;
+}
+
+static BOOL
+moda_mmout_IsThere (void)
+{
+ return 1;
+}
+
+static BOOL
+moda_mmout_Init (void)
+{
+ md_mode |= DMODE_SOFT_MUSIC | DMODE_SOFT_SNDFX;
+ return VC_Init ();
+}
+
+static void
+moda_mmout_Exit (void)
+{
+ VC_Exit ();
+}
+
+static void
+moda_mmout_Update (void)
+{
+ written = 0;
+ if (!buffer || bufsize == 0)
+ return;
+
+ written = VC_WriteBytes (buffer, bufsize);
+}
+
+static BOOL
+moda_mmout_Reset (void)
+{
+ return 0;
+}
+
+static char MDRIVER_name[] = "Mem Buffer";
+static char MDRIVER_version[] = "Mem Buffer driver v1.1";
+static char MDRIVER_alias[] = "membuf";
+
+static MDRIVER moda_mmout_drv =
+{
+ NULL,
+ //xxx libmikmod does not declare these fields const; it probably should.
+ MDRIVER_name, // Name
+ MDRIVER_version, // Version
+ 0, 255, // Voice limits
+ MDRIVER_alias, // Alias
+
+// The minimum mikmod version we support is 3.1.8
+#if (LIBMIKMOD_VERSION_MAJOR > 3) || \
+ ((LIBMIKMOD_VERSION_MAJOR == 3) && (LIBMIKMOD_VERSION_MINOR >= 2))
+ NULL, // Cmdline help
+#endif
+
+ NULL,
+ moda_mmout_IsThere,
+ VC_SampleLoad,
+ VC_SampleUnload,
+ VC_SampleSpace,
+ VC_SampleLength,
+ moda_mmout_Init,
+ moda_mmout_Exit,
+ moda_mmout_Reset,
+ VC_SetNumVoices,
+ VC_PlayStart,
+ VC_PlayStop,
+ moda_mmout_Update,
+ NULL, /* FIXME: Pause */
+ VC_VoiceSetVolume,
+ VC_VoiceGetVolume,
+ VC_VoiceSetFrequency,
+ VC_VoiceGetFrequency,
+ VC_VoiceSetPanning,
+ VC_VoiceGetPanning,
+ VC_VoicePlay,
+ VC_VoiceStop,
+ VC_VoiceStopped,
+ VC_VoiceGetPosition,
+ VC_VoiceRealVolume
+};
+
+
+static const TFB_DecoderFormats* moda_formats = NULL;
+
+// MikMod READER interface
+// we provide our own so that we can do loading via uio
+//
+typedef struct MUIOREADER
+{
+ MREADER core;
+ uio_Stream* file;
+
+} MUIOREADER;
+
+static BOOL
+moda_uioReader_Eof (MREADER* reader)
+{
+ return uio_feof (((MUIOREADER*)reader)->file);
+}
+
+static BOOL
+moda_uioReader_Read (MREADER* reader, void* ptr, size_t size)
+{
+ return uio_fread (ptr, size, 1, ((MUIOREADER*)reader)->file);
+}
+
+static int
+moda_uioReader_Get (MREADER* reader)
+{
+ return uio_fgetc (((MUIOREADER*)reader)->file);
+}
+
+static BOOL
+moda_uioReader_Seek (MREADER* reader, long offset, int whence)
+{
+ return uio_fseek (((MUIOREADER*)reader)->file, offset, whence);
+}
+
+static long
+moda_uioReader_Tell (MREADER* reader)
+{
+ return uio_ftell (((MUIOREADER*)reader)->file);
+}
+
+static MREADER*
+moda_new_uioReader (uio_Stream* fp)
+{
+ MUIOREADER* reader = (MUIOREADER*) HMalloc (sizeof(MUIOREADER));
+ if (reader)
+ {
+ reader->core.Eof = &moda_uioReader_Eof;
+ reader->core.Read = &moda_uioReader_Read;
+ reader->core.Get = &moda_uioReader_Get;
+ reader->core.Seek = &moda_uioReader_Seek;
+ reader->core.Tell = &moda_uioReader_Tell;
+ reader->file = fp;
+ }
+ return (MREADER*)reader;
+}
+
+static void
+moda_delete_uioReader (MREADER* reader)
+{
+ if (reader)
+ HFree (reader);
+}
+
+
+static const char*
+moda_GetName (void)
+{
+ return "MikMod";
+}
+
+static bool
+moda_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ MikMod_RegisterDriver (&moda_mmout_drv);
+ MikMod_RegisterAllLoaders ();
+
+ if (flags & audio_QUALITY_HIGH)
+ {
+ md_mode = DMODE_HQMIXER|DMODE_STEREO|DMODE_16BITS|DMODE_INTERP|DMODE_SURROUND;
+ md_mixfreq = 44100;
+ md_reverb = 1;
+ }
+ else if (flags & audio_QUALITY_LOW)
+ {
+ md_mode = DMODE_SOFT_MUSIC|DMODE_STEREO|DMODE_16BITS;
+#ifdef __SYMBIAN32__
+ md_mixfreq = 11025;
+#else
+ md_mixfreq = 22050;
+#endif
+ md_reverb = 0;
+ }
+ else
+ {
+ md_mode = DMODE_SOFT_MUSIC|DMODE_STEREO|DMODE_16BITS|DMODE_INTERP;
+ md_mixfreq = 44100;
+ md_reverb = 0;
+ }
+
+ md_pansep = 64;
+
+ if (MikMod_Init (NULL))
+ {
+ log_add (log_Error, "MikMod_Init() failed, %s",
+ MikMod_strerror (MikMod_errno));
+ return false;
+ }
+
+ moda_formats = fmts;
+
+ return true;
+}
+
+static void
+moda_TermModule (void)
+{
+ MikMod_Exit ();
+}
+
+static uint32
+moda_GetStructSize (void)
+{
+ return sizeof (TFB_ModSoundDecoder);
+}
+
+static int
+moda_GetError (THIS_PTR)
+{
+ TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+ int ret = moda->last_error;
+ moda->last_error = 0;
+ return ret;
+}
+
+static bool
+moda_Init (THIS_PTR)
+{
+ //TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+ This->need_swap =
+ moda_formats->big_endian != moda_formats->want_big_endian;
+ return true;
+}
+
+static void
+moda_Term (THIS_PTR)
+{
+ //TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+ moda_Close (This); // ensure cleanup
+}
+
+static bool
+moda_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+ uio_Stream *fp;
+ MREADER* reader;
+ MODULE* mod;
+
+ fp = uio_fopen (dir, filename, "rb");
+ if (!fp)
+ {
+ moda->last_error = errno;
+ return false;
+ }
+
+ reader = moda_new_uioReader (fp);
+ if (!reader)
+ {
+ moda->last_error = -1;
+ uio_fclose (fp);
+ return false;
+ }
+
+ mod = Player_LoadGeneric (reader, 8, 0);
+
+ // can already dispose of reader and fileh
+ moda_delete_uioReader (reader);
+ uio_fclose (fp);
+ if (!mod)
+ {
+ log_add (log_Warning, "moda_Open(): could not load %s", filename);
+ return false;
+ }
+
+ moda->module = mod;
+ mod->extspd = 1;
+ mod->panflag = 1;
+ mod->wrap = 0;
+ mod->loop = 1;
+
+ This->format = moda_formats->stereo16;
+ This->frequency = md_mixfreq;
+ This->length = 0; // FIXME way to obtain this from mikmod?
+
+ moda->last_error = 0;
+
+ return true;
+}
+
+static void
+moda_Close (THIS_PTR)
+{
+ TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+
+ if (moda->module)
+ {
+ Player_Free (moda->module);
+ moda->module = NULL;
+ }
+}
+
+static int
+moda_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+ volatile ULONG* poutsize;
+
+ Player_Start (moda->module);
+ if (!Player_Active())
+ return 0;
+
+ poutsize = moda_mmout_SetOutputBuffer (buf, bufsize);
+ MikMod_Update ();
+
+ return *poutsize;
+}
+
+static uint32
+moda_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+
+ Player_Start (moda->module);
+ if (pcm_pos)
+ log_add (log_Debug, "moda_Seek(): "
+ "non-zero seek positions not supported for mod");
+ Player_SetPosition (0);
+
+ return 0;
+}
+
+static uint32
+moda_GetFrame (THIS_PTR)
+{
+ TFB_ModSoundDecoder* moda = (TFB_ModSoundDecoder*) This;
+ return moda->module->sngpos;
+}
diff --git a/src/libs/sound/decoders/modaud.h b/src/libs/sound/decoders/modaud.h
new file mode 100644
index 0000000..3b0eb86
--- /dev/null
+++ b/src/libs/sound/decoders/modaud.h
@@ -0,0 +1,26 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* MikMod adapter */
+
+#ifndef MODAUD_H
+#define MODAUD_H
+
+#include "decoder.h"
+
+extern TFB_SoundDecoderFuncs moda_DecoderVtbl;
+
+#endif // MODAUD_H
diff --git a/src/libs/sound/decoders/oggaud.c b/src/libs/sound/decoders/oggaud.c
new file mode 100644
index 0000000..6227120
--- /dev/null
+++ b/src/libs/sound/decoders/oggaud.c
@@ -0,0 +1,278 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Ogg Vorbis decoder (.ogg adapter)
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "libs/log.h"
+#include "port.h"
+#include "types.h"
+#include "libs/uio.h"
+#include "decoder.h"
+#ifdef OVCODEC_TREMOR
+# include <tremor/ivorbiscodec.h>
+# include <tremor/ivorbisfile.h>
+#else
+# include <vorbis/codec.h>
+# include <vorbis/vorbisfile.h>
+#endif /* OVCODEC_TREMOR */
+#include "oggaud.h"
+
+
+#define THIS_PTR TFB_SoundDecoder* This
+
+static const char* ova_GetName (void);
+static bool ova_InitModule (int flags, const TFB_DecoderFormats*);
+static void ova_TermModule (void);
+static uint32 ova_GetStructSize (void);
+static int ova_GetError (THIS_PTR);
+static bool ova_Init (THIS_PTR);
+static void ova_Term (THIS_PTR);
+static bool ova_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void ova_Close (THIS_PTR);
+static int ova_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 ova_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 ova_GetFrame (THIS_PTR);
+
+TFB_SoundDecoderFuncs ova_DecoderVtbl =
+{
+ ova_GetName,
+ ova_InitModule,
+ ova_TermModule,
+ ova_GetStructSize,
+ ova_GetError,
+ ova_Init,
+ ova_Term,
+ ova_Open,
+ ova_Close,
+ ova_Decode,
+ ova_Seek,
+ ova_GetFrame,
+};
+
+typedef struct tfb_oggsounddecoder
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // private
+ sint32 last_error;
+ OggVorbis_File vf;
+
+} TFB_OggSoundDecoder;
+
+static const TFB_DecoderFormats* ova_formats = NULL;
+
+static size_t
+ogg_read (void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+ return uio_fread (ptr, size, nmemb, (uio_Stream *) datasource);
+}
+
+static int
+ogg_seek (void *datasource, ogg_int64_t offset, int whence)
+{
+ long off = (long) offset;
+ return uio_fseek ((uio_Stream *) datasource, off, whence);
+}
+
+static int
+ogg_close (void *datasource)
+{
+ return uio_fclose ((uio_Stream *) datasource);
+}
+
+static long
+ogg_tell (void *datasource)
+{
+ return uio_ftell ((uio_Stream *) datasource);
+}
+
+static const ov_callbacks ogg_callbacks =
+{
+ ogg_read,
+ ogg_seek,
+ ogg_close,
+ ogg_tell,
+};
+
+static const char*
+ova_GetName (void)
+{
+ return "Ogg Vorbis";
+}
+
+static bool
+ova_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ ova_formats = fmts;
+ return true;
+
+ (void)flags; // laugh at compiler warning
+}
+
+static void
+ova_TermModule (void)
+{
+ // no specific module term
+}
+
+static uint32
+ova_GetStructSize (void)
+{
+ return sizeof (TFB_OggSoundDecoder);
+}
+
+static int
+ova_GetError (THIS_PTR)
+{
+ TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+ int ret = ova->last_error;
+ ova->last_error = 0;
+ return ret;
+}
+
+static bool
+ova_Init (THIS_PTR)
+{
+ //TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+ This->need_swap = false;
+ return true;
+}
+
+static void
+ova_Term (THIS_PTR)
+{
+ //TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+ ova_Close (This); // ensure cleanup
+}
+
+static bool
+ova_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+ int rc;
+ uio_Stream *fp;
+ vorbis_info *vinfo;
+
+ fp = uio_fopen (dir, filename, "rb");
+ if (fp == NULL)
+ {
+ log_add (log_Warning, "ova_Open(): could not open %s", filename);
+ return false;
+ }
+
+ rc = ov_open_callbacks (fp, &ova->vf, NULL, 0, ogg_callbacks);
+ if (rc != 0)
+ {
+ log_add (log_Warning, "ova_Open(): "
+ "ov_open_callbacks failed for %s, error code %d",
+ filename, rc);
+ uio_fclose (fp);
+ return false;
+ }
+
+ vinfo = ov_info (&ova->vf, -1);
+ if (!vinfo)
+ {
+ log_add (log_Warning, "ova_Open(): "
+ "failed to retrieve ogg bitstream info for %s",
+ filename);
+ ov_clear (&ova->vf);
+ return false;
+ }
+
+ This->frequency = vinfo->rate;
+#ifdef OVCODEC_TREMOR
+ // With tremor ov_time_total returns an integer, in milliseconds.
+ This->length = ((float) ov_time_total (&ova->vf, -1)) / 1000.0f;
+#else
+ // With libvorbis ov_time_total returns a double, in seconds.
+ This->length = (float) ov_time_total (&ova->vf, -1);
+#endif /* OVCODEC_TREMOR */
+
+ if (vinfo->channels == 1)
+ This->format = ova_formats->mono16;
+ else
+ This->format = ova_formats->stereo16;
+
+ ova->last_error = 0;
+
+ return true;
+}
+
+static void
+ova_Close (THIS_PTR)
+{
+ TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+
+ ov_clear (&ova->vf);
+}
+
+static int
+ova_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+ long rc;
+ int bitstream;
+
+#ifdef OVCODEC_TREMOR
+ rc = ov_read (&ova->vf, buf, bufsize, &bitstream);
+#else
+ rc = ov_read (&ova->vf, buf, bufsize, ova_formats->want_big_endian,
+ 2, 1, &bitstream);
+#endif /* OVCODEC_TREMOR */
+
+ if (rc < 0)
+ ova->last_error = rc;
+ else
+ ova->last_error = 0;
+
+ return rc;
+}
+
+static uint32
+ova_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+ int ret;
+
+ ret = ov_pcm_seek (&ova->vf, pcm_pos);
+ if (ret != 0)
+ {
+ ova->last_error = ret;
+ return (uint32) ov_pcm_tell (&ova->vf);
+ }
+ else
+ return pcm_pos;
+}
+
+static uint32
+ova_GetFrame (THIS_PTR)
+{
+ TFB_OggSoundDecoder* ova = (TFB_OggSoundDecoder*) This;
+ // this is the closest to a frame there is in ogg vorbis stream
+ // doesn't seem to be a func to retrive it
+#ifdef OVCODEC_TREMOR
+ return ova->vf.os->pageno;
+#else
+ return ova->vf.os.pageno;
+#endif /* OVCODEC_TREMOR */
+}
+
diff --git a/src/libs/sound/decoders/oggaud.h b/src/libs/sound/decoders/oggaud.h
new file mode 100644
index 0000000..4e443c4
--- /dev/null
+++ b/src/libs/sound/decoders/oggaud.h
@@ -0,0 +1,26 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Ogg Vorbis adapter */
+
+#ifndef OGGAUD_H
+#define OGGAUD_H
+
+#include "decoder.h"
+
+extern TFB_SoundDecoderFuncs ova_DecoderVtbl;
+
+#endif // OGGAUD_H
diff --git a/src/libs/sound/decoders/wav.c b/src/libs/sound/decoders/wav.c
new file mode 100644
index 0000000..c22f63f
--- /dev/null
+++ b/src/libs/sound/decoders/wav.c
@@ -0,0 +1,385 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Wave decoder (.wav adapter)
+ * Code is based on Creative's Win32 OpenAL implementation.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include "port.h"
+#include "types.h"
+#include "libs/uio.h"
+#include "endian_uqm.h"
+#include "libs/log.h"
+#include "wav.h"
+
+#define wave_MAKE_ID(x1, x2, x3, x4) \
+ (((x4) << 24) | ((x3) << 16) | ((x2) << 8) | (x1))
+
+#define wave_RiffID wave_MAKE_ID('R', 'I', 'F', 'F')
+#define wave_WaveID wave_MAKE_ID('W', 'A', 'V', 'E')
+#define wave_FmtID wave_MAKE_ID('f', 'm', 't', ' ')
+#define wave_DataID wave_MAKE_ID('d', 'a', 't', 'a')
+
+typedef struct
+{
+ uint32 id;
+ uint32 size;
+ uint32 type;
+} wave_FileHeader;
+
+typedef struct
+{
+ uint16 format;
+ uint16 channels;
+ uint32 samplesPerSec;
+ uint32 bytesPerSec;
+ uint16 blockAlign;
+ uint16 bitsPerSample;
+} wave_FormatHeader;
+
+typedef struct
+{
+ uint32 id;
+ uint32 size;
+} wave_ChunkHeader;
+
+
+#define THIS_PTR TFB_SoundDecoder* This
+
+static const char* wava_GetName (void);
+static bool wava_InitModule (int flags, const TFB_DecoderFormats*);
+static void wava_TermModule (void);
+static uint32 wava_GetStructSize (void);
+static int wava_GetError (THIS_PTR);
+static bool wava_Init (THIS_PTR);
+static void wava_Term (THIS_PTR);
+static bool wava_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void wava_Close (THIS_PTR);
+static int wava_Decode (THIS_PTR, void* buf, sint32 bufsize);
+static uint32 wava_Seek (THIS_PTR, uint32 pcm_pos);
+static uint32 wava_GetFrame (THIS_PTR);
+
+TFB_SoundDecoderFuncs wava_DecoderVtbl =
+{
+ wava_GetName,
+ wava_InitModule,
+ wava_TermModule,
+ wava_GetStructSize,
+ wava_GetError,
+ wava_Init,
+ wava_Term,
+ wava_Open,
+ wava_Close,
+ wava_Decode,
+ wava_Seek,
+ wava_GetFrame,
+};
+
+typedef struct tfb_wavesounddecoder
+{
+ // always the first member
+ TFB_SoundDecoder decoder;
+
+ // private
+ sint32 last_error;
+ uio_Stream *fp;
+ wave_FormatHeader fmtHdr;
+ uint32 data_ofs;
+ uint32 data_size;
+ uint32 max_pcm;
+ uint32 cur_pcm;
+
+} TFB_WaveSoundDecoder;
+
+static const TFB_DecoderFormats* wava_formats = NULL;
+
+
+static const char*
+wava_GetName (void)
+{
+ return "Wave";
+}
+
+static bool
+wava_InitModule (int flags, const TFB_DecoderFormats* fmts)
+{
+ wava_formats = fmts;
+ return true;
+
+ (void)flags; // laugh at compiler warning
+}
+
+static void
+wava_TermModule (void)
+{
+ // no specific module term
+}
+
+static uint32
+wava_GetStructSize (void)
+{
+ return sizeof (TFB_WaveSoundDecoder);
+}
+
+static int
+wava_GetError (THIS_PTR)
+{
+ TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+ int ret = wava->last_error;
+ wava->last_error = 0;
+ return ret;
+}
+
+static bool
+wava_Init (THIS_PTR)
+{
+ //TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+ This->need_swap = wava_formats->want_big_endian;
+ return true;
+}
+
+static void
+wava_Term (THIS_PTR)
+{
+ //TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+ wava_Close (This); // ensure cleanup
+}
+
+static bool
+read_le_16 (uio_Stream *fp, uint16 *v)
+{
+ if (!uio_fread (v, sizeof(*v), 1, fp))
+ return false;
+ *v = UQM_SwapLE16 (*v);
+ return true;
+}
+
+static bool
+read_le_32 (uio_Stream *fp, uint32 *v)
+{
+ if (!uio_fread (v, sizeof(*v), 1, fp))
+ return false;
+ *v = UQM_SwapLE32 (*v);
+ return true;
+}
+
+static bool
+wava_readFileHeader (TFB_WaveSoundDecoder* wava, wave_FileHeader* hdr)
+{
+ if (!read_le_32 (wava->fp, &hdr->id) ||
+ !read_le_32 (wava->fp, &hdr->size) ||
+ !read_le_32 (wava->fp, &hdr->type))
+ {
+ wava->last_error = errno;
+ return false;
+ }
+ return true;
+}
+
+static bool
+wava_readChunkHeader (TFB_WaveSoundDecoder* wava, wave_ChunkHeader* chunk)
+{
+ if (!read_le_32 (wava->fp, &chunk->id) ||
+ !read_le_32 (wava->fp, &chunk->size))
+ {
+ wava->last_error = errno;
+ return false;
+ }
+ return true;
+}
+
+static bool
+wava_readFormatHeader (TFB_WaveSoundDecoder* wava, wave_FormatHeader* fmt)
+{
+ if (!read_le_16 (wava->fp, &fmt->format) ||
+ !read_le_16 (wava->fp, &fmt->channels) ||
+ !read_le_32 (wava->fp, &fmt->samplesPerSec) ||
+ !read_le_32 (wava->fp, &fmt->bytesPerSec) ||
+ !read_le_16 (wava->fp, &fmt->blockAlign) ||
+ !read_le_16 (wava->fp, &fmt->bitsPerSample))
+ {
+ wava->last_error = errno;
+ return false;
+ }
+ return true;
+}
+
+static bool
+wava_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+ wave_FileHeader fileHdr;
+ wave_ChunkHeader chunkHdr;
+ long dataLeft;
+
+ wava->fp = uio_fopen (dir, filename, "rb");
+ if (!wava->fp)
+ {
+ wava->last_error = errno;
+ return false;
+ }
+
+ wava->data_size = 0;
+ wava->data_ofs = 0;
+
+ // read wave header
+ if (!wava_readFileHeader (wava, &fileHdr))
+ {
+ wava->last_error = errno;
+ wava_Close (This);
+ return false;
+ }
+ if (fileHdr.id != wave_RiffID || fileHdr.type != wave_WaveID)
+ {
+ log_add (log_Warning, "wava_Open(): "
+ "not a wave file, ID 0x%08x, Type 0x%08x",
+ fileHdr.id, fileHdr.type);
+ wava_Close (This);
+ return false;
+ }
+
+ for (dataLeft = ((fileHdr.size + 1) & ~1) - 4; dataLeft > 0;
+ dataLeft -= (((chunkHdr.size + 1) & ~1) + 8))
+ {
+ if (!wava_readChunkHeader (wava, &chunkHdr))
+ {
+ wava_Close (This);
+ return false;
+ }
+
+ if (chunkHdr.id == wave_FmtID)
+ {
+ if (!wava_readFormatHeader (wava, &wava->fmtHdr))
+ {
+ wava_Close (This);
+ return false;
+ }
+ uio_fseek (wava->fp, chunkHdr.size - 16, SEEK_CUR);
+ }
+ else
+ {
+ if (chunkHdr.id == wave_DataID)
+ {
+ wava->data_size = chunkHdr.size;
+ wava->data_ofs = uio_ftell (wava->fp);
+ }
+ uio_fseek (wava->fp, chunkHdr.size, SEEK_CUR);
+ }
+
+ // 2-align the file ptr
+ // XXX: I do not think this is necessary in WAVE files;
+ // possibly a remnant of ported AIFF reader
+ uio_fseek (wava->fp, chunkHdr.size & 1, SEEK_CUR);
+ }
+
+ if (!wava->data_size || !wava->data_ofs)
+ {
+ log_add (log_Warning, "wava_Open(): bad wave file,"
+ " no DATA chunk found");
+ wava_Close (This);
+ return false;
+ }
+
+ if (wava->fmtHdr.format != 0x0001)
+ { // not a PCM format
+ log_add (log_Warning, "wava_Open(): unsupported format %x",
+ wava->fmtHdr.format);
+ wava_Close (This);
+ return false;
+ }
+ if (wava->fmtHdr.channels != 1 && wava->fmtHdr.channels != 2)
+ {
+ log_add (log_Warning, "wava_Open(): unsupported number of channels %u",
+ (unsigned)wava->fmtHdr.channels);
+ wava_Close (This);
+ return false;
+ }
+
+ if (dataLeft != 0)
+ log_add (log_Warning, "wava_Open(): bad or unsupported wave file, "
+ "size in header does not match read chunks");
+
+ This->format = (wava->fmtHdr.channels == 1 ?
+ (wava->fmtHdr.bitsPerSample == 8 ?
+ wava_formats->mono8 : wava_formats->mono16)
+ :
+ (wava->fmtHdr.bitsPerSample == 8 ?
+ wava_formats->stereo8 : wava_formats->stereo16)
+ );
+ This->frequency = wava->fmtHdr.samplesPerSec;
+
+ uio_fseek (wava->fp, wava->data_ofs, SEEK_SET);
+ wava->max_pcm = wava->data_size / wava->fmtHdr.blockAlign;
+ wava->cur_pcm = 0;
+ This->length = (float) wava->max_pcm / wava->fmtHdr.samplesPerSec;
+ wava->last_error = 0;
+
+ return true;
+}
+
+static void
+wava_Close (THIS_PTR)
+{
+ TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+
+ if (wava->fp)
+ {
+ uio_fclose (wava->fp);
+ wava->fp = NULL;
+ }
+}
+
+static int
+wava_Decode (THIS_PTR, void* buf, sint32 bufsize)
+{
+ TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+ uint32 dec_pcm;
+
+ dec_pcm = bufsize / wava->fmtHdr.blockAlign;
+ if (dec_pcm > wava->max_pcm - wava->cur_pcm)
+ dec_pcm = wava->max_pcm - wava->cur_pcm;
+
+ dec_pcm = uio_fread (buf, wava->fmtHdr.blockAlign, dec_pcm, wava->fp);
+ wava->cur_pcm += dec_pcm;
+
+ return dec_pcm * wava->fmtHdr.blockAlign;
+}
+
+static uint32
+wava_Seek (THIS_PTR, uint32 pcm_pos)
+{
+ TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+
+ if (pcm_pos > wava->max_pcm)
+ pcm_pos = wava->max_pcm;
+ wava->cur_pcm = pcm_pos;
+ uio_fseek (wava->fp,
+ wava->data_ofs + pcm_pos * wava->fmtHdr.blockAlign,
+ SEEK_SET);
+
+ return pcm_pos;
+}
+
+static uint32
+wava_GetFrame (THIS_PTR)
+{
+ //TFB_WaveSoundDecoder* wava = (TFB_WaveSoundDecoder*) This;
+ return 0; // only 1 frame for now
+
+ (void)This; // laugh at compiler warning
+}
diff --git a/src/libs/sound/decoders/wav.h b/src/libs/sound/decoders/wav.h
new file mode 100644
index 0000000..9aaf347
--- /dev/null
+++ b/src/libs/sound/decoders/wav.h
@@ -0,0 +1,26 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Wave decoder */
+
+#ifndef WAV_H
+#define WAV_H
+
+#include "decoder.h"
+
+extern TFB_SoundDecoderFuncs wava_DecoderVtbl;
+
+#endif
diff --git a/src/libs/sound/fileinst.c b/src/libs/sound/fileinst.c
new file mode 100644
index 0000000..cafbb8f
--- /dev/null
+++ b/src/libs/sound/fileinst.c
@@ -0,0 +1,87 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sound.h"
+#include "sndintrn.h"
+#include "options.h"
+#include "libs/reslib.h"
+#include <string.h>
+
+
+SOUND_REF
+LoadSoundFile (const char *pStr)
+{
+ uio_Stream *fp;
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ fp = res_OpenResFile (contentDir, pStr, "rb");
+ if (fp)
+ {
+ SOUND_REF hData;
+
+ _cur_resfile_name = pStr;
+ hData = (SOUND_REF)_GetSoundBankData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+
+ res_CloseResFile (fp);
+
+ return hData;
+ }
+
+ return NULL;
+}
+
+MUSIC_REF
+LoadMusicFile (const char *pStr)
+{
+ uio_Stream *fp;
+ char filename[256];
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ strncpy (filename, pStr, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ CheckMusicResName (filename);
+
+ // Opening the res file is not technically necessary right now
+ // since _GetMusicData() completely ignores the arguments
+ // But just for the sake of correctness
+ fp = res_OpenResFile (contentDir, filename, "rb");
+ if (fp)
+ {
+ MUSIC_REF hData;
+
+ _cur_resfile_name = filename;
+ hData = (MUSIC_REF)_GetMusicData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+
+ res_CloseResFile (fp);
+
+ return hData;
+ }
+
+ return (0);
+}
+
diff --git a/src/libs/sound/mixer/Makeinfo b/src/libs/sound/mixer/Makeinfo
new file mode 100644
index 0000000..66f960d
--- /dev/null
+++ b/src/libs/sound/mixer/Makeinfo
@@ -0,0 +1,3 @@
+uqm_SUBDIRS="sdl nosound"
+uqm_CFILES="mixer.c"
+uqm_HFILES="mixer.h mixerint.h"
diff --git a/src/libs/sound/mixer/mixer.c b/src/libs/sound/mixer/mixer.c
new file mode 100644
index 0000000..3e14ddd
--- /dev/null
+++ b/src/libs/sound/mixer/mixer.c
@@ -0,0 +1,1760 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Mixer for low-level sound output drivers
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include "mixer.h"
+#include "mixerint.h"
+#include "libs/misc.h"
+#include "libs/threadlib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+static uint32 mixer_initialized = 0;
+static uint32 mixer_format;
+static uint32 mixer_chansize;
+static uint32 mixer_sampsize;
+static uint32 mixer_freq;
+static uint32 mixer_channels;
+static uint32 last_error = MIX_NO_ERROR;
+static mixer_Quality mixer_quality;
+static mixer_Resampling mixer_resampling;
+static mixer_Flags mixer_flags;
+
+/* when locking more than one mutex
+ * you must lock them in this order
+ */
+static RecursiveMutex src_mutex;
+static RecursiveMutex buf_mutex;
+static RecursiveMutex act_mutex;
+
+#define MAX_SOURCES 8
+mixer_Source *active_sources[MAX_SOURCES];
+
+
+/*************************************************
+ * Internals
+ */
+
+static void
+mixer_SetError (uint32 error)
+{
+ last_error = error;
+}
+
+
+/*************************************************
+ * General interface
+ */
+
+uint32
+mixer_GetError (void)
+{
+ uint32 error = last_error;
+ last_error = MIX_NO_ERROR;
+ return error;
+}
+
+/* Initialize the mixer with a certain audio format */
+bool
+mixer_Init (uint32 frequency, uint32 format, mixer_Quality quality,
+ mixer_Flags flags)
+{
+ if (mixer_initialized)
+ mixer_Uninit ();
+
+ last_error = MIX_NO_ERROR;
+ memset (active_sources, 0, sizeof(mixer_Source*) * MAX_SOURCES);
+
+ mixer_chansize = MIX_FORMAT_BPC (format);
+ mixer_channels = MIX_FORMAT_CHANS (format);
+ mixer_sampsize = MIX_FORMAT_SAMPSIZE (format);
+ mixer_freq = frequency;
+ mixer_quality = quality;
+ mixer_format = format;
+ mixer_flags = flags;
+
+ mixer_resampling.None = mixer_ResampleNone;
+ mixer_resampling.Downsample = mixer_ResampleNearest;
+ if (mixer_quality == MIX_QUALITY_DEFAULT)
+ mixer_resampling.Upsample = mixer_UpsampleLinear;
+ else if (mixer_quality == MIX_QUALITY_HIGH)
+ mixer_resampling.Upsample = mixer_UpsampleCubic;
+ else
+ mixer_resampling.Upsample = mixer_ResampleNearest;
+
+ src_mutex = CreateRecursiveMutex("mixer_SourceMutex", SYNC_CLASS_AUDIO);
+ buf_mutex = CreateRecursiveMutex("mixer_BufferMutex", SYNC_CLASS_AUDIO);
+ act_mutex = CreateRecursiveMutex("mixer_ActiveMutex", SYNC_CLASS_AUDIO);
+
+ mixer_initialized = 1;
+
+ return true;
+}
+
+/* Uninitialize the mixer */
+void
+mixer_Uninit (void)
+{
+ if (mixer_initialized)
+ {
+ DestroyRecursiveMutex (src_mutex);
+ DestroyRecursiveMutex (buf_mutex);
+ DestroyRecursiveMutex (act_mutex);
+ mixer_initialized = 0;
+ }
+}
+
+
+/**********************************************************
+ * THE mixer
+ *
+ */
+
+void
+mixer_MixChannels (void *userdata, uint8 *stream, sint32 len)
+{
+ uint8 *end_stream = stream + len;
+ bool left = true;
+
+ /* keep this order or die */
+ LockRecursiveMutex (src_mutex);
+ LockRecursiveMutex (buf_mutex);
+ LockRecursiveMutex (act_mutex);
+
+ for (; stream < end_stream; stream += mixer_chansize)
+ {
+ uint32 i;
+ float fullsamp = 0;
+
+ for (i = 0; i < MAX_SOURCES; i++)
+ {
+ mixer_Source *src;
+ float samp = 0;
+
+ /* find next source */
+ for (; i < MAX_SOURCES && (
+ (src = active_sources[i]) == 0
+ || src->state != MIX_PLAYING
+ || !mixer_SourceGetNextSample (src, &samp, left));
+ i++)
+ ;
+
+ if (i < MAX_SOURCES)
+ {
+ /* sample acquired */
+ fullsamp += samp;
+ }
+ }
+
+ /* clip the sample */
+ if (mixer_chansize == 2)
+ {
+ /* check S16 clipping */
+ if (fullsamp > SINT16_MAX)
+ fullsamp = SINT16_MAX;
+ else if (fullsamp < SINT16_MIN)
+ fullsamp = SINT16_MIN;
+ }
+ else
+ {
+ /* check S8 clipping */
+ if (fullsamp > SINT8_MAX)
+ fullsamp = SINT8_MAX;
+ else if (fullsamp < SINT8_MIN)
+ fullsamp = SINT8_MIN;
+ }
+
+ mixer_PutSampleExt (stream, mixer_chansize, (sint32)fullsamp);
+ if (mixer_channels == 2)
+ left = !left;
+ }
+
+ /* keep this order or die */
+ UnlockRecursiveMutex (act_mutex);
+ UnlockRecursiveMutex (buf_mutex);
+ UnlockRecursiveMutex (src_mutex);
+
+ (void) userdata; // satisfying compiler - unused arg
+}
+
+/* fake mixer -- only process buffer and source states */
+void
+mixer_MixFake (void *userdata, uint8 *stream, sint32 len)
+{
+ uint8 *end_stream = stream + len;
+ bool left = true;
+
+ /* keep this order or die */
+ LockRecursiveMutex (src_mutex);
+ LockRecursiveMutex (buf_mutex);
+ LockRecursiveMutex (act_mutex);
+
+ for (; stream < end_stream; stream += mixer_chansize)
+ {
+ uint32 i;
+
+ for (i = 0; i < MAX_SOURCES; i++)
+ {
+ mixer_Source *src;
+ float samp;
+
+ /* find next source */
+ for (; i < MAX_SOURCES && (
+ (src = active_sources[i]) == 0
+ || src->state != MIX_PLAYING
+ || !mixer_SourceGetFakeSample (src, &samp, left));
+ i++)
+ ;
+ }
+ if (mixer_channels == 2)
+ left = !left;
+ }
+
+ /* keep this order or die */
+ UnlockRecursiveMutex (act_mutex);
+ UnlockRecursiveMutex (buf_mutex);
+ UnlockRecursiveMutex (src_mutex);
+
+ (void) userdata; // satisfying compiler - unused arg
+}
+
+
+/*************************************************
+ * Sources interface
+ */
+
+/* generate n sources */
+void
+mixer_GenSources (uint32 n, mixer_Object *psrcobj)
+{
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!psrcobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GenSources() called with null ptr");
+ return;
+ }
+ for (; n; n--, psrcobj++)
+ {
+ mixer_Source *src;
+
+ src = (mixer_Source *) HMalloc (sizeof (mixer_Source));
+ src->magic = mixer_srcMagic;
+ src->locked = false;
+ src->state = MIX_INITIAL;
+ src->looping = false;
+ src->gain = MIX_GAIN_ADJ;
+ src->cqueued = 0;
+ src->cprocessed = 0;
+ src->firstqueued = 0;
+ src->nextqueued = 0;
+ src->prevqueued = 0;
+ src->lastqueued = 0;
+ src->pos = 0;
+ src->count = 0;
+
+ *psrcobj = (mixer_Object) src;
+ }
+}
+
+/* delete n sources */
+void
+mixer_DeleteSources (uint32 n, mixer_Object *psrcobj)
+{
+ uint32 i;
+ mixer_Object *pcurobj;
+
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!psrcobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteSources() called with null ptr");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ /* check to make sure we can delete all sources */
+ for (i = n, pcurobj = psrcobj; i && pcurobj; i--, pcurobj++)
+ {
+ mixer_Source *src = (mixer_Source *) *pcurobj;
+
+ if (!src)
+ continue;
+
+ if (src->magic != mixer_srcMagic)
+ break;
+ }
+
+ if (i)
+ { /* some source failed */
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteSources(): not a source");
+ }
+ else
+ { /* all sources checked out */
+ for (; n; n--, psrcobj++)
+ {
+ mixer_Source *src = (mixer_Source *) *psrcobj;
+
+ if (!src)
+ continue;
+
+ /* stopping should not be necessary
+ * under ideal circumstances
+ */
+ if (src->state != MIX_INITIAL)
+ mixer_SourceStop_internal (src);
+
+ /* unqueueing should not be necessary
+ * under ideal circumstances
+ */
+ mixer_SourceUnqueueAll (src);
+ HFree (src);
+ *psrcobj = 0;
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* check if really is a source */
+bool
+mixer_IsSource (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+ bool ret;
+
+ if (!src)
+ return false;
+
+ LockRecursiveMutex (src_mutex);
+ ret = src->magic == mixer_srcMagic;
+ UnlockRecursiveMutex (src_mutex);
+
+ return ret;
+}
+
+/* set source integer property */
+void
+mixer_Sourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcei() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcei(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_LOOPING:
+ src->looping = value;
+ break;
+ case MIX_BUFFER:
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) value;
+
+ if (src->cqueued > 0)
+ mixer_SourceUnqueueAll (src);
+
+ if (buf && !mixer_CheckBufferState (buf, "mixer_Sourcei"))
+ break;
+
+ src->firstqueued = buf;
+ src->nextqueued = src->firstqueued;
+ src->prevqueued = 0;
+ src->lastqueued = src->nextqueued;
+ if (src->lastqueued)
+ src->lastqueued->next = 0;
+ src->cqueued = 1;
+ }
+ break;
+ case MIX_SOURCE_STATE:
+ if (value == MIX_INITIAL)
+ {
+ mixer_SourceRewind_internal (src);
+ }
+ else
+ {
+ log_add (log_Debug, "mixer_Sourcei(MIX_SOURCE_STATE): "
+ "unsupported state, call ignored");
+ }
+ break;
+ default:
+ mixer_SetError (MIX_INVALID_ENUM);
+ log_add (log_Debug, "mixer_Sourcei() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* set source float property */
+void
+mixer_Sourcef (mixer_Object srcobj, mixer_SourceProp pname, float value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcef() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_Sourcef(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_GAIN:
+ src->gain = value * MIX_GAIN_ADJ;
+ break;
+ default:
+ log_add (log_Debug, "mixer_Sourcei() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* set source float array property (CURRENTLY NOT IMPLEMENTED) */
+void mixer_Sourcefv (mixer_Object srcobj, mixer_SourceProp pname, float *value)
+{
+ (void)srcobj;
+ (void)pname;
+ (void)value;
+}
+
+
+/* get source integer property */
+void
+mixer_GetSourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal *value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src || !value)
+ {
+ mixer_SetError (src ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcei() called with null param");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcei(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_LOOPING:
+ *value = src->looping;
+ break;
+ case MIX_BUFFER:
+ *value = (mixer_IntVal) src->firstqueued;
+ break;
+ case MIX_SOURCE_STATE:
+ *value = src->state;
+ break;
+ case MIX_BUFFERS_QUEUED:
+ *value = src->cqueued;
+ break;
+ case MIX_BUFFERS_PROCESSED:
+ *value = src->cprocessed;
+ break;
+ default:
+ mixer_SetError (MIX_INVALID_ENUM);
+ log_add (log_Debug, "mixer_GetSourcei() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* get source float property */
+void
+mixer_GetSourcef (mixer_Object srcobj, mixer_SourceProp pname,
+ float *value)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src || !value)
+ {
+ mixer_SetError (src ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcef() called with null param");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetSourcef(): not a source");
+ }
+ else
+ {
+ switch (pname)
+ {
+ case MIX_GAIN:
+ *value = src->gain / MIX_GAIN_ADJ;
+ break;
+ default:
+ log_add (log_Debug, "mixer_GetSourcef() called "
+ "with unsupported property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* start the source; add it to active array */
+void
+mixer_SourcePlay (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePlay() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePlay(): not a source");
+ }
+ else /* should make the source active */
+ {
+ if (src->state < MIX_PLAYING)
+ {
+ if (src->firstqueued && !src->nextqueued)
+ mixer_SourceRewind_internal (src);
+ mixer_SourceActivate (src);
+ }
+ src->state = MIX_PLAYING;
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* stop the source; remove it from active array and requeue buffers */
+void
+mixer_SourceRewind (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceRewind() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePlay(): not a source");
+ }
+ else
+ {
+ mixer_SourceRewind_internal (src);
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* pause the source; keep in active array */
+void
+mixer_SourcePause (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePause() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourcePause(): not a source");
+ }
+ else /* should keep all buffers and offsets */
+ {
+ if (src->state < MIX_PLAYING)
+ mixer_SourceActivate (src);
+ src->state = MIX_PAUSED;
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* stop the source; remove it from active array
+ * and unqueue 'queued' buffers
+ */
+void
+mixer_SourceStop (mixer_Object srcobj)
+{
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceStop() called with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceStop(): not a source");
+ }
+ else /* should remove queued buffers */
+ {
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+ mixer_SourceStop_internal (src);
+ src->state = MIX_STOPPED;
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/* queue buffers on the source */
+void
+mixer_SourceQueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj)
+{
+ uint32 i;
+ mixer_Object* pobj;
+ mixer_Source *src = (mixer_Source *) srcobj;
+
+ if (!src || !pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceQueueBuffers() called "
+ "with null param");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+ /* check to make sure we can safely queue all buffers */
+ for (i = n, pobj = pbufobj; i; i--, pobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pobj;
+ if (!buf || !mixer_CheckBufferState (buf,
+ "mixer_SourceQueueBuffers"))
+ {
+ break;
+ }
+ }
+ UnlockRecursiveMutex (buf_mutex);
+
+ if (i == 0)
+ { /* all buffers checked out */
+ LockRecursiveMutex (src_mutex);
+ LockRecursiveMutex (buf_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceQueueBuffers(): not a source");
+ }
+ else
+ {
+ for (i = n, pobj = pbufobj; i; i--, pobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pobj;
+
+ /* add buffer to the chain */
+ if (src->lastqueued)
+ src->lastqueued->next = buf;
+ src->lastqueued = buf;
+
+ if (!src->firstqueued)
+ {
+ src->firstqueued = buf;
+ src->nextqueued = buf;
+ src->prevqueued = 0;
+ }
+ src->cqueued++;
+ buf->state = MIX_BUF_QUEUED;
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+ UnlockRecursiveMutex (src_mutex);
+ }
+}
+
+/* unqueue buffers from the source */
+void
+mixer_SourceUnqueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj)
+{
+ uint32 i;
+ mixer_Source *src = (mixer_Source *) srcobj;
+ mixer_Buffer *curbuf = 0;
+
+ if (!src || !pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceUnqueueBuffers() called "
+ "with null source");
+ return;
+ }
+
+ LockRecursiveMutex (src_mutex);
+
+ if (src->magic != mixer_srcMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_SourceUnqueueBuffers(): not a source");
+ }
+ else if (n > src->cqueued)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ }
+ else
+ {
+ LockRecursiveMutex (buf_mutex);
+
+ /* check to make sure we can unqueue all buffers */
+ for (i = n, curbuf = src->firstqueued;
+ i && curbuf && curbuf->state != MIX_BUF_PLAYING;
+ i--, curbuf = curbuf->next)
+ ;
+
+ if (i)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_SourceUnqueueBuffers(): "
+ "active buffer attempted");
+ }
+ else
+ { /* all buffers checked out */
+ for (i = n; i; i--, pbufobj++)
+ {
+ mixer_Buffer *buf = src->firstqueued;
+
+ /* remove buffer from the chain */
+ if (src->nextqueued == buf)
+ src->nextqueued = buf->next;
+ if (src->prevqueued == buf)
+ src->prevqueued = 0;
+ if (src->lastqueued == buf)
+ src->lastqueued = 0;
+ src->firstqueued = buf->next;
+ src->cqueued--;
+
+ if (buf->state == MIX_BUF_PROCESSED)
+ src->cprocessed--;
+
+ buf->state = MIX_BUF_FILLED;
+ buf->next = 0;
+ *pbufobj = (mixer_Object) buf;
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+ }
+
+ UnlockRecursiveMutex (src_mutex);
+}
+
+/*************************************************
+ * Sources internals
+ */
+
+static void
+mixer_SourceUnqueueAll (mixer_Source *src)
+{
+ mixer_Buffer *buf;
+ mixer_Buffer *nextbuf;
+
+ if (!src)
+ {
+ log_add (log_Debug, "mixer_SourceUnqueueAll() called "
+ "with null source");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ for (buf = src->firstqueued; buf; buf = nextbuf)
+ {
+ if (buf->state == MIX_BUF_PLAYING)
+ {
+ log_add (log_Debug, "mixer_SourceUnqueueAll(): "
+ "attempted on active buffer");
+ }
+ nextbuf = buf->next;
+ buf->state = MIX_BUF_FILLED;
+ buf->next = 0;
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+
+ src->firstqueued = 0;
+ src->nextqueued = 0;
+ src->prevqueued = 0;
+ src->lastqueued = 0;
+ src->cqueued = 0;
+ src->cprocessed = 0;
+ src->pos = 0;
+ src->count = 0;
+}
+
+/* add the source to the active array */
+static void
+mixer_SourceActivate (mixer_Source* src)
+{
+ uint32 i;
+
+ LockRecursiveMutex (act_mutex);
+
+ /* check active sources, see if this source is there already */
+ for (i = 0; i < MAX_SOURCES && active_sources[i] != src; i++)
+ ;
+ if (i < MAX_SOURCES)
+ { /* source found */
+ log_add (log_Debug, "mixer_SourceActivate(): "
+ "source already active in slot %u", i);
+ UnlockRecursiveMutex (act_mutex);
+ return;
+ }
+
+ /* find an empty slot */
+ for (i = 0; i < MAX_SOURCES && active_sources[i] != 0; i++)
+ ;
+ if (i < MAX_SOURCES)
+ { /* slot found */
+ active_sources[i] = src;
+ }
+ else
+ {
+ log_add (log_Debug, "mixer_SourceActivate(): "
+ "no more slots available (max=%d)", MAX_SOURCES);
+ }
+
+ UnlockRecursiveMutex (act_mutex);
+}
+
+/* remove the source from the active array */
+static void
+mixer_SourceDeactivate (mixer_Source* src)
+{
+ uint32 i;
+
+ LockRecursiveMutex (act_mutex);
+
+ /* check active sources, see if this source is there */
+ for (i = 0; i < MAX_SOURCES && active_sources[i] != src; i++)
+ ;
+ if (i < MAX_SOURCES)
+ { /* source found */
+ active_sources[i] = 0;
+ }
+ else
+ { /* source not found */
+ log_add (log_Debug, "mixer_SourceDeactivate(): source not active");
+ }
+
+ UnlockRecursiveMutex (act_mutex);
+}
+
+static void
+mixer_SourceStop_internal (mixer_Source *src)
+{
+ mixer_Buffer *buf;
+ mixer_Buffer *nextbuf;
+
+ if (!src->firstqueued)
+ return;
+
+ /* assert the source buffers state */
+ if (!src->lastqueued)
+ {
+ log_add (log_Debug, "mixer_SourceStop_internal(): "
+ "desynced source state");
+#ifdef DEBUG
+ explode ();
+#endif
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ /* find last 'processed' buffer */
+ for (buf = src->firstqueued;
+ buf && buf->next && buf->next != src->nextqueued;
+ buf = buf->next)
+ ;
+ src->lastqueued = buf;
+ if (buf)
+ buf->next = 0; /* break the chain */
+
+ /* unqueue all 'queued' buffers */
+ for (buf = src->nextqueued; buf; buf = nextbuf)
+ {
+ nextbuf = buf->next;
+ buf->state = MIX_BUF_FILLED;
+ buf->next = 0;
+ src->cqueued--;
+ }
+
+ if (src->cqueued == 0)
+ { /* all buffers were removed */
+ src->firstqueued = 0;
+ src->lastqueued = 0;
+ }
+ src->nextqueued = 0;
+ src->prevqueued = 0;
+ src->pos = 0;
+ src->count = 0;
+
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+static void
+mixer_SourceRewind_internal (mixer_Source *src)
+{
+ /* should change the processed buffers to queued */
+ mixer_Buffer *buf;
+
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+
+ LockRecursiveMutex (buf_mutex);
+
+ for (buf = src->firstqueued;
+ buf && buf->state != MIX_BUF_QUEUED;
+ buf = buf->next)
+ {
+ buf->state = MIX_BUF_QUEUED;
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+
+ src->pos = 0;
+ src->count = 0;
+ src->cprocessed = 0;
+ src->nextqueued = src->firstqueued;
+ src->prevqueued = 0;
+ src->state = MIX_INITIAL;
+}
+
+/* get the sample next in queue in internal format */
+static inline bool
+mixer_SourceGetNextSample (mixer_Source *src, float *psamp, bool left)
+{
+ /* fake the data if requested */
+ if (mixer_flags & MIX_FAKE_DATA)
+ return mixer_SourceGetFakeSample (src, psamp, left);
+
+ while (src->nextqueued)
+ {
+ mixer_Buffer *buf = src->nextqueued;
+
+ if (!buf->data || buf->size < mixer_sampsize)
+ {
+ /* buffer invalid, go next */
+ buf->state = MIX_BUF_PROCESSED;
+ src->pos = 0;
+ src->nextqueued = src->nextqueued->next;
+ src->cprocessed++;
+ continue;
+ }
+
+ if (!left && buf->orgchannels == 1)
+ {
+ /* mono source so we can copy left channel to right */
+ *psamp = src->samplecache;
+ }
+ else
+ {
+ *psamp = src->samplecache = buf->Resample(src, left) * src->gain;
+ }
+
+ if (src->pos < buf->size ||
+ (left && buf->sampsize != mixer_sampsize))
+ {
+ buf->state = MIX_BUF_PLAYING;
+ }
+ else
+ {
+ /* buffer exhausted, go next */
+ buf->state = MIX_BUF_PROCESSED;
+ src->pos = 0;
+ src->prevqueued = src->nextqueued;
+ src->nextqueued = src->nextqueued->next;
+ src->cprocessed++;
+ }
+
+ return true;
+ }
+
+ /* no more playable buffers */
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+
+ src->state = MIX_STOPPED;
+
+ return false;
+}
+
+/* fake the next sample, but process buffers and states */
+static inline bool
+mixer_SourceGetFakeSample (mixer_Source *src, float *psamp, bool left)
+{
+ while (src->nextqueued)
+ {
+ mixer_Buffer *buf = src->nextqueued;
+
+ if (left || buf->orgchannels != 1)
+ {
+ if (mixer_freq == buf->orgfreq)
+ src->pos += mixer_chansize;
+ else
+ mixer_SourceAdvance(src, left);
+ }
+ *psamp = 0;
+
+ if (src->pos < buf->size ||
+ (left && buf->sampsize != mixer_sampsize))
+ {
+ buf->state = MIX_BUF_PLAYING;
+ }
+ else
+ {
+ /* buffer exhausted, go next */
+ buf->state = MIX_BUF_PROCESSED;
+ src->pos = 0;
+ src->prevqueued = src->nextqueued;
+ src->nextqueued = src->nextqueued->next;
+ src->cprocessed++;
+ }
+
+ return true;
+ }
+
+ /* no more playable buffers */
+ if (src->state >= MIX_PLAYING)
+ mixer_SourceDeactivate (src);
+
+ src->state = MIX_STOPPED;
+
+ return false;
+}
+
+/* advance position in currently queued buffer */
+static inline uint32
+mixer_SourceAdvance (mixer_Source *src, bool left)
+{
+ mixer_Buffer *curr = src->nextqueued;
+ if (curr->orgchannels == 2 && mixer_channels == 2)
+ {
+ if (!left)
+ {
+ src->pos += curr->high;
+ src->count += curr->low;
+ if (src->count > UINT16_MAX)
+ {
+ src->count -= UINT16_MAX;
+ src->pos += curr->sampsize;
+ }
+ return mixer_chansize;
+ }
+ }
+ else
+ {
+ src->pos += curr->high;
+ src->count += curr->low;
+ if (src->count > UINT16_MAX)
+ {
+ src->count -= UINT16_MAX;
+ src->pos += curr->sampsize;
+ }
+ }
+ return 0;
+}
+
+
+/*************************************************
+ * Buffers interface
+ */
+
+/* generate n buffer objects */
+void
+mixer_GenBuffers (uint32 n, mixer_Object *pbufobj)
+{
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_VALUE);
+ log_add (log_Debug, "mixer_GenBuffers() called with null ptr");
+ return;
+ }
+ for (; n; n--, pbufobj++)
+ {
+ mixer_Buffer *buf;
+
+ buf = (mixer_Buffer *) HMalloc (sizeof (mixer_Buffer));
+ buf->magic = mixer_bufMagic;
+ buf->locked = false;
+ buf->state = MIX_BUF_INITIAL;
+ buf->data = 0;
+ buf->size = 0;
+ buf->next = 0;
+ buf->orgdata = 0;
+ buf->orgfreq = 0;
+ buf->orgsize = 0;
+ buf->orgchannels = 0;
+ buf->orgchansize = 0;
+
+ *pbufobj = (mixer_Object) buf;
+ }
+}
+
+/* delete n buffer objects */
+void
+mixer_DeleteBuffers (uint32 n, mixer_Object *pbufobj)
+{
+ uint32 i;
+ mixer_Object *pcurobj;
+
+ if (n == 0)
+ return; /* do nothing per OpenAL */
+
+ if (!pbufobj)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteBuffers() called with null ptr");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ /* check to make sure we can delete all buffers */
+ for (i = n, pcurobj = pbufobj; i && pcurobj; i--, pcurobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pcurobj;
+
+ if (!buf)
+ continue;
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_DeleteBuffers(): not a buffer");
+ break;
+ }
+ else if (buf->locked)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_DeleteBuffers(): locked buffer");
+ break;
+ }
+ else if (buf->state >= MIX_BUF_QUEUED)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_DeleteBuffers(): "
+ "attempted on queued/active buffer");
+ break;
+ }
+ }
+
+ if (i == 0)
+ {
+ /* all buffers check out */
+ for (; n; n--, pbufobj++)
+ {
+ mixer_Buffer *buf = (mixer_Buffer *) *pbufobj;
+
+ if (!buf)
+ continue;
+
+ if (buf->data)
+ HFree (buf->data);
+ HFree (buf);
+
+ *pbufobj = 0;
+ }
+ }
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+/* check if really a buffer object */
+bool
+mixer_IsBuffer (mixer_Object bufobj)
+{
+ mixer_Buffer *buf = (mixer_Buffer *) bufobj;
+ bool ret;
+
+ if (!buf)
+ return false;
+
+ LockRecursiveMutex (buf_mutex);
+ ret = buf->magic == mixer_bufMagic;
+ UnlockRecursiveMutex (buf_mutex);
+
+ return ret;
+}
+
+/* get buffer property */
+void
+mixer_GetBufferi (mixer_Object bufobj, mixer_BufferProp pname,
+ mixer_IntVal *value)
+{
+ mixer_Buffer *buf = (mixer_Buffer *) bufobj;
+
+ if (!buf || !value)
+ {
+ mixer_SetError (buf ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetBufferi() called with null param");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ if (buf->locked)
+ {
+ UnlockRecursiveMutex (buf_mutex);
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_GetBufferi() called with locked buffer");
+ return;
+ }
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_GetBufferi(): not a buffer");
+ }
+ else
+ {
+ /* Return original buffer values
+ */
+ switch (pname)
+ {
+ case MIX_FREQUENCY:
+ *value = buf->orgfreq;
+ break;
+ case MIX_BITS:
+ *value = buf->orgchansize << 3;
+ break;
+ case MIX_CHANNELS:
+ *value = buf->orgchannels;
+ break;
+ case MIX_SIZE:
+ *value = buf->orgsize;
+ break;
+ case MIX_DATA:
+ *value = (mixer_IntVal) buf->orgdata;
+ break;
+ default:
+ mixer_SetError (MIX_INVALID_ENUM);
+ log_add (log_Debug, "mixer_GetBufferi() called "
+ "with invalid property %u", pname);
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+/* fill buffer with external data */
+void
+mixer_BufferData (mixer_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ mixer_Buffer *buf = (mixer_Buffer *) bufobj;
+ mixer_Convertion conv;
+ uint32 dstsize;
+
+ if (!buf || !data || !size)
+ {
+ mixer_SetError (buf ? MIX_INVALID_VALUE : MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_BufferData() called with bad param");
+ return;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+
+ if (buf->locked)
+ {
+ UnlockRecursiveMutex (buf_mutex);
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_BufferData() called "
+ "with locked buffer");
+ return;
+ }
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "mixer_BufferData(): not a buffer");
+ }
+ else if (buf->state > MIX_BUF_FILLED)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "mixer_BufferData() attempted "
+ "on in-use buffer");
+ }
+ else
+ {
+ if (buf->data)
+ HFree (buf->data);
+ buf->data = 0;
+ buf->size = 0;
+
+ /* Store original buffer values for OpenAL compatibility */
+ buf->orgdata = data;
+ buf->orgfreq = freq;
+ buf->orgsize = size;
+ buf->orgchannels = MIX_FORMAT_CHANS (format);
+ buf->orgchansize = MIX_FORMAT_BPC (format);
+
+ conv.srcsamples = conv.dstsamples =
+ size / MIX_FORMAT_SAMPSIZE (format);
+
+ if (conv.dstsamples >
+ UINT32_MAX / MIX_FORMAT_SAMPSIZE (format))
+ {
+ mixer_SetError (MIX_INVALID_VALUE);
+ }
+ else
+ {
+ if (MIX_FORMAT_CHANS (format) < MIX_FORMAT_CHANS (mixer_format))
+ buf->sampsize = MIX_FORMAT_BPC (mixer_format) *
+ MIX_FORMAT_CHANS (format);
+ else
+ buf->sampsize = MIX_FORMAT_SAMPSIZE (mixer_format);
+ buf->size = dstsize = conv.dstsamples * buf->sampsize;
+
+ /* only copy/convert the data if not faking */
+ if (! (mixer_flags & MIX_FAKE_DATA))
+ {
+ buf->data = HMalloc (dstsize);
+
+ if (MIX_FORMAT_BPC (format) == MIX_FORMAT_BPC (mixer_format) &&
+ MIX_FORMAT_CHANS (format) <= MIX_FORMAT_CHANS (mixer_format))
+ {
+ /* format is compatible with internal */
+ buf->locked = true;
+ UnlockRecursiveMutex (buf_mutex);
+
+ memcpy (buf->data, data, size);
+ if (MIX_FORMAT_BPC (format) == 1)
+ {
+ /* convert buffer to S8 format internally */
+ uint8* dst;
+ for (dst = buf->data; dstsize; dstsize--, dst++)
+ *dst ^= 0x80;
+ }
+
+ LockRecursiveMutex (buf_mutex);
+ buf->locked = false;
+ }
+ else
+ {
+ /* needs convertion */
+ conv.srcfmt = format;
+ conv.srcdata = data;
+ conv.srcsize = size;
+
+ if (MIX_FORMAT_CHANS (format) < MIX_FORMAT_CHANS (mixer_format))
+ conv.dstfmt = MIX_FORMAT_MAKE (mixer_chansize,
+ MIX_FORMAT_CHANS (format));
+ else
+ conv.dstfmt = mixer_format;
+ conv.dstdata = buf->data;
+ conv.dstsize = dstsize;
+
+ buf->locked = true;
+ UnlockRecursiveMutex (buf_mutex);
+
+ mixer_ConvertBuffer_internal (&conv);
+
+ LockRecursiveMutex (buf_mutex);
+ buf->locked = false;
+ }
+ }
+
+ buf->state = MIX_BUF_FILLED;
+ buf->high = (buf->orgfreq / mixer_freq) * buf->sampsize;
+ buf->low = (((buf->orgfreq % mixer_freq) << 16) / mixer_freq);
+ if (mixer_freq == buf->orgfreq)
+ buf->Resample = mixer_resampling.None;
+ else if (mixer_freq > buf->orgfreq)
+ buf->Resample = mixer_resampling.Upsample;
+ else
+ buf->Resample = mixer_resampling.Downsample;
+ }
+ }
+
+ UnlockRecursiveMutex (buf_mutex);
+}
+
+
+/*************************************************
+ * Buffer internals
+ */
+
+static inline bool
+mixer_CheckBufferState (mixer_Buffer *buf, const char* FuncName)
+{
+ if (!buf)
+ return false;
+
+ if (buf->magic != mixer_bufMagic)
+ {
+ mixer_SetError (MIX_INVALID_NAME);
+ log_add (log_Debug, "%s(): not a buffer", FuncName);
+ return false;
+ }
+
+ if (buf->locked)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "%s(): locked buffer attempted", FuncName);
+ return false;
+ }
+
+ if (buf->state != MIX_BUF_FILLED)
+ {
+ mixer_SetError (MIX_INVALID_OPERATION);
+ log_add (log_Debug, "%s: invalid buffer attempted", FuncName);
+ return false;
+ }
+ return true;
+}
+
+static void
+mixer_ConvertBuffer_internal (mixer_Convertion *conv)
+{
+ conv->srcbpc = MIX_FORMAT_BPC (conv->srcfmt);
+ conv->srcchans = MIX_FORMAT_CHANS (conv->srcfmt);
+ conv->dstbpc = MIX_FORMAT_BPC (conv->dstfmt);
+ conv->dstchans = MIX_FORMAT_CHANS (conv->dstfmt);
+
+ conv->flags = 0;
+ if (conv->srcbpc > conv->dstbpc)
+ conv->flags |= mixConvSizeDown;
+ else if (conv->srcbpc < conv->dstbpc)
+ conv->flags |= mixConvSizeUp;
+ if (conv->srcchans > conv->dstchans)
+ conv->flags |= mixConvStereoDown;
+ else if (conv->srcchans < conv->dstchans)
+ conv->flags |= mixConvStereoUp;
+
+ mixer_ResampleFlat (conv);
+}
+
+/*************************************************
+ * Resampling routines
+ */
+
+/* get a sample from external buffer
+ * in internal format
+ */
+static inline sint32
+mixer_GetSampleExt (void *src, uint32 bpc)
+{
+ if (bpc == 2)
+ return *(sint16 *)src;
+ else
+ return (*(uint8 *)src) - 128;
+}
+
+/* get a sample from internal buffer */
+static inline sint32
+mixer_GetSampleInt (void *src, uint32 bpc)
+{
+ if (bpc == 2)
+ return *(sint16 *)src;
+ else
+ return *(sint8 *)src;
+}
+
+/* put a sample into an external buffer
+ * from internal format
+ */
+static inline void
+mixer_PutSampleExt (void *dst, uint32 bpc, sint32 samp)
+{
+ if (bpc == 2)
+ *(sint16 *)dst = samp;
+ else
+ *(uint8 *)dst = samp ^ 0x80;
+}
+
+/* put a sample into an internal buffer
+ * in internal format
+ */
+static inline void
+mixer_PutSampleInt (void *dst, uint32 bpc, sint32 samp)
+{
+ if (bpc == 2)
+ *(sint16 *)dst = samp;
+ else
+ *(sint8 *)dst = samp;
+}
+
+/* get a sample from source */
+static float
+mixer_ResampleNone (mixer_Source *src, bool left)
+{
+ uint8 *d0 = src->nextqueued->data + src->pos;
+ src->pos += mixer_chansize;
+ (void) left; // satisfying compiler - unused arg
+ return mixer_GetSampleInt (d0, mixer_chansize);
+}
+
+/* get a resampled (up/down) sample from source (nearest neighbor) */
+static float
+mixer_ResampleNearest (mixer_Source *src, bool left)
+{
+ uint8 *d0 = src->nextqueued->data + src->pos;
+ d0 += mixer_SourceAdvance (src, left);
+ return mixer_GetSampleInt (d0, mixer_chansize);
+}
+
+/* get an upsampled sample from source (linear interpolation) */
+static float
+mixer_UpsampleLinear (mixer_Source *src, bool left)
+{
+ mixer_Buffer *curr = src->nextqueued;
+ mixer_Buffer *next = src->nextqueued->next;
+ uint8 *d0, *d1;
+ float s0, s1, t;
+
+ t = src->count / 65536.0f;
+ d0 = curr->data + src->pos;
+ d0 += mixer_SourceAdvance (src, left);
+
+ if (d0 + curr->sampsize >= curr->data + curr->size)
+ {
+ if (next && next->data && next->size >= curr->sampsize)
+ {
+ d1 = next->data;
+ if (!left)
+ d1 += mixer_chansize;
+ }
+ else
+ d1 = d0;
+ }
+ else
+ d1 = d0 + curr->sampsize;
+
+ s0 = mixer_GetSampleInt (d0, mixer_chansize);
+ s1 = mixer_GetSampleInt (d1, mixer_chansize);
+ return s0 + t * (s1 - s0);
+}
+
+/* get an upsampled sample from source (cubic interpolation) */
+static float
+mixer_UpsampleCubic (mixer_Source *src, bool left)
+{
+ mixer_Buffer *prev = src->prevqueued;
+ mixer_Buffer *curr = src->nextqueued;
+ mixer_Buffer *next = src->nextqueued->next;
+ uint8 *d0, *d1, *d2, *d3; /* prev, curr, next, next + 1 */
+ float t, t2, a, b, c, s0, s1, s2, s3;
+
+ t = src->count / 65536.0f;
+ t2 = t * t;
+ d1 = curr->data + src->pos;
+ d1 += mixer_SourceAdvance (src, left);
+
+ if (d1 - curr->sampsize < curr->data)
+ {
+ if (prev && prev->data && prev->size >= curr->sampsize)
+ {
+ d0 = prev->data + prev->size - curr->sampsize;
+ if (!left)
+ d0 += mixer_chansize;
+ }
+ else
+ d0 = d1;
+ }
+ else
+ d0 = d1 - curr->sampsize;
+
+ if (d1 + curr->sampsize >= curr->data + curr->size)
+ {
+ if (next && next->data && next->size >= curr->sampsize * 2)
+ {
+ d2 = next->data;
+ if (!left)
+ d2 += mixer_chansize;
+ d3 = d2 + curr->sampsize;
+ }
+ else
+ d2 = d3 = d1;
+ }
+ else
+ {
+ d2 = d1 + curr->sampsize;
+ if (d2 + curr->sampsize >= curr->data + curr->size)
+ {
+ if (next && next->data && next->size >= curr->sampsize)
+ {
+ d3 = next->data;
+ if (!left)
+ d3 += mixer_chansize;
+ }
+ else
+ d3 = d2;
+ }
+ else
+ d3 = d2 + curr->sampsize;
+ }
+
+ s0 = mixer_GetSampleInt (d0, mixer_chansize);
+ s1 = mixer_GetSampleInt (d1, mixer_chansize);
+ s2 = mixer_GetSampleInt (d2, mixer_chansize);
+ s3 = mixer_GetSampleInt (d3, mixer_chansize);
+
+ a = (3.0f * (s1 - s2) - s0 + s3) * 0.5f;
+ b = 2.0f * s2 + s0 - ((5.0f * s1 + s3) * 0.5f);
+ c = (s2 - s0) * 0.5f;
+
+ return a * t2 * t + b * t2 + c * t + s1;
+}
+
+/* get next sample from external buffer
+ * in internal format, while performing
+ * convertion if necessary
+ */
+static inline sint32
+mixer_GetConvSample (uint8 **psrc, uint32 bpc, uint32 flags)
+{
+ sint32 samp;
+
+ samp = mixer_GetSampleExt (*psrc, bpc);
+ *psrc += bpc;
+ if (flags & mixConvStereoDown)
+ {
+ /* downmix to mono - average up channels */
+ samp = (samp + mixer_GetSampleExt (*psrc, bpc)) / 2;
+ *psrc += bpc;
+ }
+
+ if (flags & mixConvSizeUp)
+ {
+ /* convert S8 to S16 */
+ samp <<= 8;
+ }
+ else if (flags & mixConvSizeDown)
+ {
+ /* convert S16 to S8
+ * if arithmetic shift is available to the compiler
+ * it will use it to optimize this
+ */
+ samp /= 0x100;
+ }
+
+ return samp;
+}
+
+/* put next sample into an internal buffer
+ * in internal format, while performing
+ * convertion if necessary
+ */
+static inline void
+mixer_PutConvSample (uint8 **pdst, uint32 bpc, uint32 flags, sint32 samp)
+{
+ mixer_PutSampleInt (*pdst, bpc, samp);
+ *pdst += bpc;
+ if (flags & mixConvStereoUp)
+ {
+ mixer_PutSampleInt (*pdst, bpc, samp);
+ *pdst += bpc;
+ }
+}
+
+/* resampling with respect to sample size only */
+static void
+mixer_ResampleFlat (mixer_Convertion *conv)
+{
+ mixer_ConvFlags flags = conv->flags;
+ uint8 *src = conv->srcdata;
+ uint8 *dst = conv->dstdata;
+ uint32 srcbpc = conv->srcbpc;
+ uint32 dstbpc = conv->dstbpc;
+ uint32 samples;
+
+ samples = conv->srcsamples;
+ if ( !(conv->flags & (mixConvStereoUp | mixConvStereoDown)))
+ samples *= conv->srcchans;
+
+ for (; samples; samples--)
+ {
+ sint32 samp;
+
+ samp = mixer_GetConvSample (&src, srcbpc, flags);
+ mixer_PutConvSample (&dst, dstbpc, flags, samp);
+ }
+}
diff --git a/src/libs/sound/mixer/mixer.h b/src/libs/sound/mixer/mixer.h
new file mode 100644
index 0000000..71e7878
--- /dev/null
+++ b/src/libs/sound/mixer/mixer.h
@@ -0,0 +1,274 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Mixer for low-level sound output drivers
+ */
+
+#ifndef LIBS_SOUND_MIXER_MIXER_H_
+#define LIBS_SOUND_MIXER_MIXER_H_
+
+#include "config.h"
+#include "types.h"
+#include "endian_uqm.h"
+
+/**
+ * The interface heavily influenced by OpenAL
+ * to the point where you should use OpenAL's
+ * documentation when programming the mixer.
+ * (some source properties are not supported)
+ *
+ * EXCEPTION: You may not queue the same buffer
+ * on more than one source
+ */
+
+#ifdef WORDS_BIGENDIAN
+# define MIX_IS_BIG_ENDIAN true
+# define MIX_WANT_BIG_ENDIAN true
+#else
+# define MIX_IS_BIG_ENDIAN false
+# define MIX_WANT_BIG_ENDIAN false
+#endif
+
+/**
+ * Mixer errors (see OpenAL errors)
+ */
+enum
+{
+ MIX_NO_ERROR = 0,
+ MIX_INVALID_NAME = 0xA001U,
+ MIX_INVALID_ENUM = 0xA002U,
+ MIX_INVALID_VALUE = 0xA003U,
+ MIX_INVALID_OPERATION = 0xA004U,
+ MIX_OUT_OF_MEMORY = 0xA005U,
+
+ MIX_DRIVER_FAILURE = 0xA101U
+};
+
+/**
+ * Source properties (see OpenAL)
+ */
+typedef enum
+{
+ MIX_POSITION = 0x1004,
+ MIX_LOOPING = 0x1007,
+ MIX_BUFFER = 0x1009,
+ MIX_GAIN = 0x100A,
+ MIX_SOURCE_STATE = 0x1010,
+
+ MIX_BUFFERS_QUEUED = 0x1015,
+ MIX_BUFFERS_PROCESSED = 0x1016
+
+} mixer_SourceProp;
+
+/**
+ * Source state information
+ */
+typedef enum
+{
+ MIX_INITIAL = 0,
+ MIX_STOPPED,
+ MIX_PLAYING,
+ MIX_PAUSED,
+
+} mixer_SourceState;
+
+/**
+ * Sound buffer properties
+ */
+typedef enum
+{
+ MIX_FREQUENCY = 0x2001,
+ MIX_BITS = 0x2002,
+ MIX_CHANNELS = 0x2003,
+ MIX_SIZE = 0x2004,
+ MIX_DATA = 0x2005
+
+} mixer_BufferProp;
+
+/**
+ * Buffer states: semi-private
+ */
+typedef enum
+{
+ MIX_BUF_INITIAL = 0,
+ MIX_BUF_FILLED,
+ MIX_BUF_QUEUED,
+ MIX_BUF_PLAYING,
+ MIX_BUF_PROCESSED
+
+} mixer_BufferState;
+
+/** Sound buffers: format specifier.
+ * bits 00..07: bytes per sample
+ * bits 08..15: channels
+ * bits 15..31: meaningless
+ */
+#define MIX_FORMAT_DUMMYID 0x00170000
+#define MIX_FORMAT_BPC(f) ((f) & 0xff)
+#define MIX_FORMAT_CHANS(f) (((f) >> 8) & 0xff)
+#define MIX_FORMAT_BPC_MAX 2
+#define MIX_FORMAT_CHANS_MAX 2
+#define MIX_FORMAT_MAKE(b, c) \
+ ( MIX_FORMAT_DUMMYID | ((b) & 0xff) | (((c) & 0xff) << 8) )
+
+#define MIX_FORMAT_SAMPSIZE(f) \
+ ( MIX_FORMAT_BPC(f) * MIX_FORMAT_CHANS(f) )
+
+typedef enum
+{
+ MIX_FORMAT_MONO8 = MIX_FORMAT_MAKE (1, 1),
+ MIX_FORMAT_STEREO8 = MIX_FORMAT_MAKE (1, 2),
+ MIX_FORMAT_MONO16 = MIX_FORMAT_MAKE (2, 1),
+ MIX_FORMAT_STEREO16 = MIX_FORMAT_MAKE (2, 2)
+
+} mixer_Format;
+
+typedef enum
+{
+ MIX_QUALITY_LOW = 0,
+ MIX_QUALITY_MEDIUM,
+ MIX_QUALITY_HIGH,
+ MIX_QUALITY_DEFAULT = MIX_QUALITY_MEDIUM,
+ MIX_QUALITY_COUNT
+
+} mixer_Quality;
+
+typedef enum
+{
+ MIX_NOFLAGS = 0,
+ MIX_FAKE_DATA = 1
+} mixer_Flags;
+
+/*************************************************
+ * Interface Types
+ */
+
+typedef intptr_t mixer_Object;
+typedef intptr_t mixer_IntVal;
+
+typedef struct _mixer_Source mixer_Source;
+
+typedef struct _mixer_Buffer
+{
+ uint32 magic;
+ bool locked;
+ mixer_BufferState state;
+ uint8 *data;
+ uint32 size;
+ uint32 sampsize;
+ uint32 high;
+ uint32 low;
+ float (* Resample) (mixer_Source *src, bool left);
+ /* original buffer values for OpenAL compat */
+ void* orgdata;
+ uint32 orgfreq;
+ uint32 orgsize;
+ uint32 orgchannels;
+ uint32 orgchansize;
+ /* next buffer in chain */
+ struct _mixer_Buffer *next;
+
+} mixer_Buffer;
+
+#define mixer_bufMagic 0x4258494DU /* MIXB in LSB */
+
+struct _mixer_Source
+{
+ uint32 magic;
+ bool locked;
+ mixer_SourceState state;
+ bool looping;
+ float gain;
+ uint32 cqueued;
+ uint32 cprocessed;
+ mixer_Buffer *firstqueued; /* first buf in the queue */
+ mixer_Buffer *nextqueued; /* next to play, or 0 */
+ mixer_Buffer *prevqueued; /* previously played */
+ mixer_Buffer *lastqueued; /* last in queue */
+ uint32 pos; /* position in current buffer */
+ uint32 count; /* fractional part of pos */
+
+ float samplecache;
+
+};
+
+#define mixer_srcMagic 0x5358494DU /* MIXS in LSB */
+
+/*************************************************
+ * General interface
+ */
+uint32 mixer_GetError (void);
+
+bool mixer_Init (uint32 frequency, uint32 format, mixer_Quality quality,
+ mixer_Flags flags);
+void mixer_Uninit (void);
+void mixer_MixChannels (void *userdata, uint8 *stream, sint32 len);
+void mixer_MixFake (void *userdata, uint8 *stream, sint32 len);
+
+/*************************************************
+ * Sources
+ */
+void mixer_GenSources (uint32 n, mixer_Object *psrcobj);
+void mixer_DeleteSources (uint32 n, mixer_Object *psrcobj);
+bool mixer_IsSource (mixer_Object srcobj);
+void mixer_Sourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal value);
+void mixer_Sourcef (mixer_Object srcobj, mixer_SourceProp pname,
+ float value);
+void mixer_Sourcefv (mixer_Object srcobj, mixer_SourceProp pname,
+ float *value);
+void mixer_GetSourcei (mixer_Object srcobj, mixer_SourceProp pname,
+ mixer_IntVal *value);
+void mixer_GetSourcef (mixer_Object srcobj, mixer_SourceProp pname,
+ float *value);
+void mixer_SourceRewind (mixer_Object srcobj);
+void mixer_SourcePlay (mixer_Object srcobj);
+void mixer_SourcePause (mixer_Object srcobj);
+void mixer_SourceStop (mixer_Object srcobj);
+void mixer_SourceQueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj);
+void mixer_SourceUnqueueBuffers (mixer_Object srcobj, uint32 n,
+ mixer_Object* pbufobj);
+
+/*************************************************
+ * Buffers
+ */
+void mixer_GenBuffers (uint32 n, mixer_Object *pbufobj);
+void mixer_DeleteBuffers (uint32 n, mixer_Object *pbufobj);
+bool mixer_IsBuffer (mixer_Object bufobj);
+void mixer_GetBufferi (mixer_Object bufobj, mixer_BufferProp pname,
+ mixer_IntVal *value);
+void mixer_BufferData (mixer_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+
+/* Make sure the prop-value type is of suitable size
+ * it must be able to store both int and void*
+ * Adapted from SDL
+ * This will generate "negative subscript or subscript is too large"
+ * error during compile, if the actual size of a type is wrong
+ */
+#define MIX_COMPILE_TIME_ASSERT(name, x) \
+ typedef int mixer_dummy_##name [(x) * 2 - 1]
+
+MIX_COMPILE_TIME_ASSERT (mixer_Object,
+ sizeof(mixer_Object) >= sizeof(void*));
+MIX_COMPILE_TIME_ASSERT (mixer_IntVal,
+ sizeof(mixer_IntVal) >= sizeof(mixer_Object));
+
+#undef MIX_COMPILE_TIME_ASSERT
+
+#endif /* LIBS_SOUND_MIXER_MIXER_H_ */
diff --git a/src/libs/sound/mixer/mixerint.h b/src/libs/sound/mixer/mixerint.h
new file mode 100644
index 0000000..0605161
--- /dev/null
+++ b/src/libs/sound/mixer/mixerint.h
@@ -0,0 +1,110 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Mixer for low-level sound output drivers
+ * Internals
+ */
+
+#ifndef LIBS_SOUND_MIXER_MIXERINT_H_
+#define LIBS_SOUND_MIXER_MIXERINT_H_
+
+#include "port.h"
+#include "types.h"
+
+/*************************************************
+ * Internals
+ */
+
+/* Conversion info types and funcs */
+typedef enum
+{
+ mixConvNone = 0,
+ mixConvStereoUp = 1,
+ mixConvStereoDown = 2,
+ mixConvSizeUp = 4,
+ mixConvSizeDown = 8
+
+} mixer_ConvFlags;
+
+typedef struct
+{
+ uint32 srcfmt;
+ void *srcdata;
+ uint32 srcsize;
+ uint32 srcbpc; /* bytes/sample for 1 chan */
+ uint32 srcchans;
+ uint32 srcsamples;
+
+ uint32 dstfmt;
+ void *dstdata;
+ uint32 dstsize;
+ uint32 dstbpc; /* bytes/sample for 1 chan */
+ uint32 dstchans;
+ uint32 dstsamples;
+
+ mixer_ConvFlags flags;
+
+} mixer_Convertion;
+
+typedef struct
+{
+ float (* Upsample) (mixer_Source *src, bool left);
+ float (* Downsample) (mixer_Source *src, bool left);
+ float (* None) (mixer_Source *src, bool left);
+} mixer_Resampling;
+
+static void mixer_ConvertBuffer_internal (mixer_Convertion *conv);
+static void mixer_ResampleFlat (mixer_Convertion *conv);
+
+static inline sint32 mixer_GetSampleExt (void *src, uint32 bpc);
+static inline sint32 mixer_GetSampleInt (void *src, uint32 bpc);
+static inline void mixer_PutSampleInt (void *dst, uint32 bpc,
+ sint32 samp);
+static inline void mixer_PutSampleExt (void *dst, uint32 bpc,
+ sint32 samp);
+
+static float mixer_ResampleNone (mixer_Source *src, bool left);
+static float mixer_ResampleNearest (mixer_Source *src, bool left);
+static float mixer_UpsampleLinear (mixer_Source *src, bool left);
+static float mixer_UpsampleCubic (mixer_Source *src, bool left);
+
+/* Source manipulation */
+static void mixer_SourceUnqueueAll (mixer_Source *src);
+static void mixer_SourceStop_internal (mixer_Source *src);
+static void mixer_SourceRewind_internal (mixer_Source *src);
+static void mixer_SourceActivate (mixer_Source* src);
+static void mixer_SourceDeactivate (mixer_Source* src);
+
+static inline bool mixer_CheckBufferState (mixer_Buffer *buf,
+ const char* FuncName);
+
+/* Clipping boundaries */
+#define MIX_S16_MAX ((float) SINT16_MAX)
+#define MIX_S16_MIN ((float) SINT16_MIN)
+#define MIX_S8_MAX ((float) SINT8_MAX)
+#define MIX_S8_MIN ((float) SINT8_MIN)
+
+/* Channel gain adjustment for clipping reduction */
+#define MIX_GAIN_ADJ (0.75f)
+
+/* The Mixer */
+static inline bool mixer_SourceGetNextSample (mixer_Source *src,
+ float *psamp, bool left);
+static inline bool mixer_SourceGetFakeSample (mixer_Source *src,
+ float *psamp, bool left);
+static inline uint32 mixer_SourceAdvance (mixer_Source *src, bool left);
+
+#endif /* LIBS_SOUND_MIXER_MIXERINT_H_ */
diff --git a/src/libs/sound/mixer/nosound/Makeinfo b/src/libs/sound/mixer/nosound/Makeinfo
new file mode 100644
index 0000000..17f0c34
--- /dev/null
+++ b/src/libs/sound/mixer/nosound/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="audiodrv_nosound.c"
+uqm_HFILES="audiodrv_nosound.h"
diff --git a/src/libs/sound/mixer/nosound/audiodrv_nosound.c b/src/libs/sound/mixer/nosound/audiodrv_nosound.c
new file mode 100644
index 0000000..005bb44
--- /dev/null
+++ b/src/libs/sound/mixer/nosound/audiodrv_nosound.c
@@ -0,0 +1,410 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Nosound audio driver
+ */
+
+#include "audiodrv_nosound.h"
+#include "../../sndintrn.h"
+#include "libs/tasklib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include <stdlib.h>
+
+
+static Task PlaybackTask;
+static uint32 nosound_freq = 22050;
+
+static const audio_Driver noSound_Driver =
+{
+ noSound_Uninit,
+ noSound_GetError,
+ audio_DRIVER_NOSOUND,
+ {
+ /* Errors */
+ MIX_NO_ERROR,
+ MIX_INVALID_NAME,
+ MIX_INVALID_ENUM,
+ MIX_INVALID_VALUE,
+ MIX_INVALID_OPERATION,
+ MIX_OUT_OF_MEMORY,
+ MIX_DRIVER_FAILURE,
+
+ /* Source properties */
+ MIX_POSITION,
+ MIX_LOOPING,
+ MIX_BUFFER,
+ MIX_GAIN,
+ MIX_SOURCE_STATE,
+ MIX_BUFFERS_QUEUED,
+ MIX_BUFFERS_PROCESSED,
+
+ /* Source state information */
+ MIX_INITIAL,
+ MIX_STOPPED,
+ MIX_PLAYING,
+ MIX_PAUSED,
+
+ /* Sound buffer properties */
+ MIX_FREQUENCY,
+ MIX_BITS,
+ MIX_CHANNELS,
+ MIX_SIZE,
+ MIX_FORMAT_MONO16,
+ MIX_FORMAT_STEREO16,
+ MIX_FORMAT_MONO8,
+ MIX_FORMAT_STEREO8
+ },
+
+ /* Sources */
+ noSound_GenSources,
+ noSound_DeleteSources,
+ noSound_IsSource,
+ noSound_Sourcei,
+ noSound_Sourcef,
+ noSound_Sourcefv,
+ noSound_GetSourcei,
+ noSound_GetSourcef,
+ noSound_SourceRewind,
+ noSound_SourcePlay,
+ noSound_SourcePause,
+ noSound_SourceStop,
+ noSound_SourceQueueBuffers,
+ noSound_SourceUnqueueBuffers,
+
+ /* Buffers */
+ noSound_GenBuffers,
+ noSound_DeleteBuffers,
+ noSound_IsBuffer,
+ noSound_GetBufferi,
+ noSound_BufferData
+};
+
+
+/*
+ * Initialization
+ */
+
+sint32
+noSound_Init (audio_Driver *driver, sint32 flags)
+{
+ int i;
+ TFB_DecoderFormats formats =
+ {
+ 0, 0,
+ audio_FORMAT_MONO8, audio_FORMAT_STEREO8,
+ audio_FORMAT_MONO16, audio_FORMAT_STEREO16
+ };
+
+ log_add (log_Info, "Using nosound audio driver.");
+ log_add (log_Info, "Initializing mixer.");
+
+ if (!mixer_Init (nosound_freq, MIX_FORMAT_MAKE (1, 1),
+ MIX_QUALITY_LOW, MIX_FAKE_DATA))
+ {
+ log_add (log_Error, "Mixer initialization failed: %x",
+ mixer_GetError ());
+ return -1;
+ }
+ log_add (log_Info, "Mixer initialized.");
+
+ log_add (log_Info, "Initializing sound decoders.");
+ if (SoundDecoder_Init (flags, &formats))
+ {
+ log_add (log_Error, "Sound decoders initialization failed.");
+ mixer_Uninit ();
+ return -1;
+ }
+ log_add (log_Info, "Sound decoders initialized.");
+
+ *driver = noSound_Driver;
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ audio_GenSources (1, &soundSource[i].handle);
+ soundSource[i].stream_mutex = CreateMutex ("Nosound stream mutex", SYNC_CLASS_AUDIO);
+ }
+
+ if (InitStreamDecoder ())
+ {
+ log_add (log_Error, "Stream decoder initialization failed.");
+ // TODO: cleanup source mutexes [or is it "muti"? :) ]
+ SoundDecoder_Uninit ();
+ mixer_Uninit ();
+ return -1;
+ }
+
+ PlaybackTask = AssignTask (PlaybackTaskFunc, 1024,
+ "nosound audio playback");
+
+ return 0;
+}
+
+void
+noSound_Uninit (void)
+{
+ int i;
+
+ UninitStreamDecoder ();
+
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ if (soundSource[i].sample && soundSource[i].sample->decoder)
+ {
+ StopStream (i);
+ }
+ if (soundSource[i].sbuffer)
+ {
+ void *sbuffer = soundSource[i].sbuffer;
+ soundSource[i].sbuffer = NULL;
+ HFree (sbuffer);
+ }
+ DestroyMutex (soundSource[i].stream_mutex);
+
+ noSound_DeleteSources (1, &soundSource[i].handle);
+ }
+
+ if (PlaybackTask)
+ {
+ ConcludeTask (PlaybackTask);
+ PlaybackTask = 0;
+ }
+
+ mixer_Uninit ();
+ SoundDecoder_Uninit ();
+}
+
+
+/*
+ * Playback task
+ */
+
+int
+PlaybackTaskFunc (void *data)
+{
+ Task task = (Task)data;
+ uint8 *stream;
+ uint32 entryTime;
+ sint32 period, delay;
+ uint32 len = 2048;
+
+ stream = (uint8 *) HMalloc (len);
+ period = (sint32)((len / (double)nosound_freq) * ONE_SECOND);
+
+ while (!Task_ReadState (task, TASK_EXIT))
+ {
+ entryTime = GetTimeCounter ();
+ mixer_MixFake (NULL, stream, len);
+ delay = period - (GetTimeCounter () - entryTime);
+ if (delay > 0)
+ HibernateThread (delay);
+ }
+
+ HFree (stream);
+ FinishTask (task);
+ return 0;
+}
+
+
+/*
+ * General
+ */
+
+sint32
+noSound_GetError (void)
+{
+ sint32 value = mixer_GetError ();
+ switch (value)
+ {
+ case MIX_NO_ERROR:
+ return audio_NO_ERROR;
+ case MIX_INVALID_NAME:
+ return audio_INVALID_NAME;
+ case MIX_INVALID_ENUM:
+ return audio_INVALID_ENUM;
+ case MIX_INVALID_VALUE:
+ return audio_INVALID_VALUE;
+ case MIX_INVALID_OPERATION:
+ return audio_INVALID_OPERATION;
+ case MIX_OUT_OF_MEMORY:
+ return audio_OUT_OF_MEMORY;
+ default:
+ log_add (log_Debug, "noSound_GetError: unknown value %x",
+ value);
+ return audio_DRIVER_FAILURE;
+ break;
+ }
+}
+
+
+/*
+ * Sources
+ */
+
+void
+noSound_GenSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_GenSources (n, (mixer_Object *) psrcobj);
+}
+
+void
+noSound_DeleteSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_DeleteSources (n, (mixer_Object *) psrcobj);
+}
+
+bool
+noSound_IsSource (audio_Object srcobj)
+{
+ return mixer_IsSource ((mixer_Object) srcobj);
+}
+
+void
+noSound_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value)
+
+{
+ mixer_Sourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal) value);
+}
+
+void
+noSound_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value)
+{
+ mixer_Sourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+noSound_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_Sourcefv ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+noSound_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetSourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal *) value);
+ if (pname == MIX_SOURCE_STATE)
+ {
+ switch (*value)
+ {
+ case MIX_INITIAL:
+ *value = audio_INITIAL;
+ break;
+ case MIX_STOPPED:
+ *value = audio_STOPPED;
+ break;
+ case MIX_PLAYING:
+ *value = audio_PLAYING;
+ break;
+ case MIX_PAUSED:
+ *value = audio_PAUSED;
+ break;
+ default:
+ log_add (log_Debug, "noSound_GetSourcei(): unknown value %lx",
+ (long int) *value);
+ *value = audio_DRIVER_FAILURE;
+ }
+ }
+}
+
+void
+noSound_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_GetSourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+noSound_SourceRewind (audio_Object srcobj)
+{
+ mixer_SourceRewind ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourcePlay (audio_Object srcobj)
+{
+ mixer_SourcePlay ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourcePause (audio_Object srcobj)
+{
+ mixer_SourcePause ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourceStop (audio_Object srcobj)
+{
+ mixer_SourceStop ((mixer_Object) srcobj);
+}
+
+void
+noSound_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceQueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+void
+noSound_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceUnqueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+
+/*
+ * Buffers
+ */
+
+void
+noSound_GenBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_GenBuffers (n, (mixer_Object *) pbufobj);
+}
+
+void
+noSound_DeleteBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_DeleteBuffers (n, (mixer_Object *) pbufobj);
+}
+
+bool
+noSound_IsBuffer (audio_Object bufobj)
+{
+ return mixer_IsBuffer ((mixer_Object) bufobj);
+}
+
+void
+noSound_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetBufferi ((mixer_Object) bufobj, (mixer_BufferProp) pname,
+ (mixer_IntVal *) value);
+}
+
+void
+noSound_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ mixer_BufferData ((mixer_Object) bufobj, format, data, size, freq);
+}
diff --git a/src/libs/sound/mixer/nosound/audiodrv_nosound.h b/src/libs/sound/mixer/nosound/audiodrv_nosound.h
new file mode 100644
index 0000000..173b706
--- /dev/null
+++ b/src/libs/sound/mixer/nosound/audiodrv_nosound.h
@@ -0,0 +1,69 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Nosound audio driver
+ */
+
+#ifndef LIBS_SOUND_MIXER_NOSOUND_AUDIODRV_NOSOUND_H_
+#define LIBS_SOUND_MIXER_NOSOUND_AUDIODRV_NOSOUND_H_
+
+#include "config.h"
+#include "libs/sound/sound.h"
+#include "libs/sound/mixer/mixer.h"
+
+
+/* Playback task */
+int PlaybackTaskFunc (void *data);
+
+/* General */
+sint32 noSound_Init (audio_Driver *driver, sint32 flags);
+void noSound_Uninit (void);
+sint32 noSound_GetError (void);
+
+/* Sources */
+void noSound_GenSources (uint32 n, audio_Object *psrcobj);
+void noSound_DeleteSources (uint32 n, audio_Object *psrcobj);
+bool noSound_IsSource (audio_Object srcobj);
+void noSound_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value);
+void noSound_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value);
+void noSound_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void noSound_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value);
+void noSound_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void noSound_SourceRewind (audio_Object srcobj);
+void noSound_SourcePlay (audio_Object srcobj);
+void noSound_SourcePause (audio_Object srcobj);
+void noSound_SourceStop (audio_Object srcobj);
+void noSound_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+void noSound_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+
+/* Buffers */
+void noSound_GenBuffers (uint32 n, audio_Object *pbufobj);
+void noSound_DeleteBuffers (uint32 n, audio_Object *pbufobj);
+bool noSound_IsBuffer (audio_Object bufobj);
+void noSound_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value);
+void noSound_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+
+#endif /* LIBS_SOUND_MIXER_NOSOUND_AUDIODRV_NOSOUND_H_ */
diff --git a/src/libs/sound/mixer/sdl/Makeinfo b/src/libs/sound/mixer/sdl/Makeinfo
new file mode 100644
index 0000000..64c22c0
--- /dev/null
+++ b/src/libs/sound/mixer/sdl/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="audiodrv_sdl.c"
+uqm_HFILES="audiodrv_sdl.h"
diff --git a/src/libs/sound/mixer/sdl/audiodrv_sdl.c b/src/libs/sound/mixer/sdl/audiodrv_sdl.c
new file mode 100644
index 0000000..7ef522e
--- /dev/null
+++ b/src/libs/sound/mixer/sdl/audiodrv_sdl.c
@@ -0,0 +1,486 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* SDL audio driver
+ */
+
+#include "audiodrv_sdl.h"
+#include "../../sndintrn.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include <stdlib.h>
+
+
+/* SDL2 wants to talk to a specific device. We'll let SDL1 use the same
+ * function names and just throw the device argument away. */
+#if SDL_MAJOR_VERSION > 1
+static SDL_AudioDeviceID dev;
+#else
+#define SDL_CloseAudioDevice(x) SDL_CloseAudio ()
+#define SDL_PauseAudioDevice(x, y) SDL_PauseAudio (y)
+#endif
+static const audio_Driver mixSDL_Driver =
+{
+ mixSDL_Uninit,
+ mixSDL_GetError,
+ audio_DRIVER_MIXSDL,
+ {
+ /* Errors */
+ MIX_NO_ERROR,
+ MIX_INVALID_NAME,
+ MIX_INVALID_ENUM,
+ MIX_INVALID_VALUE,
+ MIX_INVALID_OPERATION,
+ MIX_OUT_OF_MEMORY,
+ MIX_DRIVER_FAILURE,
+
+ /* Source properties */
+ MIX_POSITION,
+ MIX_LOOPING,
+ MIX_BUFFER,
+ MIX_GAIN,
+ MIX_SOURCE_STATE,
+ MIX_BUFFERS_QUEUED,
+ MIX_BUFFERS_PROCESSED,
+
+ /* Source state information */
+ MIX_INITIAL,
+ MIX_STOPPED,
+ MIX_PLAYING,
+ MIX_PAUSED,
+
+ /* Sound buffer properties */
+ MIX_FREQUENCY,
+ MIX_BITS,
+ MIX_CHANNELS,
+ MIX_SIZE,
+ MIX_FORMAT_MONO16,
+ MIX_FORMAT_STEREO16,
+ MIX_FORMAT_MONO8,
+ MIX_FORMAT_STEREO8
+ },
+
+ /* Sources */
+ mixSDL_GenSources,
+ mixSDL_DeleteSources,
+ mixSDL_IsSource,
+ mixSDL_Sourcei,
+ mixSDL_Sourcef,
+ mixSDL_Sourcefv,
+ mixSDL_GetSourcei,
+ mixSDL_GetSourcef,
+ mixSDL_SourceRewind,
+ mixSDL_SourcePlay,
+ mixSDL_SourcePause,
+ mixSDL_SourceStop,
+ mixSDL_SourceQueueBuffers,
+ mixSDL_SourceUnqueueBuffers,
+
+ /* Buffers */
+ mixSDL_GenBuffers,
+ mixSDL_DeleteBuffers,
+ mixSDL_IsBuffer,
+ mixSDL_GetBufferi,
+ mixSDL_BufferData
+};
+
+
+static void audioCallback (void *userdata, Uint8 *stream, int len);
+
+/*
+ * Initialization
+ */
+
+sint32
+mixSDL_Init (audio_Driver *driver, sint32 flags)
+{
+ int i;
+ SDL_AudioSpec desired, obtained;
+ mixer_Quality quality;
+ TFB_DecoderFormats formats =
+ {
+ MIX_IS_BIG_ENDIAN, MIX_WANT_BIG_ENDIAN,
+ audio_FORMAT_MONO8, audio_FORMAT_STEREO8,
+ audio_FORMAT_MONO16, audio_FORMAT_STEREO16
+ };
+
+ log_add (log_Info, "Initializing SDL audio subsystem.");
+ if ((SDL_InitSubSystem(SDL_INIT_AUDIO)) == -1)
+ {
+ log_add (log_Error, "Couldn't initialize audio subsystem: %s",
+ SDL_GetError());
+ return -1;
+ }
+ log_add (log_Info, "SDL audio subsystem initialized.");
+
+ if (flags & audio_QUALITY_HIGH)
+ {
+ quality = MIX_QUALITY_HIGH;
+ desired.freq = 44100;
+ desired.samples = 4096;
+ }
+ else if (flags & audio_QUALITY_LOW)
+ {
+ quality = MIX_QUALITY_LOW;
+#ifdef __SYMBIAN32__
+ desired.freq = 11025;
+ desired.samples = 4096;
+#else
+ desired.freq = 22050;
+ desired.samples = 2048;
+#endif
+ }
+ else
+ {
+ quality = MIX_QUALITY_DEFAULT;
+ desired.freq = 44100;
+ desired.samples = 4096;
+ }
+
+ desired.format = AUDIO_S16SYS;
+ desired.channels = 2;
+ desired.callback = audioCallback;
+
+ log_add (log_Info, "Opening SDL audio device.");
+#if SDL_MAJOR_VERSION > 1
+ dev = SDL_OpenAudioDevice (NULL, 0, &desired, &obtained,
+ SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE
+#ifdef SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+ | SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+#endif
+ );
+ if (dev != 0 && obtained.channels != 1 && obtained.channels != 2)
+ {
+ /* Try again without SDL_AUDIO_ALLOW_CHANNELS_CHANGE
+ * in case the device only supports >2 channels for some
+ * reason */
+ SDL_CloseAudioDevice (dev);
+ dev = SDL_OpenAudioDevice (NULL, 0, &desired, &obtained,
+ SDL_AUDIO_ALLOW_FREQUENCY_CHANGE
+#ifdef SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+ | SDL_AUDIO_ALLOW_SAMPLES_CHANGE
+#endif
+ );
+ }
+ if (dev == 0)
+#else
+ if (SDL_OpenAudio (&desired, &obtained) < 0)
+#endif
+ {
+ log_add (log_Error, "Unable to open audio device: %s",
+ SDL_GetError ());
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+ if (obtained.format != desired.format ||
+ (obtained.channels != 1 && obtained.channels != 2))
+ {
+ log_add (log_Error, "Unable to obtain desired audio format.");
+ SDL_CloseAudio ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+
+ {
+#if SDL_MAJOR_VERSION == 1
+ char devicename[256];
+ SDL_AudioDriverName (devicename, sizeof (devicename));
+#else
+ const char *devicename = SDL_GetCurrentAudioDriver ();
+#endif
+ log_add (log_Info, " using %s at %d Hz 16 bit %s, "
+ "%d samples audio buffer",
+ devicename, obtained.freq,
+ obtained.channels > 1 ? "stereo" : "mono",
+ obtained.samples);
+ }
+
+ log_add (log_Info, "Initializing mixer.");
+ if (!mixer_Init (obtained.freq, MIX_FORMAT_MAKE (2, obtained.channels),
+ quality, 0))
+ {
+ log_add (log_Error, "Mixer initialization failed: %x",
+ mixer_GetError ());
+ SDL_CloseAudioDevice (dev);
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+ log_add (log_Info, "Mixer initialized.");
+
+ log_add (log_Info, "Initializing sound decoders.");
+ if (SoundDecoder_Init (flags, &formats))
+ {
+ log_add (log_Error, "Sound decoders initialization failed.");
+ SDL_CloseAudioDevice (dev);
+ mixer_Uninit ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+ log_add (log_Info, "Sound decoders initialized.");
+
+ *driver = mixSDL_Driver;
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ audio_GenSources (1, &soundSource[i].handle);
+ soundSource[i].stream_mutex = CreateMutex ("MixSDL stream mutex", SYNC_CLASS_AUDIO);
+ }
+
+ if (InitStreamDecoder ())
+ {
+ log_add (log_Error, "Stream decoder initialization failed.");
+ // TODO: cleanup source mutexes [or is it "muti"? :) ]
+ SDL_CloseAudioDevice (dev);
+ SoundDecoder_Uninit ();
+ mixer_Uninit ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+ return -1;
+ }
+
+ atexit (unInitAudio);
+
+ SetSFXVolume (sfxVolumeScale);
+ SetSpeechVolume (speechVolumeScale);
+ SetMusicVolume ((COUNT)musicVolume);
+
+ SDL_PauseAudioDevice (dev, 0);
+
+ return 0;
+}
+
+void
+mixSDL_Uninit (void)
+{
+ int i;
+
+ UninitStreamDecoder ();
+
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ if (soundSource[i].sample && soundSource[i].sample->decoder)
+ {
+ StopStream (i);
+ }
+ if (soundSource[i].sbuffer)
+ {
+ void *sbuffer = soundSource[i].sbuffer;
+ soundSource[i].sbuffer = NULL;
+ HFree (sbuffer);
+ }
+ DestroyMutex (soundSource[i].stream_mutex);
+ soundSource[i].stream_mutex = 0;
+
+ mixSDL_DeleteSources (1, &soundSource[i].handle);
+ }
+
+ SDL_CloseAudioDevice (dev);
+ mixer_Uninit ();
+ SoundDecoder_Uninit ();
+ SDL_QuitSubSystem (SDL_INIT_AUDIO);
+}
+
+static void
+audioCallback (void *userdata, Uint8 *stream, int len)
+{
+ mixer_MixChannels (userdata, stream, len);
+}
+
+/*
+ * General
+ */
+
+sint32
+mixSDL_GetError (void)
+{
+ sint32 value = mixer_GetError ();
+ switch (value)
+ {
+ case MIX_NO_ERROR:
+ return audio_NO_ERROR;
+ case MIX_INVALID_NAME:
+ return audio_INVALID_NAME;
+ case MIX_INVALID_ENUM:
+ return audio_INVALID_ENUM;
+ case MIX_INVALID_VALUE:
+ return audio_INVALID_VALUE;
+ case MIX_INVALID_OPERATION:
+ return audio_INVALID_OPERATION;
+ case MIX_OUT_OF_MEMORY:
+ return audio_OUT_OF_MEMORY;
+ default:
+ log_add (log_Debug, "mixSDL_GetError: unknown value %x", value);
+ return audio_DRIVER_FAILURE;
+ break;
+ }
+}
+
+
+/*
+ * Sources
+ */
+
+void
+mixSDL_GenSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_GenSources (n, (mixer_Object *) psrcobj);
+}
+
+void
+mixSDL_DeleteSources (uint32 n, audio_Object *psrcobj)
+{
+ mixer_DeleteSources (n, (mixer_Object *) psrcobj);
+}
+
+bool
+mixSDL_IsSource (audio_Object srcobj)
+{
+ return mixer_IsSource ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value)
+
+{
+ mixer_Sourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal) value);
+}
+
+void
+mixSDL_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value)
+{
+ mixer_Sourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+mixSDL_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_Sourcefv ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+mixSDL_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetSourcei ((mixer_Object) srcobj, (mixer_SourceProp) pname,
+ (mixer_IntVal *) value);
+ if (pname == MIX_SOURCE_STATE)
+ {
+ switch (*value)
+ {
+ case MIX_INITIAL:
+ *value = audio_INITIAL;
+ break;
+ case MIX_STOPPED:
+ *value = audio_STOPPED;
+ break;
+ case MIX_PLAYING:
+ *value = audio_PLAYING;
+ break;
+ case MIX_PAUSED:
+ *value = audio_PAUSED;
+ break;
+ default:
+ *value = audio_DRIVER_FAILURE;
+ }
+ }
+}
+
+void
+mixSDL_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ mixer_GetSourcef ((mixer_Object) srcobj, (mixer_SourceProp) pname, value);
+}
+
+void
+mixSDL_SourceRewind (audio_Object srcobj)
+{
+ mixer_SourceRewind ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourcePlay (audio_Object srcobj)
+{
+ mixer_SourcePlay ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourcePause (audio_Object srcobj)
+{
+ mixer_SourcePause ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourceStop (audio_Object srcobj)
+{
+ mixer_SourceStop ((mixer_Object) srcobj);
+}
+
+void
+mixSDL_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceQueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+void
+mixSDL_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ mixer_SourceUnqueueBuffers ((mixer_Object) srcobj, n,
+ (mixer_Object *) pbufobj);
+}
+
+
+/*
+ * Buffers
+ */
+
+void
+mixSDL_GenBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_GenBuffers (n, (mixer_Object *) pbufobj);
+}
+
+void
+mixSDL_DeleteBuffers (uint32 n, audio_Object *pbufobj)
+{
+ mixer_DeleteBuffers (n, (mixer_Object *) pbufobj);
+}
+
+bool
+mixSDL_IsBuffer (audio_Object bufobj)
+{
+ return mixer_IsBuffer ((mixer_Object) bufobj);
+}
+
+void
+mixSDL_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value)
+{
+ mixer_GetBufferi ((mixer_Object) bufobj, (mixer_BufferProp) pname,
+ (mixer_IntVal *) value);
+}
+
+void
+mixSDL_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ mixer_BufferData ((mixer_Object) bufobj, format, data, size, freq);
+}
diff --git a/src/libs/sound/mixer/sdl/audiodrv_sdl.h b/src/libs/sound/mixer/sdl/audiodrv_sdl.h
new file mode 100644
index 0000000..d74301b
--- /dev/null
+++ b/src/libs/sound/mixer/sdl/audiodrv_sdl.h
@@ -0,0 +1,66 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* SDL audio driver
+ */
+
+#ifndef LIBS_SOUND_MIXER_SDL_AUDIODRV_SDL_H_
+#define LIBS_SOUND_MIXER_SDL_AUDIODRV_SDL_H_
+
+#include "port.h"
+#include "libs/sound/sound.h"
+#include "libs/sound/mixer/mixer.h"
+#include SDL_INCLUDE(SDL.h)
+
+/* General */
+sint32 mixSDL_Init (audio_Driver *driver, sint32 flags);
+void mixSDL_Uninit (void);
+sint32 mixSDL_GetError (void);
+
+/* Sources */
+void mixSDL_GenSources (uint32 n, audio_Object *psrcobj);
+void mixSDL_DeleteSources (uint32 n, audio_Object *psrcobj);
+bool mixSDL_IsSource (audio_Object srcobj);
+void mixSDL_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value);
+void mixSDL_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value);
+void mixSDL_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void mixSDL_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value);
+void mixSDL_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void mixSDL_SourceRewind (audio_Object srcobj);
+void mixSDL_SourcePlay (audio_Object srcobj);
+void mixSDL_SourcePause (audio_Object srcobj);
+void mixSDL_SourceStop (audio_Object srcobj);
+void mixSDL_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+void mixSDL_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+
+/* Buffers */
+void mixSDL_GenBuffers (uint32 n, audio_Object *pbufobj);
+void mixSDL_DeleteBuffers (uint32 n, audio_Object *pbufobj);
+bool mixSDL_IsBuffer (audio_Object bufobj);
+void mixSDL_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value);
+void mixSDL_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+
+#endif /* LIBS_SOUND_MIXER_SDL_AUDIODRV_SDL_H_ */
diff --git a/src/libs/sound/music.c b/src/libs/sound/music.c
new file mode 100644
index 0000000..c3f92f0
--- /dev/null
+++ b/src/libs/sound/music.c
@@ -0,0 +1,233 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "libs/file.h"
+#include "options.h"
+#include "sound.h"
+#include "sndintrn.h"
+#include "libs/reslib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+static MUSIC_REF curMusicRef;
+static MUSIC_REF curSpeechRef;
+
+void
+PLRPlaySong (MUSIC_REF MusicRef, BOOLEAN Continuous, BYTE Priority)
+{
+ TFB_SoundSample **pmus = MusicRef;
+
+ if (pmus)
+ {
+ LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ // Always scope the music data, we may need it
+ PlayStream ((*pmus), MUSIC_SOURCE, Continuous, true, true);
+ UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+
+ curMusicRef = MusicRef;
+ }
+
+ (void) Priority; /* Satisfy compiler because of unused variable */
+}
+
+void
+PLRStop (MUSIC_REF MusicRef)
+{
+ if (MusicRef == curMusicRef || MusicRef == (MUSIC_REF)~0)
+ {
+ LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ StopStream (MUSIC_SOURCE);
+ UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+
+ curMusicRef = 0;
+ }
+}
+
+BOOLEAN
+PLRPlaying (MUSIC_REF MusicRef)
+{
+ if (curMusicRef && (MusicRef == curMusicRef || MusicRef == (MUSIC_REF)~0))
+ {
+ BOOLEAN playing;
+
+ LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ playing = PlayingStream (MUSIC_SOURCE);
+ UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+
+ return playing;
+ }
+
+ return FALSE;
+}
+
+void
+PLRSeek (MUSIC_REF MusicRef, DWORD pos)
+{
+ if (MusicRef == curMusicRef || MusicRef == (MUSIC_REF)~0)
+ {
+ LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ SeekStream (MUSIC_SOURCE, pos);
+ UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ }
+}
+
+void
+PLRPause (MUSIC_REF MusicRef)
+{
+ if (MusicRef == curMusicRef || MusicRef == (MUSIC_REF)~0)
+ {
+ LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ PauseStream (MUSIC_SOURCE);
+ UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ }
+}
+
+void
+PLRResume (MUSIC_REF MusicRef)
+{
+ if (MusicRef == curMusicRef || MusicRef == (MUSIC_REF)~0)
+ {
+ LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ ResumeStream (MUSIC_SOURCE);
+ UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ }
+}
+
+void
+snd_PlaySpeech (MUSIC_REF SpeechRef)
+{
+ TFB_SoundSample **pmus = SpeechRef;
+
+ if (pmus)
+ {
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ // Do not need to scope the music-as-speech as of now
+ PlayStream (*pmus, SPEECH_SOURCE, false, false, true);
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ curSpeechRef = SpeechRef;
+ }
+}
+
+void
+snd_StopSpeech (void)
+{
+ if (!curSpeechRef)
+ return;
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ StopStream (SPEECH_SOURCE);
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ curSpeechRef = 0;
+}
+
+BOOLEAN
+DestroyMusic (MUSIC_REF MusicRef)
+{
+ return _ReleaseMusicData (MusicRef);
+}
+
+void
+SetMusicVolume (COUNT Volume)
+{
+ float f = (Volume / (float)MAX_VOLUME) * musicVolumeScale;
+ musicVolume = Volume;
+ audio_Sourcef (soundSource[MUSIC_SOURCE].handle, audio_GAIN, f);
+}
+
+char*
+CheckMusicResName (char* fileName)
+{
+ if (!fileExists2 (contentDir, fileName))
+ log_add (log_Warning, "Requested track '%s' not found.", fileName);
+ return fileName;
+}
+
+void *
+_GetMusicData (uio_Stream *fp, DWORD length)
+{
+ MUSIC_REF h;
+ TFB_SoundSample *sample;
+ TFB_SoundDecoder *decoder;
+ char filename[256];
+
+ if (!_cur_resfile_name)
+ return NULL;
+
+ strncpy (filename, _cur_resfile_name, sizeof(filename) - 1);
+ filename[sizeof(filename) - 1] = '\0';
+ CheckMusicResName (filename);
+
+ log_add (log_Info, "_GetMusicData(): loading %s", filename);
+ decoder = SoundDecoder_Load (contentDir, filename, 4096, 0, 0);
+ if (!decoder)
+ {
+ log_add (log_Warning, "_GetMusicData(): couldn't load %s", filename);
+ return NULL;
+ }
+
+ h = AllocMusicData (sizeof (void *));
+ if (!h)
+ {
+ SoundDecoder_Free (decoder);
+ return NULL;
+ }
+
+ sample = TFB_CreateSoundSample (decoder, 64, NULL);
+ *h = sample;
+
+ log_add (log_Info, " decoder: %s, rate %d format %x",
+ SoundDecoder_GetName (sample->decoder),
+ sample->decoder->frequency, sample->decoder->format);
+
+ (void) fp; /* satisfy compiler (unused parameter) */
+ (void) length; /* satisfy compiler (unused parameter) */
+ return (h);
+}
+
+BOOLEAN
+_ReleaseMusicData (void *data)
+{
+ TFB_SoundSample **pmus = data;
+ TFB_SoundSample *sample;
+
+ if (pmus == NULL)
+ return (FALSE);
+
+ sample = *pmus;
+ assert (sample != 0);
+ if (sample->decoder)
+ {
+ TFB_SoundDecoder *decoder = sample->decoder;
+ LockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+ if (soundSource[MUSIC_SOURCE].sample == sample)
+ { // Currently playing this sample! Not good.
+ StopStream (MUSIC_SOURCE);
+ }
+ UnlockMutex (soundSource[MUSIC_SOURCE].stream_mutex);
+
+ sample->decoder = NULL;
+ SoundDecoder_Free (decoder);
+ }
+ TFB_DestroySoundSample (sample);
+ FreeMusicData (data);
+
+ return (TRUE);
+}
+
diff --git a/src/libs/sound/openal/Makeinfo b/src/libs/sound/openal/Makeinfo
new file mode 100644
index 0000000..1469461
--- /dev/null
+++ b/src/libs/sound/openal/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="audiodrv_openal.c"
+uqm_HFILES="audiodrv_openal.h"
diff --git a/src/libs/sound/openal/audiodrv_openal.c b/src/libs/sound/openal/audiodrv_openal.c
new file mode 100644
index 0000000..eee1cd1
--- /dev/null
+++ b/src/libs/sound/openal/audiodrv_openal.c
@@ -0,0 +1,420 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* OpenAL audio driver
+ */
+
+#ifdef HAVE_OPENAL
+
+
+#include "audiodrv_openal.h"
+#include "../sndintrn.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include <stdlib.h>
+
+
+ALCcontext *alcContext = NULL;
+ALCdevice *alcDevice = NULL;
+ALfloat defaultPos[] = {0.0f, 0.0f, -1.0f};
+ALfloat listenerPos[] = {0.0f, 0.0f, 0.0f};
+ALfloat listenerVel[] = {0.0f, 0.0f, 0.0f};
+ALfloat listenerOri[] = {0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f};
+
+static const audio_Driver openAL_Driver =
+{
+ openAL_Uninit,
+ openAL_GetError,
+ audio_DRIVER_OPENAL,
+ {
+ /* Errors */
+ AL_FALSE,
+ AL_INVALID_NAME,
+ AL_INVALID_ENUM,
+ AL_INVALID_VALUE,
+ AL_INVALID_OPERATION,
+ AL_OUT_OF_MEMORY,
+ audio_DRIVER_FAILURE,
+
+ /* Source properties */
+ AL_POSITION,
+ AL_LOOPING,
+ AL_BUFFER,
+ AL_GAIN,
+ AL_SOURCE_STATE,
+ AL_BUFFERS_QUEUED,
+ AL_BUFFERS_PROCESSED,
+
+ /* Source state information */
+ AL_INITIAL,
+ AL_STOPPED,
+ AL_PLAYING,
+ AL_PAUSED,
+
+ /* Sound buffer properties */
+ AL_FREQUENCY,
+ AL_BITS,
+ AL_CHANNELS,
+ AL_SIZE,
+ AL_FORMAT_MONO16,
+ AL_FORMAT_STEREO16,
+ AL_FORMAT_MONO8,
+ AL_FORMAT_STEREO8
+ },
+
+ /* Sources */
+ openAL_GenSources,
+ openAL_DeleteSources,
+ openAL_IsSource,
+ openAL_Sourcei,
+ openAL_Sourcef,
+ openAL_Sourcefv,
+ openAL_GetSourcei,
+ openAL_GetSourcef,
+ openAL_SourceRewind,
+ openAL_SourcePlay,
+ openAL_SourcePause,
+ openAL_SourceStop,
+ openAL_SourceQueueBuffers,
+ openAL_SourceUnqueueBuffers,
+
+ /* Buffers */
+ openAL_GenBuffers,
+ openAL_DeleteBuffers,
+ openAL_IsBuffer,
+ openAL_GetBufferi,
+ openAL_BufferData
+};
+
+
+/*
+ * Initialization
+ */
+
+sint32
+openAL_Init (audio_Driver *driver, sint32 flags)
+{
+ int i;
+ TFB_DecoderFormats formats =
+ {
+ MIX_IS_BIG_ENDIAN, MIX_WANT_BIG_ENDIAN,
+ audio_FORMAT_MONO8, audio_FORMAT_STEREO8,
+ audio_FORMAT_MONO16, audio_FORMAT_STEREO16
+ };
+
+ log_add (log_Info, "Initializing OpenAL.");
+ alcDevice = alcOpenDevice (NULL);
+
+ if (!alcDevice)
+ {
+ log_add (log_Error, "Couldn't initialize OpenAL: %d",
+ alcGetError (NULL));
+ return -1;
+ }
+
+ *driver = openAL_Driver;
+
+ alcContext = alcCreateContext (alcDevice, NULL);
+ if (!alcContext)
+ {
+ log_add (log_Error, "Couldn't create OpenAL context: %d",
+ alcGetError (alcDevice));
+ alcCloseDevice (alcDevice);
+ alcDevice = NULL;
+ return -1;
+ }
+
+ alcMakeContextCurrent (alcContext);
+
+ log_add (log_Info, "OpenAL initialized.\n"
+ " version: %s\n"
+ " vendor: %s\n"
+ " renderer: %s\n"
+ " device: %s",
+ alGetString (AL_VERSION), alGetString (AL_VENDOR),
+ alGetString (AL_RENDERER),
+ alcGetString (alcDevice, ALC_DEFAULT_DEVICE_SPECIFIER));
+ //log_add (log_Info, " extensions: %s", alGetString (AL_EXTENSIONS));
+
+ log_add (log_Info, "Initializing sound decoders.");
+ if (SoundDecoder_Init (flags, &formats))
+ {
+ log_add (log_Error, "Sound decoders initialization failed.");
+ alcMakeContextCurrent (NULL);
+ alcDestroyContext (alcContext);
+ alcContext = NULL;
+ alcCloseDevice (alcDevice);
+ alcDevice = NULL;
+ return -1;
+ }
+ log_add (log_Error, "Sound decoders initialized.");
+
+ alListenerfv (AL_POSITION, listenerPos);
+ alListenerfv (AL_VELOCITY, listenerVel);
+ alListenerfv (AL_ORIENTATION, listenerOri);
+
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ float zero[3] = {0.0f, 0.0f, 0.0f};
+
+ alGenSources (1, &soundSource[i].handle);
+ alSourcei (soundSource[i].handle, AL_LOOPING, AL_FALSE);
+ alSourcefv (soundSource[i].handle, AL_POSITION, defaultPos);
+ alSourcefv (soundSource[i].handle, AL_VELOCITY, zero);
+ alSourcefv (soundSource[i].handle, AL_DIRECTION, zero);
+
+ soundSource[i].stream_mutex = CreateMutex ("OpenAL stream mutex", SYNC_CLASS_AUDIO);
+ }
+
+ if (InitStreamDecoder ())
+ {
+ log_add (log_Error, "Stream decoder initialization failed.");
+ // TODO: cleanup source mutexes [or is it "muti"? :) ]
+ SoundDecoder_Uninit ();
+ alcMakeContextCurrent (NULL);
+ alcDestroyContext (alcContext);
+ alcContext = NULL;
+ alcCloseDevice (alcDevice);
+ alcDevice = NULL;
+ return -1;
+ }
+
+ alDistanceModel (AL_INVERSE_DISTANCE);
+
+ (void) driver; // eat compiler warning
+
+ return 0;
+}
+
+void
+openAL_Uninit (void)
+{
+ int i;
+
+ UninitStreamDecoder ();
+
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ if (soundSource[i].sample && soundSource[i].sample->decoder)
+ {
+ StopStream (i);
+ }
+ if (soundSource[i].sbuffer)
+ {
+ void *sbuffer = soundSource[i].sbuffer;
+ soundSource[i].sbuffer = NULL;
+ HFree (sbuffer);
+ }
+ DestroyMutex (soundSource[i].stream_mutex);
+ }
+
+ alcMakeContextCurrent (NULL);
+ alcDestroyContext (alcContext);
+ alcContext = NULL;
+ alcCloseDevice (alcDevice);
+ alcDevice = NULL;
+
+ SoundDecoder_Uninit ();
+}
+
+
+/*
+ * General
+ */
+
+sint32
+openAL_GetError (void)
+{
+ ALint value = alGetError ();
+ switch (value)
+ {
+ case AL_FALSE:
+ return audio_NO_ERROR;
+ case AL_INVALID_NAME:
+ return audio_INVALID_NAME;
+ case AL_INVALID_ENUM:
+ return audio_INVALID_ENUM;
+ case AL_INVALID_VALUE:
+ return audio_INVALID_VALUE;
+ case AL_INVALID_OPERATION:
+ return audio_INVALID_OPERATION;
+ case AL_OUT_OF_MEMORY:
+ return audio_OUT_OF_MEMORY;
+ default:
+ log_add (log_Debug, "openAL_GetError: unknown value %x", value);
+ return audio_DRIVER_FAILURE;
+ break;
+ }
+}
+
+
+/*
+ * Sources
+ */
+
+void
+openAL_GenSources (uint32 n, audio_Object *psrcobj)
+{
+ alGenSources ((ALsizei) n, (ALuint *) psrcobj);
+}
+
+void
+openAL_DeleteSources (uint32 n, audio_Object *psrcobj)
+{
+ alDeleteSources ((ALsizei) n, (ALuint *) psrcobj);
+}
+
+bool
+openAL_IsSource (audio_Object srcobj)
+{
+ return alIsSource ((ALuint) srcobj);
+}
+
+void
+openAL_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value)
+
+{
+ alSourcei ((ALuint) srcobj, (ALenum) pname, (ALint) value);
+}
+
+void
+openAL_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value)
+{
+ alSourcef ((ALuint) srcobj, (ALenum) pname, (ALfloat) value);
+}
+
+void
+openAL_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ alSourcefv ((ALuint) srcobj, (ALenum) pname, (ALfloat *) value);
+}
+
+void
+openAL_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value)
+{
+ alGetSourcei ((ALuint) srcobj, (ALenum) pname, (ALint *) value);
+ if (pname == AL_SOURCE_STATE)
+ {
+ switch (*value)
+ {
+ case AL_INITIAL:
+ *value = audio_INITIAL;
+ break;
+ case AL_STOPPED:
+ *value = audio_STOPPED;
+ break;
+ case AL_PLAYING:
+ *value = audio_PLAYING;
+ break;
+ case AL_PAUSED:
+ *value = audio_PAUSED;
+ break;
+ default:
+ log_add (log_Debug, "openAL_GetSourcei(): unknown value %x",
+ *value);
+ *value = audio_DRIVER_FAILURE;
+ }
+ }
+}
+
+void
+openAL_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value)
+{
+ alGetSourcef ((ALuint) srcobj, (ALenum) pname, (ALfloat *) value);
+}
+
+void
+openAL_SourceRewind (audio_Object srcobj)
+{
+ alSourceRewind ((ALuint) srcobj);
+}
+
+void
+openAL_SourcePlay (audio_Object srcobj)
+{
+ alSourcePlay ((ALuint) srcobj);
+}
+
+void
+openAL_SourcePause (audio_Object srcobj)
+{
+ alSourcePause ((ALuint) srcobj);
+}
+
+void
+openAL_SourceStop (audio_Object srcobj)
+{
+ alSourceStop ((ALuint) srcobj);
+}
+
+void
+openAL_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ alSourceQueueBuffers ((ALuint) srcobj, (ALsizei) n, (ALuint *) pbufobj);
+}
+
+void
+openAL_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj)
+{
+ alSourceUnqueueBuffers ((ALuint) srcobj, (ALsizei) n, (ALuint *) pbufobj);
+}
+
+
+/*
+ * Buffers
+ */
+
+void
+openAL_GenBuffers (uint32 n, audio_Object *pbufobj)
+{
+ alGenBuffers ((ALsizei) n, (ALuint *) pbufobj);
+}
+
+void
+openAL_DeleteBuffers (uint32 n, audio_Object *pbufobj)
+{
+ alDeleteBuffers ((ALsizei) n, (ALuint *) pbufobj);
+}
+
+bool
+openAL_IsBuffer (audio_Object bufobj)
+{
+ return alIsBuffer ((ALuint) bufobj);
+}
+
+void
+openAL_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value)
+{
+ alGetBufferi ((ALuint) bufobj, (ALenum) pname, (ALint *) value);
+}
+
+void
+openAL_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq)
+{
+ alBufferData ((ALuint) bufobj, (ALenum) format, (ALvoid *) data,
+ (ALsizei) size, (ALsizei) freq);
+}
+
+#endif
diff --git a/src/libs/sound/openal/audiodrv_openal.h b/src/libs/sound/openal/audiodrv_openal.h
new file mode 100644
index 0000000..c1a3eff
--- /dev/null
+++ b/src/libs/sound/openal/audiodrv_openal.h
@@ -0,0 +1,86 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* OpenAL audio driver
+ */
+
+#ifndef LIBS_SOUND_OPENAL_AUDIODRV_OPENAL_H_
+#define LIBS_SOUND_OPENAL_AUDIODRV_OPENAL_H_
+
+#include "config.h"
+#include "libs/sound/sound.h"
+#include "endian_uqm.h"
+
+#if defined (__APPLE__)
+# include <OpenAL/al.h>
+# include <OpenAL/alc.h>
+#else
+# include <AL/al.h>
+# include <AL/alc.h>
+# ifdef _MSC_VER
+# pragma comment (lib, "OpenAL32.lib")
+# endif
+#endif
+
+/* This is just a simple endianness setup for decoders */
+#ifdef WORDS_BIGENDIAN
+# define MIX_IS_BIG_ENDIAN true
+# define MIX_WANT_BIG_ENDIAN true
+#else
+# define MIX_IS_BIG_ENDIAN false
+# define MIX_WANT_BIG_ENDIAN false
+#endif
+
+
+/* General */
+sint32 openAL_Init (audio_Driver *driver, sint32 flags);
+void openAL_Uninit (void);
+sint32 openAL_GetError (void);
+
+/* Sources */
+void openAL_GenSources (uint32 n, audio_Object *psrcobj);
+void openAL_DeleteSources (uint32 n, audio_Object *psrcobj);
+bool openAL_IsSource (audio_Object srcobj);
+void openAL_Sourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal value);
+void openAL_Sourcef (audio_Object srcobj, audio_SourceProp pname,
+ float value);
+void openAL_Sourcefv (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void openAL_GetSourcei (audio_Object srcobj, audio_SourceProp pname,
+ audio_IntVal *value);
+void openAL_GetSourcef (audio_Object srcobj, audio_SourceProp pname,
+ float *value);
+void openAL_SourceRewind (audio_Object srcobj);
+void openAL_SourcePlay (audio_Object srcobj);
+void openAL_SourcePause (audio_Object srcobj);
+void openAL_SourceStop (audio_Object srcobj);
+void openAL_SourceQueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+void openAL_SourceUnqueueBuffers (audio_Object srcobj, uint32 n,
+ audio_Object* pbufobj);
+
+/* Buffers */
+void openAL_GenBuffers (uint32 n, audio_Object *pbufobj);
+void openAL_DeleteBuffers (uint32 n, audio_Object *pbufobj);
+bool openAL_IsBuffer (audio_Object bufobj);
+void openAL_GetBufferi (audio_Object bufobj, audio_BufferProp pname,
+ audio_IntVal *value);
+void openAL_BufferData (audio_Object bufobj, uint32 format, void* data,
+ uint32 size, uint32 freq);
+
+
+#endif /* LIBS_SOUND_OPENAL_AUDIODRV_OPENAL_H_ */
diff --git a/src/libs/sound/resinst.c b/src/libs/sound/resinst.c
new file mode 100644
index 0000000..dc44c49
--- /dev/null
+++ b/src/libs/sound/resinst.c
@@ -0,0 +1,65 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sound.h"
+#include "sndintrn.h"
+
+static void
+GetSoundBankFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetSoundBankData);
+}
+
+static void
+GetMusicFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetMusicData);
+}
+
+BOOLEAN
+InstallAudioResTypes (void)
+{
+ InstallResTypeVectors ("SNDRES", GetSoundBankFileData, _ReleaseSoundBankData, NULL);
+ InstallResTypeVectors ("MUSICRES", GetMusicFileData, _ReleaseMusicData, NULL);
+ return (TRUE);
+}
+
+SOUND_REF
+LoadSoundInstance (RESOURCE res)
+{
+ void *hData;
+
+ hData = res_GetResource (res);
+ if (hData)
+ res_DetachResource (res);
+
+ return ((SOUND_REF)hData);
+}
+
+MUSIC_REF
+LoadMusicInstance (RESOURCE res)
+{
+ void *hData;
+
+ hData = res_GetResource (res);
+ if (hData)
+ res_DetachResource (res);
+
+ return ((MUSIC_REF)hData);
+}
+
diff --git a/src/libs/sound/sfx.c b/src/libs/sound/sfx.c
new file mode 100644
index 0000000..3060434
--- /dev/null
+++ b/src/libs/sound/sfx.c
@@ -0,0 +1,306 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "options.h"
+#include "sound.h"
+#include "sndintrn.h"
+#include "libs/reslib.h"
+#include "libs/log.h"
+#include "libs/strlib.h"
+ // for GetStringAddress()
+#include "libs/strings/strintrn.h"
+ // for AllocStringTable(), FreeStringTable()
+#include "libs/memlib.h"
+#include <math.h>
+
+
+static void CheckFinishedChannels (void);
+
+static const SoundPosition notPositional = {FALSE, 0, 0};
+
+void
+PlayChannel (COUNT channel, SOUND snd, SoundPosition pos,
+ void *positional_object, unsigned char priority)
+{
+ SOUNDPTR snd_ptr = GetSoundAddress (snd);
+ TFB_SoundSample *sample;
+
+ StopSource (channel);
+ // all finished (stopped) channels can be cleaned up at this point
+ // since this is the only func that can initiate an sfx sound
+ CheckFinishedChannels ();
+
+ if (!snd_ptr)
+ return; // nothing to play
+
+ sample = *(TFB_SoundSample**) snd_ptr;
+
+ soundSource[channel].sample = sample;
+ soundSource[channel].positional_object = positional_object;
+
+ UpdateSoundPosition (channel, optStereoSFX ? pos : notPositional);
+
+ audio_Sourcei (soundSource[channel].handle, audio_BUFFER,
+ sample->buffer[0]);
+ audio_SourcePlay (soundSource[channel].handle);
+ (void) priority;
+}
+
+void
+StopChannel (COUNT channel, unsigned char Priority)
+{
+ StopSource (channel);
+ (void)Priority; // ignored
+}
+
+static void
+CheckFinishedChannels (void)
+{
+ int i;
+
+ for (i = FIRST_SFX_SOURCE; i <= LAST_SFX_SOURCE; ++i)
+ {
+ audio_IntVal state;
+
+ audio_GetSourcei (soundSource[i].handle, audio_SOURCE_STATE,
+ &state);
+ if (state == audio_STOPPED)
+ {
+ CleanSource (i);
+ // and if it failed... we still dont care
+ audio_GetError();
+ }
+ }
+}
+
+BOOLEAN
+ChannelPlaying (COUNT WhichChannel)
+{
+ audio_IntVal state;
+
+ audio_GetSourcei (soundSource[WhichChannel].handle,
+ audio_SOURCE_STATE, &state);
+ if (state == audio_PLAYING)
+ return TRUE;
+ return FALSE;
+}
+
+void *
+GetPositionalObject (COUNT channel)
+{
+ return soundSource[channel].positional_object;
+}
+
+void
+SetPositionalObject (COUNT channel, void *positional_object)
+{
+ soundSource[channel].positional_object = positional_object;
+}
+
+void
+UpdateSoundPosition (COUNT channel, SoundPosition pos)
+{
+ const float ATTENUATION = 160.0f;
+ const float MIN_DISTANCE = 0.5f;
+ float fpos[3];
+
+ if (pos.positional)
+ {
+ float dist;
+
+ fpos[0] = pos.x / ATTENUATION;
+ fpos[1] = 0.0f;
+ fpos[2] = pos.y / ATTENUATION;
+ dist = (float) sqrt (fpos[0] * fpos[0] + fpos[2] * fpos[2]);
+ if (dist < MIN_DISTANCE)
+ { // object is too close to listener
+ // move it away along the same vector
+ float scale = MIN_DISTANCE / dist;
+ fpos[0] *= scale;
+ fpos[2] *= scale;
+ }
+
+ audio_Sourcefv (soundSource[channel].handle, audio_POSITION, fpos);
+ //log_add (log_Debug, "UpdateSoundPosition(): channel %d, pos %d %d, posobj %x",
+ // channel, pos.x, pos.y, (unsigned int)soundSource[channel].positional_object);
+ }
+ else
+ {
+ fpos[0] = fpos[1] = 0.0f;
+ fpos[2] = -1.0f;
+ audio_Sourcefv (soundSource[channel].handle, audio_POSITION, fpos);
+ }
+}
+
+void
+SetChannelVolume (COUNT channel, COUNT volume, BYTE priority)
+ // I wonder what this whole priority business is...
+ // I can probably ignore it.
+{
+ audio_Sourcef (soundSource[channel].handle, audio_GAIN,
+ (volume / (float)MAX_VOLUME) * sfxVolumeScale);
+ (void)priority; // ignored
+}
+
+void *
+_GetSoundBankData (uio_Stream *fp, DWORD length)
+{
+ int snd_ct, n;
+ DWORD opos;
+ char CurrentLine[1024], filename[1024];
+#define MAX_FX 256
+ TFB_SoundSample *sndfx[MAX_FX];
+ STRING_TABLE Snd;
+ STRING str;
+ int i;
+
+ (void) length; // ignored
+ opos = uio_ftell (fp);
+
+ {
+ char *s1, *s2;
+
+ if (_cur_resfile_name == 0
+ || (((s2 = 0), (s1 = strrchr (_cur_resfile_name, '/')) == 0)
+ && (s2 = strrchr (_cur_resfile_name, '\\')) == 0))
+ n = 0;
+ else
+ {
+ if (s2 > s1)
+ s1 = s2;
+ n = s1 - _cur_resfile_name + 1;
+ strncpy (filename, _cur_resfile_name, n);
+ }
+ }
+
+ snd_ct = 0;
+ while (uio_fgets (CurrentLine, sizeof (CurrentLine), fp) &&
+ snd_ct < MAX_FX)
+ {
+ TFB_SoundSample* sample;
+ TFB_SoundDecoder* decoder;
+ uint32 decoded_bytes;
+
+ if (sscanf (CurrentLine, "%s", &filename[n]) != 1)
+ {
+ log_add (log_Warning, "_GetSoundBankData: bad line: '%s'",
+ CurrentLine);
+ continue;
+ }
+
+ log_add (log_Info, "_GetSoundBankData(): loading %s", filename);
+
+ decoder = SoundDecoder_Load (contentDir, filename, 4096, 0, 0);
+ if (!decoder)
+ {
+ log_add (log_Warning, "_GetSoundBankData(): couldn't load %s",
+ filename);
+ continue;
+ }
+
+ // SFX samples don't have decoders, everything is pre-decoded below
+ sample = TFB_CreateSoundSample (NULL, 1, NULL);
+
+ // Decode everything and stash it in 1 buffer
+ decoded_bytes = SoundDecoder_DecodeAll (decoder);
+ log_add (log_Info, "_GetSoundBankData(): decoded bytes %d",
+ decoded_bytes);
+
+ audio_BufferData (sample->buffer[0], decoder->format,
+ decoder->buffer, decoded_bytes, decoder->frequency);
+ // just for informational purposes
+ sample->length = decoder->length;
+
+ SoundDecoder_Free (decoder);
+
+ sndfx[snd_ct] = sample;
+ ++snd_ct;
+ }
+
+ if (!snd_ct)
+ return NULL; // no sounds decoded
+
+ Snd = AllocStringTable (snd_ct, 0);
+ if (!Snd)
+ { // Oops, have to delete everything now
+ while (snd_ct--)
+ TFB_DestroySoundSample (sndfx[snd_ct]);
+
+ return NULL;
+ }
+
+ // Populate the STRING_TABLE with ptrs to sample
+ for (i = 0, str = Snd->strings; i < snd_ct; ++i, ++str)
+ {
+ TFB_SoundSample **target = HMalloc (sizeof (sndfx[0]));
+ *target = sndfx[i];
+ str->data = (STRINGPTR)target;
+ str->length = sizeof (sndfx[0]);
+ }
+
+ return Snd;
+}
+
+BOOLEAN
+_ReleaseSoundBankData (void *Snd)
+{
+ STRING_TABLE fxTab = Snd;
+ int index;
+
+ if (!fxTab)
+ return FALSE;
+
+ for (index = 0; index < fxTab->size; ++index)
+ {
+ int i;
+ void **sptr = (void**)fxTab->strings[index].data;
+ TFB_SoundSample *sample = (TFB_SoundSample*)*sptr;
+
+ // Check all sources and see if we are currently playing this sample
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ if (soundSource[i].sample == sample)
+ { // Playing this sample. Have to stop it.
+ StopSource (i);
+ soundSource[i].sample = NULL;
+ }
+ }
+
+ if (sample->decoder)
+ SoundDecoder_Free (sample->decoder);
+ sample->decoder = NULL;
+ TFB_DestroySoundSample (sample);
+ // sptr will be deleted by FreeStringTable() below
+ }
+
+ FreeStringTable (fxTab);
+
+ return TRUE;
+}
+
+BOOLEAN
+DestroySound(SOUND_REF target)
+{
+ return _ReleaseSoundBankData (target);
+}
+
+// The type conversions are implicit and will generate errors
+// or warnings if types change imcompatibly
+SOUNDPTR
+GetSoundAddress (SOUND sound)
+{
+ return GetStringAddress (sound);
+}
diff --git a/src/libs/sound/sndintrn.h b/src/libs/sound/sndintrn.h
new file mode 100644
index 0000000..179028c
--- /dev/null
+++ b/src/libs/sound/sndintrn.h
@@ -0,0 +1,76 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_SOUND_SNDINTRN_H_
+#define LIBS_SOUND_SNDINTRN_H_
+
+#include <stdio.h>
+#include "types.h"
+#include "libs/reslib.h"
+#include "libs/memlib.h"
+
+#define PAD_SCOPE_BYTES 256
+
+extern void *_GetMusicData (uio_Stream *fp, DWORD length);
+extern BOOLEAN _ReleaseMusicData (void *handle);
+
+extern void *_GetSoundBankData (uio_Stream *fp, DWORD length);
+extern BOOLEAN _ReleaseSoundBankData (void *handle);
+
+#define AllocMusicData HMalloc
+#define FreeMusicData HFree
+
+extern char* CheckMusicResName (char* filename);
+
+// audio data
+struct tfb_soundsample
+{
+ TFB_SoundDecoder *decoder; // decoder to read from
+ float length; // total length of decoder chain in seconds
+ audio_Object *buffer;
+ uint32 num_buffers;
+ TFB_SoundTag *buffer_tag;
+ sint32 offset; // initial offset
+ void* data; // user-defined data
+ TFB_SoundCallbacks callbacks; // user-defined callbacks
+};
+
+// equivalent to channel in legacy sound code
+typedef struct tfb_soundsource
+{
+ TFB_SoundSample *sample;
+ audio_Object handle;
+ bool stream_should_be_playing;
+ Mutex stream_mutex;
+ sint32 start_time; // for tracks played-time math
+ uint32 pause_time; // keep track for paused tracks
+ void *positional_object;
+
+ audio_Object last_q_buf; // for callbacks processing
+
+ // Cyclic waveform buffer for oscilloscope
+ void *sbuffer;
+ uint32 sbuf_size;
+ uint32 sbuf_tail;
+ uint32 sbuf_head;
+ uint32 sbuf_lasttime; // timestamp of the first queued buffer
+} TFB_SoundSource;
+
+extern TFB_SoundSource soundSource[];
+
+#endif /* LIBS_SOUND_SNDINTRN_H_ */
diff --git a/src/libs/sound/sound.c b/src/libs/sound/sound.c
new file mode 100644
index 0000000..f2a790e
--- /dev/null
+++ b/src/libs/sound/sound.c
@@ -0,0 +1,178 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sound.h"
+#include "sndintrn.h"
+#include "libs/compiler.h"
+#include "libs/inplib.h"
+#include "libs/memlib.h"
+
+
+int musicVolume = NORMAL_VOLUME;
+float musicVolumeScale;
+float sfxVolumeScale;
+float speechVolumeScale;
+TFB_SoundSource soundSource[NUM_SOUNDSOURCES];
+
+
+void
+StopSound (void)
+{
+ int i;
+
+ for (i = FIRST_SFX_SOURCE; i <= LAST_SFX_SOURCE; ++i)
+ {
+ StopSource (i);
+ }
+}
+
+void
+CleanSource (int iSource)
+{
+#define MAX_STACK_BUFFERS 64
+ audio_IntVal processed;
+
+ soundSource[iSource].positional_object = NULL;
+ audio_GetSourcei (soundSource[iSource].handle,
+ audio_BUFFERS_PROCESSED, &processed);
+ if (processed != 0)
+ {
+ audio_Object stack_bufs[MAX_STACK_BUFFERS];
+ audio_Object *bufs;
+
+ if (processed > MAX_STACK_BUFFERS)
+ bufs = (audio_Object *) HMalloc (
+ sizeof (audio_Object) * processed);
+ else
+ bufs = stack_bufs;
+
+ audio_SourceUnqueueBuffers (soundSource[iSource].handle,
+ processed, bufs);
+
+ if (processed > MAX_STACK_BUFFERS)
+ HFree (bufs);
+ }
+ // set the source state to 'initial'
+ audio_SourceRewind (soundSource[iSource].handle);
+}
+
+void
+StopSource (int iSource)
+{
+ audio_SourceStop (soundSource[iSource].handle);
+ CleanSource (iSource);
+}
+
+BOOLEAN
+SoundPlaying (void)
+{
+ int i;
+
+ for (i = 0; i < NUM_SOUNDSOURCES; ++i)
+ {
+ TFB_SoundSample *sample;
+ sample = soundSource[i].sample;
+ if (sample && sample->decoder)
+ {
+ BOOLEAN result;
+ LockMutex (soundSource[i].stream_mutex);
+ result = PlayingStream (i);
+ UnlockMutex (soundSource[i].stream_mutex);
+ if (result)
+ return TRUE;
+ }
+ else
+ {
+ audio_IntVal state;
+ audio_GetSourcei (soundSource[i].handle, audio_SOURCE_STATE, &state);
+ if (state == audio_PLAYING)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+// for now just spin in a sleep() loop
+// perhaps later change to condvar implementation
+void
+WaitForSoundEnd (COUNT Channel)
+{
+ while (Channel == TFBSOUND_WAIT_ALL ?
+ SoundPlaying () : ChannelPlaying (Channel))
+ {
+ SleepThread (ONE_SECOND / 20);
+ if (QuitPosted) // Don't make users wait for sounds to end
+ break;
+ }
+}
+
+
+// Status: Ignored
+BOOLEAN
+InitSound (int argc, char* argv[])
+{
+ /* Quell compiler warnings */
+ (void)argc;
+ (void)argv;
+ return TRUE;
+}
+
+// Status: Ignored
+void
+UninitSound (void)
+{
+}
+
+void
+SetSFXVolume (float volume)
+{
+ int i;
+ for (i = FIRST_SFX_SOURCE; i <= LAST_SFX_SOURCE; ++i)
+ {
+ audio_Sourcef (soundSource[i].handle, audio_GAIN, volume);
+ }
+}
+
+void
+SetSpeechVolume (float volume)
+{
+ audio_Sourcef (soundSource[SPEECH_SOURCE].handle, audio_GAIN, volume);
+}
+
+DWORD
+FadeMusic (BYTE end_vol, SIZE TimeInterval)
+{
+ if (QuitPosted) // Don't make users wait for fades
+ TimeInterval = 0;
+
+ if (TimeInterval < 0)
+ TimeInterval = 0;
+
+ if (!SetMusicStreamFade (TimeInterval, end_vol))
+ { // fade rejected, maybe due to TimeInterval==0
+ SetMusicVolume (end_vol);
+ return GetTimeCounter ();
+ }
+ else
+ {
+ return GetTimeCounter () + TimeInterval + 1;
+ }
+}
+
+
diff --git a/src/libs/sound/sound.h b/src/libs/sound/sound.h
new file mode 100644
index 0000000..2a4f447
--- /dev/null
+++ b/src/libs/sound/sound.h
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_SOUND_SOUND_H_ // try avoiding collisions on id
+#define LIBS_SOUND_SOUND_H_
+
+#include "types.h"
+#include "audiocore.h"
+#include "decoders/decoder.h"
+#include "libs/threadlib.h"
+#include "libs/sndlib.h"
+
+
+#define FIRST_SFX_SOURCE 0
+#define LAST_SFX_SOURCE (FIRST_SFX_SOURCE + NUM_SFX_CHANNELS - 1)
+#define MUSIC_SOURCE (LAST_SFX_SOURCE + 1)
+#define SPEECH_SOURCE (MUSIC_SOURCE + 1)
+#define NUM_SOUNDSOURCES (SPEECH_SOURCE + 1)
+
+typedef struct
+{
+ int in_use;
+ audio_Object buf_name;
+ intptr_t data; // user-defined data
+} TFB_SoundTag;
+
+typedef struct tfb_soundcallbacks
+{
+ // return TRUE to continue, FALSE to abort
+ bool (* OnStartStream) (TFB_SoundSample*);
+ // return TRUE to continue, FALSE to abort
+ bool (* OnEndChunk) (TFB_SoundSample*, audio_Object);
+ // return TRUE to continue, FALSE to abort
+ void (* OnEndStream) (TFB_SoundSample*);
+ // tagged buffer callback
+ void (* OnTaggedBuffer) (TFB_SoundSample*, TFB_SoundTag*);
+ // buffer just queued
+ void (* OnQueueBuffer) (TFB_SoundSample*, audio_Object);
+} TFB_SoundCallbacks;
+
+
+extern int musicVolume;
+extern float musicVolumeScale;
+extern float sfxVolumeScale;
+extern float speechVolumeScale;
+
+void StopSource (int iSource);
+void CleanSource (int iSource);
+
+void SetSFXVolume (float volume);
+void SetSpeechVolume (float volume);
+
+TFB_SoundSample *TFB_CreateSoundSample (TFB_SoundDecoder*, uint32 num_buffers,
+ const TFB_SoundCallbacks* /* can be NULL */);
+void TFB_DestroySoundSample (TFB_SoundSample*);
+void TFB_SetSoundSampleData (TFB_SoundSample*, void* data);
+void* TFB_GetSoundSampleData (TFB_SoundSample*);
+void TFB_SetSoundSampleCallbacks (TFB_SoundSample*,
+ const TFB_SoundCallbacks* /* can be NULL */);
+TFB_SoundDecoder* TFB_GetSoundSampleDecoder (TFB_SoundSample*);
+
+TFB_SoundTag* TFB_FindTaggedBuffer (TFB_SoundSample*, audio_Object buffer);
+void TFB_ClearBufferTag (TFB_SoundTag*);
+bool TFB_TagBuffer (TFB_SoundSample*, audio_Object buffer, intptr_t data);
+
+#include "stream.h"
+
+#endif // LIBS_SOUND_SOUND_H_
diff --git a/src/libs/sound/stream.c b/src/libs/sound/stream.c
new file mode 100644
index 0000000..b7d2718
--- /dev/null
+++ b/src/libs/sound/stream.c
@@ -0,0 +1,814 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+ // for abs()
+#include "sound.h"
+#include "sndintrn.h"
+#include "libs/tasklib.h"
+#include "libs/timelib.h"
+#include "libs/threadlib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+static Task decoderTask;
+
+static TimeCount musicFadeStartTime;
+static sint32 musicFadeInterval;
+static int musicFadeStartVolume;
+static int musicFadeDelta;
+// Mutex protects fade structures
+static Mutex fade_mutex;
+
+static void add_scope_data (TFB_SoundSource *source, uint32 bytes);
+
+
+void
+PlayStream (TFB_SoundSample *sample, uint32 source, bool looping, bool scope,
+ bool rewind)
+{
+ uint32 i;
+ sint32 offset;
+ TFB_SoundDecoder *decoder;
+
+ if (!sample)
+ return;
+
+ StopStream (source);
+ if (sample->callbacks.OnStartStream &&
+ !sample->callbacks.OnStartStream (sample))
+ return; // callback failed
+
+ if (sample->buffer_tag)
+ memset (sample->buffer_tag, 0,
+ sample->num_buffers * sizeof (sample->buffer_tag[0]));
+
+ decoder = sample->decoder;
+ offset = sample->offset;
+ if (rewind)
+ SoundDecoder_Rewind (decoder);
+ else
+ offset += (sint32)(SoundDecoder_GetTime (decoder) * ONE_SECOND);
+
+ soundSource[source].sample = sample;
+ decoder->looping = looping;
+ audio_Sourcei (soundSource[source].handle, audio_LOOPING, false);
+
+ if (scope)
+ { // Prealloc the scope buffer in advance so that we do not
+ // realloc it a zillion times
+ soundSource[source].sbuf_size = sample->num_buffers *
+ decoder->buffer_size + PAD_SCOPE_BYTES;
+ soundSource[source].sbuffer = HCalloc (soundSource[source].sbuf_size);
+ }
+
+ for (i = 0; i < sample->num_buffers; ++i)
+ {
+ uint32 decoded_bytes;
+
+ decoded_bytes = SoundDecoder_Decode (decoder);
+#if 0
+ log_add (log_Debug, "PlayStream(): source:%d filename:%s start:%d "
+ "position:%d bytes:%d\n",
+ source, decoder->filename, decoder->start_sample,
+ decoder->pos, decoded_bytes);
+#endif
+ if (decoded_bytes == 0)
+ break;
+
+ audio_BufferData (sample->buffer[i], decoder->format,
+ decoder->buffer, decoded_bytes, decoder->frequency);
+ audio_SourceQueueBuffers (soundSource[source].handle, 1,
+ &sample->buffer[i]);
+ if (sample->callbacks.OnQueueBuffer)
+ sample->callbacks.OnQueueBuffer (sample, sample->buffer[i]);
+
+ if (scope)
+ add_scope_data (&soundSource[source], decoded_bytes);
+
+ if (decoder->error != SOUNDDECODER_OK)
+ {
+ if (decoder->error != SOUNDDECODER_EOF ||
+ !sample->callbacks.OnEndChunk ||
+ !sample->callbacks.OnEndChunk (sample, sample->buffer[i]))
+ { // Decoder probably run out of data before we could fill
+ // all buffers, and OnEndChunk() did not set a new one
+ break;
+ }
+ else
+ { // OnEndChunk() probably set a new decoder, get it
+ decoder = sample->decoder;
+ }
+ }
+ }
+
+ soundSource[source].sbuf_lasttime = GetTimeCounter ();
+ // Adjust the start time so it looks like the stream has been playing
+ // from the very beginning
+ soundSource[source].start_time = GetTimeCounter () - offset;
+ soundSource[source].pause_time = 0;
+ soundSource[source].stream_should_be_playing = TRUE;
+ audio_SourcePlay (soundSource[source].handle);
+}
+
+void
+StopStream (uint32 source)
+{
+ StopSource (source);
+
+ soundSource[source].stream_should_be_playing = FALSE;
+ soundSource[source].sample = NULL;
+
+ if (soundSource[source].sbuffer)
+ {
+ void *sbuffer = soundSource[source].sbuffer;
+ soundSource[source].sbuffer = NULL;
+ HFree (sbuffer);
+ }
+ soundSource[source].sbuf_size = 0;
+ soundSource[source].sbuf_head = 0;
+ soundSource[source].sbuf_tail = 0;
+ soundSource[source].pause_time = 0;
+}
+
+void
+PauseStream (uint32 source)
+{
+ soundSource[source].stream_should_be_playing = FALSE;
+ if (!soundSource[source].pause_time)
+ soundSource[source].pause_time = GetTimeCounter ();
+ audio_SourcePause (soundSource[source].handle);
+}
+
+void
+ResumeStream (uint32 source)
+{
+ if (soundSource[source].pause_time)
+ { // Adjust the start time so it looks like the stream has
+ // been playing all this time non-stop
+ soundSource[source].start_time += GetTimeCounter ()
+ - soundSource[source].pause_time;
+ }
+ soundSource[source].pause_time = 0;
+ soundSource[source].stream_should_be_playing = TRUE;
+ audio_SourcePlay (soundSource[source].handle);
+}
+
+void
+SeekStream (uint32 source, uint32 pos)
+{
+ TFB_SoundSample* sample = soundSource[source].sample;
+ bool looping;
+ bool scope;
+
+ if (!sample)
+ return;
+ looping = sample->decoder->looping;
+ scope = soundSource[source].sbuffer != NULL;
+
+ StopSource (source);
+ SoundDecoder_Seek (sample->decoder, pos);
+ PlayStream (sample, source, looping, scope, false);
+}
+
+BOOLEAN
+PlayingStream (uint32 source)
+{
+ return soundSource[source].stream_should_be_playing;
+}
+
+
+TFB_SoundSample *
+TFB_CreateSoundSample (TFB_SoundDecoder *decoder, uint32 num_buffers,
+ const TFB_SoundCallbacks *pcbs /* can be NULL */)
+{
+ TFB_SoundSample *sample;
+
+ sample = HCalloc (sizeof (*sample));
+ sample->decoder = decoder;
+ sample->num_buffers = num_buffers;
+ sample->buffer = HCalloc (sizeof (audio_Object) * num_buffers);
+ audio_GenBuffers (num_buffers, sample->buffer);
+ if (pcbs)
+ sample->callbacks = *pcbs;
+
+ return sample;
+}
+
+// Deletes all TFB_SoundSample data structures, except decoder
+void
+TFB_DestroySoundSample (TFB_SoundSample *sample)
+{
+ if (sample->buffer)
+ {
+ audio_DeleteBuffers (sample->num_buffers, sample->buffer);
+ HFree (sample->buffer);
+ }
+ HFree (sample->buffer_tag);
+ HFree (sample);
+}
+
+void
+TFB_SetSoundSampleData (TFB_SoundSample *sample, void* data)
+{
+ sample->data = data;
+}
+
+void*
+TFB_GetSoundSampleData (TFB_SoundSample *sample)
+{
+ return sample->data;
+}
+
+void
+TFB_SetSoundSampleCallbacks (TFB_SoundSample *sample,
+ const TFB_SoundCallbacks *pcbs /* can be NULL */)
+{
+ if (pcbs)
+ sample->callbacks = *pcbs;
+ else
+ memset (&sample->callbacks, 0, sizeof (sample->callbacks));
+}
+
+TFB_SoundDecoder*
+TFB_GetSoundSampleDecoder (TFB_SoundSample *sample)
+{
+ return sample->decoder;
+}
+
+TFB_SoundTag*
+TFB_FindTaggedBuffer (TFB_SoundSample *sample, audio_Object buffer)
+{
+ uint32 buf_num;
+
+ if (!sample->buffer_tag)
+ return NULL; // do not have any tags
+
+ for (buf_num = 0;
+ buf_num < sample->num_buffers &&
+ (!sample->buffer_tag[buf_num].in_use ||
+ sample->buffer_tag[buf_num].buf_name != buffer
+ );
+ buf_num++)
+ ;
+
+ return buf_num < sample->num_buffers ?
+ &sample->buffer_tag[buf_num] : NULL;
+}
+
+bool
+TFB_TagBuffer (TFB_SoundSample *sample, audio_Object buffer, intptr_t data)
+{
+ uint32 buf_num;
+
+ if (!sample->buffer_tag)
+ sample->buffer_tag = HCalloc (sizeof (TFB_SoundTag) *
+ sample->num_buffers);
+
+ for (buf_num = 0;
+ buf_num < sample->num_buffers &&
+ sample->buffer_tag[buf_num].in_use &&
+ sample->buffer_tag[buf_num].buf_name != buffer;
+ buf_num++)
+ ;
+
+ if (buf_num >= sample->num_buffers)
+ return false; // no empty slot
+
+ sample->buffer_tag[buf_num].in_use = 1;
+ sample->buffer_tag[buf_num].buf_name = buffer;
+ sample->buffer_tag[buf_num].data = data;
+
+ return true;
+}
+
+void
+TFB_ClearBufferTag (TFB_SoundTag *ptag)
+{
+ ptag->in_use = 0;
+ ptag->buf_name = 0;
+}
+
+static void
+remove_scope_data (TFB_SoundSource *source, audio_Object buffer)
+{
+ audio_IntVal buf_size;
+
+ audio_GetBufferi (buffer, audio_SIZE, &buf_size);
+ source->sbuf_head += buf_size;
+ // the buffer is cyclic
+ source->sbuf_head %= source->sbuf_size;
+
+ source->sbuf_lasttime = GetTimeCounter ();
+}
+
+static void
+add_scope_data (TFB_SoundSource *source, uint32 bytes)
+{
+ uint8 *sbuffer = source->sbuffer;
+ uint8 *dec_buf = source->sample->decoder->buffer;
+ uint32 tail_bytes;
+ uint32 wrap_bytes;
+
+ if (source->sbuf_tail + bytes > source->sbuf_size)
+ { // does not fit at the tail, have to split it up
+ tail_bytes = source->sbuf_size - source->sbuf_tail;
+ wrap_bytes = bytes - tail_bytes;
+ }
+ else
+ { // all fits at the tail
+ tail_bytes = bytes;
+ wrap_bytes = 0;
+ }
+
+ if (tail_bytes)
+ {
+ memcpy (sbuffer + source->sbuf_tail, dec_buf, tail_bytes);
+ source->sbuf_tail += tail_bytes;
+ }
+
+ if (wrap_bytes)
+ {
+ memcpy (sbuffer, dec_buf + tail_bytes, wrap_bytes);
+ source->sbuf_tail = wrap_bytes;
+ }
+}
+
+static void
+process_stream (TFB_SoundSource *source)
+{
+ TFB_SoundSample *sample = source->sample;
+ TFB_SoundDecoder *decoder = sample->decoder;
+ bool end_chunk_failed = false;
+ audio_IntVal processed;
+ audio_IntVal queued;
+
+ audio_GetSourcei (source->handle, audio_BUFFERS_PROCESSED, &processed);
+ audio_GetSourcei (source->handle, audio_BUFFERS_QUEUED, &queued);
+
+ if (processed == 0)
+ { // Nothing was played
+ audio_IntVal state;
+
+ audio_GetSourcei (source->handle, audio_SOURCE_STATE, &state);
+ if (state != audio_PLAYING)
+ {
+ if (queued == 0 && decoder->error == SOUNDDECODER_EOF)
+ { // The stream has reached the end
+ log_add (log_Info, "StreamDecoderTaskFunc(): "
+ "finished playing %s", decoder->filename);
+ source->stream_should_be_playing = FALSE;
+
+ if (sample->callbacks.OnEndStream)
+ sample->callbacks.OnEndStream (sample);
+ }
+ else
+ {
+ log_add (log_Warning, "StreamDecoderTaskFunc(): "
+ "buffer underrun playing %s", decoder->filename);
+ audio_SourcePlay (source->handle);
+ }
+ }
+ }
+
+ // Unqueue processed buffers and replace them with new ones
+ for (; processed > 0; --processed)
+ {
+ uint32 error;
+ audio_Object buffer;
+ uint32 decoded_bytes;
+
+ audio_GetError (); // clear error state
+
+ // Get the buffer that finished playing
+ audio_SourceUnqueueBuffers (source->handle, 1, &buffer);
+ error = audio_GetError();
+ if (error != audio_NO_ERROR)
+ {
+ log_add (log_Warning, "StreamDecoderTaskFunc(): "
+ "error after audio_SourceUnqueueBuffers: %x, file %s",
+ error, decoder->filename);
+ break;
+ }
+
+ // Process a callback on a tagged buffer, if any
+ if (sample->callbacks.OnTaggedBuffer)
+ {
+ TFB_SoundTag* tag = TFB_FindTaggedBuffer (sample, buffer);
+ if (tag)
+ sample->callbacks.OnTaggedBuffer (sample, tag);
+ }
+
+ if (source->sbuffer)
+ remove_scope_data (source, buffer);
+
+ // See what state the decoder was left in last time around
+ if (decoder->error != SOUNDDECODER_OK)
+ {
+ if (decoder->error == SOUNDDECODER_EOF)
+ {
+ if (end_chunk_failed)
+ continue; // should not do it again
+
+ if (!sample->callbacks.OnEndChunk ||
+ !sample->callbacks.OnEndChunk (sample, source->last_q_buf))
+ { // Reached the end of the current stream and we did not
+ // get another sample to play (relevant for Trackplayer)
+ end_chunk_failed = true;
+ continue;
+ }
+ else
+ { // OnEndChunk succeeded, so someone (read: Trackplayer)
+ // wants to keep going, probably with a new decoder.
+ // Get the new decoder
+ decoder = sample->decoder;
+ }
+ }
+ else
+ { // Decoder returned a real error, keep going
+#if 0
+ log_add (log_Debug, "StreamDecoderTaskFunc(): "
+ "decoder->error is %d for %s", decoder->error,
+ decoder->filename);
+#endif
+ continue;
+ }
+ }
+
+ // Now replace the unqueued buffer with a new one
+ decoded_bytes = SoundDecoder_Decode (decoder);
+ if (decoder->error == SOUNDDECODER_ERROR)
+ {
+ log_add (log_Warning, "StreamDecoderTaskFunc(): "
+ "SoundDecoder_Decode error %d, file %s",
+ decoder->error, decoder->filename);
+ source->stream_should_be_playing = FALSE;
+ continue;
+ }
+
+ if (decoded_bytes == 0)
+ { // Nothing was decoded, keep going
+ continue;
+ // This loses a stream buffer, which we cannot get back
+ // w/o restarting the stream, but we should never get here.
+ }
+
+ // And a new buffer is born
+ audio_BufferData (buffer, decoder->format, decoder->buffer,
+ decoded_bytes, decoder->frequency);
+ error = audio_GetError();
+ if (error != audio_NO_ERROR)
+ {
+ log_add (log_Warning, "StreamDecoderTaskFunc(): "
+ "error after audio_BufferData: %x, file %s, decoded %d",
+ error, decoder->filename, decoded_bytes);
+ continue;
+ }
+
+ // Now queue the buffer
+ audio_SourceQueueBuffers (source->handle, 1, &buffer);
+ error = audio_GetError();
+ if (error != audio_NO_ERROR)
+ {
+ log_add (log_Warning, "StreamDecoderTaskFunc(): "
+ "error after audio_SourceQueueBuffers: %x, file %s, "
+ "decoded %d", error, decoder->filename, decoded_bytes);
+ continue;
+ }
+
+ // Remember the last queued buffer so we can pass it to callbacks
+ source->last_q_buf = buffer;
+ if (sample->callbacks.OnQueueBuffer)
+ sample->callbacks.OnQueueBuffer (sample, buffer);
+
+ if (source->sbuffer)
+ add_scope_data (source, decoded_bytes);
+ }
+}
+
+static void
+processMusicFade (void)
+{
+ TimeCount Now;
+ sint32 elapsed;
+ int newVolume;
+
+ LockMutex (fade_mutex);
+
+ if (!musicFadeInterval)
+ { // there is no fade set
+ UnlockMutex (fade_mutex);
+ return;
+ }
+
+ Now = GetTimeCounter ();
+ elapsed = Now - musicFadeStartTime;
+ if (elapsed > musicFadeInterval)
+ elapsed = musicFadeInterval;
+
+ newVolume = musicFadeStartVolume + (long)musicFadeDelta * elapsed
+ / musicFadeInterval;
+ SetMusicVolume (newVolume);
+
+ if (elapsed >= musicFadeInterval)
+ musicFadeInterval = 0; // fade is over
+
+ UnlockMutex (fade_mutex);
+}
+
+static int
+StreamDecoderTaskFunc (void *data)
+{
+ Task task = (Task)data;
+ int active_streams;
+ int i;
+
+ while (!Task_ReadState (task, TASK_EXIT))
+ {
+ active_streams = 0;
+
+ processMusicFade ();
+
+ for (i = MUSIC_SOURCE; i < NUM_SOUNDSOURCES; ++i)
+ {
+ TFB_SoundSource *source = &soundSource[i];
+
+ LockMutex (source->stream_mutex);
+
+ if (!source->sample ||
+ !source->sample->decoder ||
+ !source->stream_should_be_playing ||
+ source->sample->decoder->error == SOUNDDECODER_ERROR)
+ {
+ UnlockMutex (source->stream_mutex);
+ continue;
+ }
+
+ process_stream (source);
+ active_streams++;
+
+ UnlockMutex (source->stream_mutex);
+ }
+
+ if (active_streams == 0)
+ { // Throttle down the thread when there are no active streams
+ HibernateThread (ONE_SECOND / 10);
+ }
+ else
+ TaskSwitch ();
+ }
+
+ FinishTask (task);
+ return 0;
+}
+
+static inline sint32
+readSoundSample (void *ptr, int sample_size)
+{
+ if (sample_size == sizeof (uint8))
+ return (*(uint8*)ptr - 128) << 8;
+ else
+ return *(sint16*)ptr;
+}
+
+// Graphs the current sound data for the oscilloscope.
+// Includes a rudimentary automatic gain control (AGC) to properly graph
+// the streams at different gain levels (based on running average).
+// We use AGC because different pieces of music and speech can easily be
+// at very different gain levels, because the game is moddable.
+int
+GraphForegroundStream (uint8 *data, sint32 width, sint32 height,
+ bool wantSpeech)
+{
+ int source_num;
+ TFB_SoundSource *source;
+ TFB_SoundDecoder *decoder;
+ int channels;
+ int sample_size;
+ int full_sample;
+ int step;
+ long played_time;
+ long delta;
+ uint8 *sbuffer;
+ unsigned long pos;
+ int scale;
+ sint32 i;
+ // AGC variables
+#define DEF_PAGE_MAX 28000
+#define AGC_PAGE_COUNT 16
+ static int page_sum = DEF_PAGE_MAX * AGC_PAGE_COUNT;
+ static int pages[AGC_PAGE_COUNT] =
+ {
+ DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
+ DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
+ DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
+ DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX, DEF_PAGE_MAX,
+ };
+ static int page_head;
+#define AGC_FRAME_COUNT 8
+ static int frame_sum;
+ static int frames;
+ static int avg_amp = DEF_PAGE_MAX; // running amplitude (sort of) average
+ int target_amp;
+ int max_a;
+#define VAD_MIN_ENERGY 100
+ long energy;
+
+
+ // Prefer speech to music
+ source_num = SPEECH_SOURCE;
+ source = &soundSource[source_num];
+ LockMutex (source->stream_mutex);
+ if (wantSpeech && (!source->sample ||
+ !source->sample->decoder || !source->sample->decoder->is_null))
+ { // Use speech waveform, since it's available
+ // Step is picked experimentally. Using step of 1 sample at 11025Hz,
+ // because human speech is mostly in the low frequencies, and it looks
+ // better this way.
+ step = 1;
+ }
+ else
+ { // We do not have speech -- use music waveform
+ UnlockMutex (source->stream_mutex);
+
+ source_num = MUSIC_SOURCE;
+ source = &soundSource[source_num];
+ LockMutex (source->stream_mutex);
+
+ // Step is picked experimentally. Using step of 4 samples at 11025Hz.
+ // It looks better this way.
+ step = 4;
+ }
+
+ if (!PlayingStream (source_num) || !source->sample
+ || !source->sample->decoder || !source->sbuffer
+ || source->sbuf_size == 0)
+ { // We don't have data to return, oh well.
+ UnlockMutex (source->stream_mutex);
+ return 0;
+ }
+ decoder = source->sample->decoder;
+
+ if (!audio_GetFormatInfo (decoder->format, &channels, &sample_size))
+ {
+ UnlockMutex (source->stream_mutex);
+ log_add (log_Debug, "GraphForegroundStream(): uknown format %u",
+ (unsigned)decoder->format);
+ return 0;
+ }
+ full_sample = channels * sample_size;
+
+ // See how far into the buffer we should be now
+ played_time = GetTimeCounter () - source->sbuf_lasttime;
+ delta = played_time * decoder->frequency * full_sample / ONE_SECOND;
+ // align delta to sample start
+ delta = delta & ~(full_sample - 1);
+
+ if (delta < 0)
+ {
+ log_add (log_Debug, "GraphForegroundStream(): something is messed"
+ " with timing, delta %ld", delta);
+ delta = 0;
+ }
+ else if (delta > (long)source->sbuf_size)
+ { // Stream decoder task has just had a heart attack, not much we can do
+ delta = 0;
+ }
+
+ // Step is in 11025 Hz units, so we need to adjust to source frequency
+ step = decoder->frequency * step / 11025;
+ if (step == 0)
+ step = 1;
+ step *= full_sample;
+
+ sbuffer = source->sbuffer;
+ pos = source->sbuf_head + delta;
+
+ // We are not basing the scaling factor on signal energy, because we
+ // want it to *look* pretty instead of sounding nice and even
+ target_amp = (height >> 1) >> 1;
+ scale = avg_amp / target_amp;
+
+ max_a = 0;
+ energy = 0;
+ for (i = 0; i < width; ++i, pos += step)
+ {
+ sint32 s;
+ int t;
+
+ pos %= source->sbuf_size;
+
+ s = readSoundSample (sbuffer + pos, sample_size);
+ if (channels > 1)
+ s += readSoundSample (sbuffer + pos + sample_size, sample_size);
+
+ energy += (s * s) / 0x10000;
+ t = abs(s);
+ if (t > max_a)
+ max_a = t;
+
+ s = (s / scale) + (height >> 1);
+ if (s < 0)
+ s = 0;
+ else if (s > height - 1)
+ s = height - 1;
+
+ data[i] = s;
+ }
+ energy /= width;
+
+ // Very basic VAD. We don't want to count speech pauses in the average
+ if (energy > VAD_MIN_ENERGY)
+ {
+ // Record the maximum amplitude (sort of)
+ frame_sum += max_a;
+ ++frames;
+ if (frames == AGC_FRAME_COUNT)
+ { // Got a full page
+ frame_sum /= AGC_FRAME_COUNT;
+ // Record the page
+ page_sum -= pages[page_head];
+ page_sum += frame_sum;
+ pages[page_head] = frame_sum;
+ page_head = (page_head + 1) % AGC_PAGE_COUNT;
+
+ frame_sum = 0;
+ frames = 0;
+
+ avg_amp = page_sum / AGC_PAGE_COUNT;
+ }
+ }
+
+ UnlockMutex (source->stream_mutex);
+ return 1;
+}
+
+// This function is normally called on the Starcon2Main thread
+bool
+SetMusicStreamFade (sint32 howLong, int endVolume)
+{
+ bool ret = true;
+
+ LockMutex (fade_mutex);
+
+ if (howLong < 0)
+ howLong = 0;
+
+ musicFadeStartTime = GetTimeCounter ();
+ musicFadeInterval = howLong;
+ musicFadeStartVolume = musicVolume;
+ musicFadeDelta = endVolume - musicFadeStartVolume;
+ if (!musicFadeInterval)
+ ret = false; // reject
+
+ UnlockMutex (fade_mutex);
+
+ return ret;
+}
+
+int
+InitStreamDecoder (void)
+{
+ fade_mutex = CreateMutex ("Stream fade mutex", SYNC_CLASS_AUDIO);
+ if (!fade_mutex)
+ return -1;
+
+ decoderTask = AssignTask (StreamDecoderTaskFunc, 1024,
+ "audio stream decoder");
+ if (!decoderTask)
+ return -1;
+
+ return 0;
+}
+
+void
+UninitStreamDecoder (void)
+{
+ if (decoderTask)
+ {
+ ConcludeTask (decoderTask);
+ decoderTask = NULL;
+ }
+
+ if (fade_mutex)
+ {
+ DestroyMutex (fade_mutex);
+ fade_mutex = NULL;
+ }
+}
diff --git a/src/libs/sound/stream.h b/src/libs/sound/stream.h
new file mode 100644
index 0000000..5766132
--- /dev/null
+++ b/src/libs/sound/stream.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef STREAM_H
+#define STREAM_H
+
+int InitStreamDecoder (void);
+void UninitStreamDecoder (void);
+
+void PlayStream (TFB_SoundSample *sample, uint32 source, bool looping,
+ bool scope, bool rewind);
+void StopStream (uint32 source);
+void PauseStream (uint32 source);
+void ResumeStream (uint32 source);
+void SeekStream (uint32 source, uint32 pos);
+BOOLEAN PlayingStream (uint32 source);
+
+int GraphForegroundStream (uint8 *data, sint32 width, sint32 height,
+ bool wantSpeech);
+
+// returns TRUE if the fade was accepted by stream decoder
+bool SetMusicStreamFade (sint32 howLong, int endVolume);
+
+#endif
diff --git a/src/libs/sound/trackint.h b/src/libs/sound/trackint.h
new file mode 100644
index 0000000..fe39740
--- /dev/null
+++ b/src/libs/sound/trackint.h
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TRACKINT_H
+#define TRACKINT_H
+
+#include "libs/callback.h"
+
+struct tfb_soundchunk
+{
+ TFB_SoundDecoder *decoder; // decoder for this chunk
+ float start_time; // relative time from track start
+ int tag_me; // set for chunks with subtitles
+ uint32 track_num; // logical track #, comm code needs this
+ UNICODE *text; // subtitle text
+ CallbackFunction callback; // comm callback, executed on chunk start
+ struct tfb_soundchunk *next;
+};
+
+typedef struct tfb_soundchunk TFB_SoundChunk;
+
+TFB_SoundChunk *create_SoundChunk (TFB_SoundDecoder *decoder, float start_time);
+void destroy_SoundChunk_list (TFB_SoundChunk *chain);
+TFB_SoundChunk *find_next_page (TFB_SoundChunk *cur);
+TFB_SoundChunk *find_prev_page (TFB_SoundChunk *cur);
+
+
+#endif // TRACKINT_H
diff --git a/src/libs/sound/trackplayer.c b/src/libs/sound/trackplayer.c
new file mode 100644
index 0000000..8068fc3
--- /dev/null
+++ b/src/libs/sound/trackplayer.c
@@ -0,0 +1,874 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sound.h"
+#include "sndintrn.h"
+#include "libs/sound/trackplayer.h"
+#include "trackint.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "options.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+
+static int track_count; // total number of tracks
+static int no_page_break; // set when combining several tracks into one
+
+// The one and only sample we play. Track switching is done by modifying
+// this sample while it is playing. StreamDecoderTaskFunc() picks up the
+// changes *mostly* seamlessly (keyword: mostly).
+// This is technically a hack, but a decent one ;)
+static TFB_SoundSample *sound_sample;
+
+static volatile uint32 tracks_length; // total length of tracks in game units
+
+static TFB_SoundChunk *chunks_head; // first decoder in linked list
+static TFB_SoundChunk *chunks_tail; // last decoder in linked list
+static TFB_SoundChunk *last_sub; // last chunk in the list with a subtitle
+
+static TFB_SoundChunk *cur_chunk; // currently playing chunk
+static TFB_SoundChunk *cur_sub_chunk; // currently displayed subtitle chunk
+
+// Accesses to cur_chunk and cur_sub_chunk are guarded by stream_mutex,
+// because these should only be accesses by the DoInput and the
+// stream player threads. Any other accesses would go unguarded.
+// Other data structures are unguarded and should only be accessed from
+// the DoInput thread at certain times, i.e. nothing can be modified
+// between StartTrack() and JumpTrack()/StopTrack() calls.
+// Use caution when changing code, as you may need to guard other data
+// structures the same way.
+
+static void seek_track (sint32 offset);
+
+// stream callbacks
+static bool OnStreamStart (TFB_SoundSample* sample);
+static bool OnChunkEnd (TFB_SoundSample* sample, audio_Object buffer);
+static void OnStreamEnd (TFB_SoundSample* sample);
+static void OnBufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag);
+
+static TFB_SoundCallbacks trackCBs =
+{
+ OnStreamStart,
+ OnChunkEnd,
+ OnStreamEnd,
+ OnBufferTag,
+ NULL
+};
+
+static inline sint32
+chunk_end_time (TFB_SoundChunk *chunk)
+{
+ return (sint32) ((chunk->start_time + chunk->decoder->length)
+ * ONE_SECOND);
+}
+
+static inline sint32
+tracks_end_time (void)
+{
+ return chunk_end_time (chunks_tail);
+}
+
+//JumpTrack currently aborts the current track. However, it doesn't clear the
+//data-structures as StopTrack does. this allows for rewind even after the
+//track has finished playing
+//Question: Should 'abort' call StopTrack?
+void
+JumpTrack (void)
+{
+ if (!sound_sample)
+ return; // nothing to skip
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ seek_track (tracks_length + 1);
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+// This should just start playing a stream
+void
+PlayTrack (void)
+{
+ if (!sound_sample)
+ return; // nothing to play
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ tracks_length = tracks_end_time ();
+ // decoder will be set in OnStreamStart()
+ cur_chunk = chunks_head;
+ // Always scope the speech data, we may need it
+ PlayStream (sound_sample, SPEECH_SOURCE, false, true, true);
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+void
+PauseTrack (void)
+{
+ if (!sound_sample)
+ return; // nothing to pause
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ PauseStream (SPEECH_SOURCE);
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+// ResumeTrack should resume a paused track, and do nothing for a playing track
+void
+ResumeTrack (void)
+{
+ audio_IntVal state;
+
+ if (!sound_sample)
+ return; // nothing to resume
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ if (!cur_chunk)
+ { // not playing anything, so no resuming
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ return;
+ }
+
+ audio_GetSourcei (soundSource[SPEECH_SOURCE].handle, audio_SOURCE_STATE, &state);
+ if (state == audio_PAUSED)
+ ResumeStream (SPEECH_SOURCE);
+
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+COUNT
+PlayingTrack (void)
+{
+ // This ignores the paused state and simply returns what track
+ // *should* be playing
+ COUNT result = 0; // default is none
+
+ if (!sound_sample)
+ return 0; // not playing anything
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ if (cur_chunk)
+ result = cur_chunk->track_num + 1;
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ return result;
+}
+
+void
+StopTrack (void)
+{
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ StopStream (SPEECH_SOURCE);
+ track_count = 0;
+ tracks_length = 0;
+ cur_chunk = NULL;
+ cur_sub_chunk = NULL;
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ if (chunks_head)
+ {
+ chunks_tail = NULL;
+ destroy_SoundChunk_list (chunks_head);
+ chunks_head = NULL;
+ last_sub = NULL;
+ }
+ if (sound_sample)
+ {
+ // We delete the decoders ourselves
+ sound_sample->decoder = NULL;
+ TFB_DestroySoundSample (sound_sample);
+ sound_sample = NULL;
+ }
+}
+
+static void
+DoTrackTag (TFB_SoundChunk *chunk)
+{
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ if (chunk->callback)
+ chunk->callback(0);
+
+ cur_sub_chunk = chunk;
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+// This func is called by PlayStream() when stream is about
+// to start. We have a chance to tweak the stream here.
+// This is called on the DoInput thread.
+static bool
+OnStreamStart (TFB_SoundSample* sample)
+{
+ if (sample != sound_sample)
+ return false; // Huh? Why did we get called on this?
+
+ if (!cur_chunk)
+ return false; // Stream shouldn't be playing at all
+
+ // Adjust the sample to play what we want
+ sample->decoder = cur_chunk->decoder;
+ sample->offset = (sint32) (cur_chunk->start_time * ONE_SECOND);
+
+ if (cur_chunk->tag_me)
+ DoTrackTag (cur_chunk);
+
+ return true;
+}
+
+// This func is called by StreamDecoderTaskFunc() when the last buffer
+// of the current chunk has been decoded (not when it has been *played*).
+// This is called on the stream task thread.
+static bool
+OnChunkEnd (TFB_SoundSample* sample, audio_Object buffer)
+{
+ if (sample != sound_sample)
+ return false; // Huh? Why did we get called on this?
+
+ if (!cur_chunk || !cur_chunk->next)
+ { // all chunks and tracks are done
+ return false;
+ }
+
+ // Move on to the next chunk
+ cur_chunk = cur_chunk->next;
+ // Adjust the sample to play what we want
+ sample->decoder = cur_chunk->decoder;
+ SoundDecoder_Rewind (sample->decoder);
+
+ log_add (log_Info, "Switching to stream %s at pos %d",
+ sample->decoder->filename, sample->decoder->start_sample);
+
+ if (cur_chunk->tag_me)
+ { // Tag the last buffer of the chunk with the next chunk
+ TFB_TagBuffer (sample, buffer, (intptr_t)cur_chunk);
+ }
+
+ return true;
+}
+
+// This func is called by StreamDecoderTaskFunc() when stream has ended
+// This is called on the stream task thread.
+static void
+OnStreamEnd (TFB_SoundSample* sample)
+{
+ if (sample != sound_sample)
+ return; // Huh? Why did we get called on this?
+
+ cur_chunk = NULL;
+ cur_sub_chunk = NULL;
+}
+
+// This func is called by StreamDecoderTaskFunc() when a tagged buffer
+// has finished playing.
+// This is called on the stream task thread.
+static void
+OnBufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag)
+{
+ TFB_SoundChunk* chunk = (TFB_SoundChunk*) tag->data;
+
+ assert (sizeof (tag->data) >= sizeof (chunk));
+
+ if (sample != sound_sample)
+ return; // Huh? Why did we get called on this?
+
+ TFB_ClearBufferTag (tag);
+ DoTrackTag (chunk);
+}
+
+// Parse the timestamps string into an int array.
+// Rerturns number of timestamps parsed.
+static int
+GetTimeStamps (UNICODE *TimeStamps, sint32 *time_stamps)
+{
+ int pos;
+ int num = 0;
+
+ while (*TimeStamps && (pos = strcspn (TimeStamps, ",\r\n")))
+ {
+ UNICODE valStr[32];
+ uint32 val;
+
+ memcpy (valStr, TimeStamps, pos);
+ valStr[pos] = '\0';
+ val = strtoul (valStr, NULL, 10);
+ if (val)
+ {
+ *time_stamps = val;
+ num++;
+ time_stamps++;
+ }
+ TimeStamps += pos;
+ TimeStamps += strspn (TimeStamps, ",\r\n");
+ }
+ return (num);
+}
+
+#define TEXT_SPEED 80
+// Returns number of parsed pages
+static int
+SplitSubPages (UNICODE *text, UNICODE *pages[], sint32 timestamp[], int size)
+{
+ int lead_ellips = 0;
+ COUNT page;
+
+ for (page = 0; page < size && *text != '\0'; ++page)
+ {
+ int aft_ellips;
+ int pos;
+
+ // seek to EOL or end of the string
+ pos = strcspn (text, "\r\n");
+ // XXX: this will only work when ASCII punctuation and spaces
+ // are used exclusively
+ aft_ellips = 3 * (text[pos] != '\0' && pos > 0 &&
+ !ispunct (text[pos - 1]) && !isspace (text[pos - 1]));
+ pages[page] = HMalloc (sizeof (UNICODE) *
+ (lead_ellips + pos + aft_ellips + 1));
+ if (lead_ellips)
+ strcpy (pages[page], "..");
+ memcpy (pages[page] + lead_ellips, text, pos);
+ pages[page][lead_ellips + pos] = '\0'; // string term
+ if (aft_ellips)
+ strcpy (pages[page] + lead_ellips + pos, "...");
+ timestamp[page] = pos * TEXT_SPEED;
+ if (timestamp[page] < 1000)
+ timestamp[page] = 1000;
+ lead_ellips = aft_ellips ? 2 : 0;
+ text += pos;
+ // Skip any EOL
+ text += strspn (text, "\r\n");
+ }
+
+ return page;
+}
+
+// decodes several tracks into one and adds it to queue
+// track list is NULL-terminated
+// May only be called after at least one SpliceTrack(). This is a limitation
+// for the sake of timestamps, but it does not have to be so.
+void
+SpliceMultiTrack (UNICODE *TrackNames[], UNICODE *TrackText)
+{
+#define MAX_MULTI_TRACKS 20
+#define MAX_MULTI_BUFFERS 100
+ TFB_SoundDecoder* track_decs[MAX_MULTI_TRACKS + 1];
+ int tracks;
+ int slen1, slen2;
+
+ if (!TrackText)
+ {
+ log_add (log_Debug, "SpliceMultiTrack(): no track text");
+ return;
+ }
+
+ if (!sound_sample || !chunks_tail)
+ {
+ log_add (log_Warning, "SpliceMultiTrack(): Cannot be called before SpliceTrack()");
+ return;
+ }
+
+ log_add (log_Info, "SpliceMultiTrack(): loading...");
+ for (tracks = 0; *TrackNames && tracks < MAX_MULTI_TRACKS; TrackNames++, tracks++)
+ {
+ track_decs[tracks] = SoundDecoder_Load (contentDir, *TrackNames,
+ 32768, 0, - 3 * TEXT_SPEED);
+ if (track_decs[tracks])
+ {
+ log_add (log_Info, " track: %s, decoder: %s, rate %d format %x",
+ *TrackNames,
+ SoundDecoder_GetName (track_decs[tracks]),
+ track_decs[tracks]->frequency,
+ track_decs[tracks]->format);
+ SoundDecoder_DecodeAll (track_decs[tracks]);
+
+ chunks_tail->next = create_SoundChunk (track_decs[tracks], sound_sample->length);
+ chunks_tail = chunks_tail->next;
+ chunks_tail->track_num = track_count - 1;
+ sound_sample->length += track_decs[tracks]->length;
+ }
+ else
+ {
+ log_add (log_Warning, "SpliceMultiTrack(): couldn't load %s\n",
+ *TrackNames);
+ tracks--;
+ }
+ }
+ track_decs[tracks] = 0; // term
+
+ if (tracks == 0)
+ {
+ log_add (log_Warning, "SpliceMultiTrack(): no tracks loaded");
+ return;
+ }
+
+ slen1 = strlen (last_sub->text);
+ slen2 = strlen (TrackText);
+ last_sub->text = HRealloc (last_sub->text, slen1 + slen2 + 1);
+ strcpy (last_sub->text + slen1, TrackText);
+
+ no_page_break = 1;
+}
+
+// XXX: This code and the entire trackplayer are begging to be overhauled
+void
+SpliceTrack (UNICODE *TrackName, UNICODE *TrackText, UNICODE *TimeStamp, CallbackFunction cb)
+{
+ static UNICODE last_track_name[128] = "";
+ static unsigned long dec_offset = 0;
+#define MAX_PAGES 50
+ UNICODE *pages[MAX_PAGES];
+ sint32 time_stamps[MAX_PAGES];
+ int num_pages;
+ int page;
+
+ if (!TrackText)
+ return;
+
+ if (!TrackName)
+ { // Appending a piece of subtitles to the last track
+ int slen1, slen2;
+
+ if (track_count == 0)
+ {
+ log_add (log_Warning, "SpliceTrack(): Tried to append a subtitle,"
+ " but no current track");
+ return;
+ }
+
+ if (!last_sub || !last_sub->text)
+ {
+ log_add (log_Warning, "SpliceTrack(): Tried to append a subtitle"
+ " to a NULL string");
+ return;
+ }
+
+ num_pages = SplitSubPages (TrackText, pages, time_stamps, MAX_PAGES);
+ if (num_pages == 0)
+ {
+ log_add (log_Warning, "SpliceTrack(): Failed to parse subtitles");
+ return;
+ }
+ // The last page's stamp is a suggested value. The track should
+ // actually play to the end.
+ time_stamps[num_pages - 1] = -time_stamps[num_pages - 1];
+
+ // Add the first piece to the last subtitle page
+ slen1 = strlen (last_sub->text);
+ slen2 = strlen (pages[0]);
+ last_sub->text = HRealloc (last_sub->text, slen1 + slen2 + 1);
+ strcpy (last_sub->text + slen1, pages[0]);
+ HFree (pages[0]);
+
+ // Add the rest of the pages
+ for (page = 1; page < num_pages; ++page)
+ {
+ TFB_SoundChunk *next_sub = find_next_page (last_sub);
+ if (next_sub)
+ { // nodes prepared by previous call, just fill in the subs
+ next_sub->text = pages[page];
+ last_sub = next_sub;
+ }
+ else
+ { // probably no timestamps were provided, so need more work
+ TFB_SoundDecoder *decoder = SoundDecoder_Load (contentDir,
+ last_track_name, 4096, dec_offset, time_stamps[page]);
+ if (!decoder)
+ {
+ log_add (log_Warning, "SpliceTrack(): couldn't load %s", TrackName);
+ break;
+ }
+ dec_offset += (unsigned long)(decoder->length * 1000);
+ chunks_tail->next = create_SoundChunk (decoder, sound_sample->length);
+ chunks_tail = chunks_tail->next;
+ chunks_tail->tag_me = 1;
+ chunks_tail->track_num = track_count - 1;
+ chunks_tail->text = pages[page];
+ chunks_tail->callback = cb;
+ // TODO: We may have to tag only one page with a callback
+ //cb = NULL;
+ last_sub = chunks_tail;
+ sound_sample->length += decoder->length;
+ }
+ }
+ }
+ else
+ { // Adding a new track
+ int num_timestamps = 0;
+
+ utf8StringCopy (last_track_name, sizeof (last_track_name), TrackName);
+
+ num_pages = SplitSubPages (TrackText, pages, time_stamps, MAX_PAGES);
+ if (num_pages == 0)
+ {
+ log_add (log_Warning, "SpliceTrack(): Failed to parse sutitles");
+ return;
+ }
+ // The last page's stamp is a suggested value. The track should
+ // actually play to the end.
+ time_stamps[num_pages - 1] = -time_stamps[num_pages - 1];
+
+ if (no_page_break && track_count)
+ {
+ int slen1, slen2;
+
+ slen1 = strlen (last_sub->text);
+ slen2 = strlen (pages[0]);
+ last_sub->text = HRealloc (last_sub->text, slen1 + slen2 + 1);
+ strcpy (last_sub->text + slen1, pages[0]);
+ HFree (pages[0]);
+ }
+ else
+ track_count++;
+
+ log_add (log_Info, "SpliceTrack(): loading %s", TrackName);
+
+ if (TimeStamp)
+ {
+ num_timestamps = GetTimeStamps (TimeStamp, time_stamps) + 1;
+ if (num_timestamps < num_pages)
+ {
+ log_add (log_Warning, "SpliceTrack(): number of timestamps"
+ " doesn't match number of pages!");
+ }
+ else if (num_timestamps > num_pages)
+ { // We most likely will get more subtitles appended later
+ // Set the last page to the rest of the track
+ time_stamps[num_timestamps - 1] = -100000;
+ }
+ }
+ else
+ {
+ num_timestamps = num_pages;
+ }
+
+ // Reset the offset for the new track
+ dec_offset = 0;
+ for (page = 0; page < num_timestamps; ++page)
+ {
+ TFB_SoundDecoder *decoder = SoundDecoder_Load (contentDir,
+ TrackName, 4096, dec_offset, time_stamps[page]);
+ if (!decoder)
+ {
+ log_add (log_Warning, "SpliceTrack(): couldn't load %s", TrackName);
+ break;
+ }
+
+ if (!sound_sample)
+ {
+ sound_sample = TFB_CreateSoundSample (NULL, 8, &trackCBs);
+ chunks_head = create_SoundChunk (decoder, 0.0);
+ chunks_tail = chunks_head;
+ }
+ else
+ {
+ chunks_tail->next = create_SoundChunk (decoder, sound_sample->length);
+ chunks_tail = chunks_tail->next;
+ }
+ dec_offset += (unsigned long)(decoder->length * 1000);
+#if 0
+ log_add (log_Debug, "page (%d of %d): %d ts: %d",
+ page, num_pages,
+ dec_offset, time_stamps[page]);
+#endif
+ sound_sample->length += decoder->length;
+ chunks_tail->track_num = track_count - 1;
+ if (!no_page_break)
+ {
+ chunks_tail->tag_me = 1;
+ if (page < num_pages)
+ {
+ chunks_tail->text = pages[page];
+ last_sub = chunks_tail;
+ }
+ chunks_tail->callback = cb;
+ // TODO: We may have to tag only one page with a callback
+ //cb = NULL;
+ }
+ no_page_break = 0;
+ }
+ }
+}
+
+// This function figures out the chunk that should be playing based on
+// 'offset' into the total playing time of all tracks. It then sets
+// the speech source's sample to the necessary decoder and seeks the
+// decoder to the proper point.
+// XXX: This means that whatever speech has already been queued on the
+// source will continue playing, so we may need some small timing
+// adjustments. It may be simpler to just call PlayStream().
+static void
+seek_track (sint32 offset)
+{
+ TFB_SoundChunk *cur;
+ TFB_SoundChunk *last_tag = NULL;
+
+ if (!sound_sample)
+ return; // nothing to recompute
+
+ if (offset < 0)
+ offset = 0;
+ else if ((uint32)offset > tracks_length)
+ offset = tracks_length + 1;
+
+ // Adjusting the stream start time is the only way we can arbitrarily
+ // seek the stream right now
+ soundSource[SPEECH_SOURCE].start_time = GetTimeCounter () - offset;
+
+ // Find the chunk that should be playing at this time offset
+ for (cur = chunks_head; cur && offset >= chunk_end_time (cur);
+ cur = cur->next)
+ {
+ // .. looking for the last callback as we go along
+ // XXX: this effectively set the last point where Fot is looking at.
+ // TODO: this should be somehow changed if we implement more
+ // callbacks, like Melnorme trading, offloading at Starbase, etc.
+ if (cur->tag_me)
+ last_tag = cur;
+ }
+
+ if (cur)
+ {
+ cur_chunk = cur;
+ SoundDecoder_Seek (cur->decoder, (uint32) (((float)offset / ONE_SECOND
+ - cur->start_time) * 1000));
+ sound_sample->decoder = cur->decoder;
+
+ if (cur->tag_me)
+ last_tag = cur;
+ if (last_tag)
+ DoTrackTag (last_tag);
+ }
+ else
+ { // The offset is beyond the length of all tracks
+ StopStream (SPEECH_SOURCE);
+ cur_chunk = NULL;
+ cur_sub_chunk = NULL;
+ }
+}
+
+static sint32
+get_current_track_pos (void)
+{
+ sint32 start_time = soundSource[SPEECH_SOURCE].start_time;
+ sint32 pos = GetTimeCounter () - start_time;
+ if (pos < 0)
+ pos = 0;
+ else if ((uint32)pos > tracks_length)
+ pos = tracks_length;
+ return pos;
+}
+
+void
+FastReverse_Smooth (void)
+{
+ sint32 offset;
+
+ if (!sound_sample)
+ return; // nothing is playing, so.. bye!
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ offset = get_current_track_pos ();
+ offset -= ACCEL_SCROLL_SPEED;
+ seek_track (offset);
+
+ // Restart the stream in case it ended previously
+ if (!PlayingStream (SPEECH_SOURCE))
+ PlayStream (sound_sample, SPEECH_SOURCE, false, true, false);
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+void
+FastForward_Smooth (void)
+{
+ sint32 offset;
+
+ if (!sound_sample)
+ return; // nothing is playing, so.. bye!
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ offset = get_current_track_pos ();
+ offset += ACCEL_SCROLL_SPEED;
+ seek_track (offset);
+
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+void
+FastReverse_Page (void)
+{
+ TFB_SoundChunk *prev;
+
+ if (!sound_sample)
+ return; // nothing is playing, so.. bye!
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ prev = find_prev_page (cur_sub_chunk);
+ if (prev)
+ { // Set the chunk to be played
+ cur_chunk = prev;
+ cur_sub_chunk = prev;
+ // Decoder will be set in OnStreamStart()
+ PlayStream (sound_sample, SPEECH_SOURCE, false, true, true);
+ }
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+void
+FastForward_Page (void)
+{
+ TFB_SoundChunk *next;
+
+ if (!sound_sample)
+ return; // nothing is playing, so.. bye!
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ next = find_next_page (cur_sub_chunk);
+ if (next)
+ { // Set the chunk to be played
+ cur_chunk = next;
+ cur_sub_chunk = next;
+ // Decoder will be set in OnStreamStart()
+ PlayStream (sound_sample, SPEECH_SOURCE, false, true, true);
+ }
+ else
+ { // End of the tracks (pun intended)
+ seek_track (tracks_length + 1);
+ }
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+}
+
+// Tells current position of streaming speech in the units
+// specified by the caller.
+// This is normally called on the ambient_anim_task thread.
+int
+GetTrackPosition (int in_units)
+{
+ uint32 offset;
+ uint32 length = tracks_length;
+ // detach from the static one, otherwise, we can race for
+ // it and thus divide by 0
+
+ if (!sound_sample || length == 0)
+ return 0; // nothing is playing
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ offset = get_current_track_pos ();
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ return in_units * offset / length;
+}
+
+TFB_SoundChunk *
+create_SoundChunk (TFB_SoundDecoder *decoder, float start_time)
+{
+ TFB_SoundChunk *chunk;
+ chunk = HCalloc (sizeof (*chunk));
+ chunk->decoder = decoder;
+ chunk->start_time = start_time;
+ return chunk;
+}
+
+void
+destroy_SoundChunk_list (TFB_SoundChunk *chunk)
+{
+ TFB_SoundChunk *next = NULL;
+ for ( ; chunk; chunk = next)
+ {
+ next = chunk->next;
+ if (chunk->decoder)
+ SoundDecoder_Free (chunk->decoder);
+ HFree (chunk->text);
+ HFree (chunk);
+ }
+}
+
+// Returns the next chunk with a subtitle
+TFB_SoundChunk *
+find_next_page (TFB_SoundChunk *cur)
+{
+ if (!cur)
+ return NULL;
+ for (cur = cur->next; cur && !cur->tag_me; cur = cur->next)
+ ;
+ return cur;
+}
+
+// Returns the previous chunk with a subtitle.
+// cur == 0 is treated as end of the list.
+TFB_SoundChunk *
+find_prev_page (TFB_SoundChunk *cur)
+{
+ TFB_SoundChunk *prev;
+ TFB_SoundChunk *last_valid = chunks_head;
+
+ if (cur == chunks_head)
+ return cur; // cannot go below the first track
+
+ for (prev = chunks_head; prev && prev != cur; prev = prev->next)
+ {
+ if (prev->tag_me)
+ last_valid = prev;
+ }
+ return last_valid;
+}
+
+
+// External access to the chunks list
+SUBTITLE_REF
+GetFirstTrackSubtitle (void)
+{
+ return chunks_head;
+}
+
+// External access to the chunks list
+SUBTITLE_REF
+GetNextTrackSubtitle (SUBTITLE_REF LastRef)
+{
+ if (!LastRef)
+ return NULL; // enumeration already ended
+
+ return find_next_page (LastRef);
+}
+
+// External access to the chunk subtitles
+const UNICODE *
+GetTrackSubtitleText (SUBTITLE_REF SubRef)
+{
+ if (!SubRef)
+ return NULL;
+
+ return SubRef->text;
+}
+
+// External access to currently active subtitle text
+// Returns NULL is none is active
+const UNICODE *
+GetTrackSubtitle (void)
+{
+ const UNICODE *cur_sub = NULL;
+
+ if (!sound_sample)
+ return NULL; // not playing anything
+
+ LockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+ if (cur_sub_chunk)
+ cur_sub = cur_sub_chunk->text;
+ UnlockMutex (soundSource[SPEECH_SOURCE].stream_mutex);
+
+ return cur_sub;
+}
diff --git a/src/libs/sound/trackplayer.h b/src/libs/sound/trackplayer.h
new file mode 100644
index 0000000..5964e65
--- /dev/null
+++ b/src/libs/sound/trackplayer.h
@@ -0,0 +1,52 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TRACKPLAYER_H
+#define TRACKPLAYER_H
+
+#include "libs/compiler.h"
+#include "libs/callback.h"
+
+#define ACCEL_SCROLL_SPEED 300
+
+extern void PlayTrack (void);
+extern void StopTrack (void);
+extern void JumpTrack (void);
+extern void PauseTrack (void);
+extern void ResumeTrack (void);
+extern COUNT PlayingTrack (void);
+
+extern void FastReverse_Smooth (void);
+extern void FastForward_Smooth (void);
+extern void FastReverse_Page (void);
+extern void FastForward_Page (void);
+
+extern void SpliceTrack (UNICODE *filespec, UNICODE *textspec, UNICODE *TimeStamp, CallbackFunction cb);
+extern void SpliceMultiTrack (UNICODE *TrackNames[], UNICODE *TrackText);
+
+extern int GetTrackPosition (int in_units);
+
+typedef struct tfb_soundchunk *SUBTITLE_REF;
+
+extern SUBTITLE_REF GetFirstTrackSubtitle (void);
+extern SUBTITLE_REF GetNextTrackSubtitle (SUBTITLE_REF LastRef);
+extern const UNICODE *GetTrackSubtitleText (SUBTITLE_REF SubRef);
+
+extern const UNICODE *GetTrackSubtitle (void);
+
+#endif
diff --git a/src/libs/strings/Makeinfo b/src/libs/strings/Makeinfo
new file mode 100644
index 0000000..f1e4a9e
--- /dev/null
+++ b/src/libs/strings/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="getstr.c sfileins.c sresins.c stringhashtable.c strings.c unicode.c"
+uqm_HFILES="stringhashtable.c strintrn.h"
diff --git a/src/libs/strings/getstr.c b/src/libs/strings/getstr.c
new file mode 100644
index 0000000..ba428cf
--- /dev/null
+++ b/src/libs/strings/getstr.c
@@ -0,0 +1,643 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "options.h"
+#include "strintrn.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/reslib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+#define MAX_STRINGS 2048
+#define POOL_SIZE 4096
+
+static void
+dword_convert (DWORD *dword_array, COUNT num_dwords)
+{
+ BYTE *p = (BYTE*)dword_array;
+
+ do
+ {
+ *dword_array++ = MAKE_DWORD (
+ MAKE_WORD (p[3], p[2]),
+ MAKE_WORD (p[1], p[0])
+ );
+ p += 4;
+ } while (--num_dwords);
+}
+
+static STRING
+set_strtab_entry (STRING_TABLE_DESC *strtab, int index, const char *value,
+ int len)
+{
+ STRING str = &strtab->strings[index];
+
+ if (str->data)
+ {
+ HFree (str->data);
+ str->data = NULL;
+ str->length = 0;
+ }
+ if (len)
+ {
+ str->data = HMalloc (len);
+ str->length = len;
+ memcpy (str->data, value, len);
+ }
+ return str;
+}
+
+static void
+copy_strings_to_strtab (STRING_TABLE_DESC *strtab, size_t firstIndex,
+ size_t count, const char *data, const DWORD *lens)
+{
+ size_t stringI;
+ const char *off = data;
+
+ for (stringI = 0; stringI < count; stringI++)
+ {
+ set_strtab_entry(strtab, firstIndex + stringI,
+ off, lens[stringI]);
+ off += lens[stringI];
+ }
+}
+
+// Check whether a buffer has a certain minimum size, and enlarge it
+// if necessary.
+// buf: pointer to the pointer to the buffer. May be NULL.
+// curSize: pointer to the current size (multiple of 'increment')
+// minSize: required minimum size
+// increment: size to increment the buffer with if necessary
+// On success, *buf and *curSize are updated. On failure, they are
+// unchanged.
+// returns FALSE if and only if the buffer needs to be enlarged but
+// memory allocation failed.
+static BOOLEAN
+ensureBufSize (char **buf, size_t *curSize, size_t minSize, size_t increment)
+{
+ char *newBuf;
+ size_t newSize;
+
+ if (minSize <= *curSize)
+ {
+ // Buffer is large enough as it is.
+ return TRUE;
+ }
+
+ newSize = ((minSize + (increment - 1)) / increment) * increment;
+ // Smallest multiple of 'increment' larger or equal to minSize.
+ newBuf = HRealloc (*buf, newSize);
+ if (newBuf == NULL)
+ return FALSE;
+
+ // Success
+ *buf = newBuf;
+ *curSize = newSize;
+ return TRUE;
+}
+
+void
+_GetConversationData (const char *path, RESOURCE_DATA *resdata)
+{
+ unsigned long dataLen;
+ void *result;
+ int stringI;
+ int path_len;
+ int num_data_sets;
+ DWORD opos;
+
+ char *namedata = NULL;
+ // Contains the names (indexes) of the dialogs.
+ DWORD nlen[MAX_STRINGS];
+ // Length of each of the names.
+ DWORD NameOffs;
+ size_t tot_name_size;
+
+ char *strdata = NULL;
+ // Contains the dialog strings.
+ DWORD slen[MAX_STRINGS];
+ // Length of each of the dialog strings.
+ DWORD StringOffs;
+ size_t tot_string_size;
+
+ char *clipdata = NULL;
+ // Contains the file names of the speech files.
+ DWORD clen[MAX_STRINGS];
+ // Length of each of the speech file names.
+ DWORD ClipOffs;
+ size_t tot_clip_size;
+
+ char *ts_data = NULL;
+ // Contains the timestamp data for synching the text with the
+ // speech.
+ DWORD tslen[MAX_STRINGS];
+ // Length of each of the timestamp strings.
+ DWORD TSOffs;
+ size_t tot_ts_size = 0;
+
+ char CurrentLine[1024];
+ char paths[1024];
+ char *clip_path;
+ char *ts_path;
+
+ uio_Stream *fp = NULL;
+ uio_Stream *timestamp_fp = NULL;
+ StringHashTable_HashTable *nameHashTable = NULL;
+ // Hash table of string names (such as "GLAD_WHEN_YOU_COME_BACK")
+ // to a STRING.
+
+ /* Parse out the conversation components. */
+ strncpy (paths, path, 1023);
+ paths[1023] = '\0';
+ clip_path = strchr (paths, ':');
+ if (clip_path == NULL)
+ {
+ ts_path = NULL;
+ }
+ else
+ {
+ *clip_path = '\0';
+ clip_path++;
+
+ ts_path = strchr (clip_path, ':');
+ if (ts_path != NULL)
+ {
+ *ts_path = '\0';
+ ts_path++;
+ }
+ }
+
+ fp = res_OpenResFile (contentDir, paths, "rb");
+ if (fp == NULL)
+ {
+ log_add (log_Warning, "Warning: Can't open '%s'", paths);
+ resdata->ptr = NULL;
+ return;
+ }
+
+ dataLen = LengthResFile (fp);
+ log_add (log_Info, "\t'%s' -- conversation phrases -- %lu bytes", paths,
+ dataLen);
+ if (clip_path)
+ log_add (log_Info, "\t'%s' -- voice clip directory", clip_path);
+ else
+ log_add (log_Info, "\tNo associated voice clips");
+ if (ts_path)
+ log_add (log_Info, "\t'%s' -- timestamps", ts_path);
+ else
+ log_add (log_Info, "\tNo associated timestamp file");
+
+ if (dataLen == 0)
+ {
+ log_add (log_Warning, "Warning: Trying to load empty file '%s'.",
+ path);
+ goto err;
+ }
+
+ tot_string_size = POOL_SIZE;
+ strdata = HMalloc (tot_string_size);
+ if (strdata == 0)
+ goto err;
+
+ tot_name_size = POOL_SIZE;
+ namedata = HMalloc (tot_name_size);
+ if (namedata == 0)
+ goto err;
+
+ tot_clip_size = POOL_SIZE;
+ clipdata = HMalloc (tot_clip_size);
+ if (clipdata == 0)
+ goto err;
+ ts_data = NULL;
+
+ nameHashTable = StringHashTable_newHashTable(
+ NULL, NULL, NULL, NULL, NULL, 0, 0.85, 0.9);
+ if (nameHashTable == NULL)
+ goto err;
+
+ path_len = clip_path ? strlen (clip_path) : 0;
+
+ if (ts_path)
+ {
+ timestamp_fp = uio_fopen (contentDir, ts_path, "rb");
+ if (timestamp_fp != NULL)
+ {
+ tot_ts_size = POOL_SIZE;
+ ts_data = HMalloc (tot_ts_size);
+ if (ts_data == 0)
+ goto err;
+ }
+ }
+
+ opos = uio_ftell (fp);
+ stringI = -1;
+ NameOffs = 0;
+ StringOffs = 0;
+ ClipOffs = 0;
+ TSOffs = 0;
+ for (;;)
+ {
+ int l;
+
+ if (uio_fgets (CurrentLine, sizeof (CurrentLine), fp) == NULL)
+ {
+ // EOF or read error.
+ break;
+ }
+
+ if (stringI >= MAX_STRINGS - 1)
+ {
+ // Too many strings.
+ break;
+ }
+
+ if (CurrentLine[0] == '#')
+ {
+ // String header, of the following form:
+ // #(GLAD_WHEN_YOU_COME_BACK) commander-000.ogg
+ char CopyLine[1024];
+ char *name;
+ char *ts;
+
+ strcpy (CopyLine, CurrentLine);
+ name = strtok (&CopyLine[1], "()");
+ if (name)
+ {
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 &&
+ (strdata[StringOffs - 2] == '\n' ||
+ strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ slen[++stringI] = 0;
+
+ // Store the string name.
+ l = strlen (name) + 1;
+ if (!ensureBufSize (&namedata, &tot_name_size,
+ NameOffs + l, POOL_SIZE))
+ goto err;
+ strcpy (&namedata[NameOffs], name);
+ NameOffs += l;
+ nlen[stringI] = l;
+
+ // now lets check for timestamp data
+ if (timestamp_fp)
+ {
+ // We have a time stamp file.
+ char TimeStampLine[1024];
+ char *tsptr;
+ BOOLEAN ts_ok = FALSE;
+ uio_fgets (TimeStampLine, sizeof (TimeStampLine), timestamp_fp);
+ if (TimeStampLine[0] == '#')
+ {
+ // Line is of the following form:
+ // #(GIVE_FUEL_AGAIN) 3304,3255
+ tslen[stringI] = 0;
+ tsptr = strstr (TimeStampLine, name);
+ if (tsptr)
+ {
+ tsptr += strlen(name) + 1;
+ ts_ok = TRUE;
+ while (! strcspn(tsptr," \t\r\n") && *tsptr)
+ tsptr++;
+ if (*tsptr)
+ {
+ l = strlen (tsptr) + 1;
+ if (!ensureBufSize (&ts_data, &tot_ts_size, TSOffs + l,
+ POOL_SIZE))
+ goto err;
+
+ strcpy (&ts_data[TSOffs], tsptr);
+ TSOffs += l;
+ tslen[stringI] = l;
+ }
+ }
+ }
+ if (!ts_ok)
+ {
+ // timestamp data is invalid, remove all of it
+ log_add (log_Warning, "Invalid timestamp data "
+ "for '%s'. Disabling timestamps", name);
+ HFree (ts_data);
+ ts_data = NULL;
+ uio_fclose (timestamp_fp);
+ timestamp_fp = NULL;
+ TSOffs = 0;
+ }
+ }
+ clen[stringI] = 0;
+ ts = strtok (NULL, " \t\r\n)");
+ if (ts)
+ {
+ l = path_len + strlen (ts) + 1;
+ if (!ensureBufSize (&clipdata, &tot_clip_size,
+ ClipOffs + l, POOL_SIZE))
+ goto err;
+
+ if (clip_path)
+ strcpy (&clipdata[ClipOffs], clip_path);
+ strcpy (&clipdata[ClipOffs + path_len], ts);
+ ClipOffs += l;
+ clen[stringI] = l;
+ }
+ }
+ }
+ else if (stringI >= 0)
+ {
+ char *s;
+ l = strlen (CurrentLine) + 1;
+
+ if (!ensureBufSize (&strdata, &tot_string_size, StringOffs + l,
+ POOL_SIZE))
+ goto err;
+
+ if (slen[stringI])
+ {
+ --slen[stringI];
+ --StringOffs;
+ }
+ s = &strdata[StringOffs];
+ slen[stringI] += l;
+ StringOffs += l;
+
+ strcpy (s, CurrentLine);
+ }
+
+ if ((int)uio_ftell (fp) - (int)opos >= (int)dataLen)
+ break;
+ }
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 && (strdata[StringOffs - 2] == '\n'
+ || strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ if (timestamp_fp)
+ uio_fclose (timestamp_fp);
+
+ result = NULL;
+ num_data_sets = (ClipOffs ? 1 : 0) + (TSOffs ? 1 : 0) + 1;
+ if (++stringI)
+ {
+ int flags = 0;
+ int stringCount = stringI;
+
+ if (ClipOffs)
+ flags |= HAS_SOUND_CLIPS;
+ if (TSOffs)
+ flags |= HAS_TIMESTAMP;
+ flags |= HAS_NAMEINDEX;
+
+ result = AllocStringTable (stringCount, flags);
+ if (result)
+ {
+ // Copy all the gatherered data in a STRING_TABLE
+ STRING_TABLE_DESC *lpST = (STRING_TABLE) result;
+ STRING str;
+ stringI = 0;
+
+ // Store the dialog string.
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, strdata, slen);
+ stringI += stringCount;
+
+ // Store the dialog names.
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, namedata, nlen);
+ stringI += stringCount;
+
+ // Store sound clip file names.
+ if (lpST->flags & HAS_SOUND_CLIPS)
+ {
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, clipdata, clen);
+ stringI += stringCount;
+ }
+
+ // Store time stamp data.
+ if (lpST->flags & HAS_TIMESTAMP)
+ {
+ copy_strings_to_strtab (
+ lpST, stringI, stringCount, ts_data, tslen);
+ //stringI += stringCount;
+ }
+
+ // Store the STRING in the hash table indexed by the dialog
+ // name.
+ str = &lpST->strings[stringCount];
+ for (stringI = 0; stringI < stringCount; stringI++)
+ {
+ StringHashTable_add (nameHashTable, str[stringI].data,
+ &str[stringI]);
+ }
+
+ lpST->nameIndex = nameHashTable;
+ }
+ }
+ HFree (strdata);
+ if (clipdata != NULL)
+ HFree (clipdata);
+ if (ts_data != NULL)
+ HFree (ts_data);
+
+ resdata->ptr = result;
+ return;
+
+err:
+ if (nameHashTable != NULL)
+ StringHashTable_deleteHashTable (nameHashTable);
+ if (ts_data != NULL)
+ HFree (ts_data);
+ if (clipdata != NULL)
+ HFree (clipdata);
+ if (strdata != NULL)
+ HFree (strdata);
+ res_CloseResFile (fp);
+ resdata->ptr = NULL;
+}
+
+void *
+_GetStringData (uio_Stream *fp, DWORD length)
+{
+ void *result;
+
+ int stringI;
+ DWORD opos;
+ DWORD slen[MAX_STRINGS];
+ DWORD StringOffs;
+ size_t tot_string_size;
+ char CurrentLine[1024];
+ char *strdata = NULL;
+
+ tot_string_size = POOL_SIZE;
+ strdata = HMalloc (tot_string_size);
+ if (strdata == 0)
+ goto err;
+
+ opos = uio_ftell (fp);
+ stringI = -1;
+ StringOffs = 0;
+ for (;;)
+ {
+ int l;
+
+ if (uio_fgets (CurrentLine, sizeof (CurrentLine), fp) == NULL)
+ {
+ // EOF or read error.
+ break;
+ }
+
+ if (stringI >= MAX_STRINGS - 1)
+ {
+ // Too many strings.
+ break;
+ }
+
+ if (CurrentLine[0] == '#')
+ {
+ char CopyLine[1024];
+ char *s;
+
+ strcpy (CopyLine, CurrentLine);
+ s = strtok (&CopyLine[1], "()");
+ if (s)
+ {
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 &&
+ (strdata[StringOffs - 2] == '\n' ||
+ strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ slen[++stringI] = 0;
+ }
+ }
+ else if (stringI >= 0)
+ {
+ char *s;
+ l = strlen (CurrentLine) + 1;
+
+ if (!ensureBufSize (&strdata, &tot_string_size, StringOffs + l,
+ POOL_SIZE))
+ goto err;
+
+ if (slen[stringI])
+ {
+ --slen[stringI];
+ --StringOffs;
+ }
+ s = &strdata[StringOffs];
+ slen[stringI] += l;
+ StringOffs += l;
+
+ strcpy (s, CurrentLine);
+ }
+
+ if ((int)uio_ftell (fp) - (int)opos >= (int)length)
+ break;
+ }
+ if (stringI >= 0)
+ {
+ while (slen[stringI] > 1 && (strdata[StringOffs - 2] == '\n'
+ || strdata[StringOffs - 2] == '\r'))
+ {
+ --slen[stringI];
+ --StringOffs;
+ strdata[StringOffs - 1] = '\0';
+ }
+ }
+
+ result = NULL;
+ if (++stringI)
+ {
+ int flags = 0;
+ int stringCount = stringI;
+
+ result = AllocStringTable (stringI, flags);
+ if (result)
+ {
+ STRING_TABLE_DESC *lpST = (STRING_TABLE) result;
+ copy_strings_to_strtab (lpST, 0, stringCount, strdata, slen);
+ }
+ }
+ HFree (strdata);
+
+ return result;
+
+err:
+ if (strdata != NULL)
+ HFree (strdata);
+ return 0;
+}
+
+
+void *
+_GetBinaryTableData (uio_Stream *fp, DWORD length)
+{
+ void *result;
+ result = GetResourceData (fp, length);
+
+ if (result)
+ {
+ DWORD *fileData;
+ STRING_TABLE lpST;
+
+ fileData = (DWORD *)result;
+
+ dword_convert (fileData, 1); /* Length */
+
+ lpST = AllocStringTable (fileData[0], 0);
+ if (lpST)
+ {
+ int i, size;
+ BYTE *stringptr;
+
+ size = lpST->size;
+
+ dword_convert (fileData+1, size + 1);
+ stringptr = (BYTE *)(fileData + 2 + size + fileData[1]);
+ for (i = 0; i < size; i++)
+ {
+ set_strtab_entry (lpST, i, (char *)stringptr, fileData[2+i]);
+ stringptr += fileData[2+i];
+ }
+ }
+ HFree (result);
+ result = lpST;
+ }
+
+ return result;
+}
+
diff --git a/src/libs/strings/sfileins.c b/src/libs/strings/sfileins.c
new file mode 100644
index 0000000..6ff4422
--- /dev/null
+++ b/src/libs/strings/sfileins.c
@@ -0,0 +1,50 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "strintrn.h"
+#include "libs/uio.h"
+#include "libs/reslib.h"
+
+
+STRING_TABLE
+LoadStringTableFile (uio_DirHandle *dir, const char *fileName)
+{
+ uio_Stream *fp;
+
+ // FIXME: this theoretically needs a mechanism to prevent races
+ if (_cur_resfile_name)
+ // something else is loading resources atm
+ return 0;
+
+ fp = res_OpenResFile (dir, fileName, "rb");
+ if (fp)
+ {
+ STRING_TABLE data;
+
+ _cur_resfile_name = fileName;
+ data = (STRING_TABLE) _GetStringData (fp, LengthResFile (fp));
+ _cur_resfile_name = 0;
+ res_CloseResFile (fp);
+
+ return data;
+ }
+
+ return (0);
+}
+
diff --git a/src/libs/strings/sresins.c b/src/libs/strings/sresins.c
new file mode 100644
index 0000000..af2de79
--- /dev/null
+++ b/src/libs/strings/sresins.c
@@ -0,0 +1,55 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "strintrn.h"
+
+static void
+GetStringTableFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetStringData);
+}
+
+static void
+GetBinaryTableFileData (const char *pathname, RESOURCE_DATA *resdata)
+{
+ resdata->ptr = LoadResourceFromPath (pathname, _GetBinaryTableData);
+}
+
+BOOLEAN
+InstallStringTableResType (void)
+{
+ InstallResTypeVectors ("STRTAB", GetStringTableFileData, FreeResourceData, NULL);
+ InstallResTypeVectors ("BINTAB", GetBinaryTableFileData, FreeResourceData, NULL);
+ InstallResTypeVectors ("CONVERSATION", _GetConversationData, FreeResourceData, NULL);
+ return TRUE;
+}
+
+STRING_TABLE
+LoadStringTableInstance (RESOURCE res)
+{
+ void *data;
+
+ data = res_GetResource (res);
+ if (data)
+ {
+ res_DetachResource (res);
+ }
+
+ return (STRING_TABLE)data;
+}
+
diff --git a/src/libs/strings/stringhashtable.c b/src/libs/strings/stringhashtable.c
new file mode 100644
index 0000000..ac4b4f4
--- /dev/null
+++ b/src/libs/strings/stringhashtable.c
@@ -0,0 +1,67 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define HASHTABLE_INTERNAL
+#include "stringhashtable.h"
+#include "types.h"
+#include "libs/misc.h"
+ // For unconst()
+#include "libs/uio/uioport.h"
+
+static inline uio_uint32 StringHashTable_hash(
+ StringHashTable_HashTable *hashTable, const char *string);
+static inline uio_bool StringHashTable_equal(
+ StringHashTable_HashTable *hashTable,
+ const char *key1, const char *key2);
+static inline char *StringHashTable_copy(
+ StringHashTable_HashTable *hashTable, const char *key);
+
+#include "libs/uio/hashtable.c"
+
+
+static inline uio_uint32
+StringHashTable_hash(StringHashTable_HashTable *hashTable, const char *key) {
+ uio_uint32 hash;
+
+ (void) hashTable;
+ // Rotating hash, variation of something on the web which
+ // wasn't original itself.
+ hash = 0;
+ // Hash was on that web page initialised as the length,
+ // but that isn't known at this time.
+ while (*key != '\0') {
+ hash = (hash << 4) ^ (hash >> 28) ^ *key;
+ key++;
+ }
+ return hash ^ (hash >> 10) ^ (hash >> 20);
+}
+
+static inline uio_bool
+StringHashTable_equal(StringHashTable_HashTable *hashTable,
+ const char *key1, const char *key2) {
+ (void) hashTable;
+ return strcmp(key1, key2) == 0;
+}
+
+static inline char *
+StringHashTable_copy(StringHashTable_HashTable *hashTable,
+ const char *key) {
+ (void) hashTable;
+ return unconst(key);
+}
+
diff --git a/src/libs/strings/stringhashtable.h b/src/libs/strings/stringhashtable.h
new file mode 100644
index 0000000..36f9e47
--- /dev/null
+++ b/src/libs/strings/stringhashtable.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _STRINGHASHTABLE_H
+#define _STRINGHASHTABLE_H
+
+// HashTable from 'char *' to STRING.
+// We don't actually copy the index, which means that the caller is
+// responsible for keeping them unchanged during the time that it is used in
+// the hash table.
+
+#include "libs/strlib.h"
+
+#define HASHTABLE_(identifier) StringHashTable ## _ ## identifier
+typedef char HASHTABLE_(Key);
+typedef STRING_TABLE_ENTRY_DESC HASHTABLE_(Value);
+#define StringHashTable_HASH StringHashTable_hash
+#define StringHashTable_EQUAL StringHashTable_equal
+#define StringHashTable_COPY StringHashTable_copy
+#define StringHashTable_FREEKEY(hashTable, key) \
+ ((void) (hashTable), (void) (key))
+#define StringHashTable_FREEVALUE(hashTable, value) \
+ ((void) (hashTable), (void) (value))
+
+#include "libs/uio/hashtable.h"
+
+
+#endif /* _STRINGHASHTABLE_H */
diff --git a/src/libs/strings/strings.c b/src/libs/strings/strings.c
new file mode 100644
index 0000000..7f8d5e4
--- /dev/null
+++ b/src/libs/strings/strings.c
@@ -0,0 +1,347 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "strintrn.h"
+#include "libs/memlib.h"
+
+STRING_TABLE
+AllocStringTable (int num_entries, int flags)
+{
+ STRING_TABLE strtab = HMalloc (sizeof (STRING_TABLE_DESC));
+ int i, multiplier = 1;
+
+ if (flags & HAS_NAMEINDEX)
+ {
+ multiplier++;
+ }
+ if (flags & HAS_SOUND_CLIPS)
+ {
+ multiplier++;
+ }
+ if (flags & HAS_TIMESTAMP)
+ {
+ multiplier++;
+ }
+ strtab->flags = flags;
+ strtab->size = num_entries;
+ num_entries *= multiplier;
+ strtab->strings = HMalloc (sizeof (STRING_TABLE_ENTRY_DESC) * num_entries);
+ for (i = 0; i < num_entries; i++)
+ {
+ strtab->strings[i].data = NULL;
+ strtab->strings[i].length = 0;
+ strtab->strings[i].parent = strtab;
+ strtab->strings[i].index = i;
+ }
+ strtab->nameIndex = NULL;
+ return strtab;
+}
+
+void
+FreeStringTable (STRING_TABLE strtab)
+{
+ int i, multiplier = 1;
+
+ if (strtab == NULL)
+ {
+ return;
+ }
+
+ if (strtab->flags & HAS_SOUND_CLIPS)
+ {
+ multiplier++;
+ }
+ if (strtab->flags & HAS_TIMESTAMP)
+ {
+ multiplier++;
+ }
+
+ for (i = 0; i < strtab->size * multiplier; i++)
+ {
+ if (strtab->strings[i].data != NULL)
+ {
+ HFree (strtab->strings[i].data);
+ }
+ }
+
+ HFree (strtab->strings);
+ HFree (strtab);
+}
+
+BOOLEAN
+DestroyStringTable (STRING_TABLE StringTable)
+{
+ FreeStringTable (StringTable);
+ return TRUE;
+}
+
+STRING
+CaptureStringTable (STRING_TABLE StringTable)
+{
+ if ((StringTable != 0) && (StringTable->size > 0))
+ {
+ return StringTable->strings;
+ }
+
+ return NULL;
+}
+
+STRING_TABLE
+ReleaseStringTable (STRING String)
+{
+ STRING_TABLE StringTable;
+
+ StringTable = GetStringTable (String);
+
+ return (StringTable);
+}
+
+STRING_TABLE
+GetStringTable (STRING String)
+{
+ if (String && String->parent)
+ {
+ return String->parent;
+ }
+ return NULL;
+}
+
+COUNT
+GetStringTableCount (STRING String)
+{
+ if (String && String->parent)
+ {
+ return String->parent->size;
+ }
+ return 0;
+}
+
+COUNT
+GetStringTableIndex (STRING String)
+{
+ if (String)
+ {
+ return String->index;
+ }
+ return 0;
+}
+
+STRING
+SetAbsStringTableIndex (STRING String, COUNT StringTableIndex)
+{
+ STRING_TABLE StringTablePtr;
+
+ if (!String)
+ return NULL;
+
+ StringTablePtr = String->parent;
+
+ if (StringTablePtr == NULL)
+ {
+ String = NULL;
+ }
+ else
+ {
+ StringTableIndex = StringTableIndex % StringTablePtr->size;
+ String = &StringTablePtr->strings[StringTableIndex];
+ }
+
+ return (String);
+}
+
+STRING
+SetRelStringTableIndex (STRING String, SIZE StringTableOffs)
+{
+ STRING_TABLE StringTablePtr;
+
+ if (!String)
+ return NULL;
+
+ StringTablePtr = String->parent;
+
+ if (StringTablePtr == NULL)
+ {
+ String = NULL;
+ }
+ else
+ {
+ COUNT StringTableIndex;
+
+ while (StringTableOffs < 0)
+ StringTableOffs += StringTablePtr->size;
+ StringTableIndex = (String->index + StringTableOffs)
+ % StringTablePtr->size;
+
+ String = &StringTablePtr->strings[StringTableIndex];
+ }
+
+ return (String);
+}
+
+COUNT
+GetStringLength (STRING String)
+{
+ if (String == NULL)
+ {
+ return 0;
+ }
+ return utf8StringCountN(String->data, String->data + String->length);
+}
+
+COUNT
+GetStringLengthBin (STRING String)
+{
+ if (String == NULL)
+ {
+ return 0;
+ }
+ return String->length;
+}
+
+STRINGPTR
+GetStringName (STRING String)
+{
+ STRING_TABLE StringTablePtr;
+ COUNT StringIndex;
+
+ if (String == NULL)
+ {
+ return NULL;
+ }
+
+ StringTablePtr = String->parent;
+ if (StringTablePtr == NULL)
+ {
+ return NULL;
+ }
+
+ StringIndex = String->index;
+
+ if (!(StringTablePtr->flags & HAS_NAMEINDEX))
+ {
+ return NULL;
+ }
+ StringIndex += StringTablePtr->size;
+
+ String = &StringTablePtr->strings[StringIndex];
+ if (String->length == 0)
+ {
+ return NULL;
+ }
+
+ return String->data;
+}
+
+STRINGPTR
+GetStringSoundClip (STRING String)
+{
+ STRING_TABLE StringTablePtr;
+ COUNT StringIndex;
+
+ if (String == NULL)
+ {
+ return NULL;
+ }
+
+ StringTablePtr = String->parent;
+ if (StringTablePtr == NULL)
+ {
+ return NULL;
+ }
+
+ StringIndex = String->index;
+ if (!(StringTablePtr->flags & HAS_SOUND_CLIPS))
+ {
+ return NULL;
+ }
+ StringIndex += StringTablePtr->size;
+
+ if (StringTablePtr->flags & HAS_NAMEINDEX)
+ {
+ StringIndex += StringTablePtr->size;
+ }
+
+ String = &StringTablePtr->strings[StringIndex];
+ if (String->length == 0)
+ {
+ return NULL;
+ }
+
+ return String->data;
+}
+
+STRINGPTR
+GetStringTimeStamp (STRING String)
+{
+ STRING_TABLE StringTablePtr;
+ COUNT StringIndex;
+
+ if (String == NULL)
+ {
+ return NULL;
+ }
+
+ StringTablePtr = String->parent;
+ if (StringTablePtr == NULL)
+ {
+ return NULL;
+ }
+
+ StringIndex = String->index;
+ if (!(StringTablePtr->flags & HAS_TIMESTAMP))
+ {
+ return NULL;
+ }
+ StringIndex += StringTablePtr->size;
+
+ if (StringTablePtr->flags & HAS_NAMEINDEX)
+ {
+ StringIndex += StringTablePtr->size;
+ }
+
+ if (StringTablePtr->flags & HAS_SOUND_CLIPS)
+ {
+ StringIndex += StringTablePtr->size;
+ }
+
+ String = &StringTablePtr->strings[StringIndex];
+ if (String->length == 0)
+ {
+ return NULL;
+ }
+
+ return String->data;
+}
+
+STRINGPTR
+GetStringAddress (STRING String)
+{
+ if (String == NULL)
+ {
+ return NULL;
+ }
+ return String->data;
+}
+
+STRING
+GetStringByName (STRING_TABLE StringTable, const char *index)
+{
+ return (STRING) StringHashTable_find (StringTable->nameIndex, index);
+}
+
+
diff --git a/src/libs/strings/strintrn.h b/src/libs/strings/strintrn.h
new file mode 100644
index 0000000..0c41fb0
--- /dev/null
+++ b/src/libs/strings/strintrn.h
@@ -0,0 +1,56 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_STRINGS_STRINTRN_H_
+#define LIBS_STRINGS_STRINTRN_H_
+
+#include <stdio.h>
+#include <string.h>
+#include "libs/strlib.h"
+#include "libs/reslib.h"
+#include "stringhashtable.h"
+
+struct string_table_entry
+{
+ STRINGPTR data;
+ int length; /* Internal NULs are allowed */
+ int index;
+ struct string_table *parent;
+};
+
+struct string_table
+{
+ unsigned short flags;
+ int size;
+ STRING_TABLE_ENTRY_DESC *strings;
+ StringHashTable_HashTable *nameIndex;
+};
+
+#define HAS_SOUND_CLIPS (1 << 0)
+#define HAS_TIMESTAMP (1 << 1)
+#define HAS_NAMEINDEX (1 << 2)
+
+STRING_TABLE AllocStringTable (int num_entries, int flags);
+void FreeStringTable (STRING_TABLE strtab);
+
+void *_GetStringData (uio_Stream *fp, DWORD length);
+void *_GetBinaryTableData (uio_Stream *fp, DWORD length);
+void _GetConversationData (const char *path, RESOURCE_DATA *resdata);
+
+#endif /* LIBS_STRINGS_STRINTRN_H_ */
+
diff --git a/src/libs/strings/unicode.c b/src/libs/strings/unicode.c
new file mode 100644
index 0000000..1750507
--- /dev/null
+++ b/src/libs/strings/unicode.c
@@ -0,0 +1,541 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "port.h"
+
+#define UNICODE_INTERNAL
+#include "libs/unicode.h"
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "libs/log.h"
+#include "libs/misc.h"
+
+
+// Resynchronise (skip everything starting with 0x10xxxxxx):
+static inline void
+resyncUTF8(const unsigned char **ptr) {
+ while ((**ptr & 0xc0) == 0x80)
+ (*ptr)++;
+}
+
+// Get one character from a UTF-8 encoded string.
+// *ptr will point to the start of the next character.
+// Returns 0 if the encoding is bad. This can be distinguished from the
+// '\0' character by checking whether **ptr == '\0' before calling this
+// function.
+UniChar
+getCharFromString(const unsigned char **ptr) {
+ UniChar result;
+
+ if (**ptr < 0x80) {
+ // 0xxxxxxx, regular ASCII
+ result = **ptr;
+ (*ptr)++;
+
+ return result;
+ }
+
+ if ((**ptr & 0xe0) == 0xc0) {
+ // 110xxxxx; 10xxxxxx must follow
+ // Value between 0x00000080 and 0x000007ff (inclusive)
+ result = **ptr & 0x1f;
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if (result < 0x00000080) {
+ // invalid encoding - must reject
+ goto err;
+ }
+ return result;
+ }
+
+ if ((**ptr & 0xf0) == 0xe0) {
+ // 1110xxxx; 10xxxxxx 10xxxxxx must follow
+ // Value between 0x00000800 and 0x0000ffff (inclusive)
+ result = **ptr & 0x0f;
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if (result < 0x00000800) {
+ // invalid encoding - must reject
+ goto err;
+ }
+ return result;
+ }
+
+ if ((**ptr & 0xf8) == 0xf0) {
+ // 11110xxx; 10xxxxxx 10xxxxxx 10xxxxxx must follow
+ // Value between 0x00010000 and 0x0010ffff (inclusive)
+ result = **ptr & 0x07;
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if ((**ptr & 0xc0) != 0x80)
+ goto err;
+ result = (result << 6) | ((**ptr) & 0x3f);
+ (*ptr)++;
+
+ if (result < 0x00010000) {
+ // invalid encoding - must reject
+ goto err;
+ }
+ return result;
+ }
+
+err:
+ log_add(log_Warning, "Warning: Invalid UTF8 sequence.");
+
+ // Resynchronise (skip everything starting with 0x10xxxxxx):
+ resyncUTF8(ptr);
+
+ return 0;
+}
+
+UniChar
+getCharFromStringN(const unsigned char **ptr, const unsigned char *end) {
+ size_t numBytes;
+
+ if (*ptr == end)
+ goto err;
+
+ if (**ptr < 0x80) {
+ numBytes = 1;
+ } else if ((**ptr & 0xe0) == 0xc0) {
+ numBytes = 2;
+ } else if ((**ptr & 0xf0) == 0xe0) {
+ numBytes = 3;
+ } else if ((**ptr & 0xf8) == 0xf0) {
+ numBytes = 4;
+ } else
+ goto err;
+
+ if (*ptr + numBytes > end)
+ goto err;
+
+ return getCharFromString(ptr);
+
+err:
+ *ptr = end;
+ return 0;
+}
+
+// Get one line from a string.
+// A line is terminated with either CRLF (DOS/Windows),
+// LF (Unix, MacOS X), or CR (old MacOS).
+// The end of the string is reached when **startNext == '\0'.
+// NULL is returned if the string is not valid UTF8. In this case
+// *end points to the first invalid character (or the character before if
+// it was a LF), and *startNext to the start of the next (possibly invalid
+// too) character.
+unsigned char *
+getLineFromString(const unsigned char *start, const unsigned char **end,
+ const unsigned char **startNext) {
+ const unsigned char *ptr = start;
+ const unsigned char *lastPtr;
+ UniChar ch;
+
+ // Search for the first newline.
+ for (;;) {
+ if (*ptr == '\0') {
+ *end = ptr;
+ *startNext = ptr;
+ return (unsigned char *) unconst(start);
+ }
+ lastPtr = ptr;
+ ch = getCharFromString(&ptr);
+ if (ch == '\0') {
+ // Bad string
+ *end = lastPtr;
+ *startNext = ptr;
+ return NULL;
+ }
+ if (ch == '\n') {
+ *end = lastPtr;
+ if (*ptr == '\0'){
+ // LF at the end of the string.
+ *startNext = ptr;
+ return (unsigned char *) unconst(start);
+ }
+ ch = getCharFromString(&ptr);
+ if (ch == '\0') {
+ // Bad string
+ return NULL;
+ }
+ if (ch == '\r') {
+ // LFCR
+ *startNext = ptr;
+ } else {
+ // LF
+ *startNext = *end;
+ }
+ return (unsigned char *) unconst(start);
+ } else if (ch == '\r') {
+ *end = lastPtr;
+ *startNext = ptr;
+ return (unsigned char *) unconst(start);
+ } // else: a normal character
+ }
+}
+
+size_t
+utf8StringCount(const unsigned char *start) {
+ size_t count = 0;
+ UniChar ch;
+
+ for (;;) {
+ ch = getCharFromString(&start);
+ if (ch == '\0')
+ return count;
+ count++;
+ }
+}
+
+size_t
+utf8StringCountN(const unsigned char *start, const unsigned char *end) {
+ size_t count = 0;
+ UniChar ch;
+
+ for (;;) {
+ ch = getCharFromStringN(&start, end);
+ if (ch == '\0')
+ return count;
+ count++;
+ }
+}
+
+// Locates a unicode character (ch) in a UTF-8 string (pStr)
+// returns the char positions when found
+// -1 when not found
+int
+utf8StringPos (const unsigned char *pStr, UniChar ch)
+{
+ int pos;
+
+ for (pos = 0; *pStr != '\0'; ++pos)
+ {
+ if (getCharFromString (&pStr) == ch)
+ return pos;
+ }
+
+ if (ch == '\0' && *pStr == '\0')
+ return pos;
+
+ return -1;
+}
+
+// Safe version of strcpy(), somewhat analogous to strncpy()
+// except it guarantees a 0-term when size > 0
+// when size == 0, returns NULL
+// BUG: this may result in the last character being only partially in the
+// buffer
+unsigned char *
+utf8StringCopy (unsigned char *dst, size_t size, const unsigned char *src)
+{
+ if (size == 0)
+ return 0;
+
+ strncpy ((char *) dst, (const char *) src, size);
+ dst[size - 1] = '\0';
+
+ return dst;
+}
+
+// TODO: this is not implemented with respect to collating order
+int
+utf8StringCompare (const unsigned char *str1, const unsigned char *str2)
+{
+#if 0
+ // UniChar comparing version
+ UniChar ch1;
+ UniChar ch2;
+
+ for (;;)
+ {
+ int cmp;
+
+ ch1 = getCharFromString(&str1);
+ ch2 = getCharFromString(&str2);
+ if (ch1 == '\0' || ch2 == '\0')
+ break;
+
+ cmp = utf8CompareChar (ch1, ch2);
+ if (cmp != 0)
+ return cmp;
+ }
+
+ if (ch1 != '\0')
+ {
+ // ch2 == '\0'
+ // str2 ends, str1 continues
+ return 1;
+ }
+
+ if (ch2 != '\0')
+ {
+ // ch1 == '\0'
+ // str1 ends, str2 continues
+ return -1;
+ }
+
+ // ch1 == '\0' && ch2 == '\0'.
+ // Strings match completely.
+ return 0;
+#else
+ // this will do for now
+ return strcmp ((const char *) str1, (const char *) str2);
+#endif
+}
+
+unsigned char *
+skipUTF8Chars(const unsigned char *ptr, size_t num) {
+ UniChar ch;
+ const unsigned char *oldPtr;
+
+ while (num--) {
+ oldPtr = ptr;
+ ch = getCharFromString(&ptr);
+ if (ch == '\0')
+ return (unsigned char *) unconst(oldPtr);
+ }
+ return (unsigned char *) unconst(ptr);
+}
+
+// Decodes a UTF-8 string (start) into a unicode character string (wstr)
+// returns number of chars decoded and stored, not counting 0-term
+// any chars that do not fit are truncated
+// wide string term 0 is always appended, unless the destination
+// buffer is 0 chars long
+size_t
+getUniCharFromStringN(UniChar *wstr, size_t maxcount,
+ const unsigned char *start, const unsigned char *end)
+{
+ UniChar *next;
+
+ if (maxcount == 0)
+ return 0;
+
+ // always leave room for 0-term
+ --maxcount;
+
+ for (next = wstr; maxcount > 0; ++next, --maxcount)
+ {
+ *next = getCharFromStringN(&start, end);
+ if (*next == 0)
+ break;
+ }
+
+ *next = 0; // term
+
+ return next - wstr;
+}
+
+// See getStringFromWideN() for functionality
+// the only difference is that the source string (start) length is
+// calculated by searching for 0-term
+size_t
+getUniCharFromString(UniChar *wstr, size_t maxcount,
+ const unsigned char *start)
+{
+ UniChar *next;
+
+ if (maxcount == 0)
+ return 0;
+
+ // always leave room for 0-term
+ --maxcount;
+
+ for (next = wstr; maxcount > 0; ++next, --maxcount)
+ {
+ *next = getCharFromString(&start);
+ if (*next == 0)
+ break;
+ }
+
+ *next = 0; // term
+
+ return next - wstr;
+}
+
+// Encode one wide character into UTF-8
+// returns number of bytes used in the buffer,
+// 0 : invalid or unsupported char
+// <0 : negative of bytes needed if buffer too small
+// string term '\0' is *not* appended or counted
+int
+getStringFromChar(unsigned char *ptr, size_t size, UniChar ch)
+{
+ int i;
+ static const struct range_def
+ {
+ UniChar lim;
+ int marker;
+ int mask;
+ }
+ ranges[] =
+ {
+ {0x0000007f, 0x00, 0x7f},
+ {0x000007ff, 0xc0, 0x1f},
+ {0x0000ffff, 0xe0, 0x0f},
+ {0x001fffff, 0xf0, 0x07},
+ {0x03ffffff, 0xf8, 0x03},
+ {0x7fffffff, 0xfc, 0x01},
+ {0x00000000, 0x00, 0x00} // term
+ };
+ const struct range_def *def;
+
+ // lookup the range
+ for (i = 0, def = ranges; ch > def->lim && def->mask != 0; ++i, ++def)
+ ;
+ if (def->mask == 0)
+ { // invalid or unsupported char
+ log_add(log_Warning, "Warning: Invalid or unsupported unicode "
+ "char (%lu)", (unsigned long) ch);
+ return 0;
+ }
+
+ if ((size_t)i + 1 > size)
+ return -(i + 1);
+
+ // unrolled for speed
+ switch (i)
+ {
+ case 5: ptr[5] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 4: ptr[4] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 3: ptr[3] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 2: ptr[2] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 1: ptr[1] = (ch & 0x3f) | 0x80;
+ ch >>= 6;
+ case 0: ptr[0] = (ch & def->mask) | def->marker;
+ }
+
+ return i + 1;
+}
+
+// Encode a wide char string (wstr) into a UTF-8 string (ptr)
+// returns number of bytes used in the buffer (includes 0-term)
+// any chars that do not fit are truncated
+// string term '\0' is always appended, unless the destination
+// buffer is 0 bytes long
+size_t
+getStringFromWideN(unsigned char *ptr, size_t size,
+ const UniChar *wstr, size_t count)
+{
+ unsigned char *next;
+ int used;
+
+ if (size == 0)
+ return 0;
+
+ // always leave room for 0-term
+ --size;
+
+ for (next = ptr; size > 0 && count > 0;
+ size -= used, next += used, --count, ++wstr)
+ {
+ used = getStringFromChar(next, size, *wstr);
+ if (used < 0)
+ break; // not enough room
+ if (used == 0)
+ { // bad char?
+ *next = '?';
+ used = 1;
+ }
+ }
+
+ *next = '\0'; // term
+
+ return next - ptr + 1;
+}
+
+// See getStringFromWideN() for functionality
+// the only difference is that the source string (wstr) length is
+// calculated by searching for 0-term
+size_t
+getStringFromWide(unsigned char *ptr, size_t size, const UniChar *wstr)
+{
+ const UniChar *end;
+
+ for (end = wstr; *end != 0; ++end)
+ ;
+
+ return getStringFromWideN(ptr, size, wstr, (end - wstr));
+}
+
+int
+UniChar_isGraph(UniChar ch)
+{ // this is not technically sufficient, but close enough for us
+ // we'll consider all non-control (CO and C1) chars in 'graph' class
+ // except for the "Private Use Area" (0xE000 - 0xF8FF)
+
+ // TODO: The private use area is really only glommed by OS X,
+ // and even there, not all of it. (Delete and Backspace both
+ // end up producing characters there -- see bug #942 for the
+ // gory details.)
+ return (ch > 0xa0 && (ch < 0xE000 || ch > 0xF8FF)) ||
+ (ch > 0x20 && ch < 0x7f);
+}
+
+int
+UniChar_isPrint(UniChar ch)
+{ // this is not technically sufficient, but close enough for us
+ // chars in 'print' class are 'graph' + 'space' classes
+ // the only space we currently have defined is 0x20
+ return (ch == 0x20) || UniChar_isGraph(ch);
+}
+
+UniChar
+UniChar_toUpper(UniChar ch)
+{ // this is a very basic Latin-1 implementation
+ // just to get things going
+ return (ch < 0x100) ? (UniChar) toupper((int) ch) : ch;
+}
+
+UniChar
+UniChar_toLower(UniChar ch)
+{ // this is a very basic Latin-1 implementation
+ // just to get things going
+ return (ch < 0x100) ? (UniChar) tolower((int) ch) : ch;
+}
+
diff --git a/src/libs/strlib.h b/src/libs/strlib.h
new file mode 100644
index 0000000..c313f7f
--- /dev/null
+++ b/src/libs/strlib.h
@@ -0,0 +1,80 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_STRLIB_H_
+#define LIBS_STRLIB_H_
+
+#include "libs/compiler.h"
+#include "port.h"
+#include "libs/uio.h"
+#include "libs/unicode.h"
+
+#include <stddef.h>
+
+typedef struct string_table_entry STRING_TABLE_ENTRY_DESC;
+typedef struct string_table STRING_TABLE_DESC;
+
+typedef STRING_TABLE_DESC *STRING_TABLE;
+typedef STRING_TABLE_ENTRY_DESC *STRING;
+typedef char *STRINGPTR;
+
+/* This has to go here because reslib requires the above typedefs. */
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern BOOLEAN InstallStringTableResType (void);
+extern STRING_TABLE LoadStringTableInstance (RESOURCE res);
+extern STRING_TABLE LoadStringTableFile (uio_DirHandle *dir,
+ const char *fileName);
+extern BOOLEAN DestroyStringTable (STRING_TABLE StringTable);
+extern STRING CaptureStringTable (STRING_TABLE StringTable);
+extern STRING_TABLE ReleaseStringTable (STRING String);
+extern STRING_TABLE GetStringTable (STRING String);
+extern COUNT GetStringTableCount (STRING String);
+extern COUNT GetStringTableIndex (STRING String);
+extern STRING SetAbsStringTableIndex (STRING String, COUNT
+ StringTableIndex);
+extern STRING SetRelStringTableIndex (STRING String, SIZE
+ StringTableOffs);
+extern COUNT GetStringLength (STRING String);
+extern COUNT GetStringLengthBin (STRING String);
+extern STRINGPTR GetStringAddress (STRING String);
+extern STRINGPTR GetStringName (STRING String);
+extern STRINGPTR GetStringSoundClip (STRING String);
+extern STRINGPTR GetStringTimeStamp (STRING String);
+extern STRING GetStringByName (STRING_TABLE StringTable, const char *index);
+
+#define UNICHAR_DEGREE_SIGN 0x00b0
+#define STR_DEGREE_SIGN "\xC2\xB0"
+#define UNICHAR_INFINITY_SIGN 0x221e
+#define STR_INFINITY_SIGN "\xE2\x88\x9E"
+#define UNICHAR_EARTH_SIGN 0x2641
+#define STR_EARTH_SIGN "\xE2\x99\x81"
+#define UNICHAR_MIDDLE_DOT 0x00b7
+#define STR_MIDDLE_DOT "\xC2\xB7"
+#define UNICHAR_BULLET 0x2022
+#define STR_BULLET "\xE2\x80\xA2"
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_STRLIB_H_ */
diff --git a/src/libs/task/Makeinfo b/src/libs/task/Makeinfo
new file mode 100644
index 0000000..f780c46
--- /dev/null
+++ b/src/libs/task/Makeinfo
@@ -0,0 +1 @@
+uqm_CFILES="tasklib.c"
diff --git a/src/libs/task/tasklib.c b/src/libs/task/tasklib.c
new file mode 100644
index 0000000..81f77af
--- /dev/null
+++ b/src/libs/task/tasklib.c
@@ -0,0 +1,139 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* By Michael Martin, 2002-09-21
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "libs/tasklib.h"
+#include "libs/log.h"
+
+#define TASK_MAX 64
+
+static struct taskstruct task_array[TASK_MAX];
+
+Task
+AssignTask (ThreadFunction task_func, SDWORD stackSize, const char *name)
+{
+ int i;
+ for (i = 0; i < TASK_MAX; ++i)
+ {
+ if (!Task_SetState (task_array+i, TASK_INUSE))
+ {
+ // log_add (log_Debug, "Assigning Task #%i: %s", i+1, name);
+ Task_ClearState (task_array+i, ~TASK_INUSE);
+ task_array[i].name = name;
+ task_array[i].thread = CreateThread (task_func, task_array+i,
+ stackSize, name);
+ return task_array+i;
+ }
+ }
+ log_add (log_Error, "Task error! Task array exhausted. Check for thread leaks.");
+ return NULL;
+}
+
+void
+FinishTask (Task task)
+{
+ // log_add (log_Debug, "Releasing Task: %s", task->name);
+ task->thread = 0;
+ if (!Task_ClearState (task, TASK_INUSE))
+ {
+ log_add (log_Debug, "Task error! Attempted to FinishTask '%s'... "
+ "but it was already done!", task->name);
+ }
+}
+
+/* This could probably be done better with a condition variable of some kind. */
+void
+ConcludeTask (Task task)
+{
+ Thread old = task->thread;
+ // log_add (log_Debug, "Awaiting conclusion of %s", task->name);
+ if (old)
+ {
+ Task_SetState (task, TASK_EXIT);
+ while (task->thread == old)
+ {
+ TaskSwitch ();
+ }
+ }
+}
+
+DWORD
+Task_SetState (Task task, DWORD state_mask)
+{
+ DWORD old_state;
+ LockMutex (task->state_mutex);
+ old_state = task->state;
+ task->state |= state_mask;
+ UnlockMutex (task->state_mutex);
+ old_state &= state_mask;
+ return old_state;
+}
+
+DWORD
+Task_ClearState (Task task, DWORD state_mask)
+{
+ DWORD old_state;
+ LockMutex (task->state_mutex);
+ old_state = task->state;
+ task->state &= ~state_mask;
+ UnlockMutex (task->state_mutex);
+ old_state &= state_mask;
+ return old_state;
+}
+
+DWORD
+Task_ToggleState (Task task, DWORD state_mask)
+{
+ DWORD old_state;
+ LockMutex (task->state_mutex);
+ old_state = task->state;
+ task->state ^= state_mask;
+ UnlockMutex (task->state_mutex);
+ old_state &= state_mask;
+ return old_state;
+}
+
+DWORD
+Task_ReadState (Task task, DWORD state_mask)
+{
+ return task->state & state_mask;
+}
+
+void
+InitTaskSystem (void)
+{
+ int i;
+ for (i = 0; i < TASK_MAX; ++i)
+ {
+ task_array[i].state_mutex = CreateMutex ("task manager lock", SYNC_CLASS_TOPLEVEL | SYNC_CLASS_RESOURCE);
+ }
+}
+
+void
+CleanupTaskSystem (void)
+{
+ int i;
+ for (i = 0; i < TASK_MAX; ++i)
+ {
+ DestroyMutex (task_array[i].state_mutex);
+ task_array[i].state_mutex = 0;
+ }
+}
+
diff --git a/src/libs/tasklib.h b/src/libs/tasklib.h
new file mode 100644
index 0000000..41544db
--- /dev/null
+++ b/src/libs/tasklib.h
@@ -0,0 +1,62 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* By Michael Martin, 2002-09-21
+ */
+
+/* The task libraries are a set of facilities for controlling synchronous
+ * processes. They are built on top of threads, but add the ability to
+ * modify a "state" variable to pass messages back and forth. */
+
+#ifndef LIBS_TASKLIB_H_
+#define LIBS_TASKLIB_H_
+
+#include "libs/threadlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* Bitmasks for setting task state. */
+#define TASK_INUSE 1
+#define TASK_EXIT 2
+
+struct taskstruct {
+ Mutex state_mutex;
+ volatile DWORD state; // Protected by state_mutex
+ const char *name;
+ volatile Thread thread;
+};
+
+typedef struct taskstruct *Task;
+
+extern void InitTaskSystem (void);
+extern void CleanupTaskSystem (void);
+
+extern Task AssignTask (ThreadFunction task_func, SDWORD Stacksize, const char *name);
+extern DWORD Task_SetState (Task task, DWORD state_mask);
+extern DWORD Task_ClearState (Task task, DWORD state_mask);
+extern DWORD Task_ToggleState (Task task, DWORD state_mask);
+extern DWORD Task_ReadState (Task task, DWORD state_mask);
+extern void FinishTask (Task task);
+extern void ConcludeTask (Task task);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/src/libs/threadlib.h b/src/libs/threadlib.h
new file mode 100644
index 0000000..6586d7f
--- /dev/null
+++ b/src/libs/threadlib.h
@@ -0,0 +1,186 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* By Serge van den Boom, 2002-09-12
+ */
+
+#ifndef LIBS_THREADLIB_H_
+#define LIBS_THREADLIB_H_
+
+#define NAMED_SYNCHRO /* Should synchronizable objects have names? */
+#define TRACK_CONTENTION /* Should we report when a thread sleeps on synchronize? */
+
+/* TRACK_CONTENTION implies NAMED_SYNCHRO. */
+#ifdef TRACK_CONTENTION
+# ifndef NAMED_SYNCHRO
+# define NAMED_SYNCHRO
+# endif
+#endif /* TRACK_CONTENTION */
+
+#ifdef DEBUG
+# ifndef DEBUG_THREADS
+# define DEBUG_THREADS
+# endif
+#endif /* DEBUG */
+
+#ifdef DEBUG_THREADS
+//# ifndef PROFILE_THREADS
+//# define PROFILE_THREADS
+//# endif
+#endif /* DEBUG_THREADS */
+
+#include <sys/types.h>
+#include "libs/timelib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined (PROFILE_THREADS) || defined (DEBUG_THREADS)
+#define THREAD_NAMES
+#endif
+
+void InitThreadSystem (void);
+void UnInitThreadSystem (void);
+
+typedef int (*ThreadFunction) (void *);
+
+typedef void *Thread;
+typedef void *Mutex;
+typedef void *Semaphore;
+typedef void *RecursiveMutex;
+typedef void *CondVar;
+
+/* Local data associated with each thread */
+typedef struct _threadLocal {
+ Semaphore flushSem;
+} ThreadLocal;
+
+/* The classes of synchronization objects */
+
+enum
+{
+ SYNC_CLASS_TOPLEVEL = (1 << 0), /* Exposed to the game logic */
+ SYNC_CLASS_AUDIO = (1 << 1), /* Involves the audio system */
+ SYNC_CLASS_VIDEO = (1 << 2), /* Involves the video system. Very noisy because of FlushGraphics(). */
+ SYNC_CLASS_RESOURCE = (1 << 3) /* Involves system resources (_MemoryLock) */
+};
+
+/* Note. NEVER call CreateThread from the main thread, or deadlocks
+ are guaranteed. Use StartThread instead (which doesn't wait around
+ for the main thread to actually create the thread and return
+ it). */
+
+#ifdef NAMED_SYNCHRO
+/* Logical OR of all classes we want to track. */
+#define TRACK_CONTENTION_CLASSES (SYNC_CLASS_TOPLEVEL)
+
+/* Prototypes with the "name" field */
+
+Thread CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
+void StartThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
+Semaphore CreateSemaphore_Core (DWORD initial, const char *name, DWORD syncClass);
+Mutex CreateMutex_Core (const char *name, DWORD syncClass);
+RecursiveMutex CreateRecursiveMutex_Core (const char *name, DWORD syncClass);
+CondVar CreateCondVar_Core (const char *name, DWORD syncClass);
+
+/* Preprocessor directives to forward to the appropriate routines */
+
+#define CreateThread(func, data, stackSize, name) \
+ CreateThread_Core ((func), (data), (stackSize), (name))
+#define StartThread(func, data, stackSize, name) \
+ StartThread_Core ((func), (data), (stackSize), (name))
+#define CreateSemaphore(initial, name, syncClass) \
+ CreateSemaphore_Core ((initial), (name), (syncClass))
+#define CreateMutex(name, syncClass) \
+ CreateMutex_Core ((name), (syncClass))
+#define CreateRecursiveMutex(name, syncClass) \
+ CreateRecursiveMutex_Core((name), (syncClass))
+#define CreateCondVar(name, syncClass) \
+ CreateCondVar_Core ((name), (syncClass))
+
+#else
+
+/* Prototypes without the "name" field. */
+Thread CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize);
+void StartThread_Core (ThreadFunction func, void *data, SDWORD stackSize);
+Semaphore CreateSemaphore_Core (DWORD initial);
+Mutex CreateMutex_Core (void);
+RecursiveMutex CreateRecursiveMutex_Core (void);
+CondVar CreateCondVar_Core (void);
+
+
+/* Preprocessor directives to forward to the appropriate routines.
+ The "name" field is stripped away in preprocessing. */
+
+#define CreateThread(func, data, stackSize, name) \
+ CreateThread_Core ((func), (data), (stackSize))
+#define StartThread(func, data, stackSize, name) \
+ StartThread_Core ((func), (data), (stackSize))
+#define CreateSemaphore(initial, name, syncClass) \
+ CreateSemaphore_Core ((initial))
+#define CreateMutex(name, syncClass) \
+ CreateMutex_Core ()
+#define CreateRecursiveMutex(name, syncClass) \
+ CreateRecursiveMutex_Core()
+#define CreateCondVar(name, syncClass) \
+ CreateCondVar_Core ()
+
+#endif
+
+ThreadLocal *CreateThreadLocal (void);
+void DestroyThreadLocal (ThreadLocal *tl);
+ThreadLocal *GetMyThreadLocal (void);
+
+void HibernateThread (TimePeriod timePeriod);
+void HibernateThreadUntil (TimeCount wakeTime);
+void SleepThread (TimePeriod timePeriod);
+void SleepThreadUntil (TimeCount wakeTime);
+void DestroyThread (Thread);
+void TaskSwitch (void);
+void WaitThread (Thread thread, int *status);
+
+void FinishThread (Thread);
+void ProcessThreadLifecycles (void);
+
+#ifdef PROFILE_THREADS
+void PrintThreadsStats (void);
+#endif /* PROFILE_THREADS */
+
+
+void DestroySemaphore (Semaphore sem);
+void SetSemaphore (Semaphore sem);
+void ClearSemaphore (Semaphore sem);
+
+void DestroyMutex (Mutex sem);
+void LockMutex (Mutex sem);
+void UnlockMutex (Mutex sem);
+
+void DestroyRecursiveMutex (RecursiveMutex m);
+void LockRecursiveMutex (RecursiveMutex m);
+void UnlockRecursiveMutex (RecursiveMutex m);
+int GetRecursiveMutexDepth (RecursiveMutex m);
+
+void DestroyCondVar (CondVar);
+void WaitCondVar (CondVar);
+void SignalCondVar (CondVar);
+void BroadcastCondVar (CondVar);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_THREADLIB_H_ */
diff --git a/src/libs/threads/Makeinfo b/src/libs/threads/Makeinfo
new file mode 100644
index 0000000..57f56a8
--- /dev/null
+++ b/src/libs/threads/Makeinfo
@@ -0,0 +1,11 @@
+case "$uqm_THREADLIB" in
+ SDL)
+ uqm_SUBDIRS="sdl"
+ ;;
+ PTHREAD)
+ uqm_SUBDIRS="pthread"
+ ;;
+esac
+
+uqm_CFILES="thrcommon.c"
+uqm_HFILES="thrcommon.h"
diff --git a/src/libs/threads/pthread/Makeinfo b/src/libs/threads/pthread/Makeinfo
new file mode 100644
index 0000000..251db7b
--- /dev/null
+++ b/src/libs/threads/pthread/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="posixthreads.c"
+uqm_HFILES="posixthreads.h"
diff --git a/src/libs/threads/pthread/posixthreads.c b/src/libs/threads/pthread/posixthreads.c
new file mode 100644
index 0000000..4f0d2e8
--- /dev/null
+++ b/src/libs/threads/pthread/posixthreads.c
@@ -0,0 +1,672 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "libs/misc.h"
+#include "libs/memlib.h"
+#include "posixthreads.h"
+#include <pthread.h>
+#include <unistd.h>
+
+#include <semaphore.h>
+
+#include "libs/log/uqmlog.h"
+
+typedef struct _thread {
+ pthread_t native;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+#endif
+ ThreadLocal *localData;
+ struct _thread *next;
+} *TrueThread;
+
+static volatile TrueThread threadQueue = NULL;
+static pthread_mutex_t threadQueueMutex;
+
+struct ThreadStartInfo
+{
+ ThreadFunction func;
+ void *data;
+ sem_t sem;
+ TrueThread thread;
+};
+
+void
+InitThreadSystem_PT (void)
+{
+ pthread_mutex_init (&threadQueueMutex, NULL);
+}
+
+void
+UnInitThreadSystem_PT (void)
+{
+ pthread_mutex_destroy (&threadQueueMutex);
+}
+
+static void
+QueueThread (TrueThread thread)
+{
+ pthread_mutex_lock (&threadQueueMutex);
+ thread->next = threadQueue;
+ threadQueue = thread;
+ pthread_mutex_unlock (&threadQueueMutex);
+}
+
+static void
+UnQueueThread (TrueThread thread)
+{
+ volatile TrueThread *ptr;
+
+ pthread_mutex_lock (&threadQueueMutex);
+ ptr = &threadQueue;
+ while (*ptr != thread)
+ {
+#ifdef DEBUG_THREADS
+ if (*ptr == NULL)
+ {
+ // Should not happen.
+ log_add (log_Debug, "Error: Trying to remove non-present thread "
+ "from thread queue.");
+ fflush (stderr);
+ explode ();
+ }
+#endif /* DEBUG_THREADS */
+ ptr = &(*ptr)->next;
+ }
+ *ptr = (*ptr)->next;
+ pthread_mutex_unlock (&threadQueueMutex);
+}
+
+static TrueThread
+FindThreadInfo (pthread_t threadID)
+{
+ TrueThread ptr;
+
+ pthread_mutex_lock (&threadQueueMutex);
+ ptr = threadQueue;
+ while (ptr)
+ {
+ if (ptr->native == threadID)
+ {
+ pthread_mutex_unlock (&threadQueueMutex);
+ return ptr;
+ }
+ ptr = ptr->next;
+ }
+ pthread_mutex_unlock (&threadQueueMutex);
+ return NULL;
+}
+
+#ifdef NAMED_SYNCHRO
+static const char *
+MyThreadName (void)
+{
+ TrueThread t = FindThreadInfo (pthread_self());
+ return t ? t->name : "Unknown (probably renderer)";
+}
+#endif
+
+static void *
+ThreadHelper (void *startInfo) {
+ ThreadFunction func;
+ void *data;
+ sem_t *sem;
+ TrueThread thread;
+ int result;
+
+ //log_add (log_Debug, "ThreadHelper()");
+
+ func = ((struct ThreadStartInfo *) startInfo)->func;
+ data = ((struct ThreadStartInfo *) startInfo)->data;
+ sem = &((struct ThreadStartInfo *) startInfo)->sem;
+
+ // Wait until the Thread structure is available.
+ if (sem_wait (sem))
+ {
+ log_add(log_Fatal, "ThreadHelper sem_wait fail");
+ exit(EXIT_FAILURE);
+ }
+ if (sem_destroy (sem))
+ {
+ log_add(log_Fatal, "ThreadHelper sem_destroy fail");
+ exit(EXIT_FAILURE);
+ }
+
+ thread = ((struct ThreadStartInfo *) startInfo)->thread;
+ HFree (startInfo);
+
+ result = (*func) (data);
+
+#ifdef DEBUG_THREADS
+ log_add (log_Debug, "Thread '%s' done (returned %d).",
+ thread->name, result);
+ fflush (stderr);
+#endif
+
+ UnQueueThread (thread);
+ DestroyThreadLocal (thread->localData);
+ FinishThread (thread);
+ /* Destroying the thread is the responsibility of ProcessThreadLifecycles() */
+ return (void*)result;
+}
+
+void
+DestroyThread_PT (Thread t)
+{
+ HFree (t);
+}
+
+Thread
+CreateThread_PT (ThreadFunction func, void *data, SDWORD stackSize
+#ifdef NAMED_SYNCHRO
+ , const char *name
+#endif
+ )
+{
+ TrueThread thread;
+ struct ThreadStartInfo *startInfo;
+ pthread_attr_t attr;
+
+
+ //log_add (log_Debug, "CreateThread_PT '%s'", name);
+
+ thread = (struct _thread *) HMalloc (sizeof *thread);
+#ifdef NAMED_SYNCHRO
+ thread->name = name;
+#endif
+
+ thread->localData = CreateThreadLocal ();
+
+ startInfo = (struct ThreadStartInfo *) HMalloc (sizeof (*startInfo));
+ startInfo->func = func;
+ startInfo->data = data;
+ if (sem_init(&startInfo->sem, 0, 0) < 0)
+ {
+ log_add (log_Fatal, "createthread seminit fail");
+ exit(EXIT_FAILURE);
+ }
+ startInfo->thread = thread;
+
+ pthread_attr_init(&attr);
+ if (pthread_attr_setstacksize(&attr, 75000))
+ {
+ log_add (log_Debug, "pthread stacksize fail");
+ }
+ if (pthread_create(&thread->native, &attr, ThreadHelper, (void *)startInfo))
+ {
+ log_add (log_Debug, "pthread create fail");
+ DestroyThreadLocal (thread->localData);
+ HFree (startInfo);
+ HFree (thread);
+ return NULL;
+ }
+ // The responsibility to free 'startInfo' and 'thread' is now by the new
+ // thread.
+
+ QueueThread (thread);
+
+#ifdef DEBUG_THREADS
+//#if 0
+ log_add (log_Debug, "Thread '%s' created.", thread->name);
+ fflush (stderr);
+//#endif
+#endif
+
+ // Signal to the new thread that the thread structure is ready
+ // and it can begin to use it.
+ if (sem_post (&startInfo->sem))
+ {
+ log_add(log_Fatal, "CreateThread sem_post fail");
+ exit(EXIT_FAILURE);
+ }
+
+ (void) stackSize; /* Satisfying compiler (unused parameter) */
+ return thread;
+}
+
+void
+SleepThread_PT (TimeCount sleepTime)
+{
+ usleep (sleepTime * 1000000 / ONE_SECOND);
+}
+
+void
+SleepThreadUntil_PT (TimeCount wakeTime) {
+ TimeCount now;
+
+ now = GetTimeCounter ();
+ if (wakeTime <= now)
+ TaskSwitch_PT ();
+ else
+ usleep ((wakeTime - now) * 1000000 / ONE_SECOND);
+}
+
+void
+TaskSwitch_PT (void) {
+ usleep (1000);
+}
+
+void
+WaitThread_PT (Thread thread, int *status) {
+ //log_add(log_Debug, "WaitThread_PT '%s', status %x", ((TrueThread)thread)->name, status);
+ //pthread_join(((TrueThread)thread)->native, status);
+ pthread_join(((TrueThread)thread)->native, NULL);
+ //log_add(log_Debug, "WaitThread_PT '%s' complete", ((TrueThread)thread)->name);
+}
+
+ThreadLocal *
+GetMyThreadLocal_PT (void)
+{
+ TrueThread t = FindThreadInfo (pthread_self());
+ return t ? t->localData : NULL;
+}
+
+/* These are the pthread implementations of the UQM synchronization objects. */
+
+/* Mutexes. */
+/* TODO. The w_memlib uses Mutexes right now, so we can't use HMalloc
+ * or HFree. Once that goes, this needs to change. */
+
+typedef struct _mutex {
+ pthread_mutex_t mutex;
+#ifdef TRACK_CONTENTION
+ pthread_t owner;
+#endif
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} Mut;
+
+
+Mutex
+#ifdef NAMED_SYNCHRO
+CreateMutex_PT (const char *name, DWORD syncClass)
+#else
+CreateMutex_PT (void)
+#endif
+{
+ Mut *mutex = malloc (sizeof (Mut));
+
+ if (mutex != NULL)
+ {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ if (pthread_mutex_init(&mutex->mutex, &attr))
+ {
+#ifdef NAMED_SYNCHRO
+ /* logging depends on Mutexes, so we have to use the
+ * non-threaded version instead */
+ log_add_nothread (log_Fatal, "Could not initialize mutex '%s':"
+ "aborting.", name);
+#else
+ log_add_nothread (log_Fatal, "Could not initialize mutex:"
+ "aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+ pthread_mutexattr_destroy(&attr);
+
+#ifdef TRACK_CONTENTION
+ mutex->owner = 0;
+#endif
+#ifdef NAMED_SYNCHRO
+ mutex->name = name;
+ mutex->syncClass = syncClass;
+#endif
+ }
+
+ return mutex;
+}
+
+void
+DestroyMutex_PT (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+ //log_add_nothread(log_Debug, "Destroying mutex '%s'", mutex->name);
+ pthread_mutex_destroy (&mutex->mutex);
+ free (mutex);
+}
+
+void
+LockMutex_PT (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+#ifdef TRACK_CONTENTION
+ /* This code isn't really quite right; race conditions between
+ * check and lock remain and can produce reports of contention
+ * where the thread never sleeps, or fail to report in
+ * situations where it does. If tracking with perfect
+ * accuracy becomes important, the TRACK_CONTENTION mutex will
+ * need to handle its own wake/sleep cycles with condition
+ * variables (check the history of this file for the
+ * CrossThreadMutex code). This almost-measure is being added
+ * because for the most part it should suffice. */
+ if (mutex->owner && (mutex->syncClass & TRACK_CONTENTION_CLASSES))
+ { /* logging depends on Mutexes, so we have to use the
+ * non-threaded version instead */
+ log_add_nothread (log_Debug, "Thread '%s' blocking on mutex '%s'",
+ MyThreadName (), mutex->name);
+ }
+#endif
+
+ while (pthread_mutex_lock (&mutex->mutex) != 0)
+ {
+ //log_add_nothread (log_Debug, "Attempt to acquire mutex '%s' failretry", mutex->name);
+ TaskSwitch_PT ();
+ }
+#ifdef TRACK_CONTENTION
+ mutex->owner = pthread_self();
+#endif
+}
+
+void
+UnlockMutex_PT (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+#ifdef TRACK_CONTENTION
+ mutex->owner = 0;
+#endif
+ while (pthread_mutex_unlock (&mutex->mutex) != 0)
+ {
+ TaskSwitch_PT ();
+ }
+}
+
+/* Semaphores. */
+
+typedef struct _sem {
+ sem_t sem;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} Sem;
+
+Semaphore
+CreateSemaphore_PT (DWORD initial
+#ifdef NAMED_SYNCHRO
+ , const char *name, DWORD syncClass
+#endif
+ )
+{
+ Sem *sem = (Sem *) HMalloc (sizeof (struct _sem));
+#ifdef NAMED_SYNCHRO
+ sem->name = name;
+ sem->syncClass = syncClass;
+#endif
+
+ //log_add (log_Debug, "Creating semaphore '%s'", sem->name);
+
+ if (sem_init(&sem->sem, 0, initial) < 0)
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Fatal, "Could not initialize semaphore '%s':"
+ " aborting.", name);
+#else
+ log_add (log_Fatal, "Could not initialize semaphore:"
+ " aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+ //log_add (log_Debug, "Creating semaphore '%s' success", sem->name);
+ return sem;
+}
+
+void
+DestroySemaphore_PT (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+ //log_add (log_Debug, "Destroying semaphore '%s'", sem->name);
+ if (sem_destroy (&sem->sem))
+ {
+ log_add (log_Debug, "Destroying semaphore '%s' failed", sem->name);
+ }
+ HFree (sem);
+}
+
+void
+SetSemaphore_PT (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+#ifdef TRACK_CONTENTION
+ int contention = 0;
+ sem_getvalue(&sem->sem, &contention);
+ contention = !contention;
+ if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES))
+ {
+ log_add (log_Debug, "Thread '%s' blocking on semaphore '%s'",
+ MyThreadName (), sem->name);
+ }
+#endif
+ //log_add (log_Debug, "Attempt to set semaphore '%s'", sem->name);
+ while (sem_wait (&sem->sem) == -1)
+ {
+ //log_add (log_Debug, "Attempt to set semaphore '%s' failretry", sem->name);
+ TaskSwitch_PT ();
+ }
+ //log_add (log_Debug, "Attempt to set semaphore '%s' success", sem->name);
+#ifdef TRACK_CONTENTION
+ if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES))
+ {
+ log_add (log_Debug, "Thread '%s' awakens,"
+ " released from semaphore '%s'", MyThreadName (), sem->name);
+ }
+#endif
+}
+
+void
+ClearSemaphore_PT (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+ //log_add (log_Debug, "Attempt to clear semaphore '%s' %x", sem->name, sem);
+ while (sem_post (&sem->sem) == -1)
+ {
+ //log_add (log_Debug, "Attempt to clear semaphore %x failretry", sem);
+ TaskSwitch_PT ();
+ }
+ //log_add (log_Debug, "Attempt to clear semaphore %x success", sem);
+}
+
+/* Recursive mutexes. Adapted from mixSDL code, which was adapted from
+ the original DCQ code. */
+
+typedef struct _recm {
+ pthread_mutex_t mutex;
+ pthread_t thread_id;
+ unsigned int locks;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} RecM;
+
+RecursiveMutex
+#ifdef NAMED_SYNCHRO
+CreateRecursiveMutex_PT (const char *name, DWORD syncClass)
+#else
+CreateRecursiveMutex_PT (void)
+#endif
+{
+ RecM *mtx = (RecM *) HMalloc (sizeof (struct _recm));
+
+ mtx->thread_id = 0;
+ if (pthread_mutex_init(&mtx->mutex, NULL))
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Fatal, "Could not initialize recursive "
+ "mutex '%s': aborting.", name);
+#else
+ log_add (log_Fatal, "Could not initialize recursive "
+ "mutex: aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+#ifdef NAMED_SYNCHRO
+ mtx->name = name;
+ mtx->syncClass = syncClass;
+#endif
+ mtx->locks = 0;
+ return (RecursiveMutex) mtx;
+}
+
+void
+DestroyRecursiveMutex_PT (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ pthread_mutex_destroy(&mtx->mutex);
+ HFree (mtx);
+}
+
+void
+LockRecursiveMutex_PT (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ pthread_t thread_id = pthread_self();
+ if (!mtx->locks || mtx->thread_id != thread_id)
+ {
+#ifdef TRACK_CONTENTION
+ if (mtx->thread_id && (mtx->syncClass & TRACK_CONTENTION_CLASSES))
+ {
+ log_add (log_Debug, "Thread '%s' blocking on '%s'",
+ MyThreadName (), mtx->name);
+ }
+#endif
+ while (pthread_mutex_lock (&mtx->mutex))
+ TaskSwitch_PT ();
+ mtx->thread_id = thread_id;
+ }
+ mtx->locks++;
+}
+
+void
+UnlockRecursiveMutex_PT (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ pthread_t thread_id = pthread_self();
+ if (!mtx->locks || mtx->thread_id != thread_id)
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Debug, "'%s' attempted to unlock %s when it "
+ "didn't hold it", MyThreadName (), mtx->name);
+#endif
+ }
+ else
+ {
+ mtx->locks--;
+ if (!mtx->locks)
+ {
+ mtx->thread_id = 0;
+ pthread_mutex_unlock (&mtx->mutex);
+ }
+ }
+}
+
+int
+GetRecursiveMutexDepth_PT (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ return mtx->locks;
+}
+
+typedef struct _cond {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} cvar;
+
+CondVar
+#ifdef NAMED_SYNCHRO
+CreateCondVar_PT (const char *name, DWORD syncClass)
+#else
+CreateCondVar_PT (void)
+#endif
+{
+ int err1, err2;
+ cvar *cv = (cvar *) HMalloc (sizeof (cvar));
+ err1 = pthread_cond_init(&cv->cond, NULL);
+ err2 = pthread_mutex_init(&cv->mutex, NULL);
+ if (err1 || err2)
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Fatal, "Could not initialize condition variable '%s':"
+ " aborting.", name);
+#else
+ log_add (log_Fatal, "Could not initialize condition variable:"
+ " aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+#ifdef NAMED_SYNCHRO
+ cv->name = name;
+ cv->syncClass = syncClass;
+#endif
+ return cv;
+}
+
+void
+DestroyCondVar_PT (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ pthread_cond_destroy(&cv->cond);
+ pthread_mutex_destroy(&cv->mutex);
+ HFree (cv);
+}
+
+void
+WaitCondVar_PT (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ pthread_mutex_lock (&cv->mutex);
+#ifdef TRACK_CONTENTION
+ if (cv->syncClass & TRACK_CONTENTION_CLASSES)
+ {
+ log_add (log_Debug, "Thread '%s' waiting for signal from '%s'",
+ MyThreadName (), cv->name);
+ }
+#endif
+ while (pthread_cond_wait (&cv->cond, &cv->mutex) != 0)
+ {
+ TaskSwitch_PT ();
+ }
+#ifdef TRACK_CONTENTION
+ if (cv->syncClass & TRACK_CONTENTION_CLASSES)
+ {
+ log_add (log_Debug, "Thread '%s' received signal from '%s',"
+ " awakening.", MyThreadName (), cv->name);
+ }
+#endif
+ pthread_mutex_unlock (&cv->mutex);
+}
+
+void
+SignalCondVar_PT (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ pthread_cond_signal(&cv->cond);
+}
+
+void
+BroadcastCondVar_PT (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ pthread_cond_broadcast(&cv->cond);
+}
diff --git a/src/libs/threads/pthread/posixthreads.h b/src/libs/threads/pthread/posixthreads.h
new file mode 100644
index 0000000..9945b53
--- /dev/null
+++ b/src/libs/threads/pthread/posixthreads.h
@@ -0,0 +1,103 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_THREADS_PTHREAD_POSIXTHREADS_H_
+#define LIBS_THREADS_PTHREAD_POSIXTHREADS_H_
+
+#include "port.h"
+#include "libs/threadlib.h"
+
+void InitThreadSystem_PT (void);
+void UnInitThreadSystem_PT (void);
+
+#ifdef NAMED_SYNCHRO
+/* Prototypes with the "name" field */
+Thread CreateThread_PT (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
+Mutex CreateMutex_PT (const char *name, DWORD syncClass);
+Semaphore CreateSemaphore_PT (DWORD initial, const char *name, DWORD syncClass);
+RecursiveMutex CreateRecursiveMutex_PT (const char *name, DWORD syncClass);
+CondVar CreateCondVar_PT (const char *name, DWORD syncClass);
+#else
+/* Prototypes without the "name" field. */
+Thread CreateThread_PT (ThreadFunction func, void *data, SDWORD stackSize);
+Mutex CreateMutex_PT (void);
+Semaphore CreateSemaphore_PT (DWORD initial);
+RecursiveMutex CreateRecursiveMutex_PT (void);
+CondVar CreateCondVar_PT (void);
+#endif
+
+ThreadLocal *GetMyThreadLocal_PT (void);
+
+void SleepThread_PT (TimeCount sleepTime);
+void SleepThreadUntil_PT (TimeCount wakeTime);
+void TaskSwitch_PT (void);
+void WaitThread_PT (Thread thread, int *status);
+void DestroyThread_PT (Thread thread);
+
+void DestroyMutex_PT (Mutex m);
+void LockMutex_PT (Mutex m);
+void UnlockMutex_PT (Mutex m);
+
+void DestroySemaphore_PT (Semaphore sem);
+void SetSemaphore_PT (Semaphore sem);
+void ClearSemaphore_PT (Semaphore sem);
+
+void DestroyCondVar_PT (CondVar c);
+void WaitCondVar_PT (CondVar c);
+void SignalCondVar_PT (CondVar c);
+void BroadcastCondVar_PT (CondVar c);
+
+void DestroyRecursiveMutex_PT (RecursiveMutex m);
+void LockRecursiveMutex_PT (RecursiveMutex m);
+void UnlockRecursiveMutex_PT (RecursiveMutex m);
+int GetRecursiveMutexDepth_PT (RecursiveMutex m);
+
+#define NativeInitThreadSystem InitThreadSystem_PT
+#define NativeUnInitThreadSystem UnInitThreadSystem_PT
+
+#define NativeGetMyThreadLocal GetMyThreadLocal_PT
+
+#define NativeCreateThread CreateThread_PT
+#define NativeSleepThread SleepThread_PT
+#define NativeSleepThreadUntil SleepThreadUntil_PT
+#define NativeTaskSwitch TaskSwitch_PT
+#define NativeWaitThread WaitThread_PT
+#define NativeDestroyThread DestroyThread_PT
+
+#define NativeCreateMutex CreateMutex_PT
+#define NativeDestroyMutex DestroyMutex_PT
+#define NativeLockMutex LockMutex_PT
+#define NativeUnlockMutex UnlockMutex_PT
+
+#define NativeCreateSemaphore CreateSemaphore_PT
+#define NativeDestroySemaphore DestroySemaphore_PT
+#define NativeSetSemaphore SetSemaphore_PT
+#define NativeClearSemaphore ClearSemaphore_PT
+
+#define NativeCreateCondVar CreateCondVar_PT
+#define NativeDestroyCondVar DestroyCondVar_PT
+#define NativeWaitCondVar WaitCondVar_PT
+#define NativeSignalCondVar SignalCondVar_PT
+#define NativeBroadcastCondVar BroadcastCondVar_PT
+
+#define NativeCreateRecursiveMutex CreateRecursiveMutex_PT
+#define NativeDestroyRecursiveMutex DestroyRecursiveMutex_PT
+#define NativeLockRecursiveMutex LockRecursiveMutex_PT
+#define NativeUnlockRecursiveMutex UnlockRecursiveMutex_PT
+#define NativeGetRecursiveMutexDepth GetRecursiveMutexDepth_PT
+
+#endif /* _PTTHREAD_H */
+
diff --git a/src/libs/threads/sdl/Makeinfo b/src/libs/threads/sdl/Makeinfo
new file mode 100644
index 0000000..7a7ad76
--- /dev/null
+++ b/src/libs/threads/sdl/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="sdlthreads.c"
+uqm_HFILES="sdlthreads.h"
diff --git a/src/libs/threads/sdl/sdlthreads.c b/src/libs/threads/sdl/sdlthreads.c
new file mode 100644
index 0000000..118951d
--- /dev/null
+++ b/src/libs/threads/sdl/sdlthreads.c
@@ -0,0 +1,706 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "libs/misc.h"
+#include "libs/memlib.h"
+#include "sdlthreads.h"
+#ifdef PROFILE_THREADS
+#include <signal.h>
+#include <unistd.h>
+#endif
+#include "libs/log.h"
+
+#if defined(PROFILE_THREADS) && !defined(WIN32)
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+#if SDL_MAJOR_VERSION == 1
+typedef Uint32 SDL_threadID;
+#endif
+
+typedef struct _thread {
+ void *native;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+#endif
+#ifdef PROFILE_THREADS
+ int startTime;
+#endif /* PROFILE_THREADS */
+ ThreadLocal *localData;
+ struct _thread *next;
+} *TrueThread;
+
+static volatile TrueThread threadQueue = NULL;
+static SDL_mutex *threadQueueMutex;
+
+struct ThreadStartInfo
+{
+ ThreadFunction func;
+ void *data;
+ SDL_sem *sem;
+ TrueThread thread;
+};
+
+#ifdef PROFILE_THREADS
+static void
+SigUSR1Handler (int signr) {
+ if (getpgrp () != getpid ())
+ {
+ // Only act for the main process
+ return;
+ }
+ PrintThreadsStats ();
+ // It's not a good idea in general to do many things in a signal
+ // handler, (and especially the locking) but I guess it will
+ // have to do for now (and it's only for debugging).
+ (void) signr; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+LocalStats (SDL_Thread *thread) {
+#if defined (WIN32) || !defined(SDL_PTHREADS)
+ fprintf (stderr, "Thread ID %08lx\n", (Uint64)SDL_GetThreadID (thread));
+#else /* !defined (WIN32) && defined(SDL_PTHREADS) */
+ // This only works if SDL implements threads as processes
+ pid_t pid;
+ struct rusage ru;
+ long seconds;
+
+ pid = (pid_t) SDL_GetThreadID (thread);
+ fprintf (stderr, "Pid %d\n", (int) pid);
+ getrusage(RUSAGE_SELF, &ru);
+ seconds = ru.ru_utime.tv_sec + ru.ru_utime.tv_sec;
+ fprintf (stderr, "Used %ld.%ld minutes of processor time.\n",
+ seconds / 60, seconds % 60);
+#endif /* defined (WIN32) && defined(SDL_PTHREADS) */
+}
+
+void
+PrintThreadsStats_SDL (void)
+{
+ TrueThread ptr;
+ int now;
+
+ now = GetTimeCounter ();
+ SDL_mutexP (threadQueueMutex);
+ fprintf(stderr, "--- Active threads ---\n");
+ for (ptr = threadQueue; ptr != NULL; ptr = ptr->next) {
+ fprintf (stderr, "Thread named '%s'.\n", ptr->name);
+ fprintf (stderr, "Started %d.%d minutes ago.\n",
+ (now - ptr->startTime) / 60000,
+ ((now - ptr->startTime) / 1000) % 60);
+ LocalStats (ptr->native);
+ if (ptr->next != NULL)
+ fprintf(stderr, "\n");
+ }
+ SDL_mutexV (threadQueueMutex);
+ fprintf(stderr, "----------------------\n");
+ fflush (stderr);
+}
+#endif /* PROFILE_THREADS */
+
+void
+InitThreadSystem_SDL (void)
+{
+ threadQueueMutex = SDL_CreateMutex ();
+#ifdef PROFILE_THREADS
+ signal(SIGUSR1, SigUSR1Handler);
+#endif
+}
+
+void
+UnInitThreadSystem_SDL (void)
+{
+#ifdef PROFILE_THREADS
+ signal(SIGUSR1, SIG_DFL);
+#endif
+ SDL_DestroyMutex (threadQueueMutex);
+}
+
+static void
+QueueThread (TrueThread thread)
+{
+ SDL_mutexP (threadQueueMutex);
+ thread->next = threadQueue;
+ threadQueue = thread;
+ SDL_mutexV (threadQueueMutex);
+}
+
+static void
+UnQueueThread (TrueThread thread)
+{
+ volatile TrueThread *ptr;
+
+ SDL_mutexP (threadQueueMutex);
+ ptr = &threadQueue;
+ while (*ptr != thread)
+ {
+#ifdef DEBUG_THREADS
+ if (*ptr == NULL)
+ {
+ // Should not happen.
+ log_add (log_Debug, "Error: Trying to remove non-present thread "
+ "from thread queue.");
+ fflush (stderr);
+ explode ();
+ }
+#endif /* DEBUG_THREADS */
+ ptr = &(*ptr)->next;
+ }
+ *ptr = (*ptr)->next;
+ SDL_mutexV (threadQueueMutex);
+}
+
+static TrueThread
+FindThreadInfo (SDL_threadID threadID)
+{
+ TrueThread ptr;
+
+ SDL_mutexP (threadQueueMutex);
+ ptr = threadQueue;
+ while (ptr)
+ {
+ if (SDL_GetThreadID (ptr->native) == threadID)
+ {
+ SDL_mutexV (threadQueueMutex);
+ return ptr;
+ }
+ ptr = ptr->next;
+ }
+ SDL_mutexV (threadQueueMutex);
+ return NULL;
+}
+
+#ifdef NAMED_SYNCHRO
+static const char *
+MyThreadName (void)
+{
+ TrueThread t = FindThreadInfo (SDL_ThreadID ());
+ return t ? t->name : "Unknown (probably renderer)";
+}
+#endif
+
+static int
+ThreadHelper (void *startInfo) {
+ ThreadFunction func;
+ void *data;
+ SDL_sem *sem;
+ TrueThread thread;
+ int result;
+
+ func = ((struct ThreadStartInfo *) startInfo)->func;
+ data = ((struct ThreadStartInfo *) startInfo)->data;
+ sem = ((struct ThreadStartInfo *) startInfo)->sem;
+
+ // Wait until the Thread structure is available.
+ SDL_SemWait (sem);
+ SDL_DestroySemaphore (sem);
+ thread = ((struct ThreadStartInfo *) startInfo)->thread;
+ HFree (startInfo);
+
+ result = (*func) (data);
+
+#ifdef DEBUG_THREADS
+ log_add (log_Debug, "Thread '%s' done (returned %d).",
+ thread->name, result);
+ fflush (stderr);
+#endif
+
+ UnQueueThread (thread);
+ DestroyThreadLocal (thread->localData);
+ FinishThread (thread);
+ /* Destroying the thread is the responsibility of ProcessThreadLifecycles() */
+ return result;
+}
+
+void
+DestroyThread_SDL (Thread t)
+{
+ HFree (t);
+}
+
+Thread
+CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize
+#ifdef NAMED_SYNCHRO
+ , const char *name
+#endif
+ )
+{
+ TrueThread thread;
+ struct ThreadStartInfo *startInfo;
+
+ thread = (struct _thread *) HMalloc (sizeof *thread);
+#ifdef NAMED_SYNCHRO
+ thread->name = name;
+#endif
+#ifdef PROFILE_THREADS
+ thread->startTime = GetTimeCounter ();
+#endif
+
+ thread->localData = CreateThreadLocal ();
+
+ startInfo = (struct ThreadStartInfo *) HMalloc (sizeof (*startInfo));
+ startInfo->func = func;
+ startInfo->data = data;
+ startInfo->sem = SDL_CreateSemaphore (0);
+ startInfo->thread = thread;
+
+#if SDL_MAJOR_VERSION == 1
+ // SDL1 case
+ thread->native = SDL_CreateThread (ThreadHelper, (void *) startInfo);
+#elif defined(NAMED_SYNCHRO)
+ // SDL2 with UQM-aware named threads case
+ thread->native = SDL_CreateThread (ThreadHelper, thread->name, (void *) startInfo);
+#else
+ // SDL2 without UQM-aware named threads; use a placeholder for debuggers
+ thread->native = SDL_CreateThread (ThreadHelper, "UQM worker thread", (void *)startInfo);
+#endif
+ if (!(thread->native))
+ {
+ DestroyThreadLocal (thread->localData);
+ HFree (startInfo);
+ HFree (thread);
+ return NULL;
+ }
+ // The responsibility to free 'startInfo' and 'thread' is now by the new
+ // thread.
+
+ QueueThread (thread);
+
+#ifdef DEBUG_THREADS
+#if 0
+ log_add (log_Debug, "Thread '%s' created.", ThreadName (thread));
+ fflush (stderr);
+#endif
+#endif
+
+ // Signal to the new thread that the thread structure is ready
+ // and it can begin to use it.
+ SDL_SemPost (startInfo->sem);
+
+ (void) stackSize; /* Satisfying compiler (unused parameter) */
+ return thread;
+}
+
+void
+SleepThread_SDL (TimeCount sleepTime)
+{
+ SDL_Delay (sleepTime * 1000 / ONE_SECOND);
+}
+
+void
+SleepThreadUntil_SDL (TimeCount wakeTime) {
+ TimeCount now;
+
+ now = GetTimeCounter ();
+ if (wakeTime <= now)
+ TaskSwitch_SDL ();
+ else
+ SDL_Delay ((wakeTime - now) * 1000 / ONE_SECOND);
+}
+
+void
+TaskSwitch_SDL (void) {
+ SDL_Delay (1);
+}
+
+void
+WaitThread_SDL (Thread thread, int *status) {
+ SDL_WaitThread (((TrueThread)thread)->native, status);
+}
+
+ThreadLocal *
+GetMyThreadLocal_SDL (void)
+{
+ TrueThread t = FindThreadInfo (SDL_ThreadID ());
+ return t ? t->localData : NULL;
+}
+
+/* These are the SDL implementations of the UQM synchronization objects. */
+
+/* Mutexes. */
+/* TODO. The w_memlib uses Mutexes right now, so we can't use HMalloc
+ * or HFree. Once that goes, this needs to change. */
+
+typedef struct _mutex {
+ SDL_mutex *mutex;
+#ifdef TRACK_CONTENTION
+ SDL_threadID owner;
+#endif
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} Mut;
+
+
+Mutex
+#ifdef NAMED_SYNCHRO
+CreateMutex_SDL (const char *name, DWORD syncClass)
+#else
+CreateMutex_SDL (void)
+#endif
+{
+ Mut *mutex = malloc (sizeof (Mut));
+ if (mutex != NULL)
+ {
+ mutex->mutex = SDL_CreateMutex();
+#ifdef TRACK_CONTENTION
+ mutex->owner = 0;
+#endif
+#ifdef NAMED_SYNCHRO
+ mutex->name = name;
+ mutex->syncClass = syncClass;
+#endif
+ }
+
+ if ((mutex == NULL) || (mutex->mutex == NULL))
+ {
+#ifdef NAMED_SYNCHRO
+ /* logging depends on Mutexes, so we have to use the
+ * non-threaded version instead */
+ log_add_nothread (log_Fatal, "Could not initialize mutex '%s':"
+ "aborting.", name);
+#else
+ log_add_nothread (log_Fatal, "Could not initialize mutex:"
+ "aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+
+ return mutex;
+}
+
+void
+DestroyMutex_SDL (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+ SDL_DestroyMutex (mutex->mutex);
+ free (mutex);
+}
+
+void
+LockMutex_SDL (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+#ifdef TRACK_CONTENTION
+ /* This code isn't really quite right; race conditions between
+ * check and lock remain and can produce reports of contention
+ * where the thread never sleeps, or fail to report in
+ * situations where it does. If tracking with perfect
+ * accuracy becomes important, the TRACK_CONTENTION mutex will
+ * need to handle its own wake/sleep cycles with condition
+ * variables (check the history of this file for the
+ * CrossThreadMutex code). This almost-measure is being added
+ * because for the most part it should suffice. */
+ if (mutex->owner && (mutex->syncClass & TRACK_CONTENTION_CLASSES))
+ { /* logging depends on Mutexes, so we have to use the
+ * non-threaded version instead */
+ log_add_nothread (log_Debug, "Thread '%s' blocking on mutex '%s'",
+ MyThreadName (), mutex->name);
+ }
+#endif
+ while (SDL_mutexP (mutex->mutex) != 0)
+ {
+ TaskSwitch_SDL ();
+ }
+#ifdef TRACK_CONTENTION
+ mutex->owner = SDL_ThreadID ();
+#endif
+}
+
+void
+UnlockMutex_SDL (Mutex m)
+{
+ Mut *mutex = (Mut *)m;
+#ifdef TRACK_CONTENTION
+ mutex->owner = 0;
+#endif
+ while (SDL_mutexV (mutex->mutex) != 0)
+ {
+ TaskSwitch_SDL ();
+ }
+}
+
+/* Semaphores. */
+
+typedef struct _sem {
+ SDL_sem *sem;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} Sem;
+
+Semaphore
+CreateSemaphore_SDL (DWORD initial
+#ifdef NAMED_SYNCHRO
+ , const char *name, DWORD syncClass
+#endif
+ )
+{
+ Sem *sem = (Sem *) HMalloc (sizeof (struct _sem));
+#ifdef NAMED_SYNCHRO
+ sem->name = name;
+ sem->syncClass = syncClass;
+#endif
+ sem->sem = SDL_CreateSemaphore (initial);
+ if (sem->sem == NULL)
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Fatal, "Could not initialize semaphore '%s':"
+ " aborting.", name);
+#else
+ log_add (log_Fatal, "Could not initialize semaphore:"
+ " aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+ return sem;
+}
+
+void
+DestroySemaphore_SDL (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+ SDL_DestroySemaphore (sem->sem);
+ HFree (sem);
+}
+
+void
+SetSemaphore_SDL (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+#ifdef TRACK_CONTENTION
+ BOOLEAN contention = !(SDL_SemValue (sem->sem));
+ if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES))
+ {
+ log_add (log_Debug, "Thread '%s' blocking on semaphore '%s'",
+ MyThreadName (), sem->name);
+ }
+#endif
+ while (SDL_SemWait (sem->sem) == -1)
+ {
+ TaskSwitch_SDL ();
+ }
+#ifdef TRACK_CONTENTION
+ if (contention && (sem->syncClass & TRACK_CONTENTION_CLASSES))
+ {
+ log_add (log_Debug, "Thread '%s' awakens,"
+ " released from semaphore '%s'", MyThreadName (), sem->name);
+ }
+#endif
+}
+
+void
+ClearSemaphore_SDL (Semaphore s)
+{
+ Sem *sem = (Sem *)s;
+ while (SDL_SemPost (sem->sem) == -1)
+ {
+ TaskSwitch_SDL ();
+ }
+}
+
+/* Recursive mutexes. Adapted from mixSDL code, which was adapted from
+ the original DCQ code. */
+
+typedef struct _recm {
+ SDL_mutex *mutex;
+ SDL_threadID thread_id;
+ Uint32 locks;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} RecM;
+
+RecursiveMutex
+#ifdef NAMED_SYNCHRO
+CreateRecursiveMutex_SDL (const char *name, DWORD syncClass)
+#else
+CreateRecursiveMutex_SDL (void)
+#endif
+{
+ RecM *mtx = (RecM *) HMalloc (sizeof (struct _recm));
+
+ mtx->thread_id = 0;
+ mtx->mutex = SDL_CreateMutex ();
+ if (mtx->mutex == NULL)
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Fatal, "Could not initialize recursive "
+ "mutex '%s': aborting.", name);
+#else
+ log_add (log_Fatal, "Could not initialize recursive "
+ "mutex: aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+#ifdef NAMED_SYNCHRO
+ mtx->name = name;
+ mtx->syncClass = syncClass;
+#endif
+ mtx->locks = 0;
+ return (RecursiveMutex) mtx;
+}
+
+void
+DestroyRecursiveMutex_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ SDL_DestroyMutex (mtx->mutex);
+ HFree (mtx);
+}
+
+void
+LockRecursiveMutex_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ SDL_threadID thread_id = SDL_ThreadID();
+ if (!mtx->locks || mtx->thread_id != thread_id)
+ {
+#ifdef TRACK_CONTENTION
+ if (mtx->thread_id && (mtx->syncClass & TRACK_CONTENTION_CLASSES))
+ {
+ log_add (log_Debug, "Thread '%s' blocking on '%s'",
+ MyThreadName (), mtx->name);
+ }
+#endif
+ while (SDL_mutexP (mtx->mutex))
+ TaskSwitch_SDL ();
+ mtx->thread_id = thread_id;
+ }
+ mtx->locks++;
+}
+
+void
+UnlockRecursiveMutex_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ SDL_threadID thread_id = SDL_ThreadID();
+ if (!mtx->locks || mtx->thread_id != thread_id)
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Debug, "'%s' attempted to unlock %s when it "
+ "didn't hold it", MyThreadName (), mtx->name);
+#endif
+ }
+ else
+ {
+ mtx->locks--;
+ if (!mtx->locks)
+ {
+ mtx->thread_id = 0;
+ SDL_mutexV (mtx->mutex);
+ }
+ }
+}
+
+int
+GetRecursiveMutexDepth_SDL (RecursiveMutex val)
+{
+ RecM *mtx = (RecM *)val;
+ return mtx->locks;
+}
+
+typedef struct _cond {
+ SDL_cond *cond;
+ SDL_mutex *mutex;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+ DWORD syncClass;
+#endif
+} cvar;
+
+CondVar
+#ifdef NAMED_SYNCHRO
+CreateCondVar_SDL (const char *name, DWORD syncClass)
+#else
+CreateCondVar_SDL (void)
+#endif
+{
+ cvar *cv = (cvar *) HMalloc (sizeof (cvar));
+ cv->cond = SDL_CreateCond ();
+ cv->mutex = SDL_CreateMutex ();
+ if ((cv->cond == NULL) || (cv->mutex == NULL))
+ {
+#ifdef NAMED_SYNCHRO
+ log_add (log_Fatal, "Could not initialize condition variable '%s':"
+ " aborting.", name);
+#else
+ log_add (log_Fatal, "Could not initialize condition variable:"
+ " aborting.");
+#endif
+ exit (EXIT_FAILURE);
+ }
+#ifdef NAMED_SYNCHRO
+ cv->name = name;
+ cv->syncClass = syncClass;
+#endif
+ return cv;
+}
+
+void
+DestroyCondVar_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_DestroyCond (cv->cond);
+ SDL_DestroyMutex (cv->mutex);
+ HFree (cv);
+}
+
+void
+WaitCondVar_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_mutexP (cv->mutex);
+#ifdef TRACK_CONTENTION
+ if (cv->syncClass & TRACK_CONTENTION_CLASSES)
+ {
+ log_add (log_Debug, "Thread '%s' waiting for signal from '%s'",
+ MyThreadName (), cv->name);
+ }
+#endif
+ while (SDL_CondWait (cv->cond, cv->mutex) != 0)
+ {
+ TaskSwitch_SDL ();
+ }
+#ifdef TRACK_CONTENTION
+ if (cv->syncClass & TRACK_CONTENTION_CLASSES)
+ {
+ log_add (log_Debug, "Thread '%s' received signal from '%s',"
+ " awakening.", MyThreadName (), cv->name);
+ }
+#endif
+ SDL_mutexV (cv->mutex);
+}
+
+void
+SignalCondVar_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_CondSignal (cv->cond);
+}
+
+void
+BroadcastCondVar_SDL (CondVar c)
+{
+ cvar *cv = (cvar *) c;
+ SDL_CondBroadcast (cv->cond);
+}
diff --git a/src/libs/threads/sdl/sdlthreads.h b/src/libs/threads/sdl/sdlthreads.h
new file mode 100644
index 0000000..c93c947
--- /dev/null
+++ b/src/libs/threads/sdl/sdlthreads.h
@@ -0,0 +1,106 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_THREADS_SDL_SDLTHREADS_H_
+#define LIBS_THREADS_SDL_SDLTHREADS_H_
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+#include SDL_INCLUDE(SDL_thread.h)
+#include "libs/threadlib.h"
+#include "libs/timelib.h"
+
+void InitThreadSystem_SDL (void);
+void UnInitThreadSystem_SDL (void);
+
+#ifdef NAMED_SYNCHRO
+/* Prototypes with the "name" field */
+Thread CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize, const char *name);
+Mutex CreateMutex_SDL (const char *name, DWORD syncClass);
+Semaphore CreateSemaphore_SDL (DWORD initial, const char *name, DWORD syncClass);
+RecursiveMutex CreateRecursiveMutex_SDL (const char *name, DWORD syncClass);
+CondVar CreateCondVar_SDL (const char *name, DWORD syncClass);
+#else
+/* Prototypes without the "name" field. */
+Thread CreateThread_SDL (ThreadFunction func, void *data, SDWORD stackSize);
+Mutex CreateMutex_SDL (void);
+Semaphore CreateSemaphore_SDL (DWORD initial);
+RecursiveMutex CreateRecursiveMutex_SDL (void);
+CondVar CreateCondVar_SDL (void);
+#endif
+
+ThreadLocal *GetMyThreadLocal_SDL (void);
+
+void SleepThread_SDL (TimeCount sleepTime);
+void SleepThreadUntil_SDL (TimeCount wakeTime);
+void TaskSwitch_SDL (void);
+void WaitThread_SDL (Thread thread, int *status);
+void DestroyThread_SDL (Thread thread);
+
+void DestroyMutex_SDL (Mutex m);
+void LockMutex_SDL (Mutex m);
+void UnlockMutex_SDL (Mutex m);
+
+void DestroySemaphore_SDL (Semaphore sem);
+void SetSemaphore_SDL (Semaphore sem);
+void ClearSemaphore_SDL (Semaphore sem);
+
+void DestroyCondVar_SDL (CondVar c);
+void WaitCondVar_SDL (CondVar c);
+void SignalCondVar_SDL (CondVar c);
+void BroadcastCondVar_SDL (CondVar c);
+
+void DestroyRecursiveMutex_SDL (RecursiveMutex m);
+void LockRecursiveMutex_SDL (RecursiveMutex m);
+void UnlockRecursiveMutex_SDL (RecursiveMutex m);
+int GetRecursiveMutexDepth_SDL (RecursiveMutex m);
+
+#define NativeInitThreadSystem InitThreadSystem_SDL
+#define NativeUnInitThreadSystem UnInitThreadSystem_SDL
+
+#define NativeGetMyThreadLocal GetMyThreadLocal_SDL
+
+#define NativeCreateThread CreateThread_SDL
+#define NativeSleepThread SleepThread_SDL
+#define NativeSleepThreadUntil SleepThreadUntil_SDL
+#define NativeTaskSwitch TaskSwitch_SDL
+#define NativeWaitThread WaitThread_SDL
+#define NativeDestroyThread DestroyThread_SDL
+
+#define NativeCreateMutex CreateMutex_SDL
+#define NativeDestroyMutex DestroyMutex_SDL
+#define NativeLockMutex LockMutex_SDL
+#define NativeUnlockMutex UnlockMutex_SDL
+
+#define NativeCreateSemaphore CreateSemaphore_SDL
+#define NativeDestroySemaphore DestroySemaphore_SDL
+#define NativeSetSemaphore SetSemaphore_SDL
+#define NativeClearSemaphore ClearSemaphore_SDL
+
+#define NativeCreateCondVar CreateCondVar_SDL
+#define NativeDestroyCondVar DestroyCondVar_SDL
+#define NativeWaitCondVar WaitCondVar_SDL
+#define NativeSignalCondVar SignalCondVar_SDL
+#define NativeBroadcastCondVar BroadcastCondVar_SDL
+
+#define NativeCreateRecursiveMutex CreateRecursiveMutex_SDL
+#define NativeDestroyRecursiveMutex DestroyRecursiveMutex_SDL
+#define NativeLockRecursiveMutex LockRecursiveMutex_SDL
+#define NativeUnlockRecursiveMutex UnlockRecursiveMutex_SDL
+#define NativeGetRecursiveMutexDepth GetRecursiveMutexDepth_SDL
+
+#endif /* LIBS_THREADS_SDL_SDLTHREADS_H_ */
+
diff --git a/src/libs/threads/thrcommon.c b/src/libs/threads/thrcommon.c
new file mode 100644
index 0000000..bc54751
--- /dev/null
+++ b/src/libs/threads/thrcommon.c
@@ -0,0 +1,451 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "libs/threadlib.h"
+#include "libs/timelib.h"
+#include "libs/log.h"
+#include "libs/async.h"
+#include "libs/memlib.h"
+#include "thrcommon.h"
+
+#define LIFECYCLE_SIZE 8
+typedef struct {
+ ThreadFunction func;
+ void *data;
+ SDWORD stackSize;
+ Semaphore sem;
+ Thread value;
+#ifdef NAMED_SYNCHRO
+ const char *name;
+#endif
+} SpawnRequest_struct;
+
+typedef SpawnRequest_struct *SpawnRequest;
+
+static Mutex lifecycleMutex;
+static SpawnRequest pendingBirth[LIFECYCLE_SIZE];
+static Thread pendingDeath[LIFECYCLE_SIZE];
+
+void
+InitThreadSystem (void)
+{
+ int i;
+ NativeInitThreadSystem ();
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ pendingBirth[i] = NULL;
+ pendingDeath[i] = NULL;
+ }
+ lifecycleMutex = CreateMutex ("Thread Lifecycle Mutex", SYNC_CLASS_RESOURCE);
+}
+
+void
+UnInitThreadSystem (void)
+{
+ NativeUnInitThreadSystem ();
+ DestroyMutex (lifecycleMutex);
+}
+
+static Thread
+FlagStartThread (SpawnRequest s)
+{
+ int i;
+ LockMutex (lifecycleMutex);
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ if (pendingBirth[i] == NULL)
+ {
+ pendingBirth[i] = s;
+ UnlockMutex (lifecycleMutex);
+ if (s->sem)
+ {
+ Thread result;
+ SetSemaphore (s->sem);
+ DestroySemaphore (s->sem);
+ result = s->value;
+ HFree (s);
+ return result;
+ }
+ return NULL;
+ }
+ }
+ log_add (log_Fatal, "Thread Lifecycle array filled. This is a fatal error! Make LIFECYCLE_SIZE something larger than %d.", LIFECYCLE_SIZE);
+ exit (EXIT_FAILURE);
+}
+
+void
+FinishThread (Thread thread)
+{
+ int i;
+ LockMutex (lifecycleMutex);
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ if (pendingDeath[i] == NULL)
+ {
+ pendingDeath[i] = thread;
+ UnlockMutex (lifecycleMutex);
+ return;
+ }
+ }
+ log_add (log_Fatal, "Thread Lifecycle array filled. This is a fatal error! Make LIFECYCLE_SIZE something larger than %d.", LIFECYCLE_SIZE);
+ exit (EXIT_FAILURE);
+}
+
+/* Only call from main thread! */
+void
+ProcessThreadLifecycles (void)
+{
+ int i;
+ LockMutex (lifecycleMutex);
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ SpawnRequest s = pendingBirth[i];
+ if (s != NULL)
+ {
+#ifdef NAMED_SYNCHRO
+ s->value = NativeCreateThread (s->func, s->data, s->stackSize, s->name);
+#else
+ s->value = NativeCreateThread (s->func, s->data, s->stackSize);
+#endif
+ if (s->sem)
+ {
+ ClearSemaphore (s->sem);
+ /* The spawning thread's FlagStartThread will clean up s */
+ }
+ else
+ {
+ /* The thread value has been lost to the game logic. We must
+ clean up s ourself. */
+ HFree (s);
+ }
+ pendingBirth[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < LIFECYCLE_SIZE; i++)
+ {
+ Thread t = pendingDeath[i];
+ if (t != NULL)
+ {
+ WaitThread (t, NULL);
+ pendingDeath[i] = NULL;
+ DestroyThread (t);
+ }
+ }
+ UnlockMutex (lifecycleMutex);
+}
+
+
+/* The Create routines look different based on whether NAMED_SYNCHRO
+ is defined or not. */
+
+#ifdef NAMED_SYNCHRO
+Thread
+CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->name = name;
+ s->sem = CreateSemaphore (0, "SpawnRequest semaphore", SYNC_CLASS_RESOURCE);
+ return FlagStartThread (s);
+}
+
+void
+StartThread_Core (ThreadFunction func, void *data, SDWORD stackSize, const char *name)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->name = name;
+ s->sem = NULL;
+ FlagStartThread (s);
+}
+
+Mutex
+CreateMutex_Core (const char *name, DWORD syncClass)
+{
+ return NativeCreateMutex (name, syncClass);
+}
+
+Semaphore
+CreateSemaphore_Core (DWORD initial, const char *name, DWORD syncClass)
+{
+ return NativeCreateSemaphore (initial, name, syncClass);
+}
+
+RecursiveMutex
+CreateRecursiveMutex_Core (const char *name, DWORD syncClass)
+{
+ return NativeCreateRecursiveMutex (name, syncClass);
+}
+
+CondVar
+CreateCondVar_Core (const char *name, DWORD syncClass)
+{
+ return NativeCreateCondVar (name, syncClass);
+}
+
+#else
+/* These are the versions of Create* without the names. */
+Thread
+CreateThread_Core (ThreadFunction func, void *data, SDWORD stackSize)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->sem = CreateSemaphore (0, "SpawnRequest semaphore", SYNC_CLASS_RESOURCE);
+ return FlagStartThread (s);
+}
+
+void
+StartThread_Core (ThreadFunction func, void *data, SDWORD stackSize)
+{
+ SpawnRequest s = HMalloc(sizeof (SpawnRequest_struct));
+ s->func = func;
+ s->data = data;
+ s->stackSize = stackSize;
+ s->sem = NULL;
+ FlagStartThread (s);
+}
+
+Mutex
+CreateMutex_Core (void)
+{
+ return NativeCreateMutex ();
+}
+
+Semaphore
+CreateSemaphore_Core (DWORD initial)
+{
+ return NativeCreateSemaphore (initial);
+}
+
+RecursiveMutex
+CreateRecursiveMutex_Core (void)
+{
+ return NativeCreateRecursiveMutex ();
+}
+
+CondVar
+CreateCondVar_Core (void)
+{
+ return NativeCreateCondVar ();
+}
+#endif
+
+void
+DestroyThread (Thread t)
+{
+ NativeDestroyThread (t);
+}
+
+ThreadLocal *
+CreateThreadLocal (void)
+{
+ ThreadLocal *tl = HMalloc (sizeof (ThreadLocal));
+ tl->flushSem = CreateSemaphore (0, "FlushGraphics", SYNC_CLASS_VIDEO);
+ return tl;
+}
+
+void
+DestroyThreadLocal (ThreadLocal *tl)
+{
+ DestroySemaphore (tl->flushSem);
+ HFree (tl);
+}
+
+ThreadLocal *
+GetMyThreadLocal (void)
+{
+ return NativeGetMyThreadLocal ();
+}
+
+void
+WaitThread (Thread thread, int *status)
+{
+ NativeWaitThread (thread, status);
+}
+
+#ifdef DEBUG_SLEEP
+extern uint32 mainThreadId;
+extern uint32 SDL_ThreadID(void);
+#endif /* DEBUG_SLEEP */
+
+void
+HibernateThread (TimePeriod timePeriod)
+{
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() == mainThreadId)
+ log_add (log_Debug, "HibernateThread called from main thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ NativeSleepThread (timePeriod);
+}
+
+void
+HibernateThreadUntil (TimeCount wakeTime)
+{
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() == mainThreadId)
+ log_add (log_Debug, "HibernateThreadUntil called from main "
+ "thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ NativeSleepThreadUntil (wakeTime);
+}
+
+void
+SleepThread (TimePeriod timePeriod)
+{
+ TimeCount now;
+
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() != mainThreadId)
+ log_add (log_Debug, "SleepThread called from non-main "
+ "thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ now = GetTimeCounter ();
+ SleepThreadUntil (now + timePeriod);
+}
+
+// Sleep until wakeTime, but call asynchrounous operations until then.
+void
+SleepThreadUntil (TimeCount wakeTime)
+{
+#ifdef DEBUG_SLEEP
+ if (SDL_ThreadID() != mainThreadId)
+ log_add (log_Debug, "SleepThreadUntil called from non-main "
+ "thread.\n");
+#endif /* DEBUG_SLEEP */
+
+ for (;;) {
+ uint32 nextTimeMs;
+ TimeCount nextTime;
+ TimeCount now;
+
+ Async_process ();
+
+ now = GetTimeCounter ();
+ if (wakeTime <= now)
+ return;
+
+ nextTimeMs = Async_timeBeforeNextMs ();
+ nextTime = (nextTimeMs / 1000) * ONE_SECOND +
+ ((nextTimeMs % 1000) * ONE_SECOND / 1000);
+ // Overflow-safe conversion.
+ if (wakeTime < nextTime)
+ nextTime = wakeTime;
+
+ NativeSleepThreadUntil (nextTime);
+ }
+}
+
+void
+TaskSwitch (void)
+{
+ NativeTaskSwitch ();
+}
+
+void
+DestroyMutex (Mutex sem)
+{
+ NativeDestroyMutex (sem);
+}
+
+void
+LockMutex (Mutex sem)
+{
+ NativeLockMutex (sem);
+}
+
+void
+UnlockMutex (Mutex sem)
+{
+ NativeUnlockMutex (sem);
+}
+
+void
+DestroySemaphore (Semaphore sem)
+{
+ NativeDestroySemaphore (sem);
+}
+
+void
+SetSemaphore (Semaphore sem)
+{
+ NativeSetSemaphore (sem);
+}
+
+void
+ClearSemaphore (Semaphore sem)
+{
+ NativeClearSemaphore (sem);
+}
+
+void
+DestroyCondVar (CondVar cv)
+{
+ NativeDestroyCondVar (cv);
+}
+
+void
+WaitCondVar (CondVar cv)
+{
+ NativeWaitCondVar (cv);
+}
+
+void
+SignalCondVar (CondVar cv)
+{
+ NativeSignalCondVar (cv);
+}
+
+void
+BroadcastCondVar (CondVar cv)
+{
+ NativeBroadcastCondVar (cv);
+}
+
+void
+DestroyRecursiveMutex (RecursiveMutex mutex)
+{
+ NativeDestroyRecursiveMutex (mutex);
+}
+
+void
+LockRecursiveMutex (RecursiveMutex mutex)
+{
+ NativeLockRecursiveMutex (mutex);
+}
+
+void
+UnlockRecursiveMutex (RecursiveMutex mutex)
+{
+ NativeUnlockRecursiveMutex (mutex);
+}
+
+int
+GetRecursiveMutexDepth (RecursiveMutex mutex)
+{
+ return NativeGetRecursiveMutexDepth (mutex);
+}
diff --git a/src/libs/threads/thrcommon.h b/src/libs/threads/thrcommon.h
new file mode 100644
index 0000000..d6d6d41
--- /dev/null
+++ b/src/libs/threads/thrcommon.h
@@ -0,0 +1,28 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_THREADS_THRCOMMON_H_
+#define LIBS_THREADS_THRCOMMON_H_
+
+
+#if defined(THREADLIB_SDL)
+# include "sdl/sdlthreads.h"
+#elif defined(THREADLIB_PTHREAD)
+# include "pthread/posixthreads.h"
+#endif /* defined(THREADLIB_PTHREAD) */
+
+
+#endif /* _THR_COMMON_H */
diff --git a/src/libs/time/Makeinfo b/src/libs/time/Makeinfo
new file mode 100644
index 0000000..14a7ca8
--- /dev/null
+++ b/src/libs/time/Makeinfo
@@ -0,0 +1,3 @@
+uqm_SUBDIRS="sdl"
+uqm_CFILES="timecommon.c"
+uqm_HFILES="timecommon.h"
diff --git a/src/libs/time/sdl/Makeinfo b/src/libs/time/sdl/Makeinfo
new file mode 100644
index 0000000..47180ad
--- /dev/null
+++ b/src/libs/time/sdl/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="sdltime.c"
+uqm_HFILES="sdltime.h"
diff --git a/src/libs/time/sdl/sdltime.c b/src/libs/time/sdl/sdltime.c
new file mode 100644
index 0000000..35ab856
--- /dev/null
+++ b/src/libs/time/sdl/sdltime.c
@@ -0,0 +1,30 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* By Serge van den Boom
+ */
+
+#include "sdltime.h"
+#include "libs/timelib.h"
+
+Uint32
+SDLWrapper_GetTimeCounter (void)
+{
+ Uint32 ticks = SDL_GetTicks ();
+ return (ticks / 1000) * ONE_SECOND + ((ticks % 1000) * ONE_SECOND / 1000);
+ // Use the following instead when confirming "random" lockup bugs (see #668)
+ //return ticks * ONE_SECOND / 1000;
+}
diff --git a/src/libs/time/sdl/sdltime.h b/src/libs/time/sdl/sdltime.h
new file mode 100644
index 0000000..ee72dc9
--- /dev/null
+++ b/src/libs/time/sdl/sdltime.h
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* By Serge van den Boom, 2002-09-12
+ */
+
+#ifndef LIBS_TIME_SDL_SDLTIME_H_
+#define LIBS_TIME_SDL_SDLTIME_H_
+
+#include "port.h"
+#include SDL_INCLUDE(SDL.h)
+#include "../timecommon.h"
+
+#define NativeInitTimeSystem()
+#define NativeUnInitTimeSystem()
+extern Uint32 SDLWrapper_GetTimeCounter (void);
+#define NativeGetTimeCounter() \
+ SDLWrapper_GetTimeCounter ()
+
+
+#endif /* LIBS_TIME_SDL_SDLTIME_H_ */
+
diff --git a/src/libs/time/timecommon.c b/src/libs/time/timecommon.c
new file mode 100644
index 0000000..a281efe
--- /dev/null
+++ b/src/libs/time/timecommon.c
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* By Serge van den Boom, 2002-09-12
+ */
+
+#include <stdio.h>
+#include "libs/timelib.h"
+#include "timecommon.h"
+
+void
+InitTimeSystem (void)
+{
+ NativeInitTimeSystem ();
+}
+
+void
+UnInitTimeSystem (void)
+{
+ NativeUnInitTimeSystem ();
+}
+
+TimeCount
+GetTimeCounter (void)
+{
+ return NativeGetTimeCounter ();
+}
+
diff --git a/src/libs/time/timecommon.h b/src/libs/time/timecommon.h
new file mode 100644
index 0000000..ec5892c
--- /dev/null
+++ b/src/libs/time/timecommon.h
@@ -0,0 +1,30 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* By Serge van den Boom, 2002-09-12
+ */
+
+#ifndef LIBS_TIME_TIMECOMMON_H_
+#define LIBS_TIME_TIMECOMMON_H_
+
+
+#if TIMELIB == SDL
+#include "sdl/sdltime.h"
+#endif /* TIMELIB == SDL */
+
+
+#endif /* LIBS_TIME_TIMECOMMON_H_ */
+
diff --git a/src/libs/timelib.h b/src/libs/timelib.h
new file mode 100644
index 0000000..8fc6522
--- /dev/null
+++ b/src/libs/timelib.h
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_TIMELIB_H_
+#define LIBS_TIMELIB_H_
+
+#define TIMELIB SDL
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* ONE_SECOND is the LCM of all the fractions of a second the game uses.
+ * Battle is 24 FPS, Landers are 35 FPS, most UI-level things are 15 FPS,
+ * The Interplanetary flight is 30 FPS, Comm ambient animation is 40 FPS,
+ * (also Comm Oscilloscope is 32 FPS, but it does not require a stable
+ * timer and currently runs within the Comm ambient anim paradigm anyway)
+ * Thus, the minimum value for ONE_SECOND is 840. */
+#if TIMELIB == SDL
+# define ONE_SECOND 840
+#endif
+
+typedef DWORD TimeCount;
+typedef DWORD TimePeriod;
+
+extern void InitTimeSystem (void);
+extern void UnInitTimeSystem (void);
+extern TimeCount GetTimeCounter (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_TIMLIB_H_ */
diff --git a/src/libs/uio.h b/src/libs/uio.h
new file mode 100644
index 0000000..f455049
--- /dev/null
+++ b/src/libs/uio.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_H_
+#define LIBS_UIO_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "uio/io.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_UIO_H_ */
diff --git a/src/libs/uio/COPYING b/src/libs/uio/COPYING
new file mode 100644
index 0000000..2774fef
--- /dev/null
+++ b/src/libs/uio/COPYING
@@ -0,0 +1,350 @@
+Below the text of the GNU General Public License is quoted verbatim.
+It applies to all files in and below this directory that say so.
+Note that most of these files will say only version 2 of the
+GNU General Public License applies, not any later version.
+
+
+---------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+
diff --git a/src/libs/uio/Makeinfo b/src/libs/uio/Makeinfo
new file mode 100644
index 0000000..9d127fc
--- /dev/null
+++ b/src/libs/uio/Makeinfo
@@ -0,0 +1,22 @@
+uqm_SUBDIRS="stdio"
+uqm_CFILES="charhashtable.c defaultfs.c fileblock.c fstypes.c gphys.c io.c
+ ioaux.c match.c mount.c mounttree.c paths.c physical.c uiostream.c
+ uioutils.c utils.c"
+uqm_HFILES="charhashtable.h defaultfs.h fileblock.h fstypes.h getint.h
+ gphys.h ioaux.h io.h iointrn.h match.h mem.h mount.h mounttree.h
+ paths.h physical.h types.h uioport.h uiostream.h uioutils.h utils.h"
+
+if [ -n "$uqm_USE_ZIP_IO" ]; then
+ uqm_SUBDIRS="$uqm_SUBDIRS zip"
+fi
+
+#if [ -n "$DEBUG" -o -n "$uqm_UIO_DEBUG" ]; then
+ uqm_CFILES="$uqm_CFILES debug.c"
+ uqm_HFILES="$uqm_HFILES debug.h"
+#fi
+
+if [ -n "$MEMDEBUG" ]; then
+ uqm_CFILES="$uqm_CFILES hashtable.c memdebug.c"
+ uqm_HFILES="$uqm_HFILES hashtable.h memdebug.h"
+fi
+
diff --git a/src/libs/uio/charhashtable.c b/src/libs/uio/charhashtable.c
new file mode 100644
index 0000000..df10b2a
--- /dev/null
+++ b/src/libs/uio/charhashtable.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#define HASHTABLE_INTERNAL
+#include "charhashtable.h"
+#include "types.h"
+#include "uioport.h"
+
+static inline uio_uint32 CharHashTable_hash(CharHashTable_HashTable *hashTable,
+ const char *string);
+static inline uio_bool CharHashTable_equal(CharHashTable_HashTable *hashTable,
+ const char *key1, const char *key2);
+static inline char *CharHashTable_copy(CharHashTable_HashTable *hashTable,
+ const char *key);
+static inline void CharHashTable_freeKey(CharHashTable_HashTable *hashTable,
+ char *key);
+
+#include "hashtable.c"
+
+
+static inline uio_uint32
+CharHashTable_hash(CharHashTable_HashTable *hashTable, const char *key) {
+ uio_uint32 hash;
+
+ (void) hashTable;
+ // Rotating hash, variation of something on the web which
+ // wasn't original itself.
+ hash = 0;
+ // Hash was on that web page initialised as the length,
+ // but that isn't known at this time.
+ while (*key != '\0') {
+ hash = (hash << 4) ^ (hash >> 28) ^ *key;
+ key++;
+ }
+ return hash ^ (hash >> 10) ^ (hash >> 20);
+}
+
+static inline uio_bool
+CharHashTable_equal(CharHashTable_HashTable *hashTable,
+ const char *key1, const char *key2) {
+ (void) hashTable;
+ return strcmp(key1, key2) == 0;
+}
+
+static inline char *
+CharHashTable_copy(CharHashTable_HashTable *hashTable,
+ const char *key) {
+ (void) hashTable;
+ return uio_strdup(key);
+}
+
+static inline void
+CharHashTable_freeKey(CharHashTable_HashTable *hashTable,
+ char *key) {
+ (void) hashTable;
+ uio_free(key);
+}
+
+
diff --git a/src/libs/uio/charhashtable.h b/src/libs/uio/charhashtable.h
new file mode 100644
index 0000000..3cd0add
--- /dev/null
+++ b/src/libs/uio/charhashtable.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_CHARHASHTABLE_H_
+#define LIBS_UIO_CHARHASHTABLE_H_
+
+
+#define HASHTABLE_(identifier) CharHashTable ## _ ## identifier
+typedef char HASHTABLE_(Key);
+typedef void HASHTABLE_(Value);
+#define CharHashTable_HASH CharHashTable_hash
+#define CharHashTable_EQUAL CharHashTable_equal
+#define CharHashTable_COPY CharHashTable_copy
+#define CharHashTable_FREEKEY CharHashTable_freeKey
+#define CharHashTable_FREEVALUE(hashTable, value) \
+ ((void) (hashTable), (void) (value))
+
+#include "hashtable.h"
+
+
+#endif /* _CHARHASHTABLE_H */
+
diff --git a/src/libs/uio/debug.c b/src/libs/uio/debug.c
new file mode 100644
index 0000000..66bab47
--- /dev/null
+++ b/src/libs/uio/debug.c
@@ -0,0 +1,914 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <time.h>
+#if defined(__unix__) && !defined(_WIN32_WCE)
+# include <sys/wait.h>
+#endif
+
+#include "debug.h"
+#include "uioport.h"
+#include "io.h"
+#include "utils.h"
+#include "types.h"
+#include "mem.h"
+#include "uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+#define LINEBUFLEN 1024
+
+typedef struct DebugContext {
+ uio_bool exit;
+ FILE *in;
+ FILE *out;
+ FILE *err;
+ uio_DirHandle *cwd;
+} DebugContext;
+
+typedef struct {
+ const char *name;
+ int (*fun)(DebugContext *, int, char *[]);
+} DebugCommand;
+
+#ifdef STAND_ALONE
+void initRepository(void);
+void unInitRepository(void);
+#endif
+
+static int debugCmdCat(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdCd(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdExec(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdExit(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdLs(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMem(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMount(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdMv(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdRm(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdStat(DebugContext *debugContext, int argc, char *argv[]);
+static int debugCmdWriteTest(DebugContext *debugContext, int argc,
+ char *argv[]);
+static int debugCmdFwriteTest(DebugContext *debugContext, int argc,
+ char *argv[]);
+
+static void makeArgs(char *lineBuf, int *argc, char ***argv);
+static int debugCallCommand(DebugContext *debugContext,
+ int argc, char *argv[]);
+uio_MountHandle *
+debugMountOne(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative);
+
+// In alphabetic order:
+DebugCommand debugCommands[] = {
+ { "cat", debugCmdCat },
+ { "cd", debugCmdCd },
+ { "exec", debugCmdExec },
+ { "exit", debugCmdExit },
+ { "fwritetest", debugCmdFwriteTest },
+ { "ls", debugCmdLs },
+ { "mem", debugCmdMem },
+ { "mkdir", debugCmdMkDir },
+ { "mount", debugCmdMount },
+ { "mv", debugCmdMv },
+ { "pwd", debugCmdPwd },
+ { "rm", debugCmdRm },
+ { "rmdir", debugCmdRmDir },
+ { "stat", debugCmdStat },
+ { "writetest", debugCmdWriteTest },
+};
+
+
+#ifndef STAND_ALONE
+extern uio_Repository *repository;
+#else
+uio_Repository *repository;
+uio_MountHandle *mountHandles[9]; // TODO: remove (this is just a test)
+
+int
+main(int argc, char *argv[]) {
+ initRepository();
+ uio_debugInteractive(stdin, stdout, stderr);
+ unInitRepository();
+ (void) argc;
+ (void) argv;
+ return EXIT_SUCCESS;
+}
+
+uio_MountHandle *
+debugMountOne(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative) {
+ uio_MountHandle *mountHandle;
+
+ mountHandle = uio_mountDir(destRep, mountPoint, fsType, sourceDir,
+ sourcePath, inPath, autoMount, flags, relative);
+ if (mountHandle == NULL) {
+ int savedErrno = errno;
+ fprintf(stderr, "Could not mount '%s' and graft '%s' from that "
+ "into the repository at '%s': %s\n",
+ sourcePath, inPath, mountPoint, strerror(errno));
+ errno = savedErrno;
+ }
+ return mountHandle;
+}
+
+void
+initRepository(void) {
+ static uio_AutoMount autoMountZip = {
+ .pattern = "*.zip",
+ .matchType = match_MATCH_SUFFIX,
+ .fileSystemID = uio_FSTYPE_ZIP,
+ .mountFlags = uio_MOUNT_BELOW | uio_MOUNT_RDONLY
+ };
+ static uio_AutoMount *autoMount[] = {
+ &autoMountZip,
+ NULL
+ };
+
+ uio_init();
+ repository = uio_openRepository(0);
+
+ memset(&mountHandles, '\0', sizeof mountHandles);
+#if 1
+ mountHandles[0] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/home/svdb/cvs/sc2/content", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[1] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/home/svdb/cvs/sc2/src/sc2code/ships",
+ autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[2] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/tmp/vfstest", autoMount, uio_MOUNT_TOP, NULL);
+#endif
+#if 1
+ mountHandles[3] = debugMountOne(repository, "/", uio_FSTYPE_STDIO,
+ NULL, NULL, "/tmp/vfstest2", autoMount, uio_MOUNT_TOP, NULL);
+#endif
+
+ // TODO: should work too:
+#if 0
+ mountHandle[4] = debugMountOne(repository, "/zip/", uio_FSTYPE_ZIP, NULL,
+ NULL, "/ziptest/foo.zip", autoMount, uio_MOUNT_TOP, NULL);
+#endif
+ {
+ uio_DirHandle *rootDir;
+ rootDir = uio_openDir(repository, "/", 0);
+ if (rootDir == NULL) {
+ fprintf(stderr, "Could not open '/' dir.\n");
+ } else {
+#if 1
+ mountHandles[4] = debugMountOne(repository, "/example/",
+ uio_FSTYPE_ZIP, rootDir, "/example2.zip", "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[5] = debugMountOne(repository, "/example/",
+ uio_FSTYPE_ZIP, rootDir, "/example/example.zip", "/",
+ autoMount, uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[6] = debugMountOne(repository, "/zip/",
+ uio_FSTYPE_ZIP, rootDir, "/voice.zip", "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+#if 1
+ mountHandles[7] = debugMountOne(repository, "/foo/",
+ uio_FSTYPE_ZIP, rootDir, "/foo2.zip", "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+ uio_closeDir(rootDir);
+ }
+ }
+ mountHandles[8] = debugMountOne(repository, "/tmp/",
+ uio_FSTYPE_STDIO, NULL, NULL, "/tmp/", autoMount,
+ uio_MOUNT_TOP, NULL);
+
+#if 1
+ mountHandles[8] = debugMountOne(repository, "/root/root/",
+ uio_FSTYPE_STDIO, NULL, NULL, "/", autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+#endif
+
+}
+
+void
+unInitRepository(void) {
+#if 1
+ int i;
+// uio_printMountTree(stderr, repository->mountTree, 0);
+// fprintf(stderr, "\n");
+ for (i = 7; i >= 0; i--) {
+ if (mountHandles[i] != NULL)
+ uio_unmountDir(mountHandles[i]);
+// uio_printMountTree(stderr, repository->mountTree, 0);
+// uio_printMounts(stderr, repository);
+// fprintf(stderr, "\n");
+ }
+#endif
+ uio_closeRepository(repository);
+ uio_unInit();
+}
+#endif /* STAND_ALONE */
+
+void
+uio_debugInteractive(FILE *in, FILE *out, FILE *err) {
+ char lineBuf[LINEBUFLEN];
+ size_t lineLen;
+ int argc;
+ char **argv;
+ DebugContext debugContext;
+ uio_bool interactive;
+
+ memset(&debugContext, '\0', sizeof (DebugContext));
+ debugContext.exit = false;
+ debugContext.in = in;
+ debugContext.out = out;
+ debugContext.err = err;
+ debugContext.cwd = uio_openDir(repository, "/", 0);
+ if (debugContext.cwd == NULL) {
+ fprintf(err, "Fatal: Could not open working dir.\n");
+ abort();
+ }
+
+ interactive = isatty(fileno(in));
+ do {
+ if (interactive)
+ fprintf(out, "> ");
+ if (fgets(lineBuf, LINEBUFLEN, in) == NULL) {
+ if (feof(in)) {
+ // user pressed ^D
+ break;
+ }
+ // error occured
+ clearerr(in);
+ continue;
+ }
+ lineLen = strlen(lineBuf);
+ if (lineBuf[lineLen - 1] != '\n' && lineBuf[lineLen - 1] != '\r') {
+ fprintf(err, "Too long command line.\n");
+ // TODO: read until EOL
+ continue;
+ }
+ makeArgs(lineBuf, &argc, &argv);
+ if (argc == 0) {
+ uio_free(argv);
+ continue;
+ }
+ debugCallCommand(&debugContext, argc, argv);
+ uio_free(argv);
+ } while (!debugContext.exit);
+ if (interactive)
+ fprintf(out, "\n");
+ uio_closeDir(debugContext.cwd);
+}
+
+static void
+makeArgs(char *lineBuf, int *argc, char ***argv) {
+ int numArg;
+ char **args;
+ char *ptr;
+
+ numArg = 0;
+ ptr = lineBuf;
+ while(true) {
+ while (isspace((int) *ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ numArg++;
+ while (!isspace((int) *ptr))
+ ptr++;
+ }
+
+ args = uio_malloc((numArg + 1) * sizeof (char *));
+ numArg = 0;
+ ptr = lineBuf;
+ while(true) {
+ while (isspace((int) *ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ args[numArg] = ptr;
+ numArg++;
+ while (!isspace((int) *ptr))
+ ptr++;
+ if (*ptr == '\0')
+ break;
+ *ptr = '\0';
+ ptr++;
+ }
+ args[numArg] = NULL;
+ *argv = args;
+ *argc = numArg;
+}
+
+static int
+debugCallCommand(DebugContext *debugContext, int argc, char *argv[]) {
+ int i;
+ int numDebugCommands;
+
+ i = 0;
+ numDebugCommands = sizeof debugCommands / sizeof debugCommands[0];
+ // could be improved with binary search
+ while(1) {
+ if (i == numDebugCommands) {
+ fprintf(debugContext->err, "Invalid command.\n");
+ return 1;
+ }
+ if (strcmp(argv[0], debugCommands[i].name) == 0)
+ break;
+ i++;
+ }
+ return debugCommands[i].fun(debugContext, argc, argv);
+}
+
+static int
+debugCmdCat(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_Handle *handle;
+#define READBUFSIZE 0x10000
+ char readBuf[READBUFSIZE];
+ char *bufPtr;
+ ssize_t numInBuf, numWritten;
+ size_t totalWritten;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ handle = uio_open(debugContext->cwd, argv[1], O_RDONLY
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (handle == NULL) {
+ fprintf(debugContext->err, "Could not open file: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ totalWritten = 0;
+ while (1) {
+ numInBuf = uio_read(handle, readBuf, READBUFSIZE);
+ if (numInBuf == -1) {
+ if (errno == EINTR)
+ continue;
+ fprintf(debugContext->err, "Could not read from file: %s\n",
+ strerror(errno));
+ uio_close(handle);
+ return 1;
+ }
+ if (numInBuf == 0)
+ break;
+ bufPtr = readBuf;
+ do {
+ numWritten = write(fileno(debugContext->out), bufPtr, numInBuf);
+ if (numWritten == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ fprintf(debugContext->err, "Could not read from file: %s\n",
+ strerror(errno));
+ uio_close(handle);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ totalWritten += numWritten;
+ } while (numInBuf > 0);
+ }
+ fprintf(debugContext->out, "[%u bytes]\n", (unsigned int) totalWritten);
+
+ uio_close(handle);
+ return 0;
+}
+
+static int
+debugCmdCd(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_DirHandle *newWd;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+ newWd = uio_openDirRelative(debugContext->cwd, argv[1], 0);
+ if (newWd == NULL) {
+ fprintf(debugContext->err, "Could not access new dir: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ uio_closeDir(debugContext->cwd);
+ debugContext->cwd = newWd;
+ return 0;
+}
+
+static int
+debugCmdExec(DebugContext *debugContext, int argc, char *argv[]) {
+ int i;
+ const char **newArgs;
+ int errCode = 0;
+ uio_StdioAccessHandle **handles;
+ uio_DirHandle *tempDir;
+
+ if (argc < 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ tempDir = uio_openDirRelative(debugContext->cwd, "/tmp", 0);
+ if (tempDir == 0) {
+ fprintf(debugContext->err, "Could not open temp dir: %s.\n",
+ strerror(errno));
+ return 1;
+ }
+
+ newArgs = uio_malloc(argc * sizeof (char *));
+ newArgs[0] = argv[1];
+ handles = uio_malloc(argc * sizeof (uio_StdioAccessHandle *));
+ handles[0] = NULL;
+
+ for (i = 2; i < argc; i++) {
+#if 0
+ if (argv[i][0] == '-') {
+ // Don't try to parse arguments that start with '-'.
+ // They are probably option flags.
+ newArgs[i - 1] = argv[i];
+ }
+#endif
+ handles[i - 1] = uio_getStdioAccess(debugContext->cwd, argv[i],
+ O_RDONLY, tempDir);
+ if (handles[i - 1] == NULL) {
+ if (errno == ENOENT) {
+ // No match; we keep what's typed literally.
+ newArgs[i - 1] = argv[i];
+ continue;
+ }
+
+ // error
+ fprintf(debugContext->err,
+ "Cannot execute: Cannot get stdio access to %s: %s.\n",
+ argv[i], strerror(errno));
+ errCode = 1;
+ argc = i + 1;
+ goto err;
+ }
+
+ newArgs[i - 1] = uio_StdioAccessHandle_getPath(handles[i - 1]);
+ }
+ newArgs[argc - 1] = NULL;
+
+ fprintf(debugContext->err, "Executing: %s", newArgs[0]);
+ for (i = 1; i < argc - 1; i++)
+ fprintf(debugContext->err, " %s", newArgs[i]);
+ fprintf(debugContext->err, "\n");
+
+#if defined(__unix__) && !defined(_WIN32_WCE)
+ {
+ pid_t pid;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ fprintf(debugContext->err, "Error: fork() failed: %s.\n",
+ strerror(errno));
+ break;
+ case 0:
+ // child
+ execvp(newArgs[0], (char * const *) newArgs);
+ fprintf(debugContext->err, "Error: execvp() failed: %s.\n",
+ strerror(errno));
+ _exit(EXIT_FAILURE);
+ break;
+ default: {
+ // parent
+ int status;
+ pid_t retVal;
+
+ while (1) {
+ retVal = waitpid(pid, &status, 0);
+ if (retVal != -1)
+ break;
+ if (errno != EINTR) {
+ fprintf(debugContext->err, "Error: waitpid() "
+ "failed: %s\n", strerror(errno));
+ break;
+ }
+ }
+ if (retVal == -1)
+ break;
+
+ if (WIFEXITED(status)) {
+ fprintf(debugContext->err, "Exit status: %d\n",
+ WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ fprintf(debugContext->err, "Terminated on signal %d.\n",
+ WTERMSIG(status));
+ } else {
+ fprintf(debugContext->err, "Error: weird exit status.\n");
+ }
+ break;
+ }
+ }
+ }
+#else
+ fprintf(debugContext->err, "Cannot execute: not supported on this "
+ "platform.\n");
+#endif
+
+err:
+ for (i = 1; i < argc - 1; i++) {
+ if (handles[i] != NULL)
+ uio_releaseStdioAccess(handles[i]);
+ }
+ uio_free(handles);
+ uio_free((void *) newArgs);
+ uio_closeDir(tempDir);
+
+ return errCode;
+}
+
+static int
+debugCmdExit(DebugContext *debugContext, int argc, char *argv[]) {
+ debugContext->exit = true;
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdFwriteTest(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_Stream *stream;
+ const char testString[] = "0123456789\n";
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ stream = uio_fopen(debugContext->cwd, argv[1], "w+b");
+ if (stream == NULL) {
+ fprintf(debugContext->err, "Could not open file: %s\n",
+ strerror(errno));
+ goto err;
+ }
+
+ if (uio_fwrite(testString, strlen(testString), 1, stream) != 1) {
+ fprintf(debugContext->err, "uio_fwrite() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fputs(testString, stream) == EOF) {
+ fprintf(debugContext->err, "uio_fputs() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fseek(stream, 15, SEEK_SET) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fputc('A', stream) != 'A') {
+ fprintf(debugContext->err, "uio_fputc() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fseek(stream, 0, SEEK_SET) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ {
+ char buf[6];
+ char *ptr;
+ int i;
+ i = 1;
+ while (1) {
+ ptr = uio_fgets(buf, sizeof buf, stream);
+ if (ptr == NULL)
+ break;
+ fprintf(debugContext->out, "%d: [%s]\n", i, ptr);
+ i++;
+ }
+ if (uio_ferror(stream)) {
+ fprintf(debugContext->err, "uio_fgets() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ uio_clearerr(stream);
+ }
+ if (uio_fseek(stream, 4, SEEK_END) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ {
+ char buf[2000];
+ memset(buf, 'Q', sizeof buf);
+ if (uio_fwrite(buf, 100, 20, stream) != 20) {
+ fprintf(debugContext->err, "uio_fwrite() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ }
+ if (uio_fseek(stream, 5, SEEK_SET) != 0) {
+ fprintf(debugContext->err, "uio_fseek() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ if (uio_fputc('B', stream) != 'B') {
+ fprintf(debugContext->err, "uio_fputc() failed: %s\n",
+ strerror(errno));
+ goto err;
+ }
+ uio_fclose(stream);
+ return 0;
+err:
+ uio_fclose(stream);
+ return 1;
+}
+
+static int
+listOneDir(DebugContext *debugContext, const char *arg) {
+ uio_DirList *dirList;
+ int i;
+ const char *pattern;
+ const char *cpath;
+ char *buf = NULL;
+
+ if (arg[0] == '\0') {
+ cpath = arg;
+ pattern = "*";
+ } else {
+ pattern = strrchr(arg, '/');
+ if (pattern == NULL) {
+ // No directory component in 'arg'.
+ cpath = "";
+ pattern = arg;
+ } else if (pattern[1] == '\0') {
+ // 'arg' ends on /
+ cpath = arg;
+ pattern = "*";
+ } else {
+ if (pattern == arg) {
+ cpath = "/";
+ } else {
+ buf = uio_malloc(pattern - arg + 1);
+ memcpy(buf, arg, pattern - arg);
+ buf[pattern - arg] = '\0';
+ cpath = buf;
+ }
+ pattern++;
+ }
+ }
+#ifdef HAVE_GLOB
+ dirList = uio_getDirList(debugContext->cwd, cpath, pattern,
+ match_MATCH_GLOB);
+#else
+ if (pattern[0] == '*' && pattern[1] == '\0') {
+ dirList = uio_getDirList(debugContext->cwd, cpath, "",
+ match_MATCH_PREFIX);
+ } else {
+ dirList = uio_getDirList(debugContext->cwd, cpath, pattern,
+ match_MATCH_LITERAL);
+ }
+#endif
+ if (dirList == NULL) {
+ fprintf(debugContext->out, "Error in uio_getDirList(): %s.\n",
+ strerror(errno));
+ if (buf != NULL)
+ uio_free(buf);
+ return 1;
+ }
+ for (i = 0; i < dirList->numNames; i++)
+ fprintf(debugContext->out, "%s\n", dirList->names[i]);
+ uio_DirList_free(dirList);
+ if (buf != NULL)
+ uio_free(buf);
+ return 0;
+}
+
+static int
+debugCmdLs(DebugContext *debugContext, int argc, char *argv[]) {
+ int argI;
+ int retVal;
+
+ if (argc == 1)
+ return listOneDir(debugContext, unconst(""));
+
+ for (argI = 1; argI < argc; argI++) {
+ retVal = listOneDir(debugContext, argv[argI]);
+ if (retVal != 0)
+ return retVal;
+ }
+
+ return 0;
+}
+
+static int
+debugCmdMem(DebugContext *debugContext, int argc, char *argv[]) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_printPointers(debugContext->out);
+#else
+ fprintf(debugContext->out, "Memory debugging not compiled in.\n");
+#endif
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdMkDir(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_mkdir(debugContext->cwd, argv[1], 0777);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not create directory: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdMount(DebugContext *debugContext, int argc, char *argv[]) {
+ if (argc == 1) {
+ uio_printMounts(debugContext->out, repository);
+// uio_printMountTree(debugContext->out, repository->mountTree, 0);
+ }
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdMv(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 3) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_rename(debugContext->cwd, argv[1],
+ debugContext->cwd, argv[2]);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not rename: %s\n", strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdPwd(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_DirHandle_print(debugContext->cwd, debugContext->out);
+ (void) argc;
+ (void) argv;
+ return 0;
+}
+
+static int
+debugCmdRm(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_unlink(debugContext->cwd, argv[1]);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not remove file: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdRmDir(DebugContext *debugContext, int argc, char *argv[]) {
+ int retVal;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ retVal = uio_rmdir(debugContext->cwd, argv[1]);
+ if (retVal == -1) {
+ fprintf(debugContext->err, "Could not remove directory: %s\n",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+static int
+debugCmdStat(DebugContext *debugContext, int argc, char *argv[]) {
+ struct stat statBuf;
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ if (uio_stat(debugContext->cwd, argv[1], &statBuf) == -1) {
+ // errno is set
+ int savedErrno;
+ savedErrno = errno;
+ fprintf(debugContext->err, "Could not stat file: %s\n",
+ strerror(errno));
+ errno = savedErrno;
+ return 1;
+ }
+
+ fprintf(debugContext->out,
+ "size %ld bytes\n"
+ "uid %d gid %d mode 0%o\n",
+ (unsigned long) statBuf.st_size,
+ (unsigned int) statBuf.st_uid,
+ (unsigned int) statBuf.st_gid,
+ (unsigned int) statBuf.st_mode &
+ (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID));
+ // Can't do these next three in one fprintf, as ctime uses a static buffer
+ // that is overwritten with each call.
+ fprintf(debugContext->out,
+ "last access: %s", ctime(&statBuf.st_atime));
+ fprintf(debugContext->out,
+ "last modification: %s", ctime(&statBuf.st_mtime));
+ fprintf(debugContext->out,
+ "last status change: %s", ctime(&statBuf.st_ctime));
+
+ return 0;
+}
+
+static int
+debugCmdWriteTest(DebugContext *debugContext, int argc, char *argv[]) {
+ uio_Handle *handle;
+ const char testString[] = "Hello world!\n";
+
+ if (argc != 2) {
+ fprintf(debugContext->err, "Invalid number of arguments.\n");
+ return 1;
+ }
+
+ handle = uio_open(debugContext->cwd, argv[1],
+ O_WRONLY | O_CREAT | O_EXCL
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0644);
+ if (handle == NULL) {
+ fprintf(debugContext->err, "Could not open file: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ if (uio_write(handle, testString, sizeof testString) == -1) {
+ fprintf(debugContext->err, "Write failed: %s\n",
+ strerror(errno));
+ return 1;
+ }
+
+ uio_close(handle);
+ return 0;
+}
+
+
diff --git a/src/libs/uio/debug.h b/src/libs/uio/debug.h
new file mode 100644
index 0000000..fd6a3ff
--- /dev/null
+++ b/src/libs/uio/debug.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef UIO_DEBUG_H
+#define UIO_DEBUG_H
+
+void uio_debugInteractive(FILE *in, FILE *out, FILE *err);
+
+
+#endif /* _DEBUG_H */
+
+
diff --git a/src/libs/uio/defaultfs.c b/src/libs/uio/defaultfs.c
new file mode 100644
index 0000000..bf6d247
--- /dev/null
+++ b/src/libs/uio/defaultfs.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "defaultfs.h"
+#include "uioport.h"
+
+extern uio_FileSystemHandler stdio_fileSystemHandler;
+#ifdef HAVE_ZIP
+extern uio_FileSystemHandler zip_fileSystemHandler;
+#endif
+
+const uio_DefaultFileSystemSetup defaultFileSystems[] = {
+ { uio_FSTYPE_STDIO, "stdio", &stdio_fileSystemHandler },
+#ifdef HAVE_ZIP
+ { uio_FSTYPE_ZIP, "zip", &zip_fileSystemHandler },
+#endif
+};
+
+int
+uio_numDefaultFileSystems(void) {
+ return sizeof defaultFileSystems / sizeof defaultFileSystems[0];
+}
+
+
diff --git a/src/libs/uio/defaultfs.h b/src/libs/uio/defaultfs.h
new file mode 100644
index 0000000..711ac0c
--- /dev/null
+++ b/src/libs/uio/defaultfs.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef uio_DEFAULTFS_H
+#define uio_DEFAULTFS_H
+
+typedef struct uio_DefaultFileSystemSetup uio_DefaultFileSystemSetup;
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "fstypes.h"
+
+struct uio_DefaultFileSystemSetup {
+ uio_FileSystemID id;
+ const char *name;
+ uio_FileSystemHandler *handler;
+};
+
+extern const uio_DefaultFileSystemSetup defaultFileSystems[];
+
+int uio_numDefaultFileSystems(void);
+
+#endif /* uio_DEFAULTFS_H */
+
diff --git a/src/libs/uio/doc/basics b/src/libs/uio/doc/basics
new file mode 100644
index 0000000..e04afd8
--- /dev/null
+++ b/src/libs/uio/doc/basics
@@ -0,0 +1,178 @@
+-= Introduction =-
+
+The file io system I present here provides the user with a virtual file
+systems on which normal POSIX-like file operations can be performed.
+
+A virtual file system consists of a number of underlying filesystems merged
+together in one directory structure.
+Underlying file systems can be POSIX filesystems, or zip archives, but
+others can be added easilly.
+Filesystems are grafted into the virtual filesystem either explicitely, by
+mounting, or implicitely, by having files of a specific type (for instance
+those ending on .zip) mounted automatically.
+When a filesystem is mounted to a directory which already exists in an
+earlier mounted file system, files in the later mounted file system hide
+files in an earlier mounted file system. Files present in a filesystem
+mounted earlier that don't exist in a filesystem mounted later will remain
+visible.
+Accessing compressed files inside compressed files is possible, though slow.
+
+
+-= Nomenclature =-
+'repository'
+A collection of files from various sources as a virtual file system.
+
+'physical directory structure'
+An underlying filesystem, such as a POSIX filesystem, or a .zip archive.
+
+'logical directory structure'
+The merger of one or more Physical directory structures.
+
+'mounting'
+Grafting a physical directory structure in a logical directory system.
+
+When mounting several dirs on top of eachother, I refer to later mounted
+dirs as 'higher' dirs, and earlier mounted dirs as 'lower' dirs.
+
+'directory entry'
+A file or subdirectory within a directory.
+
+
+-= API =-
+
+Types:
+uio_Repository - A struct describing a repository
+uio_Handle - A handle for working on files
+uio_DirHandle - A handle for working on directories
+
+
+TODO: functions
+
+
+-= Behaviour relating directory structures =-
+
+The design of the virtual filesystem is guided by the following rules:
+- Combined directories contain the entries of each directory.
+ If some of the source directories contain an entry with the same name,
+ the combined directory will contain the entry of the topmost directory.
+ (this means a directory can hide a file and the other way around)
+- Entries hidden in this way are never accessable.
+- Where possible, actions on directory entries within a combined directory
+ are done as in a normal file system.
+Because of these, some design decisions have been made:
+- New entries are created in the topmost writable filesystem. If the path to
+ the directory where the entry is to be created does not exist, it is
+ created [1].
+- When a file is to be opened for writing, and the file in the combined
+ filesystem exists, but is not writable, and there exists a writable
+ filesystem, mounted higher than the filesystem where the existing file
+ resides, the file is first copied to the topmost writable filesystem.
+
+[1] I could have decided to use a pre-existing writable directory if one
+ is available.
+ With this choice, when no such pre-existing directory exists,
+ it would make sense to complete the path in the writable filesystem
+ in which the part of the path that does exist is the longest.
+ As you can't create a directory inside a specific filesystem,
+ this would complicate and confuse things for the user.
+
+
+In specific, for various actions:
+
+opening a file:
+- if O_RDONLY:
+ - open the file in the highest dir.
+- if O_WRONLY or O_RDWR:
+ - if file already exists:
+ - if O_CREAT and O_EXCL: return EEXIST
+ - if file is writable, use that one
+ - if file is not writable, copy it to the highest writable location
+ higher than the location of that file (don't bother if O_TRUNC) and use
+ the new file. If necessary, create the path leading upto it.
+ If no such location exists, return EACCESS.
+ - if file does not exist:
+ - if not O_CREAT: return ENOENT
+ - if the path to the file does not exists, return ENOENT.
+ - find the highest writable dir and open the file there, creating
+ the path leading upto it if necessary.
+
+removing a file:
+- try removing the specified file from all physical directory structures
+ for a repository.
+- once a file is encountered that can't be removed, return an error for
+ that and don't try the rest.
+
+creating a directory:
+- as for opening a file with O_WRONLY | O_CREAT | O_EXCL
+
+removing a directory:
+- as for removing a file
+ (but a physical directory not being empty is a reason for failure)
+
+
+-= Limitations =-
+
+There's no limit to the length of a path in the logical file system.
+Paths in the underlying physical filesystem can be limited though.
+
+At the moment, the system is not thread safe. Only one thread should access
+a repository at once. Seperate threads working on seperate repositories is
+no problem, as long as the repositories don't overlap.
+
+
+-= Internals =-
+
+Types:
+
+uio_MountTree
+ A node in a data structure describing the mounted directories.
+
+uio_PRoot
+ A struct describing the a physical file system.
+
+uio_PRootExtra
+ Filesystem-dependant extra data for a PRoot.
+
+uio_GPRoot
+ Generic filesystem-dependant data for a PRoot, used as uio_PRootExtra.
+
+uio_GPRootExtra
+ Extra filesystem-dependant data for a PRoot, when using uio_GPRoot for
+ generic filesystem-dependant data.
+
+uio_GPDir
+ Generic structure representing a node in a physical directory structure
+ describing one directory.
+
+uio_GPFile
+ Generic structure describing a file in a physical file system.
+
+
+Helper functions (defined in ioaux.c):
+
+uio_copyFilePhysical
+ Copy a file from one physical directory to another.
+
+uio_getPathPhysicalDirs
+ Get handles to the (existing) physical dirs that are effective in a
+ path 'path' relative from 'dirHandle'
+
+uio_getPhysicalAccess
+ Find PDirHandle and MountInfo structures for reading and writing for a path
+ from a DirHandle.
+
+uio_makePath
+ Create a directory inside a physical directory. All non-existant
+ parent directories will be created as well.
+
+uio_resolvePath
+ Determine the absolute path given a path relative to a given directory.
+
+uio_verifyPath
+ Test whether a path is valid and exists.
+
+uio_walkPhysicalPath
+ Follow a path starting from a specified physical dir for as long as
+ possible.
+
+
diff --git a/src/libs/uio/doc/conventions b/src/libs/uio/doc/conventions
new file mode 100644
index 0000000..f156101
--- /dev/null
+++ b/src/libs/uio/doc/conventions
@@ -0,0 +1,30 @@
+This file describes the various conventions used in the source.
+
+
+-= Naming of constructors and destructors =-
+
+uio_Thing *uio_Thing_new(args)
+ Allocates the structure, and initialises it from the arguments.
+ Arguments are always used as-is; if they are reference-counted,
+ and the caller still needs a reference, it is up to the caller
+ to increment the reference counter.
+void uio_Thing_delete(uio_Thing *thing)
+ Frees the structure and all sub-structures.
+ Decrements reference counters to shared structures.
+uio_Thing *uio_Thing_alloc()
+ Allocates memory for a new structure.
+ Could be omited for standard allocators.
+ Particularly relevant when a non-standard allocation scheme is
+ used (for instance, allocation from a pool of pre-allocated strucures).
+void uio_Thing_free(Thing *thing)
+ Frees the memory allocated for thing (reverse of uio_Thing_alloc).
+ Could be omited for standard deallocators.
+void uio_Thing_ref(Thing *thing)
+ Increments the reference counter to the structure.
+void uio_Thing_unref(Thing *thing)
+ Decrements the reference counter to the structure.
+ Calls uio_Thing_Delete() if the refcounter becomes 0.
+
+These functions are always declared and defined in this order.
+
+
diff --git a/src/libs/uio/doc/todo b/src/libs/uio/doc/todo
new file mode 100644
index 0000000..b9f232c
--- /dev/null
+++ b/src/libs/uio/doc/todo
@@ -0,0 +1,144 @@
+Needed for use in UQM:
+- documentation
+- configuring for GLOB
+- when doing uio_getStdioPhysical(), if write access is required, but
+ not available on the original location, also copy the file to the
+ temporary dir.
+- Call fsync() at appropriate times.
+
+Documentation:
+- use doxygen
+- Warning: getting (part of) the contents of a directory is fairly slow,
+ as dirs need to be merged.
+- It would be (theoretically) possible to add HTTP and FTP support for
+ remote file systems.
+- on adding extra file system types:
+ open, mkdir, rmdir, and unlink should themselves make sure that
+ the physical structure is kept up to date when an entry is
+ removed or added.
+- stream stuff is not thread safe
+- uio_fflush() does not accept NULL as argument to flush all streams,
+ like stdio fflush() does.
+- uio_close() does never fail
+- The physical open function should call uio_Handle_new and store its own
+ data in it.
+ The physical close function should delete its own data. It's called
+ Will cleanup the general Handle.
+ Analogous for mount/unmount with PRoot
+- No need to store MountHandles. They will be automatically freed
+ when the repository is closed.
+- You can use mount stuff from other repositories.
+- ".." works by nullifying one path component, not by following the ".." link
+ in the directory. "/a/../b" will always be functionally equivalent to
+ "/b", even when "/a" is a symlink.
+
+Testing:
+- Test mounting an UNC directory
+
+Bugs:
+- 'openDir(repository, "dir/")' will have the trailing '/'
+ in the dirHandle, which will cause problems.
+- 'openDirRelative(repository, "/")' causes segfaults later on
+- uio_rename() doesn't work on directories
+- uio_getPhysicalAccess() needs to be changed so that it works the same on
+ dirs as on files. stat() can be cleaned up too then.
+- uio_getPhysicalAccess() will not return ENOENT when (only) the last
+ component does not exist, even when O_RDONLY is used.
+ For O_RDRW, O_CREAT should probably be checked too (function description
+ needs to be updated too then).
+- A lot of inlining is not possible because of the order of function
+ definitions.
+- sizeof(size_t) may be less than 4 on some platforms. Check what problems
+ this may cause. At least the size of zip_INPUT_BUFFER_SIZE is an
+ issue. SIZE_MAX can be used to check for sizeof(size_t) at runtime.
+- remove() is probably more compatible than unlink(). remove() is part
+ of the C standard (as well as POSIX), unlink() is just POSIX.
+- seeking in files opened as "text" on Windows goes wrong.
+- Network paths on Windows are not accepted.
+- No CRLF translation (and ^Z recognition) is done for files read from
+ zip files, even though that may be expected on Windows.
+- The order in which "uio_unmountAllDirs() unmounts the directories, may lead
+ it to unmount a dir while there still is a file descriptor for another
+ dir open, causing a warning.
+
+Extra features (not necessary for UQM):
+- Make functions to use for uio_malloc, uio_free and uio_realloc
+ configurable at init.
+- add uio_mmap()
+- add match_MATCH_ALL and match_MATCH_NONE
+- automounting
+ - Unmount automounted filesystems when the originating filesystem
+ is unmounted.
+- when doing stat() on a directory, merge the stat info of the underlying
+ physical dirs.
+- read directory information from zip files.
+- implement ungetc()
+- make fileblocks public
+- setmode() for windows?
+- prevent aliasing of files/dirs, both between two physical file
+ systems (where possible), as within the same filesystem (in particular
+ stdio). On Windows, aliasing can occur easilly when a file or dir
+ is accessed with the long vs the short ("PROGRA~1") name, or when
+ capitalisation is involved.
+- accept non-dir, non-regular-file dir entries in stdio.
+- Add non-merging mounts. Make 'merging' an option for the mount.
+- make uio_rename() work cross-fs, or provide a wrapper which does that.
+ (the system rename() doesn't work cross-fs either, so keeping uio_rename()
+ as it is makes sense, as I'm trying to stay close to the system functions,
+ even though hiding file systems from the user would be nicer)
+- Add a readdir_r(). Right now, for Symbian readdir_r() is defined in
+ uioport.h, but the actual implementation is out of the uio tree.
+
+Optimisations (not necessary for UQM):
+- use mmap for fileBlocks
+- Use a pre-allocated pool of hash table entries for allocHashEntry.
+- optimise certain strings (specifically directory entries) by making
+ a string type which has pointers to a shared char array.
+ Invariant: if two strings are the same, they point to the same
+ character array. Consequence: string comparison is pointer comparison.
+ Making a new string would imply checking the existing strings.
+ Existing strings should be in a hash table.
+- optimise paths
+ Encode all paths internally as (double-linked) chains of path components
+ This way, operations like going up one path component, are cheap.
+ Each path component could either be represented by a pointer to a string,
+ (possibly shared as above), or as a pair of begin and end pointers
+ (or begin pointer and length). In the latter case, there's no need for
+ copying strings when parsing a user-supplied string, but sharing of strings
+ would not be possible.
+ It's probably possible to use both methods next to eachother;
+ shared strings for stored paths, and pointers into a path for user-supplied
+ strings (which would be freed when the call returns).
+ Instead of a linked list, perhaps an array can be used, as paths rarely
+ change. It would be a problem if for some reason, recursively path
+ components are added to a path.
+- (maybe) keep track of already issued handles, so that the ref counter
+ could just be incremented, instead of issuing a new one.
+ (uio_getPDirEntryHandle)
+- Make it possible for people to add their own file system, without giving
+ away the internals. (no wanton including of .h files)
+- Don't cache stdio dir structure
+ (and don't read the dir leading up to the dir that is actually needed)
+- mounting foo to /Foo and foo/bar to /Foo/Bar should result in only one
+ physical structure. Preferably, it shouldn't even lead to two mountInfo
+ structures for /Foo/Bar, so that merging the dirs is not unnecessarilly
+ expensive.
+- add uio_access()
+ (uqm fileExists can be redone then)
+- implement the physical function cleanup() to clean up the physical
+ structure. Int argument that specifies how thoroughly.
+ On it depends whether cache is cleaned, whether stuff is realloc'ed
+ to defragment memory, etc.
+
+Cleanups (not necessary for UQM):
+- rename function names to be more like class names for as far as that's
+ not already done.
+ uio_PRoot_unref etc
+- Add uio_fatal(), uio_error(), uio_warning()
+- Clean up the include structure
+- use stdint.h and stdbool.h types directly, instead of using uio_int16 etc.
+ Remove types.h, and instead, if these types are missing on some platforms,
+ put the fixes in port.h.
+
+
+
diff --git a/src/libs/uio/fileblock.c b/src/libs/uio/fileblock.c
new file mode 100644
index 0000000..bb4f466
--- /dev/null
+++ b/src/libs/uio/fileblock.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define uio_INTERNAL_FILEBLOCK
+
+#include "iointrn.h"
+#include "fileblock.h"
+#include "uioport.h"
+
+#include <errno.h>
+
+static uio_FileBlock *uio_FileBlock_new(uio_Handle *handle, int flags,
+ off_t offset, size_t blockSize, char *buffer, size_t bufSize,
+ off_t bufOffset, size_t bufFill, size_t readAheadBufSize);
+static inline uio_FileBlock *uio_FileBlock_alloc(void);
+static void uio_FileBlock_free(uio_FileBlock *block);
+
+// caller should uio_Handle_ref(handle) (unless it doesn't need it's own
+// reference anymore).
+static uio_FileBlock *
+uio_FileBlock_new(uio_Handle *handle, int flags, off_t offset,
+ size_t blockSize, char *buffer, size_t bufSize, off_t bufOffset,
+ size_t bufFill, size_t readAheadBufSize) {
+ uio_FileBlock *result;
+
+ result = uio_FileBlock_alloc();
+ result->handle = handle;
+ result->flags = flags;
+ result->offset = offset;
+ result->blockSize = blockSize;
+ result->buffer = buffer;
+ result->bufSize = bufSize;
+ result->bufOffset = bufOffset;
+ result->bufFill = bufFill;
+ result->readAheadBufSize = readAheadBufSize;
+ return result;
+}
+
+static inline uio_FileBlock *
+uio_FileBlock_alloc(void) {
+ return uio_malloc(sizeof (uio_FileBlock));
+}
+
+static inline void
+uio_FileBlock_free(uio_FileBlock *block) {
+ uio_free(block);
+}
+
+uio_FileBlock *
+uio_openFileBlock(uio_Handle *handle) {
+ // TODO: if mmap support is available, and it is available natively
+ // for the filesystem (make some sort of sysctl for filesystems
+ // to check this?), use mmap.
+ // mmap the entire file if it's small enough.
+ // N.B. Keep in mind streams of which the size is not known in
+ // advance.
+ struct stat statBuf;
+ if (uio_fstat(handle, &statBuf) == -1) {
+ // errno is set
+ return NULL;
+ }
+ uio_Handle_ref(handle);
+ return uio_FileBlock_new(handle, 0, 0, statBuf.st_size, NULL, 0, 0, 0, 0);
+}
+
+uio_FileBlock *
+uio_openFileBlock2(uio_Handle *handle, off_t offset, size_t size) {
+ // TODO: mmap (see uio_openFileBlock)
+
+ // TODO: check if offset and size are acceptable.
+ // Need to handle streams of which the size is unknown.
+#if 0
+ if (uio_stat(handle, &statBuf) == -1) {
+ // errno is set
+ return NULL;
+ }
+ if (statBuf.st_size > offset || (statBuf.st_size - offset > size)) {
+ // NOT: 'if (statBuf.st_size > offset + size)', to protect
+ // against overflow.
+
+ }
+#endif
+ uio_Handle_ref(handle);
+ return uio_FileBlock_new(handle, 0, offset, size, NULL, 0, 0, 0, 0);
+}
+
+static inline ssize_t
+uio_accessFileBlockMmap(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer) {
+ // TODO
+ errno = ENOSYS;
+ (void) block;
+ (void) offset;
+ (void) length;
+ (void) buffer;
+ return -1;
+}
+
+static inline ssize_t
+uio_accessFileBlockNoMmap(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer) {
+ ssize_t numRead;
+ off_t start;
+ off_t end;
+ size_t bufSize;
+ char *oldBuffer;
+ //size_t oldBufSize;
+
+ // Don't go beyond the end of the block.
+ if (offset > (off_t) block->blockSize) {
+ *buffer = block->buffer;
+ return 0;
+ }
+ if (length > block->blockSize - offset)
+ length = block->blockSize - offset;
+
+ if (block->buffer != NULL) {
+ // Check whether the requested data is already in the buffer.
+ if (offset >= block->bufOffset &&
+ (offset - block->bufOffset) + length < block->bufFill) {
+ *buffer = block->buffer + (offset - block->bufOffset);
+ return length;
+ }
+ }
+
+ if (length < block->readAheadBufSize &&
+ (block->flags & uio_FB_USAGE_MASK) != 0) {
+ // We can buffer more data.
+ switch (block->flags & uio_FB_USAGE_MASK) {
+ case uio_FB_USAGE_FORWARD:
+ // Read extra data after the requested data.
+ start = offset;
+ end = (block->readAheadBufSize > block->blockSize - offset) ?
+ block->blockSize : offset + block->readAheadBufSize;
+ break;
+ case uio_FB_USAGE_BACKWARD:
+ // Read extra data before the requested data.
+ end = offset + length;
+ start = (end <= (off_t) block->blockSize) ?
+ 0 : end - block->bufSize;
+ break;
+ case uio_FB_USAGE_FORWARD | uio_FB_USAGE_BACKWARD: {
+ // Read extra data both before and after the requested data.
+ off_t extraBefore = (block->readAheadBufSize - length) / 2;
+ start = (offset < extraBefore) ? 0 : offset - extraBefore;
+
+ end = (block->readAheadBufSize > block->blockSize - start) ?
+ block->blockSize : start + block->readAheadBufSize;
+ break;
+ }
+ }
+ } else {
+ start = offset;
+ end = offset + length;
+ }
+ bufSize = (length > block->readAheadBufSize) ?
+ length : block->readAheadBufSize;
+
+ // Start contains the start index in the block of the data we're going
+ // to read.
+ // End contains the end index.
+ // bufSize contains the size of the buffer. bufSize >= end - start.
+
+ oldBuffer = block->buffer;
+ //oldBufSize = block->bufSize;
+ if (block->buffer != NULL || block->bufSize != bufSize) {
+ // We don't have a buffer, or we have one, but of the wrong size.
+ block->buffer = uio_malloc(bufSize);
+ block->bufSize = bufSize;
+ }
+
+ if (oldBuffer != NULL) {
+ // TODO: If we have part of the data still in the old buffer, we
+ // can keep that.
+ // memmove(...)
+
+ if (oldBuffer != block->buffer)
+ uio_free(oldBuffer);
+ }
+ block->bufFill = 0;
+ block->bufOffset = start;
+
+ // TODO: lock handle
+ if (uio_lseek(block->handle, block->offset + start, SEEK_SET) ==
+ (off_t) -1) {
+ // errno is set
+ return -1;
+ }
+
+ numRead = uio_read(block->handle, block->buffer, end - start);
+ if (numRead == -1) {
+ // errno is set
+ // TODO: unlock handle
+ return -1;
+ }
+ // TODO: unlock handle
+
+ block->bufFill = numRead;
+ *buffer = block->buffer + (offset - block->bufOffset);
+ if (numRead <= (offset - block->bufOffset))
+ return 0;
+ if ((size_t) numRead >= length)
+ return length;
+ return numRead - offset;
+}
+
+// block remains usable until the next call to uio_accessFileBlock
+// with the same block as argument, or until uio_closeFileBlock with
+// that block as argument.
+// The 'offset' parameter is wrt. the start of the block.
+// Requesting access to data beyond the file block is not an error. The
+// FileBlock is meant to be used as a replacement of seek() and read(), and
+// as with those functions, trying to go beyond the end of a file just
+// goes to the end. The return value is the number of bytes in the buffer.
+ssize_t
+uio_accessFileBlock(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer) {
+ if (block->flags & uio_FB_USE_MMAP) {
+ return uio_accessFileBlockMmap(block, offset, length, buffer);
+ } else {
+ return uio_accessFileBlockNoMmap(block, offset, length, buffer);
+ }
+}
+
+int
+uio_copyFileBlock(uio_FileBlock *block, off_t offset, char *buffer,
+ size_t length) {
+ if (block->flags & uio_FB_USE_MMAP) {
+ // TODO
+ errno = ENOSYS;
+ return -1;
+ } else {
+ ssize_t numCopied = 0;
+ ssize_t readResult;
+
+ // Don't go beyond the end of the block.
+ if (offset > (off_t) block->blockSize)
+ return 0;
+ if (length > block->blockSize - offset)
+ length = block->blockSize - offset;
+
+ // Check whether (part of) the requested data is already in our
+ // own buffer.
+ if (block->buffer != NULL && offset >= block->bufOffset
+ && offset < block->bufOffset + (off_t) block->bufFill) {
+ size_t toCopy = block->bufFill - offset;
+ if (toCopy > length)
+ toCopy = length;
+ memcpy(buffer, block->buffer + (offset - block->bufOffset),
+ toCopy);
+ numCopied += toCopy;
+ length -= toCopy;
+ if (length == 0)
+ return numCopied;
+ buffer += toCopy;
+ offset += toCopy;
+ }
+
+ // TODO: lock handle
+ if (uio_lseek(block->handle, block->offset + offset, SEEK_SET) ==
+ (off_t) -1) {
+ // errno is set
+ return -1;
+ }
+
+ readResult = uio_read(block->handle, buffer, length);
+ // TODO: unlock handle
+ if (readResult == -1) {
+ // errno is set
+ return -1;
+ }
+ numCopied += readResult;
+
+ return numCopied;
+ }
+}
+
+int
+uio_closeFileBlock(uio_FileBlock *block) {
+ if (block->flags & uio_FB_USE_MMAP) {
+#if 0
+ if (block->buffer != NULL)
+ uio_mmunmap(block->buffer);
+#endif
+ } else {
+ if (block->buffer != NULL)
+ uio_free(block->buffer);
+ }
+ uio_Handle_unref(block->handle);
+ uio_FileBlock_free(block);
+ return 0;
+}
+
+// Usage is the or'ed value of zero or more of uio_FB_USAGE_FORWARD,
+// and uio_FB_USAGE_BACKWARD.
+void
+uio_setFileBlockUsageHint(uio_FileBlock *block, int usage,
+ size_t readAheadBufSize) {
+ block->flags = (block->flags & ~uio_FB_USAGE_MASK) |
+ (usage & uio_FB_USAGE_MASK);
+ block->readAheadBufSize = readAheadBufSize;
+}
+
+// Call if you want the memory used by the fileblock to be released, but
+// still want to use the fileblock later. If you don't need the fileblock,
+// call uio_closeFileBlock() instead.
+void
+uio_clearFileBlockBuffers(uio_FileBlock *block) {
+ if (block->buffer != NULL) {
+ uio_free(block->buffer);
+ block->buffer = NULL;
+ }
+}
+
+
diff --git a/src/libs/uio/fileblock.h b/src/libs/uio/fileblock.h
new file mode 100644
index 0000000..97a81e1
--- /dev/null
+++ b/src/libs/uio/fileblock.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_FILEBLOCK_H_
+#define LIBS_UIO_FILEBLOCK_H_
+
+typedef struct uio_FileBlock uio_FileBlock;
+
+#include "io.h"
+#include "uioport.h"
+
+#include <sys/types.h>
+
+#define uio_FB_USAGE_FORWARD 1
+#define uio_FB_USAGE_BACKWARD 2
+#define uio_FB_USAGE_MASK (uio_FB_USAGE_FORWARD | uio_FB_USAGE_BACKWARD)
+
+#ifdef uio_INTERNAL_FILEBLOCK
+
+// A fileblock represents a contiguous block of data from a file.
+// It's purpose is to avoid needless copying of data, while enabling
+// buffering.
+
+struct uio_FileBlock {
+ uio_Handle *handle;
+ int flags;
+ // See above for uio_FB_USAGE_FORWARD, uio_FB_USAGE_BACKWARD.
+#define uio_FB_USE_MMAP 4
+ off_t offset;
+ // Offset to the start of the block in the file.
+ size_t blockSize;
+ // Size of the block of data represented by this FileBlock.
+ char *buffer;
+ // either allocated buffer, or buffer to mmap'ed area.
+ size_t bufSize;
+ // Size of the buffer.
+ off_t bufOffset;
+ // Offset of the start of the buffer into the block.
+ size_t bufFill;
+ // Part of 'buffer' which is in use.
+ size_t readAheadBufSize;
+ // Try to read up to this many bytes at a time, even when less
+ // is immediately needed.
+};
+// INV: The FileBlock represents 'fileData[offset..(offset + blockSize - 1)]'
+// where 'fileData' is the contents of the file.
+// INV: If buf != NULL then:
+// bufFill <= bufSize
+// bufFill <= blockSize
+// buffer[0..bufFill - 1] == fileData[
+// (offset + bufOffset)..(offset + bufOffset + bufFill - 1)]
+
+
+#endif /* uio_INTERNAL_FILEBLOCK */
+
+uio_FileBlock *uio_openFileBlock(uio_Handle *handle);
+uio_FileBlock *uio_openFileBlock2(uio_Handle *handle, off_t offset,
+ size_t size);
+ssize_t uio_accessFileBlock(uio_FileBlock *block, off_t offset, size_t length,
+ char **buffer);
+int uio_copyFileBlock(uio_FileBlock *block, off_t offset, char *buffer,
+ size_t length);
+int uio_closeFileBlock(uio_FileBlock *block);
+#define uio_FB_READAHEAD_BUFSIZE_MAX ((size_t) -1)
+void uio_setFileBlockUsageHint(uio_FileBlock *block, int usage,
+ size_t readAheadBufSize);
+void uio_clearFileBlockBuffers(uio_FileBlock *block);
+
+#endif /* LIBS_UIO_FILEBLOCK_H_ */
+
+
diff --git a/src/libs/uio/fstypes.c b/src/libs/uio/fstypes.c
new file mode 100644
index 0000000..d940e8f
--- /dev/null
+++ b/src/libs/uio/fstypes.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "fstypes.h"
+#include "mem.h"
+#include "defaultfs.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+
+static uio_bool uio_validFileSystemHandler(uio_FileSystemHandler *handler);
+static uio_FileSystemInfo *uio_FileSystemInfo_new(uio_FileSystemID id,
+ uio_FileSystemHandler *handler, char *name);
+static uio_FileSystemInfo **uio_getFileSystemInfoPtr(uio_FileSystemID id);
+
+static inline uio_FileSystemInfo *uio_FileSystemInfo_alloc(void);
+
+static inline void uio_FileSystemInfo_free(
+ uio_FileSystemInfo *fileSystemInfo);
+
+
+uio_FileSystemInfo *uio_fileSystems = NULL;
+ // list sorted by id
+
+
+
+void
+uio_registerDefaultFileSystems(void) {
+ int i;
+ int num;
+ uio_FileSystemID registerResult;
+
+ num = uio_numDefaultFileSystems();
+ for (i = 0; i < num; i++) {
+ registerResult = uio_registerFileSystem(
+ defaultFileSystems[i].id,
+ defaultFileSystems[i].name,
+ defaultFileSystems[i].handler);
+ switch (registerResult) {
+ case 0:
+ fprintf(stderr, "Warning: Default file system '%s' is "
+ "already registered.\n",
+ defaultFileSystems[i].name);
+ break;
+ case -1:
+ fprintf(stderr, "Error: Could not register '%s' file \n"
+ "system: %s\n", defaultFileSystems[i].name,
+ strerror(errno));
+ break;
+ default:
+ assert(registerResult == defaultFileSystems[i].id);
+ break;
+ }
+ }
+}
+
+void
+uio_unRegisterDefaultFileSystems(void) {
+ int i;
+ int num;
+
+ num = uio_numDefaultFileSystems();
+ for (i = 0; i < num; i++) {
+ if (uio_unRegisterFileSystem(defaultFileSystems[i].id) == -1) {
+ fprintf(stderr, "Could not unregister '%s' file system: %s\n",
+ defaultFileSystems[i].name, strerror(errno));
+ }
+ }
+}
+
+// if wantedID = 0, just pick one
+// if wantedID != 0, 0 will be returned if that id wasn't available
+// a copy of 'name' is made
+uio_FileSystemID
+uio_registerFileSystem(uio_FileSystemID wantedID, const char *name,
+ uio_FileSystemHandler *handler) {
+ uio_FileSystemInfo **ptr;
+
+ if (!uio_validFileSystemHandler(handler)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (wantedID == 0) {
+ // Search for the first free id >= uio_FIRST_CUSTOM_ID
+ // it is put in wantedID
+
+ for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next)
+ if ((*ptr)->id >= uio_FS_FIRST_CUSTOM_ID)
+ break;
+
+ wantedID = uio_FS_FIRST_CUSTOM_ID;
+ while (*ptr != NULL) {
+ if ((*ptr)->id != wantedID) {
+ // wantedID is not in use
+ break;
+ }
+ wantedID++;
+ ptr = &(*ptr)->next;
+ }
+ // wantedID contains the new ID
+ } else {
+ // search for the place in the list where to insert the wanted
+ // id, keeping the list sorted
+ for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next) {
+ if ((*ptr)->id <= wantedID) {
+ if ((*ptr)->id == wantedID)
+ return 0;
+ break;
+ }
+ }
+
+ }
+ // ptr points to the place where the new link can inserted
+
+ if (handler->init != NULL && handler->init() == -1) {
+ // errno is set
+ return -1;
+ }
+
+ {
+ uio_FileSystemInfo *newInfo;
+
+ newInfo = uio_FileSystemInfo_new(wantedID, handler, uio_strdup(name));
+ newInfo->next = *ptr;
+ *ptr = newInfo;
+ return wantedID;
+ }
+}
+
+int
+uio_unRegisterFileSystem(uio_FileSystemID id) {
+ uio_FileSystemInfo **ptr;
+ uio_FileSystemInfo *temp;
+
+ ptr = uio_getFileSystemInfoPtr(id);
+ if (ptr == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if ((*ptr)->ref > 1) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ if ((*ptr)->handler->unInit != NULL &&
+ ((*ptr)->handler->unInit() == -1)) {
+ // errno is set
+ return -1;
+ }
+
+ temp = *ptr;
+ *ptr = (*ptr)->next;
+
+// uio_FileSystemHandler_unref(temp->handler);
+ uio_free(temp->name);
+ uio_FileSystemInfo_free(temp);
+
+ return 0;
+}
+
+static uio_bool
+uio_validFileSystemHandler(uio_FileSystemHandler *handler) {
+ // Check for the essentials
+ if (handler->mount == NULL ||
+ handler->umount == NULL ||
+ handler->open == NULL ||
+ handler->close == NULL ||
+ handler->read == NULL ||
+ handler->openEntries == NULL ||
+ handler->readEntries == NULL ||
+ handler->closeEntries == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Invalid file system handler.\n");
+#endif
+ return false;
+ }
+ return true;
+}
+
+uio_FileSystemHandler *
+uio_getFileSystemHandler(uio_FileSystemID id) {
+ uio_FileSystemInfo *ptr;
+
+ for (ptr = uio_fileSystems; ptr != NULL; ptr = ptr->next) {
+ if (ptr->id == id)
+ return ptr->handler;
+ }
+ return NULL;
+}
+
+uio_FileSystemInfo *
+uio_getFileSystemInfo(uio_FileSystemID id) {
+ uio_FileSystemInfo *ptr;
+
+ for (ptr = uio_fileSystems; ptr != NULL; ptr = ptr->next) {
+ if (ptr->id == id)
+ return ptr;
+ }
+ return NULL;
+}
+
+static uio_FileSystemInfo **
+uio_getFileSystemInfoPtr(uio_FileSystemID id) {
+ uio_FileSystemInfo **ptr;
+
+ for (ptr = &uio_fileSystems; *ptr != NULL; ptr = &(*ptr)->next) {
+ if ((*ptr)->id == id)
+ return ptr;
+ }
+ return NULL;
+}
+
+// sets ref to 1
+static uio_FileSystemInfo *
+uio_FileSystemInfo_new(uio_FileSystemID id, uio_FileSystemHandler *handler,
+ char *name) {
+ uio_FileSystemInfo *result;
+
+ result = uio_FileSystemInfo_alloc();
+ result->id = id;
+ result->handler = handler;
+ result->name = name;
+ result->ref = 1;
+ return result;
+}
+
+// *** Allocators ***
+
+static inline uio_FileSystemInfo *
+uio_FileSystemInfo_alloc(void) {
+ uio_FileSystemInfo *result = uio_malloc(sizeof (uio_FileSystemInfo));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_FileSystemInfo, (void *) result);
+#endif
+ return result;
+}
+
+
+// *** Deallocators ***
+
+static inline void
+uio_FileSystemInfo_free(uio_FileSystemInfo *fileSystemInfo) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_FileSystemInfo, (void *) fileSystemInfo);
+#endif
+ uio_free(fileSystemInfo);
+}
+
+
diff --git a/src/libs/uio/fstypes.h b/src/libs/uio/fstypes.h
new file mode 100644
index 0000000..3ff01a2
--- /dev/null
+++ b/src/libs/uio/fstypes.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_FSTYPES_H_
+#define LIBS_UIO_FSTYPES_H_
+
+typedef int uio_FileSystemID;
+#define uio_FSTYPE_STDIO 1
+#define uio_FSTYPE_ZIP 2
+
+
+#ifdef uio_INTERNAL
+
+#define uio_FS_FIRST_CUSTOM_ID 0x10
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_NativeHandle;
+#endif
+
+// 'forward' declarations
+typedef struct uio_FileSystemHandler uio_FileSystemHandler;
+typedef struct uio_FileSystemInfo uio_FileSystemInfo;
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "physical.h"
+#include "uioport.h"
+
+
+/* Structure describing how to access files _in_ a directory of a certain
+ * type (not files _of_ a certain type.) Except for mount().
+ * in open(), the first arg points to the dir where the file should be
+ * in, and the second arg is the name of that file (no path)
+ */
+struct uio_FileSystemHandler {
+ int (*init) (void);
+ int (*unInit) (void);
+ void (*cleanup) (uio_PRoot *, int);
+ // Called to cleanup memory. The second argument specifies
+ // how thoroughly.
+
+ struct uio_PRoot * (*mount) (uio_Handle *, int);
+ int (*umount) (uio_PRoot *);
+
+ int (*access) (uio_PDirHandle *, const char *, int mode);
+ void (*close) (uio_Handle *);
+ // called when the last reference is closed, not
+ // necessarilly each time when uio_close() is called
+ int (*fstat) (uio_Handle *, struct stat *);
+ int (*stat) (uio_PDirHandle *, const char *,
+ struct stat *);
+ uio_PDirHandle * (*mkdir) (uio_PDirHandle *, const char *, mode_t);
+ uio_Handle * (*open) (uio_PDirHandle *, const char *, int,
+ mode_t);
+ ssize_t (*read) (uio_Handle *, void *, size_t);
+ int (*rename) (uio_PDirHandle *, const char *,
+ uio_PDirHandle *, const char *);
+ int (*rmdir) (uio_PDirHandle *, const char *);
+ off_t (*seek) (uio_Handle *, off_t, int);
+ ssize_t (*write) (uio_Handle *, const void *, size_t);
+ int (*unlink) (uio_PDirHandle *, const char *);
+
+ uio_NativeEntriesContext (*openEntries) (uio_PDirHandle *);
+ int (*readEntries) (uio_NativeEntriesContext *, char *,
+ size_t);
+ void (*closeEntries) (uio_NativeEntriesContext);
+
+ uio_PDirEntryHandle * (*getPDirEntryHandle) (
+ const uio_PDirHandle *, const char *);
+ void (*deletePRootExtra) (uio_PRootExtra pRootExtra);
+ void (*deletePDirHandleExtra) (
+ uio_PDirHandleExtra pDirHandleExtra);
+ void (*deletePFileHandleExtra) (
+ uio_PFileHandleExtra pFileHandleExtra);
+};
+
+struct uio_FileSystemInfo {
+ int ref;
+ uio_FileSystemID id;
+ char *name; // name of the file system
+ uio_FileSystemHandler *handler;
+ struct uio_FileSystemInfo *next;
+};
+
+void uio_registerDefaultFileSystems(void);
+void uio_unRegisterDefaultFileSystems(void);
+uio_FileSystemID uio_registerFileSystem(uio_FileSystemID wantedID,
+ const char *name, uio_FileSystemHandler *handler);
+int uio_unRegisterFileSystem(uio_FileSystemID id);
+uio_FileSystemHandler *uio_getFileSystemHandler(uio_FileSystemID id);
+uio_FileSystemInfo *uio_getFileSystemInfo(uio_FileSystemID id);
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_FSTYPES_H_ */
+
diff --git a/src/libs/uio/getint.h b/src/libs/uio/getint.h
new file mode 100644
index 0000000..ad6e810
--- /dev/null
+++ b/src/libs/uio/getint.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2007 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_GETINT_H_
+#define LIBS_UIO_GETINT_H_
+
+/* All these functions return true on success, or false on failure */
+
+#include "types.h"
+#include "uioport.h"
+
+static inline uio_bool
+uio_getU8(uio_Stream *stream, uio_uint8 *result) {
+ int val = uio_getc(stream);
+ if (val == EOF)
+ return false;
+
+ *result = (uio_uint8) val;
+ return true;
+}
+
+static inline uio_bool
+uio_getS8(uio_Stream *stream, uio_sint8 *result) {
+ int val = uio_getc(stream);
+ if (val == EOF)
+ return false;
+
+ *result = (uio_sint8) val;
+ return true;
+}
+
+static inline uio_bool
+uio_getU16LE(uio_Stream *stream, uio_uint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[1] << 8) | buf[0];
+ return true;
+}
+
+static inline uio_bool
+uio_getU16BE(uio_Stream *stream, uio_uint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[0] << 8) | buf[1];
+ return true;
+}
+
+static inline uio_bool
+uio_getS16LE(uio_Stream *stream, uio_sint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint16) ((buf[1] << 8) | buf[0]);
+ return true;
+}
+
+static inline uio_bool
+uio_getS16BE(uio_Stream *stream, uio_sint16 *result) {
+ uio_uint8 buf[2];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint16) ((buf[0] << 8) | buf[1]);
+ return true;
+}
+
+static inline uio_bool
+uio_getU32LE(uio_Stream *stream, uio_uint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ return true;
+}
+
+static inline uio_bool
+uio_getU32BE(uio_Stream *stream, uio_uint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ return true;
+}
+
+static inline uio_bool
+uio_getS32LE(uio_Stream *stream, uio_sint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint32) ((buf[3] << 24) | (buf[2] << 16) |
+ (buf[1] << 8) | buf[0]);
+ return true;
+}
+
+static inline uio_bool
+uio_getS32BE(uio_Stream *stream, uio_sint32 *result) {
+ uio_uint8 buf[4];
+
+ if (uio_fread(buf, sizeof buf, 1, stream) != 1)
+ return false;
+
+ *result = (uio_sint32) ((buf[0] << 24) | (buf[1] << 16) |
+ (buf[2] << 8) | buf[3]);
+ return true;
+}
+
+
+#endif /* LIBS_UIO_GETINT_H_ */
+
diff --git a/src/libs/uio/gphys.c b/src/libs/uio/gphys.c
new file mode 100644
index 0000000..f25f557
--- /dev/null
+++ b/src/libs/uio/gphys.c
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define uio_INTERNAL_PHYSICAL
+#define uio_INTERNAL_GPHYS
+typedef void *uio_NativeHandle;
+typedef void *uio_GPRootExtra;
+typedef void *uio_GPDirExtra;
+typedef void *uio_GPFileExtra;
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "gphys.h"
+#include "paths.h"
+#include "uioport.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+static void uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir);
+static uio_GPRoot *uio_GPRoot_alloc(void);
+
+static void uio_GPRoot_free(uio_GPRoot *gPRoot);
+
+static inline uio_GPDir *uio_GPDir_alloc(void);
+void uio_GPDir_delete(uio_GPDir *gPDir);
+static inline void uio_GPDir_free(uio_GPDir *gPDir);
+
+static inline uio_GPFile *uio_GPFile_alloc(void);
+void uio_GPFile_delete(uio_GPFile *gPFile);
+static inline void uio_GPFile_free(uio_GPFile *gPFile);
+
+// Call this when you need to edit a file 'dirName' in the GPDir 'gPDir'.
+// a new entry is created when necessary.
+// uio_gPDirCommitSubDir should be called when you're done with it.
+//
+// a copy of dirName is made if needed; the caller remains responsible for
+// freeing the original
+// Allocates a new dir if necessary.
+uio_GPDir *
+uio_GPDir_prepareSubDir(uio_GPDir *gPDir, const char *dirName) {
+ uio_GPDir *subDir;
+ uio_GPDirEntry *entry;
+
+ entry = uio_GPDirEntries_find(gPDir->entries, dirName);
+ if (entry != NULL) {
+ if (uio_GPDirEntry_isDir(entry)) {
+ // Return existing subdir.
+ uio_GPDir_ref((uio_GPDir *) entry);
+ return (uio_GPDir *) entry;
+ } else {
+ // There already exists a file with the same name.
+ // This should not happen within one file system.
+ fprintf(stderr, "Directory %s shadows file with the same name "
+ "from the same filesystem.\n", dirName);
+ }
+ }
+
+ // return new subdir
+ subDir = uio_GPDir_new(gPDir->pRoot, NULL, uio_GPDir_DETACHED);
+ // subDir->ref is initialised at 1
+ return subDir;
+}
+
+// call this when you're done with a dir acquired by a call to
+// uio_gPDirPrepareSubDir
+void
+uio_GPDir_commitSubDir(uio_GPDir *gPDir, const char *dirName,
+ uio_GPDir *subDir) {
+ if (subDir->flags & uio_GPDir_DETACHED) {
+ // New dir.
+ // reference to the subDir is passed along to the upDir,
+ // so subDir->ref should not be changed.
+ uio_GPDirEntries_add(gPDir->entries, dirName, subDir);
+ subDir->flags &= ~uio_GPDir_DETACHED;
+ if (!(subDir->flags & uio_GPDir_PERSISTENT)) {
+ // Persistent dirs have an extra reference.
+ uio_GPDir_unref(subDir);
+ }
+ } else {
+ uio_GPDir_unref(subDir);
+ }
+}
+
+// a copy of fileName is made if needed; the caller remains responsible for
+// freeing the original
+void
+uio_GPDir_addFile(uio_GPDir *gPDir, const char *fileName, uio_GPFile *file) {
+ // A file will never already exist in a dir. There can only be
+ // one entry in a physical dir with the same name.
+ uio_GPDirEntries_add(gPDir->entries, fileName, (uio_GPDirEntry *) file);
+}
+
+// Pre: 'fileName' exists in 'gPDir' and is a dir.
+void
+uio_GPDir_removeFile(uio_GPDir *gPDir, const char *fileName) {
+ uio_GPDirEntry *entry;
+ uio_GPFile *file;
+ uio_bool retVal;
+
+ entry = uio_GPDirEntries_find(gPDir->entries, fileName);
+ if (entry == NULL) {
+ // This means the file has no associated GPFile.
+ // This can happen when the GPFile structure is only used for caching.
+ return;
+ }
+
+ assert(!uio_GPDirEntry_isDir(entry));
+ file = (uio_GPFile *) entry;
+ uio_GPFile_unref(file);
+ retVal = uio_GPDirEntries_remove(gPDir->entries, fileName);
+ assert(retVal);
+}
+
+// Pre: 'dirName' exists in 'gPDir' and is a dir.
+void
+uio_GPDir_removeSubDir(uio_GPDir *gPDir, const char *dirName) {
+ uio_GPDirEntry *entry;
+ uio_GPDir *subDir;
+ uio_bool retVal;
+
+ entry = uio_GPDirEntries_find(gPDir->entries, dirName);
+ if (entry == NULL) {
+ // This means the directory has no associated GPDir.
+ // This can happen when the GPDir structure is only used for caching.
+ return;
+ }
+
+ assert(uio_GPDirEntry_isDir(entry));
+ subDir = (uio_GPDir *) entry;
+ if (subDir->flags & uio_GPDir_PERSISTENT) {
+ // Persistent dirs have an extra reference.
+ uio_GPDir_unref(subDir);
+ }
+ retVal = uio_GPDirEntries_remove(gPDir->entries, dirName);
+ assert(retVal);
+}
+
+void
+uio_GPDir_setComplete(uio_GPDir *gPDir, uio_bool flag) {
+ if (flag) {
+ gPDir->flags |= uio_GPDir_COMPLETE;
+ } else
+ gPDir->flags &= ~uio_GPDir_COMPLETE;
+}
+
+int
+uio_GPDir_entryCount(const uio_GPDir *gPDir) {
+ return uio_GPDirEntries_count(gPDir->entries);
+}
+
+static void
+uio_GPDir_access(uio_GPDir *gPDir) {
+ if (!(gPDir->flags & uio_GPDir_COMPLETE))
+ uio_GPDir_fill(gPDir);
+}
+
+// The ref counter for the dir entry is not incremented.
+uio_GPDirEntry *
+uio_GPDir_getGPDirEntry(uio_GPDir *gPDir, const char *name) {
+ uio_GPDir_access(gPDir);
+ return uio_GPDirEntries_find(gPDir->entries, name);
+}
+
+// The ref counter for the dir entry is not incremented.
+uio_PDirEntryHandle *
+uio_GPDir_getPDirEntryHandle(const uio_PDirHandle *pDirHandle,
+ const char *name) {
+ uio_GPDirEntry *gPDirEntry;
+
+ gPDirEntry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (gPDirEntry == NULL)
+ return NULL;
+ uio_GPDirEntry_ref(gPDirEntry);
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(pDirHandle->pRoot,
+ (uio_GPDir *) gPDirEntry);
+ } else {
+ return (uio_PDirEntryHandle *) uio_PFileHandle_new(pDirHandle->pRoot,
+ (uio_GPFile *) gPDirEntry);
+ }
+}
+
+/*
+ * Follow a path starting from a specified physical dir as long as possible.
+ * When you can get no further, 'endGPDir' will be filled in with the dir
+ * where you ended up, and 'pathRest' will point into the original path. to
+ * the beginning of the part that was not matched.
+ * It is allowed to have endGPDir point to gPDir and/or restPath
+ * point to path when calling this function.
+ * returns: 0 if the complete path was matched
+ * ENOENT if some component (the next one) didn't exists
+ * ENODIR if a component (the next one) exists but isn't a dir
+ * See also uio_walkPhysicalPath. The difference is that this one works
+ * directly on the GPDirs and is faster because of that.
+ */
+int
+uio_walkGPPath(uio_GPDir *startGPDir, const char *path,
+ size_t pathLen, uio_GPDir **endGPDir, const char **pathRest) {
+ const char *pathEnd;
+ const char *partStart, *partEnd;
+ char *tempBuf;
+ uio_GPDir *gPDir;
+ uio_GPDirEntry *entry;
+ int retVal;
+
+ gPDir = startGPDir;
+ tempBuf = uio_malloc(strlen(path) + 1);
+ // XXX: Use a dynamically allocated array when moving to C99.
+ pathEnd = path + pathLen;
+ getFirstPathComponent(path, pathEnd, &partStart, &partEnd);
+ while (1) {
+ if (partStart == pathEnd) {
+ retVal = 0;
+ break;
+ }
+ memcpy(tempBuf, partStart, partEnd - partStart);
+ tempBuf[partEnd - partStart] = '\0';
+
+ entry = uio_GPDir_getGPDirEntry(gPDir, tempBuf);
+ if (entry == NULL) {
+ retVal = ENOENT;
+ break;
+ }
+ if (!uio_GPDirEntry_isDir(entry)) {
+ retVal = ENOTDIR;
+ break;
+ }
+ gPDir = (uio_GPDir *) entry;
+ getNextPathComponent(pathEnd, &partStart, &partEnd);
+ }
+
+ uio_free(tempBuf);
+ *pathRest = partStart;
+ *endGPDir = gPDir;
+ return retVal;
+}
+
+uio_GPDirEntries_Iterator *
+uio_GPDir_openEntries(uio_PDirHandle *pDirHandle) {
+ uio_GPDir_access(pDirHandle->extra);
+ return uio_GPDirEntries_getIterator(pDirHandle->extra->entries);
+}
+
+// the start of 'buf' will be filled with pointers to strings
+// those strings are stored elsewhere in buf.
+// The function returns the number of strings passed along, or -1 for error.
+// If there are no more entries, the last pointer will be NULL.
+// (this pointer counts towards the return value)
+int
+uio_GPDir_readEntries(uio_GPDirEntries_Iterator **iterator,
+ char *buf, size_t len) {
+ char *end;
+ char **start;
+ int num;
+ const char *name;
+ size_t nameLen;
+
+ // buf will be filled like this:
+ // The start of buf will contain pointers to char *,
+ // the end will contain the actual char[] that those pointers point to.
+ // The start and the end will grow towards eachother.
+ start = (char **) buf;
+ end = buf + len;
+ num = 0;
+ while (!uio_GPDirEntries_iteratorDone(*iterator)) {
+ name = uio_GPDirEntries_iteratorName(*iterator);
+ nameLen = strlen(name) + 1;
+
+ // Does this work with systems that need memory access to be
+ // aligned on a certain number of bytes?
+ if ((size_t) (sizeof (char *) + nameLen) >
+ (size_t) (end - (char *) start)) {
+ // Not enough room to fit the pointer (at the beginning) and
+ // the string (at the end).
+ return num;
+ }
+ end -= nameLen;
+ memcpy(end, name, nameLen);
+ *start = end;
+ start++;
+ num++;
+ *iterator = uio_GPDirEntries_iteratorNext(*iterator);
+ }
+ if (sizeof (char *) > (size_t) (end - (char *) start)) {
+ // not enough room to fit the NULL pointer.
+ // It will have to be reported seperately the next time.
+ return num;
+ }
+ *start = NULL;
+ num++;
+ return num;
+}
+
+void
+uio_GPDir_closeEntries(uio_GPDirEntries_Iterator *iterator) {
+ uio_GPDirEntries_freeIterator(iterator);
+}
+
+void
+uio_GPDir_fill(uio_GPDir *gPDir) {
+ if ((gPDir->flags & uio_GPDir_COMPLETE) &&
+ !(gPDir->flags & uio_GPDir_NOCACHE))
+ return;
+ assert(gPDir->pRoot->extra->ops->fillGPDir != NULL);
+ gPDir->pRoot->extra->ops->fillGPDir(gPDir);
+}
+
+void
+uio_GPRoot_deleteGPRootExtra(uio_GPRoot *gPRoot) {
+ if (gPRoot->extra == NULL)
+ return;
+ assert(gPRoot->ops->deleteGPRootExtra != NULL);
+ gPRoot->ops->deleteGPRootExtra(gPRoot->extra);
+}
+
+void
+uio_GPDir_deleteGPDirExtra(uio_GPDir *gPDir) {
+ if (gPDir->extra == NULL)
+ return;
+ assert(gPDir->pRoot->extra->ops->deleteGPDirExtra != NULL);
+ gPDir->pRoot->extra->ops->deleteGPDirExtra(gPDir->extra);
+}
+
+void
+uio_GPFile_deleteGPFileExtra(uio_GPFile *gPFile) {
+ if (gPFile->extra == NULL)
+ return;
+ assert(gPFile->pRoot->extra->ops->deleteGPFileExtra != NULL);
+ gPFile->pRoot->extra->ops->deleteGPFileExtra(gPFile->extra);
+}
+
+int
+uio_gPDirFlagsFromPRootFlags(int flags) {
+ int newFlags;
+
+ newFlags = 0;
+ if (flags & uio_PRoot_NOCACHE)
+ newFlags |= uio_GPDir_NOCACHE;
+
+ return newFlags;
+}
+
+int
+uio_gPFileFlagsFromPRootFlags(int flags) {
+ int newFlags;
+
+ newFlags = 0;
+ if (flags & uio_PRoot_NOCACHE)
+ newFlags |= uio_GPFile_NOCACHE;
+
+ return newFlags;
+}
+
+// This function is to be called from the physical layer.
+// uio_GPDirHandle is the extra information for an uio_PDirHandle.
+// This is in practice a pointer to the uio_GPDir.
+void
+uio_GPDirHandle_delete(uio_GPDirHandle *gPDirHandle) {
+ uio_GPDir_unref((uio_GPDir *) gPDirHandle);
+ (void) gPDirHandle;
+}
+
+// This function is to be called from the physical layer.
+// uio_GPFileHandle is the extra information for an uio_PFileHandle.
+// This is in practice a pointer to the uio_GPFile.
+void
+uio_GPFileHandle_delete(uio_GPFileHandle *gPFileHandle) {
+ uio_GPFile_unref((uio_GPFile *) gPFileHandle);
+ (void) gPFileHandle;
+}
+
+void
+uio_GPDirEntry_delete(uio_GPDirEntry *gPDirEntry) {
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_GPDir_delete((uio_GPDir *) gPDirEntry);
+ } else {
+ uio_GPFile_delete((uio_GPFile *) gPDirEntry);
+ }
+}
+
+// note: sets ref count to 1
+uio_GPDir *
+uio_GPDir_new(uio_PRoot *pRoot, uio_GPDirExtra extra, int flags) {
+ uio_GPDir *gPDir;
+
+ gPDir = uio_GPDir_alloc();
+ gPDir->ref = 1;
+ gPDir->pRoot = pRoot;
+ gPDir->entries = uio_GPDirEntries_new();
+ gPDir->extra = extra;
+ flags |= uio_gPDirFlagsFromPRootFlags(gPDir->pRoot->flags);
+ if (pRoot->extra->flags & uio_GPRoot_PERSISTENT)
+ flags |= uio_GPDir_PERSISTENT;
+ gPDir->flags = flags | uio_GPDirEntry_TYPE_DIR;
+ return gPDir;
+}
+
+// pre: There are no more references to within the gPDir, and
+// gPDir is the last reference to the gPDir itself.
+void
+uio_GPDir_delete(uio_GPDir *gPDir) {
+#if 0
+ uio_GPDirEntry *entry;
+
+ uio_GPDirEntries_Iterator *iterator;
+
+ iterator = uio_GPDirEntries_getIterator(gPDir->entries);
+ while (!uio_GPDirEntries_iteratorDone(iterator)) {
+ entry = uio_GPDirEntries_iteratorItem(iterator);
+ assert(entry->ref == 0);
+ if (uio_GPDirEntry_isDir(entry)) {
+ uio_GPDir_delete((uio_GPDir *) entry);
+ } else {
+ uio_GPFile_delete((uio_GPFile *) entry);
+ }
+ iterator = uio_GPDirEntries_iteratorNext(iterator);
+ }
+#endif
+
+ assert(gPDir->ref == 0);
+ uio_GPDirEntries_deleteHashTable(gPDir->entries);
+ uio_GPDir_deleteGPDirExtra(gPDir);
+ uio_GPDir_free(gPDir);
+}
+
+static void
+uio_GPDir_deepPersistentUnref(uio_GPDir *gPDir) {
+ uio_GPDirEntry *entry;
+ uio_GPDirEntries_Iterator *iterator;
+
+ iterator = uio_GPDirEntries_getIterator(gPDir->entries);
+ while (!uio_GPDirEntries_iteratorDone(iterator)) {
+ entry = uio_GPDirEntries_iteratorItem(iterator);
+ if (uio_GPDirEntry_isDir(entry)) {
+ uio_GPDir_deepPersistentUnref((uio_GPDir *) entry);
+ } else {
+ uio_GPFile_unref((uio_GPFile *) entry);
+ }
+ iterator = uio_GPDirEntries_iteratorNext(iterator);
+ }
+ uio_GPDirEntries_freeIterator(iterator);
+ if (gPDir->flags & uio_GPDir_PERSISTENT) {
+ uio_GPDir_unref(gPDir);
+ } else {
+ gPDir->flags &= ~uio_GPDir_COMPLETE;
+ }
+}
+
+static inline uio_GPDir *
+uio_GPDir_alloc(void) {
+ uio_GPDir *result = uio_malloc(sizeof (uio_GPDir));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_GPDir, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_GPDir_free(uio_GPDir *gPDir) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_GPDir, (void *) gPDir);
+#endif
+ uio_free(gPDir);
+}
+
+// note: sets ref count to 1
+uio_GPFile *
+uio_GPFile_new(uio_PRoot *pRoot, uio_GPFileExtra extra, int flags) {
+ uio_GPFile *gPFile;
+
+ gPFile = uio_GPFile_alloc();
+ gPFile->ref = 1;
+ gPFile->pRoot = pRoot;
+ gPFile->extra = extra;
+ gPFile->flags = flags;
+ return gPFile;
+}
+
+void
+uio_GPFile_delete(uio_GPFile *gPFile) {
+ assert(gPFile->ref == 0);
+ uio_GPFile_deleteGPFileExtra(gPFile);
+ uio_GPFile_free(gPFile);
+}
+
+static inline uio_GPFile *
+uio_GPFile_alloc(void) {
+ uio_GPFile *result = uio_malloc(sizeof (uio_GPFile));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_GPFile, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_GPFile_free(uio_GPFile *gPFile) {
+ uio_free(gPFile);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_GPFile, (void *) gPFile);
+#endif
+}
+
+// The ref counter to 'handle' is not incremented.
+uio_PRoot *
+uio_GPRoot_makePRoot(uio_FileSystemHandler *handler, int pRootFlags,
+ uio_GPRoot_Operations *ops, uio_GPRootExtra gPRootExtra, int gPRootFlags,
+ uio_Handle *handle, uio_GPDirExtra gPDirExtra, int gPDirFlags) {
+ uio_PRoot *result;
+ uio_GPDir *gPTopDir;
+ uio_GPRoot *gPRoot;
+
+ gPRoot = uio_GPRoot_new(ops, gPRootExtra, gPRootFlags);
+ result = uio_PRoot_new(NULL, handler, handle, gPRoot, pRootFlags);
+
+ gPTopDir = uio_GPDir_new(result, gPDirExtra, gPDirFlags);
+ if (gPRoot->flags & uio_GPRoot_PERSISTENT)
+ uio_GPDir_ref(gPTopDir);
+ result->rootDir = uio_GPDir_makePDirHandle(gPTopDir);
+
+ return result;
+}
+
+// Pre: there are no more references to PRoot or anything inside it.
+int
+uio_GPRoot_umount(uio_PRoot *pRoot) {
+ uio_PDirHandle *topDirHandle;
+ uio_GPDir *topDir;
+
+ topDirHandle = uio_PRoot_getRootDirHandle(pRoot);
+ topDir = topDirHandle->extra;
+ if (pRoot->extra->flags & uio_GPRoot_PERSISTENT)
+ uio_GPDir_deepPersistentUnref(topDir);
+ uio_PDirHandle_unref(topDirHandle);
+ (void) pRoot;
+ return 0;
+}
+
+uio_GPRoot *
+uio_GPRoot_new(uio_GPRoot_Operations *ops, uio_GPRootExtra extra, int flags) {
+ uio_GPRoot *result;
+
+ result = uio_GPRoot_alloc();
+ result->ops = ops;
+ result->extra = extra;
+ result->flags = flags;
+ return result;
+}
+
+void
+uio_GPRoot_delete(uio_GPRoot *gPRoot) {
+ uio_GPRoot_deleteGPRootExtra(gPRoot);
+ uio_GPRoot_free(gPRoot);
+}
+
+static uio_GPRoot *
+uio_GPRoot_alloc(void) {
+ uio_GPRoot *result = uio_malloc(sizeof (uio_GPRoot));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_GPRoot, (void *) result);
+#endif
+ return result;
+}
+
+static void
+uio_GPRoot_free(uio_GPRoot *gPRoot) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_GPRoot, (void *) gPRoot);
+#endif
+ uio_free(gPRoot);
+}
+
+// The ref counter to the gPDir is not inremented.
+uio_PDirHandle *
+uio_GPDir_makePDirHandle(uio_GPDir *gPDir) {
+ return uio_PDirHandle_new(gPDir->pRoot, gPDir);
+}
+
+#ifdef DEBUG
+void
+uio_GPDirEntry_print(FILE *outStream, uio_GPDirEntry *gPDirEntry) {
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_GPDir_print(outStream, (uio_GPDir *) gPDirEntry);
+ } else {
+ uio_GPFile_print(outStream, (uio_GPFile *) gPDirEntry);
+ }
+}
+
+void
+uio_GPDir_print(FILE *outStream, uio_GPDir *gPDir) {
+ (void) outStream;
+ (void) gPDir;
+}
+
+void
+uio_GPFile_print(FILE *outStream, uio_GPFile *gPFile) {
+ (void) outStream;
+ (void) gPFile;
+}
+#endif
+
+
diff --git a/src/libs/uio/gphys.h b/src/libs/uio/gphys.h
new file mode 100644
index 0000000..7a9d6be
--- /dev/null
+++ b/src/libs/uio/gphys.h
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_GPHYS_H_
+#define LIBS_UIO_GPHYS_H_
+
+#include "uioport.h"
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_GPRootExtra;
+typedef void *uio_GPDirExtra;
+typedef void *uio_GPFileExtra;
+#endif
+
+typedef struct CharHashTable_HashTable uio_GPDirEntries;
+
+#define uio_GPDirEntries_new() \
+ ((uio_GPDirEntries *) CharHashTable_newHashTable(NULL, NULL, NULL, \
+ NULL, NULL, 0, 0.85, 0.9))
+#define uio_GPDirEntries_add(hashTable, name, item) \
+ CharHashTable_add((CharHashTable_HashTable *) hashTable, name, \
+ (void *) item)
+#define uio_GPDirEntries_remove(hashTable, name) \
+ CharHashTable_remove((CharHashTable_HashTable *) hashTable, name)
+#define uio_GPDirEntries_count(hashTable) \
+ CharHashTable_count((CharHashTable_HashTable *) hashTable)
+#define uio_GPDirEntries_find(hashTable, name) \
+ ((uio_GPDirEntry *) CharHashTable_find( \
+ (CharHashTable_HashTable *) hashTable, name))
+#define uio_GPDirEntries_deleteHashTable(hashTable) \
+ CharHashTable_deleteHashTable((CharHashTable_HashTable *) hashTable)
+//#define uio_GPDirEntries_clear(hashTable)
+// CharHashTable_clear((CharHashTable_HashTable *) hashTable)
+#define uio_GPDirEntries_getIterator(hashTable) \
+ ((uio_GPDirEntries_Iterator *) CharHashTable_getIterator( \
+ (const CharHashTable_HashTable *) hashTable))
+#define uio_GPDirEntries_iteratorDone(iterator) \
+ CharHashTable_iteratorDone((const CharHashTable_Iterator *) iterator)
+#define uio_GPDirEntries_iteratorName(iterator) \
+ CharHashTable_iteratorKey((CharHashTable_Iterator *) iterator)
+#define uio_GPDirEntries_iteratorItem(iterator) \
+ ((uio_GPDirEntry *) CharHashTable_iteratorValue( \
+ (CharHashTable_Iterator *) iterator))
+#define uio_GPDirEntries_iteratorNext(iterator) \
+ ((uio_GPDirEntries_Iterator *) CharHashTable_iteratorNext( \
+ (CharHashTable_Iterator *) iterator))
+#define uio_GPDirEntries_freeIterator(iterator) \
+ CharHashTable_freeIterator(iterator)
+
+// 'forward' declarations
+typedef struct uio_GPDirEntry uio_GPDirEntry;
+typedef struct uio_GPDir uio_GPDir;
+typedef struct uio_GPFile uio_GPFile;
+typedef struct uio_GPRoot_Operations uio_GPRoot_Operations;
+typedef struct uio_GPRoot uio_GPRoot;
+
+#include "charhashtable.h"
+typedef CharHashTable_Iterator uio_GPDirEntries_Iterator;
+
+#ifdef uio_INTERNAL_GPHYS
+typedef uio_GPDirEntries_Iterator *uio_NativeEntriesContext;
+#endif
+typedef struct uio_GPRoot *uio_PRootExtra;
+typedef struct uio_GPDir uio_GPDirHandle;
+typedef uio_GPDirHandle *uio_PDirHandleExtra;
+typedef struct uio_GPFile uio_GPFileHandle;
+typedef uio_GPFileHandle *uio_PFileHandleExtra;
+
+
+#ifdef DEBUG
+# include <stdio.h>
+#endif
+#include "iointrn.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+struct uio_GPRoot_Operations {
+ void (*fillGPDir)(uio_GPDir *);
+ void (*deleteGPRootExtra)(uio_GPRootExtra);
+ void (*deleteGPDirExtra)(uio_GPDirExtra);
+ void (*deleteGPFileExtra)(uio_GPFileExtra);
+};
+
+struct uio_GPRoot {
+ int flags;
+#define uio_GPRoot_PERSISTENT 0x4000
+ /* Set if directories in this file system should not be deleted
+ * as long as the file system is mounted. If this flag is not
+ * set, the GPDir structure is only a cache.
+ */
+ uio_GPRoot_Operations *ops;
+ uio_GPRootExtra extra;
+};
+
+#define uio_GPDirEntry_COMMON \
+ int flags; \
+ int ref; \
+ /* Number of times this structure is referenced from the \
+ * outside (so not counting the references from subdirs \
+ * or files when the entry is a directory) \
+ */
+
+#define uio_GPDirEntry_NOCACHE uio_PRoot_NOCACHE
+
+/*
+ * uio_GPDirEntry
+ * super-'class' of uio_GPDir and uio_GPFile
+ */
+struct uio_GPDirEntry {
+ uio_GPDirEntry_COMMON
+ void *extra;
+};
+
+#define uio_GPDirEntry_TYPE_REG 0x0000
+#define uio_GPDirEntry_TYPE_DIR 0x0001
+#define uio_GPDirEntry_TYPEMASK 0x0001
+
+/*
+ * uio_GPDir
+ * Represents a directory in a physical directory structure.
+ * sub-'class' of uio_GPDirEntry
+ */
+struct uio_GPDir {
+ uio_GPDirEntry_COMMON
+# define uio_GPDir_NOCACHE uio_GPDirEntry_NOCACHE
+ /* This directory info will not be cached.
+ * PDIR_COMPLETE is irrelevant in this case */
+# define uio_GPDir_COMPLETE 0x1000
+ /* Set if fillDir should not be called if an entry does not
+ * exist in a directory. Usually set if the entire dir has been
+ * completely read in.
+ */
+# define uio_GPDir_DETACHED 0x2000
+ /* Set if this dir is not linked to from elsewhere in the physical
+ * structure */
+# define uio_GPDir_PERSISTENT 0x4000
+ /* Set if this dir should not be deleted as long as the file
+ * system is mounted. If this flag is not set, the GPDir
+ * structure is only a cache.
+ */
+ uio_GPDirExtra extra;
+ /* extra internal data for some filesystem types */
+ uio_PRoot *pRoot;
+ uio_GPDirEntries *entries;
+};
+
+
+/*
+ * uio_GPFile
+ * Represents a file in a physical directory structure.
+ * sub-'class' of uio_GPDirEntry
+ */
+struct uio_GPFile {
+ uio_GPDirEntry_COMMON
+# define uio_GPFile_NOCACHE uio_GPDirEntry_NOCACHE
+ /* Info on this file will not be cached. */
+ uio_GPFileExtra extra;
+ /* extra internal data for some filesystem types */
+ uio_PRoot *pRoot;
+};
+
+
+static inline uio_bool
+uio_GPDirEntry_isReg(uio_GPDirEntry *gPDirEntry) {
+ return (gPDirEntry->flags & uio_GPDirEntry_TYPEMASK) ==
+ uio_GPDirEntry_TYPE_REG;
+}
+
+static inline uio_bool
+uio_GPDirEntry_isDir(uio_GPDirEntry *gPDirEntry) {
+ return (gPDirEntry->flags & uio_GPDirEntry_TYPEMASK) ==
+ uio_GPDirEntry_TYPE_DIR;
+}
+
+
+#ifdef DEBUG
+void uio_GPDirEntry_print(FILE *outStream, uio_GPDirEntry *gPDirEntry);
+void uio_GPDir_print(FILE *outStream, uio_GPDir *gPDir);
+void uio_GPFile_print(FILE *outStream, uio_GPFile *pFile);
+#endif
+
+uio_NativeEntriesContext uio_GPDir_openEntries(uio_PDirHandle *pDirHandle);
+int uio_GPDir_readEntries(uio_NativeEntriesContext *iterator,
+ char *buf, size_t len);
+void uio_GPDir_closeEntries(uio_NativeEntriesContext iterator);
+int uio_GPDir_entryCount(const uio_GPDir *gPDir);
+
+int uio_gPDirFlagsFromPRootFlags(int flags);
+int uio_gPFileFlagsFromPRootFlags(int flags);
+uio_PRoot *uio_GPRoot_makePRoot(uio_FileSystemHandler *handler, int pRootFlags,
+ uio_GPRoot_Operations *ops, uio_GPRootExtra gPRootExtra, int gPRootFlags,
+ uio_Handle *handle, uio_GPDirExtra gPDirExtra, int gPDirFlags);
+int uio_GPRoot_umount(uio_PRoot *pRoot);
+
+uio_GPDir *uio_GPDir_prepareSubDir(uio_GPDir *gPDir, const char *dirName);
+void uio_GPDir_commitSubDir(uio_GPDir *gPDir, const char *dirName,
+ uio_GPDir *subDir);
+void uio_GPDir_addFile(uio_GPDir *gPDir, const char *fileName,
+ uio_GPFile *file);
+void uio_GPDir_removeFile(uio_GPDir *gPDir, const char *fileName);
+void uio_GPDir_removeSubDir(uio_GPDir *gPDir, const char *dirName);
+void uio_GPDir_setComplete(uio_GPDir *gPDir, uio_bool flag);
+uio_GPDirEntry *uio_GPDir_getGPDirEntry(uio_GPDir *gPDir,
+ const char *name);
+uio_PDirEntryHandle *uio_GPDir_getPDirEntryHandle(
+ const uio_PDirHandle *pDirHandle, const char *name);
+int uio_walkGPPath(uio_GPDir *startGPDir, const char *path,
+ size_t pathLen, uio_GPDir **endGPDir, const char **pathRest);
+uio_PDirHandle *uio_GPDir_makePDirHandle(uio_GPDir *gPDir);
+
+void uio_GPDir_fill(uio_GPDir *gPDir);
+void uio_GPRoot_deleteGPRootExtra(uio_GPRoot *gPRoot);
+void uio_GPDir_deleteGPDirExtra(uio_GPDir *gPDir);
+void uio_GPFile_deleteGPFileExtra(uio_GPFile *gPFile);
+
+void uio_GPDirHandle_delete(uio_GPDirHandle *gPDirHandle);
+void uio_GPFileHandle_delete(uio_GPFileHandle *gPFileHandle);
+void uio_GPDirEntry_delete(uio_GPDirEntry *gPDirEntry);
+uio_GPRoot *uio_GPRoot_new(uio_GPRoot_Operations *ops, uio_GPRootExtra extra,
+ int flags);
+void uio_GPRoot_delete(uio_GPRoot *gPRoot);
+uio_GPDir *uio_GPDir_new(uio_PRoot *pRoot, uio_GPDirExtra extra, int flags);
+void uio_GPDir_delete(uio_GPDir *gPDir);
+uio_GPFile *uio_GPFile_new(uio_PRoot *pRoot, uio_GPFileExtra extra, int flags);
+void uio_GPFile_delete(uio_GPFile *gPFile);
+
+
+static inline void
+uio_GPDirEntry_ref(uio_GPDirEntry *gPDirEntry) {
+#ifdef uio_MEM_DEBUG
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_MemDebug_debugRef(uio_GPDir, (void *) gPDirEntry);
+ } else {
+ uio_MemDebug_debugRef(uio_GPFile, (void *) gPDirEntry);
+ }
+#endif
+ gPDirEntry->ref++;
+}
+
+static inline void
+uio_GPDirEntry_unref(uio_GPDirEntry *gPDirEntry) {
+ assert(gPDirEntry->ref > 0);
+#ifdef uio_MEM_DEBUG
+ if (uio_GPDirEntry_isDir(gPDirEntry)) {
+ uio_MemDebug_debugUnref(uio_GPDir, (void *) gPDirEntry);
+ } else {
+ uio_MemDebug_debugUnref(uio_GPFile, (void *) gPDirEntry);
+ }
+#endif
+ gPDirEntry->ref--;
+ if (gPDirEntry->ref == 0)
+ uio_GPDirEntry_delete(gPDirEntry);
+}
+
+static inline void
+uio_GPDir_ref(uio_GPDir *gPDir) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugRef(uio_GPDir, (void *) gPDir);
+#endif
+ gPDir->ref++;
+}
+
+static inline void
+uio_GPDir_unref(uio_GPDir *gPDir) {
+ assert(gPDir->ref > 0);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugUnref(uio_GPDir, (void *) gPDir);
+#endif
+ gPDir->ref--;
+ if (gPDir->ref == 0)
+ uio_GPDir_delete(gPDir);
+}
+
+static inline void
+uio_GPFile_ref(uio_GPFile *gPFile) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugRef(uio_GPFile, (void *) gPFile);
+#endif
+ gPFile->ref++;
+}
+
+static inline void
+uio_GPFile_unref(uio_GPFile *gPFile) {
+ assert(gPFile->ref > 0);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugUnref(uio_GPFile, (void *) gPFile);
+#endif
+ gPFile->ref--;
+ if (gPFile->ref == 0)
+ uio_GPFile_delete(gPFile);
+}
+
+
+#endif /* LIBS_UIO_GPHYS_H_ */
+
diff --git a/src/libs/uio/hashtable.c b/src/libs/uio/hashtable.c
new file mode 100644
index 0000000..1f376e1
--- /dev/null
+++ b/src/libs/uio/hashtable.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef HASHTABLE_INTERNAL
+ // If HASHTABLE_INTERNAL is already defined, this file is included
+ // as a template. In this case hashtable.h has already been included.
+# define HASHTABLE_INTERNAL
+# include "hashtable.h"
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <math.h>
+
+#include "mem.h"
+#include "uioport.h"
+
+static void HASHTABLE_(setup)(HASHTABLE_(HashTable) *HashTable,
+ uio_uint32 size);
+static void HASHTABLE_(resize)(HASHTABLE_(HashTable) *hashTable);
+static inline uio_uint32 nextPower2(uio_uint32 x);
+
+static inline HASHTABLE_(HashTable) *HASHTABLE_(allocHashTable)(void);
+static inline HASHTABLE_(HashEntry) *HASHTABLE_(newHashEntry)(uio_uint32 hash,
+ HASHTABLE_(Key) *key, HASHTABLE_(Value) *value,
+ HASHTABLE_(HashEntry) *next);
+static inline HASHTABLE_(HashEntry) *HASHTABLE_(allocHashEntry)(void);
+static inline void HASHTABLE_(freeHashEntry)(
+ HASHTABLE_(HashEntry) *entry);
+
+// Create a new HashTable.
+HASHTABLE_(HashTable) *
+HASHTABLE_(newHashTable)(
+ HASHTABLE_(HashFunction) hashFunction,
+ HASHTABLE_(EqualFunction) equalFunction,
+ HASHTABLE_(CopyFunction) copyFunction,
+ HASHTABLE_(FreeKeyFunction) freeKeyFunction,
+ HASHTABLE_(FreeValueFunction) freeValueFunction,
+ uio_uint32 initialSize,
+ double minFillQuotient,
+ double maxFillQuotient) {
+ HASHTABLE_(HashTable) *hashTable;
+
+ assert(maxFillQuotient >= minFillQuotient);
+
+ hashTable = HASHTABLE_(allocHashTable)();
+ hashTable->hashFunction = hashFunction;
+ hashTable->equalFunction = equalFunction;
+ hashTable->copyFunction = copyFunction;
+ hashTable->freeKeyFunction = freeKeyFunction;
+ hashTable->freeValueFunction = freeValueFunction;
+
+ hashTable->minFillQuotient = minFillQuotient;
+ hashTable->maxFillQuotient = maxFillQuotient;
+ HASHTABLE_(setup)(hashTable, initialSize);
+
+ return hashTable;
+}
+
+// Add an entry to the HashTable.
+uio_bool
+HASHTABLE_(add)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key, HASHTABLE_(Value) *value) {
+ uio_uint32 hash;
+ struct HASHTABLE_(HashEntry) *entry;
+
+ hash = HASHTABLE_(HASH)(hashTable, key);
+ entry = hashTable->entries[hash & hashTable->hashMask];
+ while (entry != NULL) {
+ if (HASHTABLE_(EQUAL)(hashTable, key, entry->key)) {
+ // key is already present
+ return false;
+ }
+ entry = entry->next;
+ }
+
+#ifdef HashTable_PROFILE
+ if (hashTable->entries[hash & hashTable->hashMask] != NULL)
+ hashTable->numCollisions++;
+#endif
+ hashTable->entries[hash & hashTable->hashMask] =
+ HASHTABLE_(newHashEntry)(hash,
+ HASHTABLE_(COPY)(hashTable, key), value,
+ hashTable->entries[hash & hashTable->hashMask]);
+
+ hashTable->numEntries++;
+ if (hashTable->numEntries > hashTable->maxSize)
+ HASHTABLE_(resize)(hashTable);
+
+ return true;
+}
+
+// Remove an entry with a specified Key from the HashTable.
+uio_bool
+HASHTABLE_(remove)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key) {
+ uio_uint32 hash;
+ struct HASHTABLE_(HashEntry) **entry, *next;
+
+ hash = HASHTABLE_(HASH)(hashTable, key);
+ entry = &hashTable->entries[hash & hashTable->hashMask];
+ while (1) {
+ if (*entry == NULL)
+ return false;
+ if (HASHTABLE_(EQUAL)(hashTable, key, (*entry)->key)) {
+ // found the key
+ break;
+ }
+ entry = &(*entry)->next;
+ }
+ next = (*entry)->next;
+ HASHTABLE_(FREEKEY)(hashTable, (*entry)->key);
+ HASHTABLE_(FREEVALUE)(hashTable, (*entry)->value);
+ HASHTABLE_(freeHashEntry)(*entry);
+ *entry = next;
+
+ hashTable->numEntries--;
+ if (hashTable->numEntries < hashTable->minSize)
+ HASHTABLE_(resize)(hashTable);
+
+ return true;
+}
+
+// Find the Value stored for some Key.
+HASHTABLE_(Value) *
+HASHTABLE_(find)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key) {
+ uio_uint32 hash;
+ struct HASHTABLE_(HashEntry) *entry;
+
+ hash = HASHTABLE_(HASH)(hashTable, key);
+ entry = hashTable->entries[hash & hashTable->hashMask];
+ while (entry != NULL) {
+ if (HASHTABLE_(EQUAL)(hashTable, key, entry->key)) {
+ // found the key
+ return entry->value;
+ }
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+// Returns the number of entries in the HashTable.
+uio_uint32
+HASHTABLE_(count)(const HASHTABLE_(HashTable) *hashTable) {
+ return hashTable->numEntries;
+}
+
+// Auxiliary function to (re)initialise the buckets in the HashTable.
+static void
+HASHTABLE_(setup)(HASHTABLE_(HashTable) *hashTable, uio_uint32 initialSize) {
+ if (initialSize < 4)
+ initialSize = 4;
+ hashTable->size = nextPower2(ceil(((double) initialSize) /
+ hashTable->maxFillQuotient));
+ hashTable->hashMask = hashTable->size - 1;
+ hashTable->minSize = ceil(((double) (hashTable->size >> 1))
+ * hashTable->minFillQuotient);
+ hashTable->maxSize = floor(((double) hashTable->size)
+ * hashTable->maxFillQuotient);
+ hashTable->entries = uio_calloc(hashTable->size,
+ sizeof (HASHTABLE_(HashEntry) *));
+ hashTable->numEntries = 0;
+#ifdef HashTable_PROFILE
+ hashTable->numCollisions = 0;
+#endif
+}
+
+// Resize the buckets in the HashTable.
+static void
+HASHTABLE_(resize)(HASHTABLE_(HashTable) *hashTable) {
+ HASHTABLE_(HashEntry) **oldEntries;
+ HASHTABLE_(HashEntry) *entry, *next;
+ HASHTABLE_(HashEntry) **newLocation;
+ uio_uint32 oldNumEntries;
+ uio_uint32 i;
+
+ oldNumEntries = hashTable->numEntries;
+ oldEntries = hashTable->entries;
+
+ HASHTABLE_(setup)(hashTable, hashTable->numEntries);
+ hashTable->numEntries = oldNumEntries;
+
+ i = 0;
+ while (oldNumEntries > 0) {
+ entry = oldEntries[i];
+ while (entry != NULL) {
+ next = entry->next;
+ newLocation = &hashTable->entries[entry->hash &
+ hashTable->hashMask];
+#ifdef HashTable_PROFILE
+ if (*newLocation != NULL)
+ hashTable->numCollisions++;
+#endif
+ entry->next = *newLocation;
+ *newLocation = entry;
+ oldNumEntries--;
+ entry = next;
+ }
+ i++;
+ }
+
+ uio_free(oldEntries);
+}
+
+// Adapted from "Hackers Delight"
+// Returns the smallest power of two greater or equal to x.
+static inline uio_uint32
+nextPower2(uio_uint32 x) {
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return x + 1;
+}
+
+// Get an iterator to iterate through all the entries in the HashTable.
+// NB: Iterator should be considered invalid if the HashTable is changed.
+// TODO: change this (make it thread-safe)
+// this can be done by only marking items as deleted when
+// there are outstanding iterators.
+HASHTABLE_(Iterator) *
+HASHTABLE_(getIterator)(const HASHTABLE_(HashTable) *hashTable) {
+ HASHTABLE_(Iterator) *iterator;
+ uio_uint32 i;
+
+ iterator = uio_malloc(sizeof (HASHTABLE_(Iterator)));
+ iterator->hashTable = hashTable;
+
+ // Look for the first used bucket.
+ for (i = 0; i < iterator->hashTable->size; i++) {
+ if (iterator->hashTable->entries[i] != NULL) {
+ // Found a used bucket.
+ iterator->bucketNr = i;
+ iterator->entry = iterator->hashTable->entries[i];
+ return iterator;
+ }
+ }
+
+ // No entries were found.
+ iterator->bucketNr = i;
+ iterator->entry = NULL;
+ return iterator;
+}
+
+// Returns true if and only if there are no more entries in the hash table
+// for the Iterator to find.
+int
+HASHTABLE_(iteratorDone)(const HASHTABLE_(Iterator) *iterator) {
+ return iterator->bucketNr >= iterator->hashTable->size;
+}
+
+// Get the Key of the entry pointed to by an Iterator.
+HASHTABLE_(Key) *
+HASHTABLE_(iteratorKey)(HASHTABLE_(Iterator) *iterator) {
+ return iterator->entry->key;
+}
+
+// Get the Value of the entry pointed to by an Iterator.
+HASHTABLE_(Value) *
+HASHTABLE_(iteratorValue)(HASHTABLE_(Iterator) *iterator) {
+ return iterator->entry->value;
+}
+
+// Move the Iterator to the next entry in the HashTable.
+// Should not be called if the iterator is already past the last entry.
+HASHTABLE_(Iterator) *
+HASHTABLE_(iteratorNext)(HASHTABLE_(Iterator) *iterator) {
+ uio_uint32 i;
+
+ // If there's another entry in this bucket, use that.
+ iterator->entry = iterator->entry->next;
+ if (iterator->entry != NULL)
+ return iterator;
+
+ // Look for the next used bucket.
+ for (i = iterator->bucketNr + 1; i < iterator->hashTable->size; i++) {
+ if (iterator->hashTable->entries[i] != NULL) {
+ // Found another used bucket.
+ iterator->bucketNr = i;
+ iterator->entry = iterator->hashTable->entries[i];
+ return iterator;
+ }
+ }
+
+ // No more entries were found.
+ iterator->bucketNr = i;
+ iterator->entry = NULL;
+ return iterator;
+}
+
+// Free the Iterator.
+void
+HASHTABLE_(freeIterator)(HASHTABLE_(Iterator) *iterator) {
+ uio_free(iterator);
+}
+
+// Auxiliary function to allocate a HashTable.
+static inline HASHTABLE_(HashTable) *
+HASHTABLE_(allocHashTable)(void) {
+ return uio_malloc(sizeof (HASHTABLE_(HashTable)));
+}
+
+// Auxiliary function to create a HashEntry.
+static inline HASHTABLE_(HashEntry) *
+HASHTABLE_(newHashEntry)(uio_uint32 hash, HASHTABLE_(Key) *key,
+ HASHTABLE_(Value) *value, HASHTABLE_(HashEntry) *next) {
+ HASHTABLE_(HashEntry) *result;
+
+ result = HASHTABLE_(allocHashEntry)();
+ result->hash = hash;
+ result->key = key;
+ result->value = value;
+ result->next = next;
+ return result;
+}
+
+// Allocate a new HashEntry.
+static inline HASHTABLE_(HashEntry) *
+HASHTABLE_(allocHashEntry)(void) {
+ return uio_malloc(sizeof (HASHTABLE_(HashEntry)));
+}
+
+// Delete the HashTable.
+void
+HASHTABLE_(deleteHashTable)(HASHTABLE_(HashTable) *hashTable) {
+ uio_uint32 i;
+ HASHTABLE_(HashEntry) *entry, *next;
+ HASHTABLE_(HashEntry) **bucketPtr;
+
+ i = hashTable->numEntries;
+ bucketPtr = hashTable->entries;
+ while (i > 0) {
+ entry = *bucketPtr;
+ while (entry != NULL) {
+ next = entry->next;
+ HASHTABLE_(FREEKEY)(hashTable, entry->key);
+ HASHTABLE_(FREEVALUE)(hashTable, entry->value);
+ HASHTABLE_(freeHashEntry)(entry);
+ entry = next;
+ i--;
+ }
+ bucketPtr++;
+ }
+ uio_free(hashTable->entries);
+ uio_free(hashTable);
+}
+
+// Auxiliary function to deallocate a HashEntry.
+static inline void
+HASHTABLE_(freeHashEntry)(HASHTABLE_(HashEntry) *entry) {
+ uio_free(entry);
+}
+
diff --git a/src/libs/uio/hashtable.h b/src/libs/uio/hashtable.h
new file mode 100644
index 0000000..eb4437f
--- /dev/null
+++ b/src/libs/uio/hashtable.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// The 'already included' check must be done slightly more complicated
+// than usually. This file may be included directly only once,
+// but it may be included my derivative HashTable definitions that use
+// this file as a template more than once.
+#if !defined(_HASHTABLE_H) || defined(HASHTABLE_GENERIC)
+#if defined(HASHTABLE_)
+# define HASHTABLE_GENERIC
+#endif
+
+#include "types.h"
+#include "uioport.h"
+
+// Define to enable profiling.
+#define HashTable_PROFILE
+
+// You can use inline hash functions for extra speed, by using this file as
+// a template.
+// To do this, make a new .h and .c file. In the .h file, define the macros
+// (and typedefs) from the HASHTABLE_ block below.
+// In the .c file, #define HASHTABLE_INTERNAL, #include the .h file
+// and hashtable.c (in this order), and add the necessary functions.
+// If you do not need to free the Value, you can define HashTable_FREEVALUE
+// as a no-op.
+#ifndef HASHTABLE_
+# define HASHTABLE_(identifier) HashTable ## _ ## identifier
+ typedef void HashTable_Key;
+ typedef void HashTable_Value;
+# define HashTable_HASH(hashTable, hashValue) \
+ (hashTable)->hashFunction(hashValue)
+# define HashTable_EQUAL(hashTable, hashKey1, hashKey2) \
+ (hashTable)->equalFunction(hashKey1, hashKey2)
+# define HashTable_COPY(hashTable, hashKey) \
+ (hashTable)->copyFunction(hashKey)
+# define HashTable_FREEKEY(hashTable, hashKey) \
+ (hashTable)->freeKeyFunction(hashKey)
+# define HashTable_FREEVALUE(hashTable, hashValue) \
+ (hashTable)->freeValueFunction(hashValue)
+#endif
+
+
+
+typedef uio_uint32 (*HASHTABLE_(HashFunction))(const HASHTABLE_(Key) *);
+typedef uio_bool (*HASHTABLE_(EqualFunction))(const HASHTABLE_(Key) *,
+ const HASHTABLE_(Key) *);
+typedef HASHTABLE_(Value) *(*HASHTABLE_(CopyFunction))(
+ const HASHTABLE_(Key) *);
+typedef void (*HASHTABLE_(FreeKeyFunction))(HASHTABLE_(Key) *);
+typedef void (*HASHTABLE_(FreeValueFunction))(HASHTABLE_(Value) *);
+
+typedef struct HASHTABLE_(HashTable) HASHTABLE_(HashTable);
+typedef struct HASHTABLE_(HashEntry) HASHTABLE_(HashEntry);
+typedef struct HASHTABLE_(Iterator) HASHTABLE_(Iterator);
+
+struct HASHTABLE_(HashTable) {
+ HASHTABLE_(HashFunction) hashFunction;
+ // Function creating a uio_uint32 hash of a key.
+ HASHTABLE_(EqualFunction) equalFunction;
+ // Function used to compare two keys.
+ HASHTABLE_(CopyFunction) copyFunction;
+ // Function used to copy a key.
+ HASHTABLE_(FreeKeyFunction) freeKeyFunction;
+ // Function used to free a key.
+ HASHTABLE_(FreeValueFunction) freeValueFunction;
+ // Function used to free a value. Called when an entry is
+ // removed using the remove function, or for entries
+ // still in the HashTable when the HashTable is deleted.
+
+ double minFillQuotient;
+ // How much of half of the hashtable needs to be filled
+ // before resizing to size/2.
+ double maxFillQuotient;
+ // How much of the hashTable needs to be filled before
+ // resizing to size*2.
+ uio_uint32 minSize;
+ // Resize to size/2 when below this size.
+ uio_uint32 maxSize;
+ // Resize to size*2 when above this size.
+ uio_uint32 size;
+ // The number of buckets in the hash table.
+ uio_uint32 hashMask;
+ // Mask to take on a the calculated hash value, to make it
+ // fit into the table.
+
+ HASHTABLE_(HashEntry) **entries;
+ // The actual entries
+
+ uio_uint32 numEntries;
+#ifdef HashTable_PROFILE
+ uio_uint32 numCollisions;
+#endif
+};
+
+struct HASHTABLE_(HashEntry) {
+ uio_uint32 hash;
+ HASHTABLE_(Key) *key;
+ HASHTABLE_(Value) *value;
+ HASHTABLE_(HashEntry) *next;
+};
+
+struct HASHTABLE_(Iterator) {
+ const HASHTABLE_(HashTable) *hashTable;
+ uio_uint32 bucketNr;
+ HASHTABLE_(HashEntry) *entry;
+};
+
+HASHTABLE_(HashTable) *HASHTABLE_(newHashTable)(
+ HASHTABLE_(HashFunction) hashFunction,
+ HASHTABLE_(EqualFunction) equalFunction,
+ HASHTABLE_(CopyFunction) copyFunction,
+ HASHTABLE_(FreeKeyFunction) freeKeyFunction,
+ HASHTABLE_(FreeValueFunction) freeValueFunction,
+ uio_uint32 initialSize,
+ double minFillQuotient, double maxFillQuotient);
+uio_bool HASHTABLE_(add)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key, HASHTABLE_(Value) *value);
+uio_bool HASHTABLE_(remove)(HASHTABLE_(HashTable) *hashTable,
+ const HASHTABLE_(Key) *key);
+HASHTABLE_(Value) *HASHTABLE_(find)(
+ HASHTABLE_(HashTable) *hashTable, const HASHTABLE_(Key) *key);
+uio_uint32 HASHTABLE_(count)(const HASHTABLE_(HashTable) *hashTable);
+void HASHTABLE_(deleteHashTable)(HASHTABLE_(HashTable) *hashTable);
+HASHTABLE_(Iterator) *HASHTABLE_(getIterator)(
+ const HASHTABLE_(HashTable) *hashTable);
+int HASHTABLE_(iteratorDone)(const HASHTABLE_(Iterator) *iterator);
+HASHTABLE_(Key) *HASHTABLE_(iteratorKey)(HASHTABLE_(Iterator) *iterator);
+HASHTABLE_(Value) *HASHTABLE_(iteratorValue)(HASHTABLE_(Iterator) *iterator);
+HASHTABLE_(Iterator) *HASHTABLE_(iteratorNext)(HASHTABLE_(Iterator) *iterator);
+void HASHTABLE_(freeIterator)(HASHTABLE_(Iterator) *iterator);
+
+#ifndef HASHTABLE_INTERNAL
+# undef HASHTABLE_
+#endif
+
+#endif /* !defined(_HASHTABLE_H) || defined(HASHTABLE_GENERIC) */
+
+
+
diff --git a/src/libs/uio/io.c b/src/libs/uio/io.c
new file mode 100644
index 0000000..247d1e2
--- /dev/null
+++ b/src/libs/uio/io.c
@@ -0,0 +1,1859 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "iointrn.h"
+#include "ioaux.h"
+#include "mount.h"
+#include "fstypes.h"
+#include "mounttree.h"
+#include "physical.h"
+#include "paths.h"
+#include "mem.h"
+#include "uioutils.h"
+#include "uioport.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+#if 0
+static int uio_accessDir(uio_DirHandle *dirHandle, const char *path,
+ int mode);
+#endif
+static int uio_statDir(uio_DirHandle *dirHandle, const char *path,
+ struct stat *statBuf);
+static int uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf);
+
+static void uio_PDirHandles_delete(uio_PDirHandle *pDirHandles[],
+ int numPDirHandles);
+
+static inline uio_PDirHandle *uio_PDirHandle_alloc(void);
+static inline void uio_PDirHandle_free(uio_PDirHandle *pDirHandle);
+static inline uio_PFileHandle *uio_PFileHandle_alloc(void);
+static inline void uio_PFileHandle_free(uio_PFileHandle *pFileHandle);
+
+static uio_DirHandle *uio_DirHandle_new(uio_Repository *repository, char *path,
+ char *rootEnd);
+static inline uio_DirHandle *uio_DirHandle_alloc(void);
+static inline void uio_DirHandle_free(uio_DirHandle *dirHandle);
+
+static inline uio_Handle *uio_Handle_alloc(void);
+static inline void uio_Handle_free(uio_Handle *handle);
+
+static uio_MountHandle *uio_MountHandle_new(uio_Repository *repository,
+ uio_MountInfo *mountInfo);
+static inline void uio_MountHandle_delete(uio_MountHandle *mountHandle);
+static inline uio_MountHandle *uio_MountHandle_alloc(void);
+static inline void uio_MountHandle_free(uio_MountHandle *mountHandle);
+
+
+
+void
+uio_init(void) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_init();
+#endif
+ uio_registerDefaultFileSystems();
+}
+
+void
+uio_unInit(void) {
+ uio_unRegisterDefaultFileSystems();
+#ifdef uio_MEM_DEBUG
+# ifdef DEBUG
+ uio_MemDebug_printPointers(stderr);
+ fflush(stderr);
+# endif
+ uio_MemDebug_unInit();
+#endif
+}
+
+uio_Repository *
+uio_openRepository(int flags) {
+ return uio_Repository_new(flags);
+}
+
+void
+uio_closeRepository(uio_Repository *repository) {
+ uio_unmountAllDirs(repository);
+ uio_Repository_unref(repository);
+}
+
+/*
+ * Function name: uio_mountDir
+ * Description: Grafts a directory from inside a physical fileSystem
+ * into the locical filesystem, at a specified directory.
+ * Arguments: destRep - the repository where the newly mounted dir
+ * is to be grafted.
+ * mountPoint - the path to the directory where the dir
+ * is to be grafted.
+ * fsType - the file system type of physical fileSystem
+ * pointed to by sourcePath.
+ * sourceDir - the directory to which 'sourcePath' is to
+ * be taken relative.
+ * sourcePath - a path relative to sourceDir, which contains
+ * the file/directory to be mounted.
+ * If sourceDir and sourcePath are NULL, the file
+ * system of the operating system will be used.
+ * inPath - the location relative to the root of the newly
+ * mounted fileSystem, pointing to the directory
+ * that is to be grafted.
+ * Note: If fsType is uio_FSTYPE_STDIO, inPath is
+ * relative to the root of the filesystem, NOT to
+ * the current working dir.
+ * autoMount - array of automount options in function
+ * in this mountPoint.
+ * flags - one of uio_MOUNT_TOP, uio_MOUNT_BOTTOM,
+ * uio_MOUNT_BELOW, uio_MOUNT_ABOVE, specifying
+ * the precedence of this mount, OR'ed with
+ * one or more of the following flags:
+ * uio_MOUNT_RDONLY (no writing is allowed)
+ * relative - If 'flags' includes uio_MOUNT_BELOW or
+ * uio_MOUNT_ABOVE, this is the mount handle
+ * where the new mount is relative to.
+ * Otherwise, it should be NULL.
+ * Returns: a handle suitable for uio_unmountDir()
+ * NULL if an error occured. In this case 'errno' is set.
+ */
+uio_MountHandle *
+uio_mountDir(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative) {
+ uio_PRoot *pRoot;
+ uio_Handle *handle;
+ uio_FileSystemHandler *handler;
+ uio_MountInfo *relativeInfo;
+
+ switch (flags & uio_MOUNT_LOCATION_MASK) {
+ case uio_MOUNT_TOP:
+ case uio_MOUNT_BOTTOM:
+ if (relative != NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = NULL;
+ break;
+ case uio_MOUNT_BELOW:
+ case uio_MOUNT_ABOVE:
+ if (relative == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = relative->mountInfo;
+ break;
+ default:
+ abort();
+ }
+
+ if (mountPoint[0] == '/')
+ mountPoint++;
+ if (!validPathName(mountPoint, strlen(mountPoint))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ // TODO: check if the filesystem is already mounted, and if so, reuse it.
+ // A RO filedescriptor will need to be replaced though if the
+ // filesystem needs to be remounted RW now.
+ if (sourceDir == NULL) {
+ if (sourcePath != NULL) {
+ // bad: sourceDir is NULL, but sourcePath isn't
+ errno = EINVAL;
+ return NULL;
+ }
+ handle = NULL;
+ } else {
+ if (sourcePath == NULL) {
+ // bad: sourcePath is NULL, but sourceDir isn't
+ errno = EINVAL;
+ return NULL;
+ }
+ handle = uio_open(sourceDir, sourcePath,
+ ((flags & uio_MOUNT_RDONLY) == uio_MOUNT_RDONLY ?
+ O_RDONLY : O_RDWR)
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (handle == NULL) {
+ // errno is set
+ return NULL;
+ }
+ }
+
+ handler = uio_getFileSystemHandler(fsType);
+ if (handler == NULL) {
+ if (handle)
+ uio_close(handle);
+ errno = ENODEV;
+ return NULL;
+ }
+
+ assert(handler->mount != NULL);
+ pRoot = (handler->mount)(handle, flags);
+ if (pRoot == NULL) {
+ int savedErrno;
+
+ savedErrno = errno;
+ if (handle)
+ uio_close(handle);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ if (handle) {
+ // Close this reference to handle.
+ // The physical layer may store the link in pRoot, in which it
+ // will be cleaned up from uio_unmount().
+ uio_close(handle);
+ }
+
+ // The new file system is ready, now we need to find the specified
+ // dir inside it and put it in its place in the mountTree.
+ {
+ uio_PDirHandle *endDirHandle;
+ const char *endInPath;
+ char *dirName;
+ uio_MountInfo *mountInfo;
+ uio_MountTree *mountTree;
+ uio_PDirHandle *pRootHandle;
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ char *unixPath;
+
+ unixPath = dosToUnixPath(inPath);
+ inPath = unixPath;
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+
+ if (inPath[0] == '/')
+ inPath++;
+ pRootHandle = uio_PRoot_getRootDirHandle(pRoot);
+ uio_walkPhysicalPath(pRootHandle, inPath, strlen(inPath),
+ &endDirHandle, &endInPath);
+ if (*endInPath != '\0') {
+ // Path inside the filesystem to mount does not exist.
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ uio_free(unixPath);
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+ uio_PDirHandle_unref(endDirHandle);
+ uio_PRoot_unrefMount(pRoot);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ dirName = uio_malloc(endInPath - inPath + 1);
+ memcpy(dirName, inPath, endInPath - inPath);
+ dirName[endInPath - inPath] = '\0';
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ // InPath is a copy with the paths fixed.
+ uio_free(unixPath);
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+ mountInfo = uio_MountInfo_new(fsType, NULL, endDirHandle, dirName,
+ autoMount, NULL, flags);
+ uio_repositoryAddMount(destRep, mountInfo,
+ flags & uio_MOUNT_LOCATION_MASK, relativeInfo);
+ mountTree = uio_mountTreeAddMountInfo(destRep, destRep->mountTree,
+ mountInfo, mountPoint, flags & uio_MOUNT_LOCATION_MASK,
+ relativeInfo);
+ // mountTree is the node in destRep->mountTree where mountInfo
+ // leads to.
+ mountInfo->mountTree = mountTree;
+ mountInfo->mountHandle = uio_MountHandle_new(destRep, mountInfo);
+ return mountInfo->mountHandle;
+ }
+}
+
+// Mount a repository directory into same repository at a different location
+// From fossil.
+uio_MountHandle *
+uio_transplantDir(const char *mountPoint, uio_DirHandle *sourceDir, int flags,
+ uio_MountHandle *relative) {
+ uio_MountInfo *relativeInfo;
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+ uio_MountTreeItem **treeItems;
+ int i;
+ uio_MountHandle *handle = NULL;
+
+ if ((flags & uio_MOUNT_RDONLY) != uio_MOUNT_RDONLY) {
+ // Only read-only transplants supported atm
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ switch (flags & uio_MOUNT_LOCATION_MASK) {
+ case uio_MOUNT_TOP:
+ case uio_MOUNT_BOTTOM:
+ if (relative != NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = NULL;
+ break;
+ case uio_MOUNT_BELOW:
+ case uio_MOUNT_ABOVE:
+ if (relative == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ relativeInfo = relative->mountInfo;
+ break;
+ default:
+ abort();
+ }
+
+ if (mountPoint[0] == '/')
+ mountPoint++;
+ if (!validPathName(mountPoint, strlen(mountPoint))) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (uio_getPathPhysicalDirs(sourceDir, "", 0,
+ &pDirHandles, &numPDirHandles, &treeItems) == -1) {
+ // errno is set
+ return NULL;
+ }
+ if (numPDirHandles == 0) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ // TODO: We only transplant the first read-only physical dir that we find
+ // Maybe transplant all of them? We would then have several
+ // uio_MountHandles to return.
+ for (i = 0; i < numPDirHandles; ++i) {
+ uio_PDirHandle *pDirHandle = pDirHandles[i];
+ uio_MountInfo *oldMountInfo = treeItems[i]->mountInfo;
+ uio_Repository *rep = oldMountInfo->mountHandle->repository;
+ uio_MountInfo *mountInfo;
+ uio_MountTree *mountTree;
+
+ // Only interested in read-only dirs in this incarnation
+ if (!uio_mountInfoIsReadOnly(oldMountInfo))
+ continue;
+
+ mountInfo = uio_MountInfo_new(oldMountInfo->fsID, NULL, pDirHandle,
+ uio_strdup(""), oldMountInfo->autoMount, NULL, flags);
+ // New mount references the same handles
+ uio_PDirHandle_ref(pDirHandle);
+ uio_PRoot_refMount(pDirHandle->pRoot);
+
+ uio_repositoryAddMount(rep, mountInfo,
+ flags & uio_MOUNT_LOCATION_MASK, relativeInfo);
+ mountTree = uio_mountTreeAddMountInfo(rep, rep->mountTree,
+ mountInfo, mountPoint, flags & uio_MOUNT_LOCATION_MASK,
+ relativeInfo);
+ // mountTree is the node in rep->mountTree where mountInfo leads to
+ mountInfo->mountTree = mountTree;
+ mountInfo->mountHandle = uio_MountHandle_new(rep, mountInfo);
+ handle = mountInfo->mountHandle;
+ break;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(treeItems);
+
+ if (handle == NULL)
+ errno = ENOENT;
+
+ return handle;
+}
+
+int
+uio_unmountDir(uio_MountHandle *mountHandle) {
+ uio_PRoot *pRoot;
+
+ pRoot = mountHandle->mountInfo->pDirHandle->pRoot;
+
+ // check if it's in use
+#ifdef DEBUG
+ if (pRoot->mountRef == 1 && pRoot->handleRef > 0) {
+ fprintf(stderr, "Warning: File system to be unmounted still "
+ "has file descriptors open. The file system will not "
+ "be deallocated until these are all closed.\n");
+ }
+#endif
+
+ // TODO: lock (and furtheron unlock) repository
+
+ // remove from mount tree
+ uio_mountTreeRemoveMountInfo(mountHandle->repository,
+ mountHandle->mountInfo->mountTree,
+ mountHandle->mountInfo);
+
+ // remove from mount list.
+ uio_repositoryRemoveMount(mountHandle->repository,
+ mountHandle->mountInfo);
+
+ uio_MountInfo_delete(mountHandle->mountInfo);
+
+ uio_MountHandle_delete(mountHandle);
+ uio_PRoot_unrefMount(pRoot);
+ return 0;
+}
+
+int
+uio_unmountAllDirs(uio_Repository *repository) {
+ int i;
+
+ i = repository->numMounts;
+ while (i--)
+ uio_unmountDir(repository->mounts[i]->mountHandle);
+ return 0;
+}
+
+uio_FileSystemID
+uio_getMountFileSystemType(uio_MountHandle *mountHandle) {
+ return mountHandle->mountInfo->fsID;
+}
+
+int
+uio_close(uio_Handle *handle) {
+ uio_Handle_unref(handle);
+ return 0;
+}
+
+int
+uio_rename(uio_DirHandle *oldDir, const char *oldPath,
+ uio_DirHandle *newDir, const char *newPath) {
+ uio_PDirHandle *oldPReadDir, *newPReadDir, *newPWriteDir;
+ uio_MountInfo *oldReadMountInfo, *newReadMountInfo, *newWriteMountInfo;
+ char *oldName, *newName;
+ int retVal;
+
+ if (uio_getPhysicalAccess(oldDir, oldPath, O_RDONLY, 0,
+ &oldReadMountInfo, &oldPReadDir, NULL,
+ NULL, NULL, NULL, &oldName) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (uio_getPhysicalAccess(newDir, newPath, O_WRONLY | O_CREAT | O_EXCL,
+ uio_GPA_NOWRITE, &newReadMountInfo, &newPReadDir, NULL,
+ &newWriteMountInfo, &newPWriteDir, NULL, &newName) == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_free(oldName);
+ errno = savedErrno;
+ return -1;
+ }
+
+ if (oldReadMountInfo != newWriteMountInfo) {
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = EXDEV;
+ return -1;
+ }
+
+ if (uio_mountInfoIsReadOnly(oldReadMountInfo)) {
+ // XXX: Doesn't uio_getPhysicalAccess already handle this?
+ // It doesn't return EROFS though; perhaps it should.
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = EROFS;
+ return -1;
+ }
+
+ if (oldReadMountInfo->pDirHandle->pRoot->handler->rename == NULL) {
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = ENOSYS;
+ return -1;
+ }
+ retVal = (oldReadMountInfo->pDirHandle->pRoot->handler->rename)(
+ oldPReadDir, oldName, newPWriteDir, newName);
+ if (retVal == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(oldPReadDir);
+ uio_PDirHandle_unref(newPReadDir);
+ uio_PDirHandle_unref(newPWriteDir);
+ uio_free(oldName);
+ uio_free(newName);
+ return 0;
+}
+
+int
+uio_access(uio_DirHandle *dir, const char *path, int mode) {
+ (void) dir;
+ (void) path;
+ (void) mode;
+ errno = ENOSYS; // Not implemented.
+ return -1;
+
+#if 0
+ uio_PDirHandle *pReadDir;
+ uio_MountInfo *readMountInfo;
+ char *name;
+ int result;
+
+ if (uio_getPhysicalAccess(dir, path, O_RDONLY, 0,
+ &readMountInfo, &pReadDir, NULL,
+ NULL, NULL, NULL, &name) == -1) {
+ // XXX: I copied this part from uio_stat(). Is this what I need?
+ if (uio_accessDir(dir, path, statBuf) == -1) {
+ // errno is set
+ return -1;
+ }
+ return 0;
+ }
+
+ if (pReadDir->pRoot->handler->access == NULL) {
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ result = (pReadDir->pRoot->handler->access)(pReadDir, name, mode);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ return result;
+#endif
+}
+
+#if 0
+// auxiliary function to uio_access
+static int
+uio_accessDir(uio_DirHandle *dirHandle, const char *path, int mode) {
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+
+ if (mode & R_OK)
+ {
+ // Read permission is always granted. Nothing to check here.
+ }
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path),
+ &pDirHandles, &numPDirHandles, NULL) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (numPDirHandles == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (mode & F_OK)
+ {
+ // We need to check whether each of the directories is complete
+
+ // WORK
+ }
+
+ if (mode & W_OK) {
+ // If there is any directory where writing is allowed, then
+ // we can write.
+
+ // WORK
+ errno = ENOENT;
+ return -1;
+
+#if 0
+ if (uio_statOneDir(pDirHandles[0], statBuf) == -1) {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+ // TODO: atm, fstat'ing a dir will show the info for the topmost
+ // dir. Maybe it would make sense of merging the bits. (How?)
+
+#if 0
+ for (i = 1; i < numPDirHandles; i++) {
+ struct stat statOne;
+ uio_PDirHandle *pDirHandle;
+
+ if (statOneDir(pDirHandles[i], &statOne) == -1) {
+ // errno is set
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+
+ // Merge dirs:
+
+
+ }
+#endif
+#endif
+ }
+
+ if (mode & X_OK) {
+ // XXX: Not implemented.
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ return 0;
+}
+#endif
+
+int
+uio_fstat(uio_Handle *handle, struct stat *statBuf) {
+ if (handle->root->handler->fstat == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (handle->root->handler->fstat)(handle, statBuf);
+}
+
+int
+uio_stat(uio_DirHandle *dir, const char *path, struct stat *statBuf) {
+ uio_PDirHandle *pReadDir;
+ uio_MountInfo *readMountInfo;
+ char *name;
+ int result;
+
+ if (uio_getPhysicalAccess(dir, path, O_RDONLY, 0,
+ &readMountInfo, &pReadDir, NULL,
+ NULL, NULL, NULL, &name) == -1) {
+ if (uio_statDir(dir, path, statBuf) == -1) {
+ // errno is set
+ return -1;
+ }
+ return 0;
+ }
+
+ if (pReadDir->pRoot->handler->stat == NULL) {
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ result = (pReadDir->pRoot->handler->stat)(pReadDir, name, statBuf);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(pReadDir);
+ uio_free(name);
+ return result;
+}
+
+// auxiliary function to uio_stat
+static int
+uio_statDir(uio_DirHandle *dirHandle, const char *path,
+ struct stat *statBuf) {
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path),
+ &pDirHandles, &numPDirHandles, NULL) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ if (numPDirHandles == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (uio_statOneDir(pDirHandles[0], statBuf) == -1) {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+ // TODO: atm, fstat'ing a dir will show the info for the topmost
+ // dir. Maybe it would make sense of merging the bits. (How?)
+
+#if 0
+ for (i = 1; i < numPDirHandles; i++) {
+ struct stat statOne;
+ uio_PDirHandle *pDirHandle;
+
+ if (statOneDir(pDirHandles[i], &statOne) == -1) {
+ // errno is set
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ return -1;
+ }
+
+ // Merge dirs:
+
+
+ }
+#endif
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ return 0;
+}
+
+static int
+uio_statOneDir(uio_PDirHandle *pDirHandle, struct stat *statBuf) {
+ if (pDirHandle->pRoot->handler->stat == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (pDirHandle->pRoot->handler->stat)(pDirHandle, ".", statBuf);
+ // sets errno on error
+}
+
+int
+uio_mkdir(uio_DirHandle *dir, const char *path, mode_t mode) {
+ uio_PDirHandle *pReadDir, *pWriteDir;
+ uio_MountInfo *readMountInfo, *writeMountInfo;
+ char *name;
+ uio_PDirHandle *newDirHandle;
+
+ if (uio_getPhysicalAccess(dir, path, O_WRONLY | O_CREAT | O_EXCL, 0,
+ &readMountInfo, &pReadDir, NULL,
+ &writeMountInfo, &pWriteDir, NULL, &name) == -1) {
+ // errno is set
+ if (errno == EISDIR)
+ errno = EEXIST;
+ return -1;
+ }
+ uio_PDirHandle_unref(pReadDir);
+
+ if (pWriteDir->pRoot->handler->mkdir == NULL) {
+ uio_free(name);
+ uio_PDirHandle_unref(pWriteDir);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ newDirHandle = (pWriteDir->pRoot->handler->mkdir)(pWriteDir, name, mode);
+ if (newDirHandle == NULL) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(pWriteDir);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_PDirHandle_unref(pWriteDir);
+ uio_PDirHandle_unref(newDirHandle);
+ uio_free(name);
+ return 0;
+}
+
+uio_Handle *
+uio_open(uio_DirHandle *dir, const char *path, int flags, mode_t mode) {
+ uio_PDirHandle *readPDirHandle, *writePDirHandle, *pDirHandle;
+ uio_MountInfo *readMountInfo, *writeMountInfo;
+ char *name;
+ uio_Handle *handle;
+
+ if (uio_getPhysicalAccess(dir, path, flags, 0,
+ &readMountInfo, &readPDirHandle, NULL,
+ &writeMountInfo, &writePDirHandle, NULL, &name) == -1) {
+ // errno is set
+ return NULL;
+ }
+
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ // WritePDirHandle is not filled in.
+ pDirHandle = readPDirHandle;
+ } else if (readPDirHandle == writePDirHandle) {
+ // In general, the dirs can be the same even when the handles are
+ // not the same. But here it works, because uio_getPhysicalAccess
+ // guarantees it.
+ uio_PDirHandle_unref(writePDirHandle);
+ pDirHandle = readPDirHandle;
+ } else {
+ // need to write
+ uio_PDirEntryHandle *entry;
+
+ entry = uio_getPDirEntryHandle(readPDirHandle, name);
+ if (entry != NULL) {
+ // file already exists
+ uio_PDirEntryHandle_unref(entry);
+ if ((flags & O_CREAT) == O_CREAT &&
+ (flags & O_EXCL) == O_EXCL) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ errno = EEXIST;
+ return NULL;
+ }
+ if ((flags & O_TRUNC) == O_TRUNC) {
+ // No use copying the file to the writable dir.
+ // As it doesn't exists there, O_TRUNC needs to be turned off
+ // though.
+ flags &= ~O_TRUNC;
+ } else {
+ // file needs to be copied
+ if (uio_copyFilePhysical(readPDirHandle, name, writePDirHandle,
+ name) == -1) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ errno = savedErrno;
+ return NULL;
+ }
+ }
+ } else {
+ // file does not exist
+ if (((flags & O_ACCMODE) == O_RDONLY) ||
+ (flags & O_CREAT) != O_CREAT) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ errno = ENOENT;
+ return NULL;
+ }
+ }
+ uio_PDirHandle_unref(readPDirHandle);
+ pDirHandle = writePDirHandle;
+ }
+
+ handle = (pDirHandle->pRoot->handler->open)(pDirHandle, name, flags, mode);
+ // Also adds a new entry to the physical dir if appropriate.
+ if (handle == NULL) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(pDirHandle);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ uio_free(name);
+ uio_PDirHandle_unref(pDirHandle);
+ return handle;
+}
+
+uio_DirHandle *
+uio_openDir(uio_Repository *repository, const char *path, int flags) {
+ uio_DirHandle *dirHandle;
+ const char * const rootStr = "";
+
+ dirHandle = uio_DirHandle_new(repository,
+ unconst(rootStr), unconst(rootStr));
+ // dirHandle->path will be replaced before uio_openDir()
+ // exits()
+ if (uio_verifyPath(dirHandle, path, &dirHandle->path) == -1) {
+ int savedErrno = errno;
+ uio_DirHandle_free(dirHandle);
+ errno = savedErrno;
+ return NULL;
+ }
+ // dirHandle->path is no longer equal to 'path' at this point.
+ // TODO: increase ref in repository?
+ dirHandle->rootEnd = dirHandle->path;
+ if (flags & uio_OD_ROOT)
+ dirHandle->rootEnd += strlen(dirHandle->path);
+ return dirHandle;
+}
+
+uio_DirHandle *
+uio_openDirRelative(uio_DirHandle *base, const char *path, int flags) {
+ uio_DirHandle *dirHandle;
+ char *newPath;
+
+ if (uio_verifyPath(base, path, &newPath) == -1) {
+ // errno is set
+ return NULL;
+ }
+ if (flags & uio_OD_ROOT) {
+ dirHandle = uio_DirHandle_new(base->repository,
+ newPath, newPath + strlen(newPath));
+ // TODO: increase ref in base->repository?
+ } else {
+ // use the root of the base dir
+ dirHandle = uio_DirHandle_new(base->repository,
+ newPath, newPath + (base->rootEnd - base->path));
+ }
+ return dirHandle;
+}
+
+int
+uio_closeDir(uio_DirHandle *dirHandle) {
+ uio_DirHandle_unref(dirHandle);
+ return 0;
+}
+
+ssize_t
+uio_read(uio_Handle *handle, void *buf, size_t count) {
+ return (handle->root->handler->read)(handle, buf, count);
+}
+
+int
+uio_rmdir(uio_DirHandle *dirHandle, const char *path) {
+ int numPDirHandles;
+ uio_PDirHandle *pDirHandle, **pDirHandles;
+ const char *pathEnd, *name;
+ uio_PDirEntryHandle *entry;
+ uio_MountTreeItem **items;
+ int i;
+ int numDeleted;
+
+ pathEnd = strrchr(path, '/');
+ if (pathEnd == NULL) {
+ pathEnd = path;
+ name = path;
+ } else
+ name = pathEnd + 1;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, pathEnd - path,
+ &pDirHandles, &numPDirHandles, &items) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ entry = NULL;
+ // Should be set before a possible goto.
+
+ if (name[0] == '\0') {
+ // path was of the form "foo/bar/" or "/foo/bar/"
+ // These are intentionally not accepted.
+ // I see this as a path and not as a directory identifier.
+ errno = ENOENT;
+ goto err;
+ }
+
+ numDeleted = 0;
+ for (i = 0; i < numPDirHandles; i++) {
+ pDirHandle = pDirHandles[i];
+ entry = uio_getPDirEntryHandle(pDirHandle, name);
+
+ if (entry == NULL)
+ continue;
+
+ if (!uio_PDirEntryHandle_isDir(entry)) {
+ errno = ENOTDIR;
+ goto err;
+ }
+
+ if (uio_mountInfoIsReadOnly(items[i]->mountInfo)) {
+ errno = EROFS;
+ goto err;
+ }
+
+ if (pDirHandle->pRoot->handler->rmdir == NULL) {
+ errno = ENOSYS;
+ goto err;
+ }
+
+ if ((pDirHandle->pRoot->handler->rmdir)(pDirHandle, name) == -1) {
+ // errno is set
+ goto err;
+ }
+ numDeleted++;
+ uio_PDirEntryHandle_unref(entry);
+ }
+ entry = NULL;
+
+ if (numDeleted == 0) {
+ errno = ENOENT;
+ goto err;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ if (entry != NULL)
+ uio_PDirEntryHandle_unref(entry);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static void
+uio_PDirHandles_delete(uio_PDirHandle *pDirHandles[], int numPDirHandles) {
+ while (numPDirHandles--)
+ uio_PDirHandle_unref(pDirHandles[numPDirHandles]);
+ uio_free(pDirHandles);
+}
+
+int
+uio_lseek(uio_Handle *handle, off_t offset, int whence) {
+ if (handle->root->handler->seek == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (handle->root->handler->seek)(handle, offset, whence);
+}
+
+ssize_t
+uio_write(uio_Handle *handle, const void *buf, size_t count) {
+ if (handle->root->handler->write == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+ return (handle->root->handler->write)(handle, buf, count);
+}
+
+int
+uio_unlink(uio_DirHandle *dirHandle, const char *path) {
+ int numPDirHandles;
+ uio_PDirHandle *pDirHandle, **pDirHandles;
+ const char *pathEnd, *name;
+ uio_PDirEntryHandle *entry;
+ uio_MountTreeItem **items;
+ int i;
+ int numDeleted;
+
+ pathEnd = strrchr(path, '/');
+ if (pathEnd == NULL) {
+ pathEnd = path;
+ name = path;
+ } else
+ name = pathEnd + 1;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, pathEnd - path,
+ &pDirHandles, &numPDirHandles, &items) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ entry = NULL;
+ // Should be set before a possible goto.
+
+ if (name[0] == '\0') {
+ // path was of the form "foo/bar/" or "/foo/bar/"
+ errno = ENOENT;
+ goto err;
+ }
+
+ numDeleted = 0;
+ for (i = 0; i < numPDirHandles; i++) {
+ pDirHandle = pDirHandles[i];
+ entry = uio_getPDirEntryHandle(pDirHandle, name);
+
+ if (entry == NULL)
+ continue;
+
+ if (uio_PDirEntryHandle_isDir(entry)) {
+ errno = EISDIR;
+ goto err;
+ }
+
+ if (uio_mountInfoIsReadOnly(items[i]->mountInfo)) {
+ errno = EROFS;
+ goto err;
+ }
+
+ if (pDirHandle->pRoot->handler->unlink == NULL) {
+ errno = ENOSYS;
+ goto err;
+ }
+
+ if ((pDirHandle->pRoot->handler->unlink)(pDirHandle, name) == -1) {
+ // errno is set
+ goto err;
+ }
+ numDeleted++;
+ uio_PDirEntryHandle_unref(entry);
+ }
+ entry = NULL;
+
+ if (numDeleted == 0) {
+ errno = ENOENT;
+ goto err;
+ }
+
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ uio_free(items);
+ if (entry != NULL)
+ uio_PDirEntryHandle_unref(entry);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+// inPath and *outPath may point to the same location
+int
+uio_getFileLocation(uio_DirHandle *dir, const char *inPath,
+ int flags, uio_MountHandle **mountHandle, char **outPath) {
+ uio_PDirHandle *readPDirHandle, *writePDirHandle;
+ uio_MountInfo *readMountInfo, *writeMountInfo, *mountInfo;
+ char *name;
+ char *readPRootPath, *writePRootPath, *pRootPath;
+
+ if (uio_getPhysicalAccess(dir, inPath, flags, 0,
+ &readMountInfo, &readPDirHandle, &readPRootPath,
+ &writeMountInfo, &writePDirHandle, &writePRootPath,
+ &name) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // TODO: This code is partly the same as the code in uio_open().
+ // probably some code could be put in a seperate function.
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ // WritePDirHandle is not filled in.
+ uio_PDirHandle_unref(readPDirHandle);
+ pRootPath = readPRootPath;
+ mountInfo = readMountInfo;
+ } else if (readPDirHandle == writePDirHandle) {
+ // In general, the dirs can be the same even when the handles are
+ // not the same. But here it works, because uio_getPhysicalAccess
+ // guarantees it.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ pRootPath = readPRootPath;
+ mountInfo = readMountInfo;
+ uio_free(writePRootPath);
+ } else {
+ // need to write
+ uio_PDirEntryHandle *entry;
+
+ entry = uio_getPDirEntryHandle(readPDirHandle, name);
+ if (entry != NULL) {
+ // file already exists
+ uio_PDirEntryHandle_unref(entry);
+ if ((flags & O_CREAT) == O_CREAT &&
+ (flags & O_EXCL) == O_EXCL) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(readPRootPath);
+ uio_PDirHandle_unref(writePDirHandle);
+ uio_free(writePRootPath);
+ errno = EEXIST;
+ return -1;
+ }
+ if ((flags & O_TRUNC) == O_TRUNC) {
+ // No use copying the file to the writable dir.
+ // As it doesn't exists there, O_TRUNC needs to be turned off
+ // though.
+ flags &= ~O_TRUNC;
+ } else {
+ // file needs to be copied
+ if (uio_copyFilePhysical(readPDirHandle, name, writePDirHandle,
+ name) == -1) {
+ int savedErrno = errno;
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(readPRootPath);
+ uio_PDirHandle_unref(writePDirHandle);
+ uio_free(writePRootPath);
+ errno = savedErrno;
+ return -1;
+ }
+ }
+ } else {
+ // file does not exist
+ if (((flags & O_ACCMODE) == O_RDONLY) ||
+ (flags & O_CREAT) != O_CREAT) {
+ uio_free(name);
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(readPRootPath);
+ uio_PDirHandle_unref(writePDirHandle);
+ uio_free(writePRootPath);
+ errno = ENOENT;
+ return -1;
+ }
+ }
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_PDirHandle_unref(writePDirHandle);
+ pRootPath = writePRootPath;
+ mountInfo = writeMountInfo;
+ uio_free(readPRootPath);
+ }
+
+ uio_free(name);
+
+ *mountHandle = mountInfo->mountHandle;
+ *outPath = pRootPath;
+ return 0;
+}
+
+
+// *** begin dirList stuff *** //
+
+#define uio_DIR_BUFFER_SIZE 2048
+ // Large values will give significantly better speed for large
+ // directories. What is stored in the buffer is file names
+ // plus one pointer per file name, so with an average size of 16
+ // characters per file (including \0), a buffer of size 2048 will
+ // store approximately 100 files.
+ // It should be at least big enough to store one entry (NAME_MAX is
+ // 255 on POSIX systems).
+ // TODO: add a compile-time check for this
+
+typedef struct uio_DirBufferLink {
+ char *buffer;
+ int numEntries;
+ struct uio_DirBufferLink *next;
+} uio_DirBufferLink;
+
+static int strPtrCmp(const char * const *ptr1, const char * const *ptr2);
+static void uio_DirBufferLink_free(uio_DirBufferLink *sdbl);
+static void uio_DirBufferChain_free(uio_DirBufferLink *dirBufferLink);
+static uio_DirList *uio_getDirListMulti(uio_PDirHandle **pDirHandles,
+ int numPDirHandles, const char *pattern, match_MatchType matchType);
+static uio_DirList *uio_makeDirList(const char **newNames,
+ const char * const *names, int numNames);
+static uio_DirList *uio_DirList_new(const char **names, int numNames,
+ char *buffer);
+static void uio_collectDirEntries(uio_PDirHandle *pDirHandle,
+ uio_DirBufferLink **linkPtr, int *numEntries);
+static inline uio_DirList *uio_DirList_alloc(void);
+static void uio_filterNames(const char * const *names, int numNames,
+ const char **newNames, int *numNewNames,
+ match_MatchContext *matchContext);
+
+static uio_EntriesContext *uio_openEntriesPhysical(uio_PDirHandle *dirHandle);
+static int uio_readEntriesPhysical(uio_EntriesContext *iterator, char *buf,
+ size_t len);
+static void uio_closeEntriesPhysical(uio_EntriesContext *iterator);
+static uio_EntriesContext *uio_EntriesContext_new(uio_PRoot *pRoot,
+ uio_NativeEntriesContext *native);
+static inline uio_EntriesContext *uio_EntriesContext_alloc(void);
+static inline void uio_EntriesContext_delete(uio_EntriesContext *entriesContext);
+static inline void uio_EntriesContext_free(uio_EntriesContext
+ *entriesContext);
+
+// The caller may modify the elements of the .names field of the result, but
+// .names itself, and the rest of the elements of dirList should be left
+// alone, so that they will be freed by uio_DirList_free().
+uio_DirList *
+uio_getDirList(uio_DirHandle *dirHandle, const char *path, const char *pattern,
+ match_MatchType matchType) {
+ int numPDirHandles;
+ uio_PDirHandle **pDirHandles;
+ uio_DirList *result;
+
+ if (uio_getPathPhysicalDirs(dirHandle, path, strlen(path),
+ &pDirHandles, &numPDirHandles, NULL) == -1) {
+ // errno is set
+ return NULL;
+ }
+
+ if (numPDirHandles == 0) {
+ assert(pDirHandles == NULL);
+ // nothing to free
+ return uio_DirList_new(NULL, 0, NULL);
+ }
+
+ result = uio_getDirListMulti(pDirHandles, numPDirHandles, pattern,
+ matchType);
+
+ {
+ int savedErrno;
+ savedErrno = errno;
+ uio_PDirHandles_delete(pDirHandles, numPDirHandles);
+ errno = savedErrno;
+ }
+ return result;
+}
+
+// Names and newNames may point to the same location.
+// numNewNames may point to &numNames.
+static void
+uio_filterDoubleNames(const char * const *names, int numNames,
+ const char **newNames, int *numNewNames) {
+ const char * const *endNames;
+ const char *prevName;
+ int newNum;
+
+ if (numNames == 0) {
+ *numNewNames = 0;
+ return;
+ }
+
+ endNames = names + numNames;
+ prevName = *names;
+ *newNames = *names;
+ newNames++;
+ names++;
+ newNum = 1;
+ while (names < endNames) {
+ if (strcmp(prevName, *names) != 0) {
+ *newNames = *names;
+ newNum++;
+ prevName = *names;
+ newNames++;
+ }
+ names++;
+ }
+ *numNewNames = newNum;
+}
+
+static uio_DirList *
+uio_getDirListMulti(uio_PDirHandle **pDirHandles,
+ int numPDirHandles, const char *pattern, match_MatchType matchType) {
+ int pDirI; // physical dir iterator
+ uio_DirBufferLink **links; // array of bufferLinks for each physical dir
+ uio_DirBufferLink *linkPtr;
+ int *numNames; // number of entries in each physical dir
+ int totalNumNames;
+ const char **bigNameBuffer; // buffer where all names will end up together
+ const char **destPtr;
+ uio_DirList *result;
+ match_Result matchResult;
+ match_MatchContext *matchContext;
+
+ matchResult = match_prepareContext(pattern, &matchContext, matchType);
+ if (matchResult != match_OK) {
+#ifdef DEBUG
+ fprintf(stderr, "Error compiling match function: %s.\n",
+ match_errorString(matchContext, matchResult));
+#endif
+ match_freeContext(matchContext);
+ errno = EIO;
+ // I actually want to signal an internal error.
+ // EIO comes closes
+ return NULL;
+ }
+
+ // first get the directory listings for all seperate relevant dirs.
+ totalNumNames = 0;
+ links = uio_malloc(numPDirHandles * sizeof (uio_DirBufferLink *));
+ numNames = uio_malloc(numPDirHandles * sizeof (int));
+ for (pDirI = 0; pDirI < numPDirHandles; pDirI++) {
+ uio_collectDirEntries(pDirHandles[pDirI], &links[pDirI],
+ &numNames[pDirI]);
+ totalNumNames += numNames[pDirI];
+ }
+
+ bigNameBuffer = uio_malloc(totalNumNames * sizeof (uio_DirBufferLink *));
+
+ // Fill the bigNameBuffer with all the names from all the DirBufferLinks
+ // of all the physical dirs.
+ destPtr = bigNameBuffer;
+ totalNumNames = 0;
+ for (pDirI = 0; pDirI < numPDirHandles; pDirI++) {
+ for (linkPtr = links[pDirI]; linkPtr != NULL;
+ linkPtr = linkPtr->next) {
+ int numNewNames;
+ uio_filterNames((const char * const *) linkPtr->buffer,
+ linkPtr->numEntries, destPtr,
+ &numNewNames, matchContext);
+ totalNumNames += numNewNames;
+ destPtr += numNewNames;
+ }
+ }
+
+ match_freeContext(matchContext);
+
+ // Sort the bigNameBuffer
+ // Necessary for removing doubles.
+ // Not really necessary if the big list was the result of only one
+ // physical dir, but let's output a sorted list anyhow.
+ qsort((void *) bigNameBuffer, totalNumNames, sizeof (char *),
+ (int (*)(const void *, const void *)) strPtrCmp);
+
+ // remove doubles
+ // (unnecessary if the big list was the result of only one physical dir)
+ if (numPDirHandles > 1) {
+ uio_filterDoubleNames(bigNameBuffer, totalNumNames,
+ bigNameBuffer, &totalNumNames);
+ }
+
+ // resize the bigNameBuffer
+ bigNameBuffer = uio_realloc((void *) bigNameBuffer,
+ totalNumNames * sizeof (char *));
+
+ // put the lot in a DirList, copying the strings themselves
+ result = uio_makeDirList(bigNameBuffer, bigNameBuffer,
+ totalNumNames);
+
+ // free the old junk
+ for (pDirI = 0; pDirI < numPDirHandles; pDirI++)
+ uio_DirBufferChain_free(links[pDirI]);
+ uio_free(links);
+ uio_free(numNames);
+
+ return result;
+}
+
+// 'buffer' and 'names' may be the same dir
+// 'names' contains an array of 'numNames' pointers.
+// 'newNames', if non-NULL, will be used as the array of new pointers
+// (to a copy of the strings) in the DirList.
+static uio_DirList *
+uio_makeDirList(const char **newNames, const char * const *names,
+ int numNames) {
+ int i;
+ size_t len, totLen;
+ char *bufPtr;
+ uio_DirList *result;
+
+ if (newNames == NULL)
+ newNames = uio_malloc(numNames * sizeof (char *));
+
+ totLen = 0;
+ for (i = 0; i < numNames; i++)
+ totLen += strlen(names[i]);
+ totLen += numNames;
+ // for the \0's
+
+ result = uio_DirList_new(newNames, numNames, uio_malloc(totLen));
+
+ bufPtr = result->buffer;
+ for (i = 0; i < numNames; i++) {
+ len = strlen(names[i]) + 1;
+ memcpy(bufPtr, names[i], len);
+ newNames[i] = bufPtr;
+ bufPtr += len;
+ }
+ return result;
+}
+
+static void
+uio_collectDirEntries(uio_PDirHandle *pDirHandle, uio_DirBufferLink **linkPtr,
+ int *numEntries) {
+ uio_EntriesContext *entriesContext;
+ uio_DirBufferLink **linkEndPtr; // where to attach the next link
+ int numRead;
+ int totalEntries;
+ char *buffer;
+
+ entriesContext = uio_openEntriesPhysical(pDirHandle);
+ if (entriesContext == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: uio_openEntriesPhysical() failed: %s\n",
+ strerror(errno));
+#endif
+ *linkPtr = NULL;
+ *numEntries = 0;
+ return;
+ }
+ linkEndPtr = linkPtr;
+ totalEntries = 0;
+ while (1) {
+ *linkEndPtr = uio_malloc(sizeof (uio_DirBufferLink));
+ buffer = uio_malloc(uio_DIR_BUFFER_SIZE);
+ (*linkEndPtr)->buffer = buffer;
+ numRead = uio_readEntriesPhysical(entriesContext, buffer,
+ uio_DIR_BUFFER_SIZE);
+ if (numRead == 0) {
+ fprintf(stderr, "Warning: uio_DIR_BUFFER_SIZE is too small to "
+ "hold a certain large entry on its own!\n");
+ uio_DirBufferLink_free(*linkEndPtr);
+ break;
+ }
+ totalEntries += numRead;
+ (*linkEndPtr)->numEntries = numRead;
+ if (((char **) buffer)[numRead - 1] == NULL) {
+ // The entry being NULL means this is the last buffer
+ // Decrement the amount of queries to get the real number.
+ (*linkEndPtr)->numEntries--;
+ totalEntries--;
+ linkEndPtr = &(*linkEndPtr)->next;
+ break;
+ }
+ linkEndPtr = &(*linkEndPtr)->next;
+ }
+ *linkEndPtr = NULL;
+ uio_closeEntriesPhysical(entriesContext);
+ *numEntries = totalEntries;
+}
+
+static void
+uio_filterNames(const char * const *names, int numNames,
+ const char **newNames, int *numNewNames,
+ match_MatchContext *matchContext) {
+ int newNum;
+ const char * const *namesEnd;
+ match_Result matchResult;
+
+ newNum = 0;
+ namesEnd = names + numNames;
+ while (names < namesEnd) {
+ matchResult = match_matchPattern(matchContext, *names);
+ if (matchResult == match_MATCH) {
+ *newNames = *names;
+ newNames++;
+ newNum++;
+ } else if (matchResult != match_NOMATCH) {
+ fprintf(stderr, "Error trying to match pattern: %s.\n",
+ match_errorString(matchContext, matchResult));
+ }
+ names++;
+ }
+ *numNewNames = newNum;
+}
+
+static int
+strPtrCmp(const char * const *ptr1, const char * const *ptr2) {
+ return strcmp(*ptr1, *ptr2);
+}
+
+static uio_EntriesContext *
+uio_openEntriesPhysical(uio_PDirHandle *dirHandle) {
+ uio_NativeEntriesContext *native;
+ uio_PRoot *pRoot;
+
+ pRoot = dirHandle->pRoot;
+
+ assert(pRoot->handler->openEntries != NULL);
+ native = pRoot->handler->openEntries(dirHandle);
+ if (native == NULL)
+ return NULL;
+ uio_PRoot_refHandle(pRoot);
+ return uio_EntriesContext_new(pRoot, native);
+}
+
+static int
+uio_readEntriesPhysical(uio_EntriesContext *iterator, char *buf, size_t len) {
+ assert(iterator->pRoot->handler->readEntries != NULL);
+ return iterator->pRoot->handler->readEntries(&iterator->native, buf, len);
+}
+
+static void
+uio_closeEntriesPhysical(uio_EntriesContext *iterator) {
+ assert(iterator->pRoot->handler->closeEntries != NULL);
+ iterator->pRoot->handler->closeEntries(iterator->native);
+ uio_PRoot_unrefHandle(iterator->pRoot);
+ uio_EntriesContext_delete(iterator);
+}
+
+static uio_EntriesContext *
+uio_EntriesContext_new(uio_PRoot *pRoot, uio_NativeEntriesContext *native) {
+ uio_EntriesContext *result;
+ result = uio_EntriesContext_alloc();
+ result->pRoot = pRoot;
+ result->native = native;
+ return result;
+}
+
+static inline uio_EntriesContext *
+uio_EntriesContext_alloc(void) {
+ return uio_malloc(sizeof (uio_EntriesContext));
+}
+
+static inline void
+uio_EntriesContext_delete(uio_EntriesContext *entriesContext) {
+ uio_EntriesContext_free(entriesContext);
+}
+
+static inline void
+uio_EntriesContext_free(uio_EntriesContext *entriesContext) {
+ uio_free(entriesContext);
+}
+
+static void
+uio_DirBufferLink_free(uio_DirBufferLink *dirBufferLink) {
+ uio_free(dirBufferLink->buffer);
+ uio_free(dirBufferLink);
+}
+
+static void
+uio_DirBufferChain_free(uio_DirBufferLink *dirBufferLink) {
+ uio_DirBufferLink *next;
+
+ while (dirBufferLink != NULL) {
+ next = dirBufferLink->next;
+ uio_DirBufferLink_free(dirBufferLink);
+ dirBufferLink = next;
+ }
+}
+
+static uio_DirList *
+uio_DirList_new(const char **names, int numNames, char *buffer) {
+ uio_DirList *result;
+
+ result = uio_DirList_alloc();
+ result->names = names;
+ result->numNames = numNames;
+ result->buffer = buffer;
+ return result;
+}
+
+static uio_DirList *
+uio_DirList_alloc(void) {
+ return uio_malloc(sizeof (uio_DirList));
+}
+
+void
+uio_DirList_free(uio_DirList *dirList) {
+ if (dirList->buffer)
+ uio_free(dirList->buffer);
+ if (dirList->names)
+ uio_free((void *) dirList->names);
+ uio_free(dirList);
+}
+
+// *** end DirList stuff *** //
+
+
+// *** PDirEntryHandle stuff *** //
+
+uio_PDirEntryHandle *
+uio_getPDirEntryHandle(const uio_PDirHandle *dirHandle,
+ const char *name) {
+ assert(dirHandle->pRoot->handler != NULL);
+ return dirHandle->pRoot->handler->getPDirEntryHandle(dirHandle, name);
+}
+
+void
+uio_PDirHandle_deletePDirHandleExtra(uio_PDirHandle *pDirHandle) {
+ if (pDirHandle->extra == NULL)
+ return;
+ assert(pDirHandle->pRoot->handler->deletePDirHandleExtra != NULL);
+ pDirHandle->pRoot->handler->deletePDirHandleExtra(pDirHandle->extra);
+}
+
+void
+uio_PFileHandle_deletePFileHandleExtra(uio_PFileHandle *pFileHandle) {
+ if (pFileHandle->extra == NULL)
+ return;
+ assert(pFileHandle->pRoot->handler->deletePFileHandleExtra != NULL);
+ pFileHandle->pRoot->handler->deletePFileHandleExtra(pFileHandle->extra);
+}
+
+uio_PDirHandle *
+uio_PDirHandle_new(uio_PRoot *pRoot, uio_PDirHandleExtra extra) {
+ uio_PDirHandle *result;
+
+ result = uio_PDirHandle_alloc();
+ result->flags = uio_PDirEntryHandle_TYPE_DIR;
+ result->ref = 1;
+ result->pRoot = pRoot;
+ result->extra = extra;
+ return result;
+}
+
+void
+uio_PDirEntryHandle_delete(uio_PDirEntryHandle *pDirEntryHandle) {
+ if (uio_PDirEntryHandle_isDir(pDirEntryHandle)) {
+ uio_PDirHandle_delete((uio_PDirHandle *) pDirEntryHandle);
+ } else {
+ uio_PFileHandle_delete((uio_PFileHandle *) pDirEntryHandle);
+ }
+}
+
+void
+uio_PDirHandle_delete(uio_PDirHandle *pDirHandle) {
+ uio_PDirHandle_deletePDirHandleExtra(pDirHandle);
+ uio_PDirHandle_free(pDirHandle);
+}
+
+static inline uio_PDirHandle *
+uio_PDirHandle_alloc(void) {
+ uio_PDirHandle *result = uio_malloc(sizeof (uio_PDirHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PDirHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PDirHandle_free(uio_PDirHandle *pDirHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PDirHandle, (void *) pDirHandle);
+#endif
+ uio_free(pDirHandle);
+}
+
+uio_PFileHandle *
+uio_PFileHandle_new(uio_PRoot *pRoot, uio_PFileHandleExtra extra) {
+ uio_PFileHandle *result;
+
+ result = uio_PFileHandle_alloc();
+ result->flags = uio_PDirEntryHandle_TYPE_REG;
+ result->ref = 1;
+ result->pRoot = pRoot;
+ result->extra = extra;
+ return result;
+}
+
+void
+uio_PFileHandle_delete(uio_PFileHandle *pFileHandle) {
+ uio_PFileHandle_deletePFileHandleExtra(pFileHandle);
+ uio_PFileHandle_free(pFileHandle);
+}
+
+static inline uio_PFileHandle *
+uio_PFileHandle_alloc(void) {
+ uio_PFileHandle *result = uio_malloc(sizeof (uio_PFileHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PFileHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PFileHandle_free(uio_PFileHandle *pFileHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PFileHandle, (void *) pFileHandle);
+#endif
+ uio_free(pFileHandle);
+}
+
+// *** PDirEntryHandle stuff *** //
+
+
+// ref count set to 1
+uio_Handle *
+uio_Handle_new(uio_PRoot *root, uio_NativeHandle native, int openFlags) {
+ uio_Handle *handle;
+
+ handle = uio_Handle_alloc();
+ handle->ref = 1;
+ uio_PRoot_refHandle(root);
+ handle->root = root;
+ handle->native = native;
+ handle->openFlags = openFlags;
+ return handle;
+}
+
+void
+uio_Handle_delete(uio_Handle *handle) {
+ (handle->root->handler->close)(handle);
+ uio_PRoot_unrefHandle(handle->root);
+ uio_Handle_free(handle);
+}
+
+static inline uio_Handle *
+uio_Handle_alloc(void) {
+ uio_Handle *result = uio_malloc(sizeof (uio_Handle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_Handle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_Handle_free(uio_Handle *handle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_Handle, (void *) handle);
+#endif
+ uio_free(handle);
+}
+
+// ref count set to 1
+static uio_DirHandle *
+uio_DirHandle_new(uio_Repository *repository, char *path, char *rootEnd) {
+ uio_DirHandle *result;
+
+ result = uio_DirHandle_alloc();
+ result->ref = 1;
+ result->repository = repository;
+ result->path = path;
+ result->rootEnd = rootEnd;
+ return result;
+}
+
+void
+uio_DirHandle_delete(uio_DirHandle *dirHandle) {
+ uio_free(dirHandle->path);
+ uio_DirHandle_free(dirHandle);
+}
+
+static inline uio_DirHandle *
+uio_DirHandle_alloc(void) {
+ uio_DirHandle *result = uio_malloc(sizeof (uio_DirHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_DirHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_DirHandle_free(uio_DirHandle *dirHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_DirHandle, (void *) dirHandle);
+#endif
+ uio_free(dirHandle);
+}
+
+void
+uio_DirHandle_print(const uio_DirHandle *dirHandle, FILE *out) {
+ fprintf(out, "[");
+ fwrite(dirHandle->path, dirHandle->path - dirHandle->rootEnd, 1, out);
+ fprintf(out, "]%s", dirHandle->rootEnd);
+}
+
+static uio_MountHandle *
+uio_MountHandle_new(uio_Repository *repository, uio_MountInfo *mountInfo) {
+ uio_MountHandle *result;
+
+ result = uio_MountHandle_alloc();
+ result->repository = repository;
+ result->mountInfo = mountInfo;
+ return result;
+}
+
+static inline void
+uio_MountHandle_delete(uio_MountHandle *mountHandle) {
+ uio_MountHandle_free(mountHandle);
+}
+
+static inline uio_MountHandle *
+uio_MountHandle_alloc(void) {
+ uio_MountHandle *result = uio_malloc(sizeof (uio_MountHandle));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountHandle, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountHandle_free(uio_MountHandle *mountHandle) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountHandle, (void *) mountHandle);
+#endif
+ uio_free(mountHandle);
+}
+
+
+
diff --git a/src/libs/uio/io.h b/src/libs/uio/io.h
new file mode 100644
index 0000000..813a2c3
--- /dev/null
+++ b/src/libs/uio/io.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_IO_H_
+#define LIBS_UIO_IO_H_
+
+#include <assert.h>
+#include <sys/stat.h>
+
+typedef struct uio_Handle uio_Handle;
+typedef struct uio_DirHandle uio_DirHandle;
+typedef struct uio_DirList uio_DirList;
+typedef struct uio_MountHandle uio_MountHandle;
+
+typedef enum {
+ uio_MOUNT_BOTTOM = (0 << 2),
+ uio_MOUNT_TOP = (1 << 2),
+ uio_MOUNT_BELOW = (2 << 2),
+ uio_MOUNT_ABOVE = (3 << 2)
+} uio_MountLocation;
+
+#include "match.h"
+#include "fstypes.h"
+#include "mount.h"
+#include "mounttree.h"
+#include "uiostream.h"
+#include "getint.h"
+#include "debug.h"
+
+struct uio_AutoMount {
+ const char *pattern;
+ match_MatchType matchType;
+ uio_FileSystemID fileSystemID;
+ int mountFlags;
+// uio_AutoMount **autoMount;
+ // automount rules to apply to file systems automounted
+ // because of this automount rule.
+};
+
+#ifndef uio_INTERNAL
+struct uio_DirList {
+ const char **names;
+ int numNames;
+ // The rest of the fields are not visible from the outside
+};
+#endif
+
+
+// Initialise the resource system
+void uio_init(void);
+
+// Uninitialise the resource system
+void uio_unInit(void);
+
+// Open a repository.
+uio_Repository *uio_openRepository(int flags);
+
+// Close a repository opened by uio_openRepository().
+void uio_closeRepository(uio_Repository *repository);
+
+// Mount a directory into a repository
+uio_MountHandle *uio_mountDir(uio_Repository *destRep, const char *mountPoint,
+ uio_FileSystemID fsType,
+ uio_DirHandle *sourceDir, const char *sourcePath,
+ const char *inPath, uio_AutoMount **autoMount, int flags,
+ uio_MountHandle *relative);
+
+// Mount a repository directory into same repository at a different
+// location.
+// From fossil.
+uio_MountHandle *uio_transplantDir(const char *mountPoint,
+ uio_DirHandle *sourceDir, int flags, uio_MountHandle *relative);
+
+// Unmount a previously mounted dir.
+int uio_unmountDir(uio_MountHandle *mountHandle);
+
+// Unmount all previously mounted dirs.
+int uio_unmountAllDirs(uio_Repository *repository);
+
+// Get the filesystem identifier for a mounted directory.
+uio_FileSystemID uio_getMountFileSystemType(uio_MountHandle *mountHandle);
+
+// Open a file
+uio_Handle *uio_open(uio_DirHandle *dir, const char *file, int flags,
+ mode_t mode);
+
+// Close a file descriptor for a file opened by uio_open
+int uio_close(uio_Handle *handle);
+
+// Rename or move a file or directory.
+int uio_rename(uio_DirHandle *oldDir, const char *oldPath,
+ uio_DirHandle *newDir, const char *newPath);
+
+// Test permissions on a file or directory.
+int uio_access(uio_DirHandle *dir, const char *path, int mode);
+
+// Fstat a file descriptor
+int uio_fstat(uio_Handle *handle, struct stat *statBuf);
+
+int uio_stat(uio_DirHandle *dir, const char *path, struct stat *statBuf);
+
+int uio_mkdir(uio_DirHandle *dir, const char *name, mode_t mode);
+
+ssize_t uio_read(uio_Handle *handle, void *buf, size_t count);
+
+int uio_rmdir(uio_DirHandle *dirHandle, const char *path);
+
+int uio_lseek(uio_Handle *handle, off_t offset, int whence);
+
+ssize_t uio_write(uio_Handle *handle, const void *buf, size_t count);
+
+int uio_unlink(uio_DirHandle *dirHandle, const char *path);
+
+int uio_getFileLocation(uio_DirHandle *dir, const char *inPath,
+ int flags, uio_MountHandle **mountHandle, char **outPath);
+
+// Get a directory handle.
+uio_DirHandle *uio_openDir(uio_Repository *repository, const char *path,
+ int flags);
+#define uio_OD_ROOT 1
+
+// Get a directory handle using a path relative to another handle.
+uio_DirHandle *uio_openDirRelative(uio_DirHandle *base, const char *path,
+ int flags);
+
+// Release a directory handle
+int uio_closeDir(uio_DirHandle *dirHandle);
+
+uio_DirList *uio_getDirList(uio_DirHandle *dirHandle, const char *path,
+ const char *pattern, match_MatchType matchType);
+void uio_DirList_free(uio_DirList *dirList);
+
+// For debugging purposes
+void uio_DirHandle_print(const uio_DirHandle *dirHandle, FILE *out);
+
+
+#ifdef DEBUG
+# define uio_DEBUG
+#endif
+
+#endif /* LIBS_UIO_IO_H_ */
+
diff --git a/src/libs/uio/ioaux.c b/src/libs/uio/ioaux.c
new file mode 100644
index 0000000..3dc0880
--- /dev/null
+++ b/src/libs/uio/ioaux.c
@@ -0,0 +1,930 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// auxiliary functions for use by io.c
+
+#include <errno.h>
+#include <fcntl.h>
+
+#include "ioaux.h"
+#include "iointrn.h"
+#include "uioport.h"
+#include "paths.h"
+
+static int copyError(int error,
+ uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle,
+ uio_FileSystemHandler *toHandler, uio_Handle *toHandle,
+ uio_PDirHandle *toDir, const char *toName, char *buf);
+
+/**
+ * Follow a path starting from a specified physical dir for as long as
+ * possible.
+ *
+ * @param[in] startPDirHandle The physical dir to start from.
+ * @param[in] path The path to follow, relative to
+ * 'startPDirHandle'.
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] endPDirHandle The physical dir where you end up after
+ * following 'path' for as long as possible. Unmodified if an error
+ * occurs.
+ * @param[out] pathRest '*pathRest' will point into 'path' to the
+ * start the part that was not matched. Unmodified if an error occurs.
+ *
+ * @retval 0 if the complete path was matched
+ * @retval ENOENT if some component (the next one in '*pathRest') didn't
+ * exist.
+ * @retval ENODIR if a component (the next one in '*pathRest') did exist,
+ * but wasn't a dir.
+ *
+ * @note It is allowed to have 'endPDirHandle' point to pDirHandle, but
+ * care should be taken to keep a reference to the original so its
+ * reference counter can be decremented.
+ * @note It is allowed to have 'pathRest' point to 'path'.
+ */
+int
+uio_walkPhysicalPath(uio_PDirHandle *startPDirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle **endPDirHandle,
+ const char **pathRest) {
+ const char *pathEnd;
+ const char *partStart, *partEnd;
+ char *tempBuf;
+ uio_PDirHandle *pDirHandle;
+ uio_PDirEntryHandle *entry;
+ int retVal;
+
+ uio_PDirHandle_ref(startPDirHandle);
+ pDirHandle = startPDirHandle;
+ tempBuf = uio_malloc(strlen(path) + 1);
+ // XXX: Use a dynamically allocated array when moving to C99.
+ pathEnd = path + pathLen;
+ getFirstPathComponent(path, pathEnd, &partStart, &partEnd);
+ for (;;) {
+ if (partStart == pathEnd) {
+ retVal = 0;
+ break;
+ }
+ memcpy(tempBuf, partStart, partEnd - partStart);
+ tempBuf[partEnd - partStart] = '\0';
+
+ entry = uio_getPDirEntryHandle(pDirHandle, tempBuf);
+ if (entry == NULL) {
+ retVal = ENOENT;
+ break;
+ }
+ if (!uio_PDirEntryHandle_isDir(entry)) {
+ uio_PDirEntryHandle_unref(entry);
+ retVal = ENOTDIR;
+ break;
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ pDirHandle = (uio_PDirHandle *) entry;
+ getNextPathComponent(pathEnd, &partStart, &partEnd);
+ }
+
+ uio_free(tempBuf);
+ *pathRest = partStart;
+ *endPDirHandle = pDirHandle;
+ return retVal;
+}
+
+/**
+ * Create a directory inside a physical directory. All non-existant
+ * parent directories will be created as well.
+ *
+ * @param[in] pDirHandle The physical directory to which 'path' is relative
+ * @param[in] path The path to the directory to create, relative to
+ * 'pDirHandle'
+ * @param[in] pathLen The string length of 'path'.
+ * @param[in] mode The access mode for the newly created directories.
+ *
+ * @returns the new (physical) directory, or NULL if an error occurs, in
+ * which case #errno will be set.
+ */
+uio_PDirHandle *
+uio_makePath(uio_PDirHandle *pDirHandle, const char *path, size_t pathLen,
+ mode_t mode) {
+ const char *rest, *start, *end;
+ uio_PDirHandle *(*mkdirFun)(uio_PDirHandle *, const char *, mode_t);
+ char *buf;
+ const char *pathEnd;
+ uio_PDirHandle *newPDirHandle;
+
+ mkdirFun = pDirHandle->pRoot->handler->mkdir;
+ if (mkdirFun == NULL) {
+ errno = ENOSYS;
+ return NULL;
+ }
+
+ pathEnd = path + pathLen;
+
+ buf = uio_malloc(pathLen + 1);
+ // worst case length
+ // XXX: Use a dynamically allocated array when moving to C99.
+
+ uio_walkPhysicalPath(pDirHandle, path, pathLen, &pDirHandle, &rest);
+ // The reference to the original pDirHandle is still kept
+ // by the calling function; uio_PDirHandle_unref should not
+ // be called for it.
+ // Rest now points into 'path' to the part from where no physical
+ // dir exists.
+
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+
+ newPDirHandle = mkdirFun(pDirHandle, buf, mode);
+ if (newPDirHandle == NULL) {
+ int savedErrno = errno;
+ uio_PDirHandle_unref(pDirHandle);
+ errno = savedErrno;
+ uio_free(buf);
+ return NULL;
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ pDirHandle = newPDirHandle;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ uio_free(buf);
+ return pDirHandle;
+}
+
+
+/**
+ * Copy a file from one physical directory to another.
+ * The copy will have the same file permissions as the original.
+ *
+ * @param[in] fromDir The physical directory where the file to copy is
+ * located.
+ * @param[in] fromName The name of the file to copy.
+ * @param[in] toDir The physical directory where to put the copy.
+ * @param[in] toName The name to use for the copy.
+ *
+ * @note It is up to the caller to make any relevant permissions checks.
+ *
+ * @note This function will fail if a file with the name in 'toName' already
+ * exists, leaving the original file intact. If an error occurs during
+ * copying, an attempt is made to remove the file that was to be the
+ * copy.
+ */
+int
+uio_copyFilePhysical(uio_PDirHandle *fromDir, const char *fromName,
+ uio_PDirHandle *toDir, const char *toName) {
+ uio_FileSystemHandler *fromHandler, *toHandler;
+ uio_Handle *fromHandle;
+ uio_Handle *toHandle;
+#define BUFSIZE 0x10000
+ struct stat statBuf;
+ char *buf, *bufPtr;
+ ssize_t numInBuf, numWritten;
+
+ fromHandler = fromDir->pRoot->handler;
+ toHandler = toDir->pRoot->handler;
+ if (toHandler->write == NULL || fromHandler->fstat == NULL ||
+ toHandler->unlink == NULL) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ fromHandle = (fromHandler->open)(fromDir, fromName, O_RDONLY, 0);
+ if (fromHandle == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ if ((fromHandler->fstat)(fromHandle, &statBuf) == -1)
+ return copyError(errno, fromHandler, fromHandle,
+ toHandler, NULL, NULL, NULL, NULL);
+
+ toHandle = (toHandler->open)(toDir, toName, O_WRONLY | O_CREAT | O_EXCL,
+ statBuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (toHandle == NULL)
+ return copyError(errno, fromHandler, fromHandle,
+ toHandler, NULL, NULL, NULL, NULL);
+
+ buf = uio_malloc(BUFSIZE);
+ // not allocated on the stack, as this function may be called
+ // from a thread with little stack space.
+ while (1) {
+ numInBuf = (fromHandler->read)(fromHandle, buf, BUFSIZE);
+ if (numInBuf == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (errno, fromHandler, fromHandle,
+ toHandler, toHandle, toDir, toName, buf);
+ }
+ if (numInBuf == 0)
+ break;
+
+ bufPtr = buf;
+ do {
+ numWritten = (toHandler->write)(toHandle, bufPtr, numInBuf);
+ if (numWritten == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (errno, fromHandler, fromHandle,
+ toHandler, toHandle, toDir, toName, buf);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ } while (numInBuf > 0);
+ }
+
+ uio_free(buf);
+ (toHandler->close)(toHandle);
+ (fromHandler->close)(fromHandle);
+ return 0;
+}
+
+/*
+ * Closes fromHandle if it's not -1.
+ * Closes fromHandle if it's not -1.
+ * Removes 'toName' from 'toDir' if it's not NULL.
+ * Frees 'buf' if not NULL.
+ * Always returns -1, setting errno to 'error'.
+ */
+static int
+copyError(int error,
+ uio_FileSystemHandler *fromHandler, uio_Handle *fromHandle,
+ uio_FileSystemHandler *toHandler, uio_Handle *toHandle,
+ uio_PDirHandle *toDir, const char *toName, char *buf) {
+#ifdef DEBUG
+ fprintf(stderr, "Error while copying: %s\n", strerror(error));
+#endif
+
+ if (fromHandle != NULL)
+ (fromHandler->close)(fromHandle);
+
+ if (toHandle != NULL)
+ (toHandler->close)(toHandle);
+
+ if (toName != NULL)
+ (toHandler->unlink)(toDir, toName);
+
+ if (buf != NULL)
+ uio_free(buf);
+
+ errno = error;
+ return -1;
+}
+
+/*
+ * Description: find PDirHandle and MountInfo structures for reading and
+ * writing for a path from a DirHandle.
+ * This can be used for opening a file and creating directories.
+ * Arguments: dirHandle - the directory to which the path is relative.
+ * path - the path to the component that is to be accessed.
+ * The last component does not have to exist.
+ * flags - used to specify what kind of access is requested
+ * Either O_RDONLY, O_RDWR, O_WRONLY. They may be
+ * OR'd with other values accepted by open(). These
+ * are ignored.
+ * XXX: this is no longer true.
+ * TODO: update this doc, and check uio_open() and
+ * perhaps others (uio_mkdir()) for unnecessary
+ * checks on O_CREAT and O_EXCL.
+ * extraFlags - either 0 or uio_GPA_NOWRITE
+ * When 0, the path will be created if it doesn't
+ * exist in the writing location, but does exist
+ * in the reading location. With uio_GPA_NOWRITE, it
+ * won't be created, and -1 will be returned and errno
+ * will be set to ENOENT.
+ * mountInfoReadPtr - pointer to location where the pointer
+ * to the MountInfo structure for the reading location
+ * should be stored.
+ * readPDirHandlePtr - pointer to the location where the pointer
+ * to the PDirHandle used for reading should be stored.
+ * readPRootPath - pointer to the location where the pointer
+ * to the physical path to the reading location
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * mountInfoWritePtr - pointer to location where the pointer
+ * to the MountInfo structure for the writing location
+ * should be stored.
+ * writePDirHandlePtr - pointer to the location where the pointer
+ * to the PDirHandle used for writing should be stored.
+ * NULL if O_RDONLY was specified.
+ * If this is the same dir as the one refered
+ * to by readPDirHandlePtr, the handles will be the
+ * same too.
+ * writePRootPath - pointer to the location where the pointer
+ * to the physical path to the writing location
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * restPtr - pointer to the location where a newly created
+ * string with as contents the last component of 'path'
+ * is to be stored.
+ * The caller is responsible for freeing this.
+ * Ignored if NULL.
+ * Returns: 0 - success
+ * -1 - failure (errno set)
+ * NB: This is the function that would most benefit from
+ * the introduction of LDirs.
+ * This is also the most messy function. It could
+ * use some more comments, or a cleanup (if possible).
+ */
+int
+uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path,
+ int flags, int extraFlags,
+ uio_MountInfo **mountInfoReadPtr, uio_PDirHandle **readPDirHandlePtr,
+ char **readPRootPathPtr,
+ uio_MountInfo **mountInfoWritePtr, uio_PDirHandle **writePDirHandlePtr,
+ char **writePRootPathPtr,
+ char **restPtr) {
+ char *fullPath; // path from dirHandle with 'path' added
+ const char *pRootPath; // path from the pRoot of a physical tree
+ const char *readPRootPath, *writePRootPath;
+ const char *rest, *readRest;
+ uio_MountTree *tree;
+ uio_MountTreeItem *item;
+ uio_MountTreeItem *readItem, *writeItem;
+ uio_PDirHandle *readPDirHandle, *writePDirHandle, *pDirHandle;
+ int retVal;
+ uio_bool entryExists;
+ // Set if the entry pointed to by path exists (including
+ // the last component of the path)
+
+ // 'path' is relative to dirHandle.
+ // Fill 'fullPath' with the absolute path.
+ if (uio_resolvePath(dirHandle, path, strlen(path), &fullPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // Walk the tree of mount points along 'fullPath'.
+ // 'tree' will be the part of the tree where we end up when we can go
+ // no further. tree->pLocs are all the mounts relevant there.
+ // 'rest' will point within 'fullPath' to what is left, after we can
+ // walk the tree of mountpoints no further.
+ uio_findMountTree(dirHandle->repository->mountTree, fullPath,
+ &tree, &rest);
+
+ readItem = NULL;
+ readPDirHandle = NULL;
+ readPRootPath = NULL;
+ writeItem = NULL;
+ writePDirHandle = NULL;
+ writePRootPath = NULL;
+ readRest = NULL;
+ // Satisfy compiler.
+ entryExists = false;
+ // try all the MountInfo structures in effect for this MountTree
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath);
+ retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest);
+ // rest points inside fullPath
+ if (retVal == 0) {
+ // Even the last component appeared to be a dir.
+ // As the last component did exist, we don't go on.
+ uio_free(fullPath);
+ uio_PDirHandle_unref(pDirHandle);
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = EISDIR;
+ return -1;
+ }
+ // check if this MountTreeItem is suitable for writing
+ if (writeItem == NULL && !uio_mountInfoIsReadOnly(item->mountInfo))
+ writeItem = item;
+ if (strchr(rest, '/') == NULL) {
+ // There's only one dir component that was not matched.
+ uio_PDirEntryHandle *entry;
+
+ // This MountInfo will do for reading, if the file from the last
+ // component is present in this dir.
+ entry = uio_getPDirEntryHandle(pDirHandle, rest);
+ if (entry != NULL) {
+ // 'rest' exists, and it is not a dir, as otherwise
+ // uio_getPDirEntryHandle wouldn't have stopped where it did.
+ uio_PDirEntryHandle_unref(entry);
+ readItem = item;
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ readPDirHandle = pDirHandle;
+ readPRootPath = pRootPath;
+ readRest = rest;
+ entryExists = true;
+ break;
+ } else {
+ // 'rest' doesn't exist
+ // We're only interested in this dir if we want to write too.
+ if (((flags & O_ACCMODE) != O_RDONLY) && readItem == NULL) {
+ // Keep the first one.
+ readItem = item;
+ assert(readPDirHandle == NULL);
+ readPDirHandle = pDirHandle;
+ readPRootPath = pRootPath;
+ readRest = rest;
+ continue;
+ }
+ }
+ } else {
+ // There is more than one dir component that was not matched.
+ // If the first component of the non-matched part is a file,
+ // stop here and don't check lower dirs.
+ if (retVal == ENOTDIR) {
+ uio_free(fullPath);
+ uio_PDirHandle_unref(pDirHandle);
+ if (readPDirHandle != NULL)
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+ uio_PDirHandle_unref(pDirHandle);
+ } // for
+ // readItem is set to the first readItem for which the path completely
+ // exists (including the final component). If there's no such readItem,
+ // it is set to the first path for which the path exists, but without
+ // the final component (entryExists is false in this case). If there's
+ // no such path either, it's set to NULL.
+
+ if (readItem == NULL) {
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+ if ((flags & O_ACCMODE) == O_RDONLY) {
+ // write access is not needed
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ // Don't touch mountInfoWritePtr and writePDirHandlePtr.
+ // they'd be NULL.
+ *restPtr = uio_strdup(readRest);
+ uio_free(fullPath);
+ return 0;
+ } else {
+ if (entryExists) {
+ if ((flags & O_CREAT) && (flags & O_EXCL)) {
+ // An entry should be created, but it already exists and
+ // it may not be overwritten.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = EEXIST;
+ return -1;
+ }
+ } else {
+ // Though the path to the entry existed (readPDirHandle is
+ // set to it), the entry itself doesn't, so we can't use it
+ // unless we intend to create it.
+ if (flags & O_CREAT) {
+ // The entry does not exist, but we can create it.
+ // Handled below.
+ } else {
+ // O_CREAT was not specified, so we cannot create
+ // this entry.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+
+ }
+ }
+ if (writeItem == readItem) {
+ // The read directory is usable as write directory too.
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ *mountInfoWritePtr = writeItem->mountInfo;
+ // writeItem == readItem
+ uio_PDirHandle_ref(readPDirHandle);
+ *writePDirHandlePtr = readPDirHandle;
+ // No copy&paste error, the read PDirHandle is the write
+ // pDirHandle too.
+ if (writePRootPathPtr != NULL)
+ *writePRootPathPtr = joinPathsAbsolute(
+ writeItem->mountInfo->dirName, writePRootPath);
+ if (restPtr != NULL)
+ *restPtr = uio_strdup(readRest);
+ uio_free(fullPath);
+ return 0;
+ }
+ if (writeItem == NULL) {
+ uio_free(fullPath);
+ uio_PDirHandle_unref(readPDirHandle);
+ errno = EPERM;
+ // readItem is not NULL, so ENOENT would not be correct here.
+ return -1;
+ }
+
+ // Left is the case where the write location is different from the
+ // read location.
+
+ pRootPath = uio_mountTreeItemRestPath(writeItem, tree->lastComp,
+ fullPath);
+
+ rest = strrchr(pRootPath, '/');
+ // rest points inside fullPath
+ if (rest == NULL) {
+ rest = pRootPath;
+ uio_PDirHandle_ref(writeItem->mountInfo->pDirHandle);
+ writePDirHandle = writeItem->mountInfo->pDirHandle;
+ } else {
+ // There exists no path for a write dir, so it will have to be created.
+ // writeMountInfo indicates the physical tree where it should end up.
+
+ if (extraFlags & uio_GPA_NOWRITE) {
+ // The caller has specified that the path should not be created.
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = ENOENT;
+ return -1;
+ }
+
+ writePDirHandle = uio_makePath(writeItem->mountInfo->pDirHandle,
+ pRootPath, rest - pRootPath, 0777);
+ if (writePDirHandle == NULL) {
+ int savedErrno;
+ if (errno == ENOSYS) {
+ // mkdir not supported. We want to report that we failed
+ // because of an error in the underlying layer.
+ // EIO sounds like the best choice.
+ errno = EIO;
+ }
+ savedErrno = errno;
+ uio_PDirHandle_unref(readPDirHandle);
+ uio_free(fullPath);
+ errno = savedErrno;
+ return -1;
+ }
+ rest++; // skip the '/'
+ }
+
+ if (!entryExists) {
+ // The path to the read dir exists, but the entry itself doesn't.
+ // After we created the write dir, the same thing holds for
+ // the write dir. As it occurs in an earlier MountItem, we'll use
+ // the writeItem (and writePDirHandle) for reading too.
+ readItem = writeItem;
+ uio_PDirHandle_ref(writePDirHandle);
+ uio_PDirHandle_unref(readPDirHandle);
+ readPDirHandle = writePDirHandle;
+ }
+
+ *mountInfoReadPtr = readItem->mountInfo;
+ *readPDirHandlePtr = readPDirHandle;
+ if (readPRootPathPtr != NULL)
+ *readPRootPathPtr = joinPathsAbsolute(
+ readItem->mountInfo->dirName, readPRootPath);
+ *mountInfoWritePtr = writeItem->mountInfo;
+ *writePDirHandlePtr = writePDirHandle;
+ if (writePRootPathPtr != NULL)
+ *writePRootPathPtr = joinPathsAbsolute(
+ writeItem->mountInfo->dirName, writePRootPath);
+ if (restPtr != NULL)
+ *restPtr = uio_strdup(rest);
+ uio_free(fullPath);
+ return 0;
+}
+
+
+/**
+ * Get handles to the (existing) physical dirs that are effective in a
+ * path 'path' relative from 'dirHandle'
+ *
+ * @param[in] pDirHandle The physical directory to which 'path' is
+ * relative.
+ * @param[in] path The path to get the physical dirs for, relative to
+ * 'pDirHandle'
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] resPDirHandles *resPDirHandles is set to the handles to the
+ * (existing) physical dirs that are effective in 'path' (relative to
+ * pDirHandle), or NULL if there are none.
+ * @param[out] resNumPDirHandles The number of PDirHandles found.
+ * @param[out] resItems If 'resItems' != NULL, *resItems is set to the
+ * MountTreeItems belonging to $pDirHandles, or NULL if none were found.
+ *
+ * @retval 0 if everything went ok.
+ * @retval -1 if an error occurred; #errno is set.
+ */
+int
+uio_getPathPhysicalDirs(uio_DirHandle *dirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle ***resPDirHandles,
+ int *resNumPDirHandles, uio_MountTreeItem ***resItems) {
+ uio_PDirHandle **pDirHandles;
+ char *fullPath; // path from dirHandle with 'path' added
+ uio_MountTree *tree;
+ const char *rest;
+ uio_MountTreeItem *item, **items;
+ const char *pRootPath; // path from the pRoot of a physical tree
+ int numPDirHandles;
+ int pDirI; // PDirHandle iterator
+
+ // Determine the absolute path from 'path', which is relative to dirHandle.
+ if (uio_resolvePath(dirHandle, path, pathLen, &fullPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // get the MountTree effective for the path
+ uio_findMountTree(dirHandle->repository->mountTree, fullPath,
+ &tree, &rest);
+
+ // fill pDirHandles with all the PDirHandles for the path
+ numPDirHandles = uio_mountTreeCountPLocs(tree);
+ pDirHandles = uio_malloc(numPDirHandles * sizeof (uio_PDirHandle *));
+ if (resItems != NULL) {
+ items = uio_malloc(numPDirHandles * sizeof (uio_MountTreeItem *));
+ } else {
+ items = NULL; // satisfy compiler
+ }
+ pDirI = 0;
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ uio_PDirHandle *pDirHandle;
+
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp, fullPath);
+ switch (uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest)) {
+ case 0:
+ // complete path was matched
+ pDirHandles[pDirI] = pDirHandle;
+ if (resItems != NULL)
+ items[pDirI] = item;
+ pDirI++;
+ continue;
+ case ENOENT:
+ // some component couldn't be matched
+ uio_PDirHandle_unref(pDirHandle);
+ continue;
+ case ENOTDIR:
+ // next component was not a dir
+ // Don't look further at other mount Items.
+ uio_PDirHandle_unref(pDirHandle);
+ break;
+ default:
+ assert(false);
+ uio_PDirHandle_unref(pDirHandle);
+ continue;
+ }
+ break;
+ }
+ numPDirHandles = pDirI;
+
+ uio_free(fullPath);
+
+ *resPDirHandles = uio_realloc(pDirHandles,
+ numPDirHandles * sizeof (uio_PDirHandle *));
+ if (resItems != NULL)
+ *resItems = uio_realloc(items,
+ numPDirHandles * sizeof (uio_MountTreeItem *));
+ *resNumPDirHandles = numPDirHandles;
+
+ return 0;
+}
+
+// returns 0 if the path is valid and exists
+// returns -1 if the path is not valid or does not exist.
+// in this case errno will be set to:
+// ENOENT if some component didn't exist
+// ENOTDIR is some component exists, but is not a dir
+// something else (like EPATHTOOLONG) for internal errors
+// On success, 'resolvedPath' will be set to the absolute path as returned by
+// uio_resolvePath.
+int
+uio_verifyPath(uio_DirHandle *dirHandle, const char *path,
+ char **resolvedPath) {
+ uio_MountTree *tree;
+ uio_MountTreeItem *item;
+ const char *rest;
+ int retVal;
+
+ // TODO: "////", "/somedir//", and "//somedir" are accepted as valid
+
+ // Determine the absolute path from 'path' which is relative to dirHandle.
+ if (uio_resolvePath(dirHandle, path, strlen(path), resolvedPath) == -1) {
+ // errno is set
+ return -1;
+ }
+
+ // get the MountTree effective for the path
+ uio_findMountTree(dirHandle->repository->mountTree, *resolvedPath,
+ &tree, &rest);
+
+ if (rest[0] == '\0') {
+ // Complete match. Even if there are no pLocs in effect here
+ // (which can only happen in case the mount Tree is empty and
+ // we're viewing '/').
+ return 0;
+ }
+
+ // Try all the MountInfo structures in effect for this MountTree.
+ for (item = tree->pLocs; item != NULL; item = item->next) {
+ const char *pRootPath;
+ uio_PDirHandle *pDirHandle;
+
+ pRootPath = uio_mountTreeItemRestPath(item, tree->lastComp,
+ *resolvedPath);
+ retVal = uio_walkPhysicalPath(item->mountInfo->pDirHandle, pRootPath,
+ strlen(pRootPath), &pDirHandle, &rest);
+ uio_PDirHandle_unref(pDirHandle);
+ switch (retVal) {
+ case 0:
+ // Complete match. We're done.
+ return 0;
+ case ENOTDIR:
+ // A component is matched, but not as a dir. Failed.
+ uio_free(*resolvedPath);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ // No match; try the next pLoc.
+ continue;
+ default:
+ // Unknown error. Let's bail out just to be safe.
+#ifdef DEBUG
+ fprintf(stderr, "Warning: Unknown error from "
+ "uio_walkPhysicalPath: %s\n", strerror(retVal));
+#endif
+ uio_free(*resolvedPath);
+ errno = retVal;
+ return -1;
+ }
+ }
+
+ // No match, exit with ENOENT.
+ uio_free(*resolvedPath);
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * Determine the absolute path given a path relative to a given directory.
+ *
+ * @param[in] dirHandle The directory to which 'path' is relative.
+ * @param[in] path The path, relative to 'dirHandle', to make
+ * absolute.
+ * @param[in] pathLen The string length of 'path'.
+ * @param[out] destPath Filled with a newly allocated string containing
+ * the sought absolute path. It will not contain a '/' as the first
+ * or last character. It should be freed with uio_free().
+ * Unmodified if an error occurs.
+ *
+ * @returns the length of '*destPath', or -1 if an error occurs, in which
+ * case #errno will be set.
+ */
+ssize_t
+uio_resolvePath(uio_DirHandle *dirHandle, const char *path, size_t pathLen,
+ char **destPath) {
+ size_t len;
+ const char *pathEnd, *start, *end;
+ int numUp; // number of ".." dirs still need to be matched.
+ char *buffer;
+ char *endBufPtr;
+ uio_bool absolute;
+
+ absolute = path[0] == '/';
+ pathEnd = path + pathLen;
+
+ // Pass 1: count the amount of space needed
+ len = 0;
+ numUp = 0;
+ for (getLastPathComponent(path, pathEnd, &start, &end);
+ end > path;
+ getPreviousPathComponent(path, &start, &end)) {
+ if (start[0] == '.') {
+ if (start + 1 == end) {
+ // "." matched
+ continue;
+ }
+ if (start[1] == '.' && start + 2 == end) {
+ // ".." matched
+ numUp++;
+ continue;
+ }
+ }
+ if (numUp > 0) {
+ // last 'numUp' components were ".."
+ numUp--;
+ continue;
+ }
+ len += (end - start) + 1;
+ // the 1 is for the '/'
+ }
+
+ // The part from 'dirHandle->path' to 'dirHandle->rootEnd' is
+ // always copied (for a valid path). The rest we'll have to count.
+ // (Note the 'rootEnd' in the initialiser of the for loop.)
+ len += (dirHandle->rootEnd - dirHandle->path);
+ if (!absolute) {
+ for (getLastPath0Component(dirHandle->rootEnd, &start, &end);
+ end > dirHandle->rootEnd;
+ getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) {
+ if (numUp > 0) {
+ numUp--;
+ continue;
+ }
+ len += (end - start) + 1;
+ // the 1 is for the '/'
+ }
+ }
+ if (numUp > 0) {
+ // too many ".."
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (len == 0) {
+ *destPath = uio_malloc(1);
+ (*destPath)[0] = '\0';
+ return 0;
+ }
+
+ // len--; // we don't want a '/' at the start
+ // len++; // for the terminating '\0'
+ buffer = uio_malloc(len);
+
+ // Pass 2: fill the buffer
+ endBufPtr = buffer + len - 1;
+ *endBufPtr = '\0';
+ numUp = 0;
+ for (getLastPathComponent(path, pathEnd, &start, &end);
+ end > path;
+ getPreviousPathComponent(path, &start, &end)) {
+ if (start[0] == '.') {
+ if (start + 1 == end) {
+ // "." matched
+ continue;
+ }
+ if (start[1] == '.' && start + 2 == end) {
+ // ".." matched
+ numUp++;
+ continue;
+ }
+ }
+ if (numUp > 0) {
+ // last 'numUp' components were ".."
+ numUp--;
+ continue;
+ }
+ endBufPtr -= (end - start);
+ memcpy(endBufPtr, start, end - start);
+ if (endBufPtr != buffer) {
+ // We want no '/' at the beginning
+ endBufPtr--;
+ *endBufPtr = '/';
+ } else {
+ // We're already done. We might as well take advantage of
+ // the fact that we know that and exit immediatly:
+ *destPath = buffer;
+ return len;
+ }
+ }
+ // copy the part from dirHandle->path to dirHandle->rootEnd
+ endBufPtr -= (dirHandle->rootEnd - dirHandle->path);
+ memcpy(endBufPtr, dirHandle->path, dirHandle->rootEnd - dirHandle->path);
+ if (!absolute) {
+ // copy (some of) the components from dirHandle->rootEnd on.
+ for (getLastPath0Component(dirHandle->rootEnd, &start, &end);
+ end > dirHandle->rootEnd;
+ getPreviousPath0Component(dirHandle->rootEnd, &start, &end)) {
+ if (numUp > 0) {
+ numUp--;
+ continue;
+ }
+ endBufPtr -= (end - start);
+ memcpy(endBufPtr, start, end - start);
+ if (endBufPtr != buffer) {
+ // We want no '/' at the beginning
+ endBufPtr--;
+ *endBufPtr = '/';
+ } else {
+ // We're already done. We might as well take advantage of
+ // the fact that we know that and exit immediatly:
+ break;
+ }
+ }
+ }
+
+ *destPath = buffer;
+ return len;
+}
+
+
diff --git a/src/libs/uio/ioaux.h b/src/libs/uio/ioaux.h
new file mode 100644
index 0000000..41c7e39
--- /dev/null
+++ b/src/libs/uio/ioaux.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_IOAUX_H_
+#define LIBS_UIO_IOAUX_H_
+
+#include "iointrn.h"
+#include "physical.h"
+#include "uioport.h"
+
+int uio_walkPhysicalPath(uio_PDirHandle *startPDirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle **endPDirHandle,
+ const char **pathRest);
+uio_PDirHandle *uio_makePath(uio_PDirHandle *pDirHandle, const char *path,
+ size_t pathLen, mode_t mode);
+int uio_copyFilePhysical(uio_PDirHandle *fromDir, const char *fromName,
+ uio_PDirHandle *toDir, const char *toName);
+int uio_getPhysicalAccess(uio_DirHandle *dirHandle, const char *path,
+ int flags, int extraFlags,
+ uio_MountInfo **mountInfoReadPtr, uio_PDirHandle **readPDirHandlePtr,
+ char **readPRootPathPtr,
+ uio_MountInfo **mountInfoWritePtr, uio_PDirHandle **writePDirHandlePtr,
+ char **writePRootPathPtr,
+ char **restPtr);
+#define uio_GPA_NOWRITE 1
+int uio_getPathPhysicalDirs(uio_DirHandle *dirHandle, const char *path,
+ size_t pathLen, uio_PDirHandle ***resPDirHandles,
+ int *resNumPDirHandles, uio_MountTreeItem ***resItems);
+int uio_verifyPath(uio_DirHandle *dirHandle, const char *path,
+ char **resolvedPath);
+ssize_t uio_resolvePath(uio_DirHandle *dirHandle, const char *path,
+ size_t pathLen, char **destPath);
+
+
+#endif /* LIBS_UIO_IOAUX_H_ */
+
diff --git a/src/libs/uio/iointrn.h b/src/libs/uio/iointrn.h
new file mode 100644
index 0000000..522ce18
--- /dev/null
+++ b/src/libs/uio/iointrn.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_IOINTRN_H_
+#define LIBS_UIO_IOINTRN_H_
+
+#define uio_INTERNAL
+
+typedef struct uio_PDirEntryHandle uio_PDirEntryHandle;
+typedef struct uio_PDirHandle uio_PDirHandle;
+typedef struct uio_PFileHandle uio_PFileHandle;
+typedef struct uio_EntriesContext uio_EntriesContext;
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_PDirHandleExtra;
+typedef void *uio_PFileHandleExtra;
+#endif
+
+#include "io.h"
+#include "uioport.h"
+#include "physical.h"
+#include "mount.h"
+#include "mounttree.h"
+#include "match.h"
+#include "mem.h"
+
+struct uio_Handle {
+ int ref;
+ struct uio_PRoot *root;
+ uio_NativeHandle native;
+ int openFlags;
+ // need to know whether the handle is a RO or RW handle.
+};
+
+struct uio_DirHandle {
+ int ref;
+ struct uio_Repository *repository;
+ char *path;
+ // does not contain any '.' or '..'; does not start or end
+ // with a /
+ char *rootEnd;
+ // points to the end of the part of path that is considered
+ // the root dir. (you can't use '..' to get above the root dir)
+};
+
+struct uio_MountHandle {
+ struct uio_Repository *repository;
+ struct uio_MountInfo *mountInfo;
+};
+
+struct uio_DirList {
+ const char **names;
+ int numNames;
+ char *buffer;
+};
+
+
+#define uio_PHandle_COMMON \
+ int flags; \
+ int ref; \
+ uio_PRoot *pRoot;
+
+#define uio_PDirEntryHandle_TYPE_REG 0x0000
+#define uio_PDirEntryHandle_TYPE_DIR 0x0001
+#define uio_PDirEntryHandle_TYPEMASK 0x0001
+
+struct uio_PDirEntryHandle {
+ uio_PHandle_COMMON
+};
+
+struct uio_PDirHandle {
+ uio_PHandle_COMMON
+ uio_PDirHandleExtra extra;
+};
+
+struct uio_PFileHandle {
+ uio_PHandle_COMMON
+ uio_PFileHandleExtra extra;
+};
+
+struct uio_EntriesContext {
+ uio_PRoot *pRoot;
+ uio_NativeEntriesContext native;
+};
+
+
+uio_Handle *uio_Handle_new(uio_PRoot *root, uio_NativeHandle native,
+ int openFlags);
+void uio_Handle_delete(uio_Handle *handle);
+void uio_DirHandle_delete(uio_DirHandle *dirHandle);
+
+uio_PDirEntryHandle *uio_getPDirEntryHandle(
+ const uio_PDirHandle *dirHandle, const char *name);
+void uio_PDirHandle_deletePDirHandleExtra(uio_PDirHandle *pDirHandle);
+void uio_PFileHandle_deletePFileHandleExtra(uio_PFileHandle *pFileHandle);
+
+uio_PDirHandle *uio_PDirHandle_new(uio_PRoot *pRoot,
+ uio_PDirHandleExtra extra);
+uio_PFileHandle *uio_PFileHandle_new(uio_PRoot *pRoot,
+ uio_PFileHandleExtra extra);
+void uio_PDirEntryHandle_delete(uio_PDirEntryHandle *pDirEntryHandle);
+void uio_PDirHandle_delete(uio_PDirHandle *pDirHandle);
+void uio_PFileHandle_delete(uio_PFileHandle *pFileHandle);
+
+
+static inline void
+uio_Handle_ref(uio_Handle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_Handle_unref(uio_Handle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_Handle_delete(handle);
+}
+
+static inline void
+uio_DirHandle_ref(uio_DirHandle *dirHandle) {
+ dirHandle->ref++;
+}
+
+static inline void
+uio_DirHandle_unref(uio_DirHandle *dirHandle) {
+ assert(dirHandle->ref > 0);
+ dirHandle->ref--;
+ if (dirHandle->ref == 0)
+ uio_DirHandle_delete(dirHandle);
+}
+
+static inline uio_bool
+uio_PDirEntryHandle_isDir(const uio_PDirEntryHandle *handle) {
+ return (handle->flags & uio_PDirEntryHandle_TYPEMASK) ==
+ uio_PDirEntryHandle_TYPE_DIR;
+}
+
+static inline void
+uio_PDirEntryHandle_ref(uio_PDirEntryHandle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_PDirEntryHandle_unref(uio_PDirEntryHandle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_PDirEntryHandle_delete(handle);
+}
+
+static inline void
+uio_PDirHandle_ref(uio_PDirHandle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_PDirHandle_unref(uio_PDirHandle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_PDirHandle_delete(handle);
+}
+
+static inline void
+uio_PFileHandle_ref(uio_PFileHandle *handle) {
+ handle->ref++;
+}
+
+static inline void
+uio_PFileHandle_unref(uio_PFileHandle *handle) {
+ assert(handle->ref > 0);
+ handle->ref--;
+ if (handle->ref == 0)
+ uio_PFileHandle_delete(handle);
+}
+
+
+#endif /* LIBS_UIO_IOINTRN_H_ */
+
+
diff --git a/src/libs/uio/match.c b/src/libs/uio/match.c
new file mode 100644
index 0000000..68e6897
--- /dev/null
+++ b/src/libs/uio/match.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <string.h>
+#ifdef DEBUG
+# include <stdio.h>
+#endif
+
+#define match_INTERNAL
+
+#include "match.h"
+#include "mem.h"
+#include "uioport.h"
+
+#ifdef HAVE_GLOB
+# include <fnmatch.h>
+#endif
+
+
+static inline match_MatchContext *match_allocMatchContext(void);
+static inline void match_freeMatchContext(match_MatchContext *context);
+
+static inline match_LiteralContext *match_newLiteralContext(char *pattern);
+static inline match_LiteralContext *match_allocLiteralContext(void);
+static inline void match_freeLiteralContext(match_LiteralContext *context);
+static inline match_PrefixContext *match_newPrefixContext(char *pattern);
+static inline match_PrefixContext *match_allocPrefixContext(void);
+static inline void match_freePrefixContext(match_PrefixContext *context);
+static inline match_SuffixContext *match_newSuffixContext(
+ char *pattern, size_t len);
+static inline match_SuffixContext *match_allocSuffixContext(void);
+static inline void match_freeSuffixContext(match_SuffixContext *context);
+static inline match_SubStringContext *match_newSubStringContext(
+ char *pattern);
+static inline match_SubStringContext *match_allocSubStringContext(void);
+static inline void match_freeSubStringContext(match_SubStringContext *context);
+#ifdef HAVE_GLOB
+static inline match_GlobContext *match_newGlobContext(char *pattern);
+static inline match_GlobContext *match_allocGlobContext(void);
+static inline void match_freeGlobContext(match_GlobContext *context);
+#endif /* HAVE_GLOB */
+#ifdef HAVE_REGEX
+static inline match_RegexContext *match_newRegexContext(void);
+static inline match_RegexContext *match_allocRegexContext(void);
+static inline void match_freeRegexContext(match_RegexContext *context);
+#endif /* HAVE_REGEX */
+
+
+// *** General part ***
+
+static inline match_MatchContext *
+match_allocMatchContext(void) {
+ return uio_malloc(sizeof (match_MatchContext));
+}
+
+static inline void
+match_freeMatchContext(match_MatchContext *context) {
+ uio_free(context);
+}
+
+// NB: Even if this function fails, *contextPtr contains a context
+// which needs to be freed.
+match_Result
+match_prepareContext(const char *pattern, match_MatchContext **contextPtr,
+ match_MatchType type) {
+ match_Result result;
+
+ *contextPtr = match_allocMatchContext();
+ (*contextPtr)->type = type;
+ switch (type) {
+ case match_MATCH_LITERAL:
+ result = match_prepareLiteral(pattern,
+ &(*contextPtr)->u.literal);
+ break;
+ case match_MATCH_PREFIX:
+ result = match_preparePrefix(pattern, &(*contextPtr)->u.prefix);
+ break;
+ case match_MATCH_SUFFIX:
+ result = match_prepareSuffix(pattern, &(*contextPtr)->u.suffix);
+ break;
+ case match_MATCH_SUBSTRING:
+ result = match_prepareSubString(pattern,
+ &(*contextPtr)->u.subString);
+ break;
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ result = match_prepareGlob(pattern, &(*contextPtr)->u.glob);
+ break;
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ result = match_prepareRegex(pattern, &(*contextPtr)->u.regex);
+ break;
+#endif
+ default:
+#ifdef DEBUG
+ fprintf(stderr, "match_prepareContext called with unsupported "
+ "type %d matching.\n", type);
+#endif
+ return match_ENOSYS;
+ }
+ return result;
+}
+
+match_Result
+match_matchPattern(match_MatchContext *context, const char *string) {
+ switch (context->type) {
+ case match_MATCH_LITERAL:
+ return match_matchLiteral(context->u.literal, string);
+ case match_MATCH_PREFIX:
+ return match_matchPrefix(context->u.prefix, string);
+ case match_MATCH_SUFFIX:
+ return match_matchSuffix(context->u.suffix, string);
+ case match_MATCH_SUBSTRING:
+ return match_matchSubString(context->u.subString, string);
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ return match_matchGlob(context->u.glob, string);
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ return match_matchRegex(context->u.regex, string);
+#endif
+ default:
+ abort();
+ }
+}
+
+// context may be NULL
+const char *
+match_errorString(match_MatchContext *context, match_Result result) {
+ switch (result) {
+ case match_OK:
+ case match_MATCH:
+// case match_NOMATCH: // same value as match_OK
+ return "Success";
+ case match_ENOSYS:
+ return "Not implemented";
+ case match_ENOTINIT:
+ return "Uninitialised use";
+ case match_ECUSTOM:
+ // Depends on match type. Printed below.
+ break;
+ default:
+ return "Unknown error";
+ }
+
+ if (context == NULL)
+ return "Unknown match-type specific error.";
+ // We can't be any more specific if no 'context' is supplied.
+
+ switch (context->type) {
+#if 0
+ case match_MATCH_LITERAL:
+ return match_errorStringLiteral(context->u.literal, result);
+ case match_MATCH_PREFIX:
+ return match_errorStringPrefix(context->u.prefix, result);
+ case match_MATCH_SUFFIX:
+ return match_errorStringSuffix(context->u.suffix, result);
+ case match_MATCH_SUBSTRING:
+ return match_errorStringSubString(context->u.subString, result);
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ return match_errorStringGlob(context->u.glob, result);
+#endif
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ return match_errorStringRegex(context->u.regex, result);
+#endif
+ default:
+ abort();
+ }
+}
+
+void
+match_freeContext(match_MatchContext *context) {
+ switch (context->type) {
+ case match_MATCH_LITERAL:
+ match_freeLiteral(context->u.literal);
+ break;
+ case match_MATCH_PREFIX:
+ match_freePrefix(context->u.prefix);
+ break;
+ case match_MATCH_SUFFIX:
+ match_freeSuffix(context->u.suffix);
+ break;
+ case match_MATCH_SUBSTRING:
+ match_freeSubString(context->u.subString);
+ break;
+#ifdef HAVE_GLOB
+ case match_MATCH_GLOB:
+ match_freeGlob(context->u.glob);
+ break;
+#endif
+#ifdef HAVE_REGEX
+ case match_MATCH_REGEX:
+ match_freeRegex(context->u.regex);
+ break;
+#endif
+ default:
+ abort();
+ }
+ match_freeMatchContext(context);
+}
+
+match_Result
+match_matchPatternOnce(const char *pattern, match_MatchType type,
+ const char *string) {
+ match_MatchContext *context;
+ match_Result result;
+
+ result = match_prepareContext(pattern, &context, type);
+ if (result != match_OK)
+ goto out;
+
+ result = match_matchPattern(context, string);
+
+out:
+ match_freeContext(context);
+ return result;
+}
+
+
+// *** Literal part ***
+
+match_Result
+match_prepareLiteral(const char *pattern,
+ match_LiteralContext **contextPtr) {
+ *contextPtr = match_newLiteralContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchLiteral(match_LiteralContext *context, const char *string) {
+ return (strcmp(context->pattern, string) == 0) ?
+ match_MATCH : match_NOMATCH;
+}
+
+void
+match_freeLiteral(match_LiteralContext *context) {
+ uio_free(context->pattern);
+ match_freeLiteralContext(context);
+}
+
+static inline match_LiteralContext *
+match_newLiteralContext(char *pattern) {
+ match_LiteralContext *result;
+
+ result = match_allocLiteralContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_LiteralContext *
+match_allocLiteralContext(void) {
+ return uio_malloc(sizeof (match_LiteralContext));
+}
+
+static inline void
+match_freeLiteralContext(match_LiteralContext *context) {
+ uio_free(context);
+}
+
+
+// *** Prefix part ***
+
+match_Result
+match_preparePrefix(const char *pattern,
+ match_PrefixContext **contextPtr) {
+ *contextPtr = match_newPrefixContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchPrefix(match_PrefixContext *context, const char *string) {
+ char *patPtr;
+
+ patPtr = context->pattern;
+ while (1) {
+ if (*patPtr == '\0') {
+ // prefix has completely matched
+ return match_MATCH;
+ }
+ if (*string == '\0') {
+ // no more string left, and still prefix to match
+ return match_NOMATCH;
+ }
+ if (*patPtr != *string)
+ return match_NOMATCH;
+ patPtr++;
+ string++;
+ }
+}
+
+void
+match_freePrefix(match_PrefixContext *context) {
+ uio_free(context->pattern);
+ match_freePrefixContext(context);
+}
+
+static inline match_PrefixContext *
+match_newPrefixContext(char *pattern) {
+ match_PrefixContext *result;
+
+ result = match_allocPrefixContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_PrefixContext *
+match_allocPrefixContext(void) {
+ return uio_malloc(sizeof (match_PrefixContext));
+}
+
+static inline void
+match_freePrefixContext(match_PrefixContext *context) {
+ uio_free(context);
+}
+
+
+// *** Suffix part ***
+
+match_Result
+match_prepareSuffix(const char *pattern,
+ match_SuffixContext **contextPtr) {
+ *contextPtr = match_newSuffixContext(
+ uio_strdup(pattern), strlen(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchSuffix(match_SuffixContext *context, const char *string) {
+ size_t stringLen;
+
+ stringLen = strlen(string);
+ if (stringLen < context->len) {
+ // Supplied suffix is larger than string
+ return match_NOMATCH;
+ }
+
+ return memcmp(string + stringLen - context->len, context->pattern,
+ context->len) == 0 ? match_MATCH : match_NOMATCH;
+}
+
+void
+match_freeSuffix(match_SuffixContext *context) {
+ uio_free(context->pattern);
+ match_freeSuffixContext(context);
+}
+
+static inline match_SuffixContext *
+match_newSuffixContext(char *pattern, size_t len) {
+ match_SuffixContext *result;
+
+ result = match_allocSuffixContext();
+ result->pattern = pattern;
+ result->len = len;
+ return result;
+}
+
+static inline match_SuffixContext *
+match_allocSuffixContext(void) {
+ return uio_malloc(sizeof (match_SuffixContext));
+}
+
+static inline void
+match_freeSuffixContext(match_SuffixContext *context) {
+ uio_free(context);
+}
+
+
+// *** SubString part ***
+
+match_Result
+match_prepareSubString(const char *pattern,
+ match_SubStringContext **contextPtr) {
+ *contextPtr = match_newSubStringContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchSubString(match_SubStringContext *context, const char *string) {
+ return strstr(string, context->pattern) != NULL;
+}
+
+void
+match_freeSubString(match_SubStringContext *context) {
+ uio_free(context->pattern);
+ match_freeSubStringContext(context);
+}
+
+static inline match_SubStringContext *
+match_newSubStringContext(char *pattern) {
+ match_SubStringContext *result;
+
+ result = match_allocSubStringContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_SubStringContext *
+match_allocSubStringContext(void) {
+ return uio_malloc(sizeof (match_SubStringContext));
+}
+
+static inline void
+match_freeSubStringContext(match_SubStringContext *context) {
+ uio_free(context);
+}
+
+
+// *** Glob part ***
+
+#ifdef HAVE_GLOB
+match_Result
+match_prepareGlob(const char *pattern, match_GlobContext **contextPtr) {
+ *contextPtr = match_newGlobContext(uio_strdup(pattern));
+ return match_OK;
+}
+
+match_Result
+match_matchGlob(match_GlobContext *context, const char *string) {
+ int retval;
+
+ retval = fnmatch(context->pattern, string, 0);
+ switch (retval) {
+ case 0:
+ return match_MATCH;
+ case FNM_NOMATCH:
+ return match_NOMATCH;
+#if 0
+ case FNM_NOSYS:
+ return match_ENOSYS;
+#endif
+ default:
+ return match_EUNKNOWN;
+ }
+}
+
+void
+match_freeGlob(match_GlobContext *context) {
+ uio_free(context->pattern);
+ match_freeGlobContext(context);
+}
+
+static inline match_GlobContext *
+match_newGlobContext(char *pattern) {
+ match_GlobContext *result;
+
+ result = match_allocGlobContext();
+ result->pattern = pattern;
+ return result;
+}
+
+static inline match_GlobContext *
+match_allocGlobContext(void) {
+ return uio_malloc(sizeof (match_GlobContext));
+}
+
+static inline void
+match_freeGlobContext(match_GlobContext *context) {
+ uio_free(context);
+}
+#endif /* HAVE_GLOB */
+
+
+// *** Regex part ***
+
+#ifdef HAVE_REGEX
+match_Result
+match_prepareRegex(const char *pattern, match_RegexContext **contextPtr) {
+ *contextPtr = match_newRegexContext();
+ (*contextPtr)->errorCode = regcomp(&(*contextPtr)->native, pattern,
+ REG_EXTENDED | REG_NOSUB);
+ if ((*contextPtr)->errorCode == 0) {
+ (*contextPtr)->flags = match_REGEX_INITIALISED;
+ return match_OK;
+ }
+ return match_ECUSTOM;
+}
+
+match_Result
+match_matchRegex(match_RegexContext *context, const char *string) {
+ int retval;
+
+ if ((context->flags & match_REGEX_INITIALISED) !=
+ match_REGEX_INITIALISED) {
+ return match_ENOTINIT;
+ }
+ if (context->errorString) {
+ uio_free(context->errorString);
+ context->errorString = NULL;
+ }
+ retval = regexec(&context->native, string, 0, NULL, 0);
+ switch (retval) {
+ case 0:
+ return match_MATCH;
+ case REG_NOMATCH:
+ return match_NOMATCH;
+ default:
+ context->errorCode = retval;
+ return match_ECUSTOM;
+ }
+}
+
+const char *
+match_errorStringRegex(match_RegexContext *context, int errorCode) {
+ size_t errorStringLength;
+
+ if (context->errorString != NULL)
+ uio_free(context->errorString);
+
+ errorStringLength = regerror(context->errorCode, &context->native,
+ NULL, 0);
+ context->errorString = uio_malloc(errorStringLength);
+ regerror(context->errorCode, &context->native,
+ context->errorString, errorStringLength);
+
+ (void) errorCode;
+ return context->errorString;
+}
+
+void
+match_freeRegex(match_RegexContext *context) {
+ regfree(&context->native);
+ if (context->errorString)
+ uio_free(context->errorString);
+ match_freeRegexContext(context);
+}
+
+static inline match_RegexContext *
+match_newRegexContext(void) {
+ match_RegexContext *result;
+ result = match_allocRegexContext();
+ result->errorString = NULL;
+ result->errorCode = 0;
+ result->flags = 0;
+ return result;
+}
+
+static inline match_RegexContext *
+match_allocRegexContext(void) {
+ return uio_malloc(sizeof (match_RegexContext));
+}
+
+static inline void
+match_freeRegexContext(match_RegexContext *context) {
+ uio_free(context);
+}
+#endif /* HAVE_REGEX */
+
diff --git a/src/libs/uio/match.h b/src/libs/uio/match.h
new file mode 100644
index 0000000..0557e7d
--- /dev/null
+++ b/src/libs/uio/match.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MATCH_H_
+#define LIBS_UIO_MATCH_H_
+
+typedef struct match_MatchContext match_MatchContext;
+
+#include <sys/types.h>
+
+// TODO: make this into a configurable option
+//#define HAVE_GLOB
+#define HAVE_REGEX
+
+
+typedef enum {
+ match_MATCH_LITERAL = 0,
+ match_MATCH_PREFIX,
+ match_MATCH_SUFFIX,
+ match_MATCH_SUBSTRING,
+#ifdef HAVE_GLOB
+ match_MATCH_GLOB,
+#endif
+#ifdef HAVE_REGEX
+ match_MATCH_REGEX,
+#endif
+} match_MatchType;
+
+typedef int match_Result;
+#define match_NOMATCH 0
+#define match_MATCH 1
+#define match_OK 0
+#define match_EUNKNOWN -1
+#define match_ENOSYS -2
+#define match_ECUSTOM -3
+#define match_ENOTINIT -4
+
+typedef struct match_LiteralContext match_LiteralContext;
+typedef struct match_PrefixContext match_PrefixContext;
+typedef struct match_SuffixContext match_SuffixContext;
+typedef struct match_SubStringContext match_SubStringContext;
+#ifdef HAVE_GLOB
+typedef struct match_GlobContext match_GlobContext;
+#endif
+#ifdef HAVE_REGEX
+typedef struct match_RegexContext match_RegexContext;
+#endif
+
+match_Result match_prepareContext(const char *pattern,
+ match_MatchContext **contextPtr, match_MatchType type);
+match_Result match_matchPattern(match_MatchContext *context,
+ const char *string);
+const char *match_errorString(match_MatchContext *context,
+ match_Result result);
+void match_freeContext(match_MatchContext *context);
+match_Result match_matchPatternOnce(const char *pattern, match_MatchType type,
+ const char *string);
+
+
+/* *** Internal definitions follow *** */
+#ifdef match_INTERNAL
+
+#include <sys/types.h>
+#ifdef HAVE_REGEX
+# include <regex.h>
+#endif
+
+#include "uioport.h"
+
+struct match_MatchContext {
+ match_MatchType type;
+ union {
+ match_LiteralContext *literal;
+ match_PrefixContext *prefix;
+ match_SuffixContext *suffix;
+ match_SubStringContext *subString;
+#ifdef HAVE_GLOB
+ match_GlobContext *glob;
+#endif
+#ifdef HAVE_REGEX
+ match_RegexContext *regex;
+#endif
+ } u;
+};
+
+struct match_LiteralContext {
+ char *pattern;
+};
+
+struct match_PrefixContext {
+ char *pattern;
+};
+
+struct match_SuffixContext {
+ char *pattern;
+ size_t len;
+ // for speed
+};
+
+struct match_SubStringContext {
+ char *pattern;
+};
+
+#ifdef HAVE_GLOB
+struct match_GlobContext {
+ char *pattern;
+};
+#endif
+
+#ifdef HAVE_REGEX
+struct match_RegexContext {
+ regex_t native;
+ char *errorString;
+ int errorCode;
+ int flags;
+#define match_REGEX_INITIALISED 1
+};
+#endif
+
+match_Result match_prepareLiteral(const char *pattern,
+ match_LiteralContext **contextPtr);
+match_Result match_matchLiteral(match_LiteralContext *context,
+ const char *string);
+void match_freeLiteral(match_LiteralContext *context);
+
+match_Result match_preparePrefix(const char *pattern,
+ match_PrefixContext **contextPtr);
+match_Result match_matchPrefix(match_PrefixContext *context,
+ const char *string);
+void match_freePrefix(match_PrefixContext *context);
+
+match_Result match_prepareSuffix(const char *pattern,
+ match_SuffixContext **contextPtr);
+match_Result match_matchSuffix(match_SuffixContext *context,
+ const char *string);
+void match_freeSuffix(match_SuffixContext *context);
+
+match_Result match_prepareSubString(const char *pattern,
+ match_SubStringContext **contextPtr);
+match_Result match_matchSubString(match_SubStringContext *context,
+ const char *string);
+void match_freeSubString(match_SubStringContext *context);
+
+#ifdef HAVE_GLOB
+match_Result match_prepareGlob(const char *pattern,
+ match_GlobContext **contextPtr);
+match_Result match_matchGlob(match_GlobContext *context,
+ const char *string);
+void match_freeGlob(match_GlobContext *context);
+#endif /* HAVE_GLOB */
+
+#ifdef HAVE_REGEX
+match_Result match_prepareRegex(const char *pattern,
+ match_RegexContext **contextPtr);
+match_Result match_matchRegex(match_RegexContext *context,
+ const char *string);
+const char *match_errorStringRegex(match_RegexContext *context,
+ int errorCode);
+void match_freeRegex(match_RegexContext *context);
+#endif
+
+#endif /* match_INTERNAL */
+
+#endif /* LIBS_UIO_MATCH_H_ */
+
diff --git a/src/libs/uio/mem.h b/src/libs/uio/mem.h
new file mode 100644
index 0000000..915837e
--- /dev/null
+++ b/src/libs/uio/mem.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MEM_H_
+#define LIBS_UIO_MEM_H_
+
+#include <stdlib.h>
+#include <string.h>
+#include "uioport.h"
+
+#define uio_malloc malloc
+#define uio_realloc realloc
+#define uio_free free
+#define uio_calloc calloc
+
+#ifdef uio_MEM_DEBUG
+// When uio_strdup is defined to the libc strdup, there's no opportunity
+// to intercept the alloc. Hence this function here.
+static inline char *
+uio_strdup(const char *s) {
+ char *result;
+ size_t size;
+
+ size = strlen(s) + 1;
+ result = uio_malloc(size);
+ memcpy(result, s, size);
+ return result;
+}
+#else
+# define uio_strdup strdup
+#endif
+
+// Allocates new memory, copies 'len' characters from 'src', and adds a '\0'.
+static inline char *
+uio_memdup0(const char *src, size_t len) {
+ char *dst = uio_malloc(len + 1);
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+ return dst;
+}
+
+#endif
+
+
diff --git a/src/libs/uio/memdebug.c b/src/libs/uio/memdebug.c
new file mode 100644
index 0000000..b6bd1b6
--- /dev/null
+++ b/src/libs/uio/memdebug.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "memdebug.h"
+#include "hashtable.h"
+#include "mem.h"
+#include "uioutils.h"
+#include "types.h"
+#include "uioport.h"
+
+// I'm intentionally not including the .h files. It would only make things
+// messy, because the second arguments would be other pointers, which
+// would require typecasts of the functions in uio_MemDebug_logTypeInfo.
+extern void uio_DirHandle_print(FILE *out, const void *);
+extern void uio_printMountInfo(FILE *out, const void *);
+extern void uio_printMountTreeItem(FILE *out, const void *);
+extern void uio_printPathComp(FILE *out, const void *);
+
+// Must be in the same order as in uio_MemDebug_LogTypes
+// Change the third field to have debug info printed for specific actions.
+// See memdebug.h file the possible values.
+const uio_MemDebug_LogTypeInfo uio_MemDebug_logTypeInfo[] = {
+ { "uio_DirHandle", uio_DirHandle_print, 0 },
+ { "uio_FileSystemInfo", NULL, 0 },
+ { "uio_GPDir", NULL, 0 },
+ { "uio_GPFile", NULL, 0 },
+ { "uio_GPRoot", NULL, 0 },
+ { "uio_Handle", NULL, 0 },
+ { "uio_MountHandle", NULL, 0 },
+ { "uio_MountInfo", uio_printMountInfo, 0 },
+ { "uio_MountTree", NULL, 0 },
+ { "uio_MountTreeItem", uio_printMountTreeItem, 0 },
+ { "uio_PathComp", uio_printPathComp, 0 },
+ { "uio_PFileHandle", NULL, 0 },
+ { "uio_PDirHandle", NULL, 0 },
+ { "uio_PRoot", NULL, 0 },
+ { "uio_Repository", NULL, 0 },
+ { "uio_Stream", NULL, 0 },
+ { "stdio_GPDirData", NULL, 0 },
+#ifdef HAVE_ZIP
+ { "zip_GPFileData", NULL, 0 },
+ { "zip_GPDirData", NULL, 0 },
+#endif
+};
+
+HashTable_HashTable **uio_MemDebug_logs;
+
+
+static uio_uint32 uio_MemDebug_pointerHash(const void *ptr);
+static uio_bool uio_MemDebug_pointerCompare(const void *ptr1, const void *ptr2);
+static void *uio_MemDebug_pointerCopy(const void *ptr);
+static void uio_MemDebug_pointerFree(void *ptr);
+
+static inline uio_MemDebug_PointerInfo *uio_MemDebug_PointerInfo_new(int ref);
+static inline void uio_MemDebug_PointerInfo_delete(
+ uio_MemDebug_PointerInfo *pointerInfo);
+static inline uio_MemDebug_PointerInfo *uio_MemDebug_PointerInfo_alloc(void);
+static inline void uio_MemDebug_PointerInfo_free(
+ uio_MemDebug_PointerInfo *pointerInfo);
+
+void
+uio_MemDebug_init(void) {
+ int i;
+
+ assert((int) uio_MemDebug_numLogTypes ==
+ (sizeof uio_MemDebug_logTypeInfo /
+ sizeof uio_MemDebug_logTypeInfo[0]));
+ uio_MemDebug_logs = uio_malloc(uio_MemDebug_numLogTypes *
+ sizeof (HashTable_HashTable *));
+
+ for (i = 0; i < uio_MemDebug_numLogTypes; i++) {
+ uio_MemDebug_logs[i] = HashTable_newHashTable(
+ uio_MemDebug_pointerHash,
+ uio_MemDebug_pointerCompare,
+ uio_MemDebug_pointerCopy,
+ uio_MemDebug_pointerFree,
+ 4, 0.85, 0.90);
+ }
+}
+
+void
+uio_MemDebug_unInit(void) {
+ int i;
+
+ for (i = 0; i < uio_MemDebug_numLogTypes; i++)
+ HashTable_deleteHashTable(uio_MemDebug_logs[i]);
+
+ uio_free(uio_MemDebug_logs);
+}
+
+static uio_uint32
+uio_MemDebug_pointerHash(const void *ptr) {
+ uio_uintptr ptrInt;
+
+ ptrInt = (uio_uintptr) ptr;
+ return (uio_uint32) (ptrInt ^ (ptrInt >> 10) ^ (ptrInt >> 20));
+}
+
+static uio_bool
+uio_MemDebug_pointerCompare(const void *ptr1, const void *ptr2) {
+ return ptr1 == ptr2;
+}
+
+static void *
+uio_MemDebug_pointerCopy(const void *ptr) {
+ return unconst(ptr);
+}
+
+static void
+uio_MemDebug_pointerFree(void *ptr) {
+ (void) ptr;
+}
+
+void
+uio_MemDebug_logAllocation(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Allocated pointer is (%s *) NULL.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_ALLOC) {
+ fprintf(stderr, "Alloc ");
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+ pointerInfo = uio_MemDebug_PointerInfo_new(1);
+ HashTable_add(uio_MemDebug_logs[type], ptr, (void *) pointerInfo);
+}
+
+void
+uio_MemDebug_logDeallocation(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Attempt to free (%s *) NULL pointer.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_FREE) {
+ fprintf(stderr, "Free ");
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+ pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr);
+ if (pointerInfo == NULL) {
+ fprintf(stderr, "Fatal: Attempt to free unallocated pointer "
+ "(%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+#if 0
+ if (pointerInfo->ref != 0) {
+ fprintf(stderr, "Fatal: Attempt to free pointer with references "
+ "left (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+#endif
+ uio_MemDebug_PointerInfo_free(pointerInfo);
+ HashTable_remove(uio_MemDebug_logs[type], ptr);
+}
+
+void
+uio_MemDebug_logRef(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Attempt to increment reference to a "
+ "(%s *) NULL pointer.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr);
+ if (pointerInfo == NULL) {
+ fprintf(stderr, "Fatal: Attempt to increment reference to "
+ "unallocated pointer (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+ pointerInfo->pointerRef++;
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_REF) {
+ fprintf(stderr, "Ref++ to %d, ", pointerInfo->pointerRef);
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+}
+
+void
+uio_MemDebug_logUnref(uio_MemDebug_LogType type, void *ptr) {
+ uio_MemDebug_PointerInfo *pointerInfo;
+
+ if (ptr == NULL) {
+ fprintf(stderr, "Fatal: Attempt to decrement reference to a "
+ "(%s *) NULL pointer.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name);
+ abort();
+ }
+ pointerInfo = HashTable_find(uio_MemDebug_logs[type], ptr);
+ if (pointerInfo == NULL) {
+ fprintf(stderr, "Fatal: Attempt to decrement reference to "
+ "unallocated pointer (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+ if (pointerInfo->pointerRef == 0) {
+ fprintf(stderr, "Fatal: Attempt to decrement reference below 0 for "
+ "pointer (%s *) %p.\n",
+ uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ abort();
+ }
+ pointerInfo->pointerRef--;
+ if (uio_MemDebug_logTypeInfo[(int) type].flags & uio_MemDebug_PRINT_UNREF) {
+ fprintf(stderr, "Ref-- to %d, ", pointerInfo->pointerRef);
+ uio_MemDebug_printPointer(stderr, type, ptr);
+ fprintf(stderr, "\n");
+ }
+}
+
+void
+uio_MemDebug_printPointer(FILE *out, uio_MemDebug_LogType type, void *ptr) {
+ fprintf(out, "(%s *) %p", uio_MemDebug_logTypeInfo[(int) type].name, ptr);
+ if (uio_MemDebug_logTypeInfo[(int) type].printFunction != NULL) {
+ fprintf(out, ": ");
+ uio_MemDebug_logTypeInfo[(int) type].printFunction(out, ptr);
+ }
+}
+
+void
+uio_MemDebug_printPointersType(FILE *out, uio_MemDebug_LogType type) {
+ HashTable_Iterator *iterator;
+
+ for (iterator = HashTable_getIterator(uio_MemDebug_logs[type]);
+ !HashTable_iteratorDone(iterator);
+ iterator = HashTable_iteratorNext(iterator)) {
+ uio_MemDebug_printPointer(out, type, HashTable_iteratorKey(iterator));
+ fprintf(out, "\n");
+ }
+ HashTable_freeIterator(iterator);
+}
+
+void
+uio_MemDebug_printPointers(FILE *out) {
+ int i;
+
+ for (i = 0; i < uio_MemDebug_numLogTypes; i++)
+ uio_MemDebug_printPointersType(out, i);
+}
+
+static inline uio_MemDebug_PointerInfo *
+uio_MemDebug_PointerInfo_new(int ref) {
+ uio_MemDebug_PointerInfo *result;
+ result = uio_MemDebug_PointerInfo_alloc();
+ result->pointerRef = ref;
+ return result;
+}
+
+static inline void
+uio_MemDebug_PointerInfo_delete(uio_MemDebug_PointerInfo *pointerInfo) {
+ uio_MemDebug_PointerInfo_free(pointerInfo);
+}
+
+static inline uio_MemDebug_PointerInfo *
+uio_MemDebug_PointerInfo_alloc(void) {
+ return uio_malloc(sizeof (uio_MemDebug_PointerInfo));
+}
+
+static inline void
+uio_MemDebug_PointerInfo_free(uio_MemDebug_PointerInfo *pointerInfo) {
+ uio_free(pointerInfo);
+}
+
diff --git a/src/libs/uio/memdebug.h b/src/libs/uio/memdebug.h
new file mode 100644
index 0000000..e04a8d2
--- /dev/null
+++ b/src/libs/uio/memdebug.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MEMDEBUG_H_
+#define LIBS_UIO_MEMDEBUG_H_
+
+#include <stdio.h>
+#include "uioport.h"
+
+// Important: if you add an item here, add it to uio_MemDebug_LogTypeInfo
+// too. The order should be the same.
+typedef enum {
+ uio_MemDebug_LogType_uio_DirHandle,
+ uio_MemDebug_LogType_uio_FileSystemInfo,
+ uio_MemDebug_LogType_uio_GPDir,
+ uio_MemDebug_LogType_uio_GPFile,
+ uio_MemDebug_LogType_uio_GPRoot,
+ uio_MemDebug_LogType_uio_Handle,
+ uio_MemDebug_LogType_uio_MountHandle,
+ uio_MemDebug_LogType_uio_MountInfo,
+ uio_MemDebug_LogType_uio_MountTree,
+ uio_MemDebug_LogType_uio_MountTreeItem,
+ uio_MemDebug_LogType_uio_PathComp,
+ uio_MemDebug_LogType_uio_PFileHandle,
+ uio_MemDebug_LogType_uio_PDirHandle,
+ uio_MemDebug_LogType_uio_PRoot,
+ uio_MemDebug_LogType_uio_Repository,
+ uio_MemDebug_LogType_uio_Stream,
+ uio_MemDebug_LogType_stdio_GPDirData,
+ uio_MemDebug_LogType_zip_GPFileData,
+ uio_MemDebug_LogType_zip_GPDirData,
+
+ uio_MemDebug_numLogTypes, /* This needs to be the last entry */
+} uio_MemDebug_LogType;
+
+typedef void (uio_MemDebug_PrintFunction)(FILE *out, const void *arg);
+
+typedef struct uio_MemDebug_LogTypeInfo {
+ const char *name;
+ uio_MemDebug_PrintFunction *printFunction;
+ int flags;
+#define uio_MemDebug_PRINT_ALLOC 0x1
+#define uio_MemDebug_PRINT_FREE 0x2
+#define uio_MemDebug_PRINT_REF 0x4
+#define uio_MemDebug_PRINT_UNREF 0x8
+#define uio_MemDebug_PRINT_ALL \
+ (uio_MemDebug_PRINT_ALLOC | uio_MemDebug_PRINT_FREE | \
+ uio_MemDebug_PRINT_REF | uio_MemDebug_PRINT_UNREF)
+} uio_MemDebug_LogTypeInfo;
+
+extern const uio_MemDebug_LogTypeInfo uio_MemDebug_logTypeInfo[];
+
+typedef struct uio_MemDebug_PointerInfo {
+ int pointerRef;
+ // Ref counter for the associated pointer, not the pointerInfo
+ // itself.
+} uio_MemDebug_PointerInfo;
+
+void uio_MemDebug_init(void);
+void uio_MemDebug_unInit(void);
+void uio_MemDebug_logAllocation(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_logDeallocation(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_logRef(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_logUnref(uio_MemDebug_LogType type, void *ptr);
+void uio_MemDebug_printPointer(FILE *out, uio_MemDebug_LogType type,
+ void *ptr);
+void uio_MemDebug_printPointersType(FILE *out, uio_MemDebug_LogType type);
+void uio_MemDebug_printPointers(FILE *out);
+
+#define uio_MemDebug_debugAlloc(type, pointer) \
+ uio_MemDebug_logAllocation(uio_MemDebug_LogType_ ## type, pointer)
+#define uio_MemDebug_debugFree(type, pointer) \
+ uio_MemDebug_logDeallocation(uio_MemDebug_LogType_ ## type, pointer)
+#define uio_MemDebug_debugRef(type, pointer) \
+ uio_MemDebug_logRef(uio_MemDebug_LogType_ ## type, pointer)
+#define uio_MemDebug_debugUnref(type, pointer) \
+ uio_MemDebug_logUnref(uio_MemDebug_LogType_ ## type, pointer)
+
+#endif /* LIBS_UIO_MEMDEBUG_H_ */
+
diff --git a/src/libs/uio/mount.c b/src/libs/uio/mount.c
new file mode 100644
index 0000000..f095d3f
--- /dev/null
+++ b/src/libs/uio/mount.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "mount.h"
+#include "mounttree.h"
+#include "mem.h"
+#include "uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+static void uio_Repository_delete(uio_Repository *repository);
+static uio_Repository *uio_Repository_alloc(void);
+static void uio_Repository_free(uio_Repository *repository);
+
+
+void
+uio_repositoryAddMount(uio_Repository *repository,
+ uio_MountInfo *mountInfo, uio_MountLocation location,
+ uio_MountInfo *relative) {
+ lockRepository(repository, uio_LOCK_WRITE);
+ switch (location) {
+ case uio_MOUNT_TOP: {
+ uio_MountInfo **newMounts;
+
+ newMounts = uio_malloc(
+ (repository->numMounts + 2) * sizeof (uio_MountInfo *));
+ newMounts[0] = mountInfo;
+ memcpy(&newMounts[1], repository->mounts,
+ (repository->numMounts + 1) * sizeof (uio_MountInfo *));
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts++;
+ break;
+ }
+ case uio_MOUNT_BOTTOM:
+ repository->mounts = uio_realloc(repository->mounts,
+ (repository->numMounts + 2) * sizeof (uio_MountInfo *));
+ repository->mounts[repository->numMounts] = mountInfo;
+ repository->numMounts++;
+ break;
+ case uio_MOUNT_ABOVE: {
+ int i;
+ uio_MountInfo **newMounts;
+
+ i = 0;
+ while (repository->mounts[i] != relative)
+ i++;
+ newMounts = (uio_MountInfo **) insertArrayPointer(
+ (const void **) repository->mounts,
+ repository->numMounts + 1, i, (void *) mountInfo);
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts++;
+ break;
+ }
+ case uio_MOUNT_BELOW: {
+ int i;
+ uio_MountInfo **newMounts;
+
+ i = 0;
+ while (repository->mounts[i] != relative)
+ i++;
+ i++;
+ newMounts = (uio_MountInfo **) insertArrayPointer(
+ (const void **) repository->mounts,
+ repository->numMounts + 1, i, (void *) mountInfo);
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts++;
+ break;
+ }
+ default:
+ assert(false);
+ }
+ unlockRepository(repository);
+}
+
+void
+uio_repositoryRemoveMount(uio_Repository *repository, uio_MountInfo *mountInfo) {
+ int i;
+ uio_MountInfo **newMounts;
+
+ lockRepository(repository, uio_LOCK_WRITE);
+
+ i = 0;
+ while (repository->mounts[i] != mountInfo)
+ i++;
+ newMounts = (uio_MountInfo **) excludeArrayPointer(
+ (const void **) repository->mounts, repository->numMounts + 1,
+ i, 1);
+ uio_free(repository->mounts);
+ repository->mounts = newMounts;
+ repository->numMounts--;
+ unlockRepository(repository);
+}
+
+// sets ref to 1
+uio_Repository *
+uio_Repository_new(int flags) {
+ uio_Repository *result;
+
+ result = uio_Repository_alloc();
+ result->ref = 1;
+ result->flags = flags;
+ result->numMounts = 0;
+ result->mounts = uio_malloc(1 * sizeof (uio_MountInfo *));
+ result->mounts[0] = NULL;
+ result->mountTree = uio_makeRootMountTree();
+ return result;
+}
+
+void
+uio_Repository_unref(uio_Repository *repository) {
+ assert(repository->ref > 0);
+ repository->ref--;
+ if (repository->ref == 0)
+ uio_Repository_delete(repository);
+}
+
+static uio_Repository *
+uio_Repository_alloc(void) {
+ uio_Repository *result = uio_malloc(sizeof (uio_Repository));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_Repository, (void *) result);
+#endif
+ return result;
+}
+
+static void
+uio_Repository_delete(uio_Repository *repository) {
+ assert(repository->numMounts == 0);
+ uio_free(repository->mounts);
+ uio_MountTree_delete(repository->mountTree);
+ uio_Repository_free(repository);
+}
+
+static void
+uio_Repository_free(uio_Repository *repository) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_Repository, (void *) repository);
+#endif
+ uio_free(repository);
+}
+
+
diff --git a/src/libs/uio/mount.h b/src/libs/uio/mount.h
new file mode 100644
index 0000000..237f54b
--- /dev/null
+++ b/src/libs/uio/mount.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MOUNT_H_
+#define LIBS_UIO_MOUNT_H_
+
+
+typedef struct uio_Repository uio_Repository;
+typedef struct uio_AutoMount uio_AutoMount;
+#define uio_MOUNT_RDONLY (1 << 1)
+
+/* *** Internal definitions follow */
+#ifdef uio_INTERNAL
+
+#define uio_MOUNT_LOCATION_MASK (3 << 2)
+
+#include "uioport.h"
+#include "fstypes.h"
+#include "mounttree.h"
+#include "match.h"
+
+struct uio_Repository {
+ int ref; /* reference counter */
+ int flags;
+ int numMounts;
+ struct uio_MountInfo **mounts;
+ // first in the list is considered the top
+ // last entry is NULL
+ struct uio_MountTree *mountTree;
+};
+
+#define lockRepository(repository, prot)
+#define unlockRepository(repository)
+
+uio_Repository *uio_Repository_new(int flags);
+void uio_Repository_unref(uio_Repository *repository);
+void uio_repositoryAddMount(uio_Repository *repository,
+ uio_MountInfo *mountInfo, uio_MountLocation location,
+ uio_MountInfo *relative);
+void uio_repositoryRemoveMount(uio_Repository *repository,
+ uio_MountInfo *mountInfo);
+
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_MOUNT_H_ */
+
diff --git a/src/libs/uio/mounttree.c b/src/libs/uio/mounttree.c
new file mode 100644
index 0000000..eb5bdec
--- /dev/null
+++ b/src/libs/uio/mounttree.c
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#ifdef DEBUG
+# include <stdio.h>
+#endif
+#include <assert.h>
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "mounttree.h"
+#include "paths.h"
+#include "types.h"
+#include "mem.h"
+#include "uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+static uio_MountTree *uio_mountTreeAddMountInfoRecTree(
+ uio_Repository *repository, uio_MountTree *tree,
+ uio_MountInfo *mountInfo, const char *start, const char *end,
+ uio_PathComp *upComp, uio_MountLocation location,
+ const uio_MountInfo *relative);
+static inline uio_MountTree *uio_mountTreeAddMountInfoRecTreeSub(
+ uio_Repository *repository, uio_MountTree **tree,
+ uio_MountInfo *mountInfo, const char *start,
+ const char *end, uio_MountLocation location,
+ const uio_MountInfo *relative);
+static void uio_mountTreeAddMountInfoLocAll(uio_Repository *repository,
+ uio_MountTree *tree, uio_MountInfo *mountInfo, int depth,
+ uio_MountLocation location, const uio_MountInfo *relative);
+static uio_MountTreeItem *uio_copyMountTreeItems(uio_MountTreeItem *item,
+ int extraDepth);
+static void uio_addMountTreeItem(uio_Repository *repository,
+ uio_MountTreeItem **pLocs, uio_MountTreeItem *item,
+ uio_MountLocation location, const uio_MountInfo *relative);
+static uio_MountTree *uio_mountTreeAddNewSubTree(uio_Repository *repository,
+ uio_MountTree *tree, const char *path, uio_MountInfo *mountInfo,
+ uio_PathComp *upComp, uio_MountLocation location,
+ const uio_MountInfo *relative);
+static void uio_mountTreeAddSub(uio_MountTree *tree, uio_MountTree *sub);
+static uio_MountTree * uio_splitMountTree(uio_MountTree **tree, uio_PathComp
+ *lastComp, int depth);
+static void uio_mountTreeRemoveMountInfoRec(uio_MountTree *mountTree,
+ uio_MountInfo *mountInfo);
+static void uio_printMount(FILE *outStream, const uio_MountInfo *mountInfo);
+
+static inline uio_MountTree * uio_MountTree_new(uio_MountTree *subTrees,
+ uio_MountTreeItem *pLocs, uio_MountTree *upTree, uio_PathComp
+ *comps, uio_PathComp *lastComp, uio_MountTree *next);
+static inline uio_MountTreeItem *uio_MountTree_newItem(
+ uio_MountInfo *mountInfo, int depth, uio_MountTreeItem *next);
+
+static inline void uio_MountTreeItem_delete(uio_MountTreeItem *item);
+
+static inline uio_MountTree *uio_MountTree_alloc(void);
+static inline uio_MountTreeItem *uio_MountTreeItem_alloc(void);
+static inline uio_MountInfo *uio_MountInfo_alloc(void);
+
+static inline void uio_MountTree_free(uio_MountTree *mountTree);
+static inline void uio_MountTreeItem_free(uio_MountTreeItem *mountTreeItem);
+static inline void uio_MountInfo_free(uio_MountInfo *mountInfo);
+
+
+// make the root mount Tree
+uio_MountTree *
+uio_makeRootMountTree(void) {
+ return uio_MountTree_new(NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+// Add a MountInfo structure to a MountTree in the place pointed
+// to by 'path'.
+// returns the MountTree for the location at the end of the path
+uio_MountTree *
+uio_mountTreeAddMountInfo(uio_Repository *repository, uio_MountTree *mountTree,
+ uio_MountInfo *mountInfo, const char *path, uio_MountLocation location,
+ const uio_MountInfo *relative) {
+ const char *start, *end;
+
+ getFirstPath0Component(path, &start, &end);
+ return uio_mountTreeAddMountInfoRecTree(repository, mountTree, mountInfo,
+ start, end, NULL, location, relative);
+}
+
+// recursive helper for uio_mountTreeAddMountInfo
+// returns the MountTree for the location at the end of the path
+static uio_MountTree *
+uio_mountTreeAddMountInfoRecTree(uio_Repository *repository, uio_MountTree *tree,
+ uio_MountInfo *mountInfo, const char *start, const char *end,
+ uio_PathComp *upComp,
+ uio_MountLocation location, const uio_MountInfo *relative) {
+ uio_MountTree **sub;
+
+ if (*start == '\0') {
+ // End of the path. Put the MountInfo here and on all subtrees below
+ // this level.
+ uio_mountTreeAddMountInfoLocAll(repository, tree, mountInfo, 0,
+ location, relative);
+ return tree;
+ }
+
+ // Check if sub trees match the path.
+ for (sub = &tree->subTrees; *sub != NULL; sub = &(*sub)->next) {
+ uio_MountTree *resTree;
+
+ resTree = uio_mountTreeAddMountInfoRecTreeSub(repository, sub,
+ mountInfo, start, end, location, relative);
+ if (resTree != NULL) {
+ // handled
+ return resTree;
+ }
+ }
+ // No subtree found matching (part of) 'path'.
+
+ // Need to add a new tree sub
+ return uio_mountTreeAddNewSubTree(repository, tree, start, mountInfo,
+ upComp, location, relative);
+}
+
+// recursive helper for uio_mountTreeAddMountInfo
+// Pre: *start != '\0'
+// returns the MountTree for the location at the end of the path, if
+// that falls within this tree. If not, returns NULL.
+static inline uio_MountTree *
+uio_mountTreeAddMountInfoRecTreeSub(uio_Repository *repository,
+ uio_MountTree **tree, uio_MountInfo *mountInfo,
+ const char *start, const char *end, uio_MountLocation location,
+ const uio_MountInfo *relative) {
+ uio_PathComp *comp, *lastComp;
+ int depth;
+
+ comp = (*tree)->comps;
+ if (strncmp(comp->name, start, end - start) != 0 ||
+ comp->name[end - start] != '\0') {
+ // first component does not match; this is not the correct subTree
+ return NULL;
+ }
+
+ depth = 1;
+ // try to match all components of the directory path to this subTree.
+ while (1) {
+ getNextPath0Component(&start, &end);
+ lastComp = comp;
+ comp = comp->next;
+
+ if (comp == NULL)
+ break;
+
+ if (*start == '\0') {
+ // end of the path reached
+ // We need to split up the components and insert a new
+ // MountTree here.
+ uio_MountTree *newTree;
+ newTree = uio_splitMountTree(tree, lastComp, depth);
+
+ // Add mountInfo to each of the MountTrees below newTree.
+ uio_mountTreeAddMountInfoLocAll(repository, newTree, mountInfo, 0,
+ location, relative);
+
+ return newTree;
+ }
+ if (strncmp(comp->name, start, end - start) != 0 ||
+ comp->name[end - start] != '\0') {
+ // Some, but not all components matched; we need to split
+ // up the components and add a new subTree here for the
+ // (non-matching) rest of the path.
+ uio_MountTree *newTree;
+
+ newTree = uio_splitMountTree(tree, lastComp, depth);
+
+ // A new Tree is added at the split-point.
+ return uio_mountTreeAddNewSubTree(repository, newTree, start,
+ mountInfo, lastComp, location, relative);
+ }
+ getNextPath0Component(&start, &end);
+ depth++;
+ }
+
+ // All components matched. We can recurse to the next subdir.
+ return uio_mountTreeAddMountInfoRecTree(repository, *tree, mountInfo,
+ start, end, lastComp, location, relative);
+}
+
+// Add a MountInfo struct 'mountInfo' to the pLocs fields of all subTrees
+// starting with 'tree'.
+// 'depth' is the distance to the MountTree where the MountInfo is located.
+static void
+uio_mountTreeAddMountInfoLocAll(uio_Repository *repository, uio_MountTree *tree,
+ uio_MountInfo *mountInfo, int depth, uio_MountLocation location,
+ const uio_MountInfo *relative) {
+ uio_MountTreeItem *newPLoc;
+ uio_MountTree *subTree;
+ int compCount;
+
+ // Add a new PLoc to this mountTree
+ newPLoc = uio_MountTree_newItem(mountInfo, depth, NULL);
+ uio_addMountTreeItem(repository, &tree->pLocs, newPLoc, location, relative);
+
+ // Recurse for subtrees
+ for (subTree = tree->subTrees; subTree != NULL;
+ subTree = subTree->next) {
+ compCount = uio_countPathComps(subTree->comps);
+ uio_mountTreeAddMountInfoLocAll(
+ repository, subTree, mountInfo, depth + compCount,
+ location, relative);
+ }
+}
+
+// pre: repository->mounts is already updated
+// pre: if location is uio_MOUNT_BELOW or uio_MOUNT_ABOVE, 'relative'
+// exists in repository->mounts
+static void
+uio_addMountTreeItem(uio_Repository *repository, uio_MountTreeItem **pLocs,
+ uio_MountTreeItem *item,
+ uio_MountLocation location, const uio_MountInfo *relative) {
+ switch (location) {
+ case uio_MOUNT_TOP:
+ item->next = *pLocs;
+ *pLocs = item;
+ break;
+ case uio_MOUNT_BOTTOM:
+ while (*pLocs != NULL)
+ pLocs = &(*pLocs)->next;
+ item->next = NULL;
+ *pLocs = item;
+ break;
+ case uio_MOUNT_ABOVE: {
+ uio_MountInfo **mountInfo;
+ mountInfo = repository->mounts;
+ while (*mountInfo != relative) {
+ assert(*mountInfo != NULL);
+ if ((*pLocs)->mountInfo == *mountInfo)
+ pLocs = &(*pLocs)->next;
+ mountInfo++;
+ }
+ item->next = *pLocs;
+ *pLocs = item;
+ break;
+ }
+ case uio_MOUNT_BELOW: {
+ uio_MountInfo **mountInfo;
+ mountInfo = repository->mounts;
+ while (*mountInfo != relative) {
+ assert(*mountInfo != NULL);
+ if ((*pLocs)->mountInfo == *mountInfo)
+ pLocs = &(*pLocs)->next;
+ mountInfo++;
+ }
+ item->next = (*pLocs)->next;
+ (*pLocs)->next = item;
+ break;
+ }
+ default:
+ assert(false);
+ }
+}
+
+// Copy a chain of MountTreeItems, but increase the depth by 'extraDepth'.
+static uio_MountTreeItem *
+uio_copyMountTreeItems(uio_MountTreeItem *item, int extraDepth) {
+ uio_MountTreeItem *result, **resPtr;
+ uio_MountTreeItem *newItem;
+
+ resPtr = &result;
+ while (item != NULL) {
+ newItem = uio_MountTree_newItem(
+ item->mountInfo, item->depth + extraDepth, NULL);
+ *resPtr = newItem;
+ resPtr = &newItem->next;
+ item = item->next;
+ }
+ *resPtr = NULL;
+ return result;
+}
+
+// add a new sub tree under a tree 'tree'.
+// 'path' is the part leading up to the new tree and
+// 'mountInfo' is the MountInfo structure to at there.
+// 'upComp' points to the last path component that lead to 'tree'.
+static uio_MountTree *
+uio_mountTreeAddNewSubTree(uio_Repository *repository, uio_MountTree *tree,
+ const char *path, uio_MountInfo *mountInfo, uio_PathComp *upComp,
+ uio_MountLocation location, const uio_MountInfo *relative) {
+ uio_MountTreeItem *item, *items;
+ uio_MountTree *newTree;
+ uio_PathComp *compList, *lastComp;
+ int compCount;
+
+ compList = uio_makePathComps(path, upComp);
+ compCount = uio_countPathComps(compList);
+ lastComp = uio_lastPathComp(compList);
+ item = uio_MountTree_newItem(mountInfo, 0, NULL);
+ item->next = NULL;
+ items = uio_copyMountTreeItems(tree->pLocs, compCount);
+ uio_addMountTreeItem(repository, &items, item, location,
+ relative);
+ newTree = uio_MountTree_new(
+ NULL /* subTrees */,
+ items /* pLocs */,
+ tree /* upTree */,
+ compList /* comps */,
+ lastComp /* lastComp */,
+ NULL /* next */);
+ uio_mountTreeAddSub(tree, newTree);
+ return newTree;
+}
+
+// add a sub structure to the end of the 'subTrees' list of a tree.
+static void
+uio_mountTreeAddSub(uio_MountTree *tree, uio_MountTree *sub) {
+ uio_MountTree **subPtr;
+
+ for (subPtr = &tree->subTrees; *subPtr != NULL;
+ subPtr = &(*subPtr)->next) {
+ // Nothing to do here.
+ }
+ *subPtr = sub;
+}
+
+// Add a new MountTree structure in between two MountTrees.
+// Tree points to the pointer for the tree in front of which the new
+// tree needs to be placed (at depth 'depth').
+// 'lastComp' is the last pathComp of the part before the splitting point
+// It returns the new MountTree.
+static uio_MountTree *
+uio_splitMountTree(uio_MountTree **tree, uio_PathComp *lastComp, int depth) {
+ uio_MountTree *newTree;
+ uio_MountTreeItem *items;
+
+ items = uio_copyMountTreeItems((*tree)->upTree->pLocs, depth);
+ newTree = uio_MountTree_new(
+ *tree /* subTrees */,
+ items /* pLocs */,
+ (*tree)->upTree /* upTree */,
+ (*tree)->comps /* comps */,
+ lastComp /* lastComp */,
+ NULL /* next */);
+ (*tree)->upTree = newTree;
+ (*tree)->comps = lastComp->next;
+ lastComp->next = NULL;
+ *tree = newTree;
+ return newTree;
+}
+
+void
+uio_mountTreeRemoveMountInfo(uio_Repository *repository,
+ uio_MountTree *mountTree, uio_MountInfo *mountInfo) {
+ uio_MountTree **subTreePtr;
+ uio_MountTree *upTree;
+
+ // If the tree has no sub-trees and it has the same items as the
+ // upTree, with 'mountInfo' added, then the tree is a dead end
+ // and can be removed entirely.
+ // First we handle the other case.
+ // Note that if the upTree has exactly one item less than the tree
+ // itself, these items must be the same, plus mountInfo for the
+ // tree itself, as each tree has at least the items of its upTree.
+ if (mountTree->upTree == NULL || mountTree->subTrees != NULL ||
+ uio_mountTreeCountPLocs(mountTree) !=
+ uio_mountTreeCountPLocs(mountTree->upTree) + 1) {
+ // We can't remove the tree itself.
+ // We need to remove the mountInfo from the tree, and all subTrees.
+ // Then we're done.
+ uio_mountTreeRemoveMountInfoRec(mountTree, mountInfo);
+ return;
+ }
+
+ // mountTree itself can be removed.
+ // First remove the tree from the list of subtrees of the upTree.
+ subTreePtr = &mountTree->upTree->subTrees;
+ while (1) {
+ assert(*subTreePtr != NULL);
+ if (*subTreePtr == mountTree)
+ break;
+ subTreePtr = &(*subTreePtr)->next;
+ }
+ *subTreePtr = mountTree->next;
+
+ // Save the upTree for later.
+ upTree = mountTree->upTree;
+
+ // Remove the tree itself.
+ uio_MountTree_delete(mountTree);
+
+ // The upTree itself could have become unnecessary now.
+ // This is the case when upTree now only has one subTree, and upTree
+ // and the subTree have the same items.
+ // Again, same item count implies same items.
+ if (upTree->subTrees == NULL || upTree->subTrees->next != NULL ||
+ uio_mountTreeCountPLocs(upTree) !=
+ uio_mountTreeCountPLocs(upTree->subTrees)) {
+ // upTree is still necessary. We're done.
+ return;
+ }
+
+ // Merge upTree and upTree->subTrees.
+ // It would be easiest to keep upTree, and throw upTree->subTrees away,
+ // but that's not possible as external links point to upTree->subTrees.
+ // First merge the path components:
+ assert(upTree->subTrees->lastComp != NULL);
+ upTree->subTrees->lastComp->next = upTree->subTrees->comps;
+ upTree->subTrees->lastComp = upTree->lastComp;
+ upTree->subTrees->comps = upTree->comps;
+ // Now let the pointer that pointed to upTree, point to upTree->subTrees.
+ // Change upTree->next accordingly.
+ if (upTree->upTree == NULL) {
+ assert(repository->mountTree == upTree);
+ repository->mountTree = upTree->subTrees;
+ // upTree->subTrees->next is already NULL
+ } else {
+ uio_MountTree *next;
+ subTreePtr = &upTree->upTree->subTrees;
+ while (1) {
+ assert(*subTreePtr != NULL);
+ if (*subTreePtr == upTree)
+ break;
+ subTreePtr = &(*subTreePtr)->next;
+ }
+ next = (*subTreePtr)->next;
+ *subTreePtr = upTree->subTrees;
+ upTree->subTrees->next = next;
+ }
+
+ // Now delete the tree itself
+ upTree->subTrees = NULL;
+ upTree->comps = NULL;
+ uio_MountTree_delete(upTree);
+}
+
+// pre: mountInfo exists in mountTree->pLocs (and hence in pLocs for
+// every sub-tree)
+static void
+uio_mountTreeRemoveMountInfoRec(uio_MountTree *mountTree,
+ uio_MountInfo *mountInfo) {
+ uio_MountTree *subTree;
+ uio_MountTreeItem **itemPtr, *item;
+
+ // recurse for all subTrees
+ for (subTree = mountTree->subTrees; subTree != NULL;
+ subTree = subTree->next)
+ uio_mountTreeRemoveMountInfoRec(subTree, mountInfo);
+
+ // Find the mount info in this tree.
+ itemPtr = &mountTree->pLocs;
+ while (1) {
+ assert(*itemPtr != NULL);
+ // We know an item with the specified mountInfo
+ // must be here somewhere.
+ if ((*itemPtr)->mountInfo == mountInfo) {
+ // Found it.
+ break;
+ }
+ itemPtr = &(*itemPtr)->next;
+ }
+
+ item = *itemPtr;
+ *itemPtr = item->next;
+ uio_MountTreeItem_delete(item);
+}
+
+// Count the number of pLocs in a tree that leads to.
+int
+uio_mountTreeCountPLocs(const uio_MountTree *tree) {
+ int count;
+ uio_MountTreeItem *item;
+
+ count = 0;
+ for (item = tree->pLocs; item != NULL; item = item->next)
+ count++;
+ return count;
+}
+
+// resTree may point to top
+// pPath may point to path
+void
+uio_findMountTree(uio_MountTree *top, const char *path,
+ uio_MountTree **resTree, const char **pPath) {
+ const char *start, *end, *pathFromTree;
+ uio_MountTree *tree, *sub;
+ uio_PathComp *comp;
+
+ getFirstPath0Component(path, &start, &end);
+ tree = top;
+ while(1) {
+ if (*start == '\0') {
+ *resTree = tree;
+ *pPath = start;
+ return;
+ }
+
+ pathFromTree = start;
+ sub = tree->subTrees;
+ while(1) {
+ if (sub == NULL) {
+ // No matching sub Dirs found. So we report back the current
+ // dir.
+ *resTree = tree;
+ *pPath = pathFromTree;
+ return;
+ }
+ comp = sub->comps;
+ if (strncmp(comp->name, start, end - start) == 0 &&
+ comp->name[end - start] == '\0')
+ break;
+ sub = sub->next;
+ }
+ // Found a Sub dir which matches at least partially.
+
+ while (1) {
+ getNextPath0Component(&start, &end);
+ comp = comp->next;
+ if (comp == NULL)
+ break;
+ if (*start == '\0' ||
+ strncmp(comp->name, start, end - start) != 0 ||
+ comp->name[end - start] != '\0') {
+ // either the path ends here, or the path in the tree does.
+ // either way, the last Tree is the one we want.
+ *resTree = tree;
+ *pPath = pathFromTree;
+ return;
+ }
+ }
+ // all components matched until the next MountTree
+ tree = sub;
+ }
+}
+
+// finds the path to the MountInfo associated with a mountTreeItem
+// given a path to the 'item' itself.
+// 'item' is the mountTreeItem
+// 'endComp' is the last PathComp leading to 'item'
+// 'start' is the start of the path to the item
+char *
+uio_mountTreeItemRestPath(const uio_MountTreeItem *item,
+ uio_PathComp *endComp, const char *path) {
+ int i;
+ const char *pathPtr;
+
+ i = item->depth;
+ while (i--)
+ endComp = endComp->up;
+
+ pathPtr = path;
+ if (endComp != NULL) {
+ while (1) {
+ pathPtr += endComp->nameLen;
+ endComp = endComp->up;
+ if (endComp == NULL)
+ break;
+ pathPtr++;
+ // for a '/'
+ }
+ }
+ if (*path == '/') {
+ // / at the beginning of the path
+ pathPtr++;
+ }
+ if (*pathPtr == '/') {
+ // / at the end of the path
+ pathPtr++;
+ }
+// return (char *) pathPtr;
+ // gives warning
+// return *((char **)((void *) &pathPtr));
+ // not portable
+ return (char *) unconst((const void *) pathPtr);
+}
+
+void
+uio_printMountTree(FILE *outStream, const uio_MountTree *tree, int indent) {
+ uio_MountTree *sub;
+ uio_PathComp *comp;
+
+ fprintf(outStream, "(");
+ uio_printMountTreeItems(outStream, tree->pLocs);
+ fprintf(outStream, ")\n");
+ for (sub = tree->subTrees; sub != NULL; sub = sub->next) {
+ int newIndent;
+
+ newIndent = indent;
+ fprintf(outStream, "%*s", indent, "");
+ for (comp = sub->comps; comp != NULL; comp = comp->next) {
+ fprintf(outStream, "/%s", comp->name);
+ newIndent += 1 + comp->nameLen;
+ }
+ fprintf(outStream, " ");
+ newIndent += 1;
+ uio_printMountTree(outStream, sub, newIndent);
+ }
+}
+
+void
+uio_printMountTreeItem(FILE *outStream, const uio_MountTreeItem *item) {
+ uio_printMountInfo(outStream, item->mountInfo);
+ fprintf(outStream, ":%d", item->depth);
+}
+
+void
+uio_printMountTreeItems(FILE *outStream, const uio_MountTreeItem *item) {
+ if (!item)
+ return;
+ while(1) {
+ uio_printMountTreeItem(outStream, item);
+ item = item->next;
+ if (item == NULL)
+ break;
+ fprintf(outStream, ", ");
+ }
+}
+
+void
+uio_printPathToMountTree(FILE *outStream, const uio_MountTree *tree) {
+ if (tree->upTree == NULL) {
+ fprintf(outStream, "/");
+ } else
+ uio_printPathToComp(outStream, tree->lastComp);
+}
+
+void
+uio_printMountInfo(FILE *outStream, const uio_MountInfo *mountInfo) {
+ uio_FileSystemInfo *fsInfo;
+
+ fsInfo = uio_getFileSystemInfo(mountInfo->fsID);
+ fprintf(outStream, "%s:/%s", fsInfo->name, mountInfo->dirName);
+}
+
+static void
+uio_printMount(FILE *outStream, const uio_MountInfo *mountInfo) {
+ uio_FileSystemInfo *fsInfo;
+
+ fsInfo = uio_getFileSystemInfo(mountInfo->fsID);
+ fprintf(outStream, "???:%s on ", mountInfo->dirName);
+ uio_printPathToMountTree(outStream, mountInfo->mountTree);
+ fprintf(outStream, " type %s (", fsInfo->name);
+ if (mountInfo->flags & uio_MOUNT_RDONLY) {
+ fprintf(outStream, "ro");
+ } else
+ fprintf(outStream, "rw");
+ fprintf(outStream, ")\n");
+}
+
+void
+uio_printMounts(FILE *outStream, const uio_Repository *repository) {
+ int i;
+
+ for (i = 0; i < repository->numMounts; i++) {
+ uio_printMount(outStream, repository->mounts[i]);
+ }
+}
+
+
+// *** uio_MountTree*** //
+
+static inline uio_MountTree *
+uio_MountTree_new(uio_MountTree *subTrees, uio_MountTreeItem *pLocs,
+ uio_MountTree *upTree, uio_PathComp *comps, uio_PathComp *lastComp,
+ uio_MountTree *next) {
+ uio_MountTree *result;
+
+ result = uio_MountTree_alloc();
+ result->subTrees = subTrees;
+ result->pLocs = pLocs;
+ result->upTree = upTree;
+ result->comps = comps;
+ result->lastComp = lastComp;
+ result->next = next;
+ return result;
+}
+
+void
+uio_MountTree_delete(uio_MountTree *tree) {
+ uio_MountTree *subTree, *nextTree;
+ uio_MountTreeItem *item, *nextItem;
+
+ subTree = tree->subTrees;
+ while (subTree != NULL) {
+ nextTree = subTree->next;
+ uio_MountTree_delete(subTree);
+ subTree = nextTree;
+ }
+
+ item = tree->pLocs;
+ while (item != NULL) {
+ nextItem = item->next;
+ uio_MountTreeItem_delete(item);
+ item = nextItem;
+ }
+
+ if (tree->comps != NULL)
+ uio_PathComp_delete(tree->comps);
+
+ uio_MountTree_free(tree);
+}
+
+static inline uio_MountTree *
+uio_MountTree_alloc(void) {
+ uio_MountTree *result = uio_malloc(sizeof (uio_MountTree));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountTree, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountTree_free(uio_MountTree *mountTree) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountTree, (void *) mountTree);
+#endif
+ uio_free(mountTree);
+}
+
+
+// *** uio_MountTreeItem *** //
+
+static inline uio_MountTreeItem *
+uio_MountTree_newItem(uio_MountInfo *mountInfo, int depth,
+ uio_MountTreeItem *next) {
+ uio_MountTreeItem *result;
+
+ result = uio_MountTreeItem_alloc();
+ result->mountInfo = mountInfo;
+ result->depth = depth;
+ result->next = next;
+ return result;
+}
+
+static inline void
+uio_MountTreeItem_delete(uio_MountTreeItem *item) {
+ uio_MountTreeItem_free(item);
+}
+
+static inline uio_MountTreeItem *
+uio_MountTreeItem_alloc(void) {
+ uio_MountTreeItem *result = uio_malloc(sizeof (uio_MountTreeItem));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountTreeItem, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountTreeItem_free(uio_MountTreeItem *mountTreeItem) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountTreeItem, (void *) mountTreeItem);
+#endif
+ uio_free(mountTreeItem);
+}
+
+
+// *** uio_MountInfo *** //
+
+uio_MountInfo *
+uio_MountInfo_new(uio_FileSystemID fsID, uio_MountTree *mountTree,
+ uio_PDirHandle *pDirHandle, char *dirName, uio_AutoMount **autoMount,
+ uio_MountHandle *mountHandle, int flags) {
+ uio_MountInfo *result;
+
+ result = uio_MountInfo_alloc();
+ result->fsID = fsID;
+ result->mountTree = mountTree;
+ result->pDirHandle = pDirHandle;
+ result->dirName = dirName;
+ result->autoMount = autoMount;
+ result->mountHandle = mountHandle;
+ result->flags = flags;
+ return result;
+}
+
+void
+uio_MountInfo_delete(uio_MountInfo *mountInfo) {
+ uio_free(mountInfo->dirName);
+ uio_PDirHandle_unref(mountInfo->pDirHandle);
+ uio_MountInfo_free(mountInfo);
+}
+
+static inline uio_MountInfo *
+uio_MountInfo_alloc(void) {
+ uio_MountInfo *result = uio_malloc(sizeof (uio_MountInfo));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_MountInfo, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_MountInfo_free(uio_MountInfo *mountInfo) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_MountInfo, (void *) mountInfo);
+#endif
+ uio_free(mountInfo);
+}
+
+
diff --git a/src/libs/uio/mounttree.h b/src/libs/uio/mounttree.h
new file mode 100644
index 0000000..b0041bc
--- /dev/null
+++ b/src/libs/uio/mounttree.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_MOUNTTREE_H_
+#define LIBS_UIO_MOUNTTREE_H_
+
+#include <stdio.h>
+#include "mount.h"
+
+void uio_printMounts(FILE *outStream, const uio_Repository *repository);
+
+/* *** Internal definitions follow *** */
+#ifdef uio_INTERNAL
+
+#include <sys/types.h>
+
+typedef struct uio_MountTreeItem uio_MountTreeItem;
+typedef struct uio_MountTree uio_MountTree;
+typedef struct uio_MountInfo uio_MountInfo;
+
+#include "physical.h"
+#include "types.h"
+#include "uioport.h"
+#include "paths.h"
+
+
+/*
+ * A MountTree describes the relation between dirs (PRoot structures)
+ * mounted in a directory structure.
+ * A MountTree structure represents a node (a dir) in this directory
+ * structure.
+ * It describes what MountInfo structures apply in that dir and below (if
+ * not overrided in subnodes) (the 'pLocs' field).
+ * These path components are also linked together 'up'-wards (towards the
+ * root of the tree) by the 'up' field.
+ */
+
+struct uio_MountTreeItem {
+ struct uio_MountInfo *mountInfo;
+ int depth;
+ // 'mountInfo->pDirHandle' and 'depth' together point to a
+ // location in a physical tree. An uio_pDirHandle alone can't be
+ // used as the directory might not exist.
+ // So pDirHandle points to the top dir that was mounted, and
+ // 'depth' indicates how many directory names of the path to
+ // this point in the Mount Tree need to be followed.
+ // Example:
+ // This MountTreeItem is somewhere in a tree /foo/bar/bla
+ // and depth = 1. Then this MountTreeItem points to
+ // /bla in the specified root.
+ struct uio_MountTreeItem *next;
+ // The next MountTreeItem in a MountTree
+};
+
+struct uio_MountTree {
+ struct uio_MountTree *subTrees;
+ // Trees for subdirs in this MountTree
+ struct uio_MountTreeItem *pLocs;
+ // The physical locations that have effect in this MountTree.
+ struct uio_MountTree *upTree;
+ // the MountTree that pointed to this MountTree
+ struct uio_PathComp *comps;
+ // the names of the path components that lead to the tree.
+ // Not necessary every PathComp is connected to a MountTree.
+ // If you have /foo and /foo/bar/zut mounted, then
+ // there are MountTrees for /, /foo and /foo/bar/zut,
+ // but there are PathComps for 'foo', 'bar' and 'zut'.
+ struct uio_PathComp *lastComp;
+ // The last PathComp of comps that pointed to this MountTree.
+ // This can be used to trace the path back to the top.
+ struct uio_MountTree *next;
+ // If this tree is a subTree of a tree, 'next' points to the
+ // next subTree of that tree.
+};
+
+/*
+ * A MountInfo structure describes how a physical structure was mounted.
+ * A physical structure can be used by several MountInfo structures.
+ */
+struct uio_MountInfo {
+ int flags;
+ /* Mount flags */
+# define uio_MOUNTINFO_RDONLY uio_MOUNT_RDONLY
+ uio_FileSystemID fsID;
+ char *dirName;
+ /* The path inside the mounted fs leading to pDirHandle */
+ uio_PDirHandle *pDirHandle;
+ /* The pDirHandle belonging to this mount */
+ uio_MountTree *mountTree;
+ /* The MountTree node for the mountpoint */
+ uio_AutoMount **autoMount;
+ uio_MountHandle *mountHandle;
+};
+
+
+/*
+ * Say we've got mounted (in order):
+ * Bla -> /
+ * Bar -> /foo/bar
+ * Foo -> /foo
+ * Zut -> /zut/123
+ * Fop -> /zut/123
+ *
+ * This will build a tree that looks like this:
+ * (the strings between brackets are the mounted filesystems that have effect
+ * in a dir, in order)
+ *
+ * / (Bar)
+ * foo (Foo, Bla)
+ * bar (Foo, Bar, Bla)
+ * zut/123 (Fop, Zut, Bla)
+ *
+ * The MountTree will look like:
+ * / = {
+ * sub = {
+ * /foo,
+ * /zut/123
+ * },
+ * pLocs = {
+ * BlaDir:/ (0)
+ * }
+ * }
+ * /foo = {
+ * sub = {
+ * /foo/bar
+ * },
+ * pLocs = {
+ * BlaDir:/foo (1),
+ * FooDir:/ (0)
+ * }
+ * }
+ * /foo/bar = {
+ * sub = { },
+ * pLocs = {
+ * BlaDir:/foo/bar (2),
+ * BarDir:/ (0),
+ * FooDir:/bar (1)
+ * }
+ * }
+ * /zut/123 = {
+ * sub = { },
+ * pLocs = {
+ * FooDir:/ (0)
+ * ZutDir:/ (0)
+ * BlaDir:/zut/123 (2)
+ * }
+ * }
+ *
+ * 'BlaDir:/zut/123 (2)' means the pDirHandle is 'Bla', and the dir into
+ * that directory is '/zut/123', but as this is always a postfix of the
+ * path where we are, the number of dirs (the '(2)') is enough to store
+ * (apart from the pDirHandle).
+ */
+
+uio_MountTree *uio_makeRootMountTree(void);
+void uio_MountTree_delete(uio_MountTree *tree);
+uio_MountTree *uio_mountTreeAddMountInfo(uio_Repository *repository,
+ uio_MountTree *mountTree, uio_MountInfo *mountInfo, const char *path,
+ uio_MountLocation location, const uio_MountInfo *relative);
+void uio_mountTreeRemoveMountInfo(uio_Repository *repository,
+ uio_MountTree *mountTree, uio_MountInfo *mountInfo);
+void uio_findMountTree(uio_MountTree *top, const char *path,
+ uio_MountTree **resTree, const char **pPath);
+char *uio_mountTreeItemRestPath(const uio_MountTreeItem *item,
+ uio_PathComp *endComp, const char *path);
+int uio_mountTreeCountPLocs(const uio_MountTree *tree);
+uio_MountInfo *uio_MountInfo_new(uio_FileSystemID fsID,
+ uio_MountTree *mountTree, uio_PDirHandle *pDirHandle,
+ char *dirName, uio_AutoMount **autoMount,
+ uio_MountHandle *mountHandle, int flags);
+void uio_MountInfo_delete(uio_MountInfo *mountInfo);
+void uio_printMountTree(FILE *outStream, const uio_MountTree *tree,
+ int indent);
+void uio_printMountTreeItem(FILE *outStream, const uio_MountTreeItem *item);
+void uio_printMountTreeItems(FILE *outStream, const uio_MountTreeItem *item);
+void uio_printPathToMountTree(FILE *outStream, const uio_MountTree *tree);
+void uio_printMountInfo(FILE *outStream, const uio_MountInfo *mountInfo);
+
+static inline uio_bool
+uio_mountInfoIsReadOnly(uio_MountInfo *mountInfo) {
+ return (mountInfo->flags & uio_MOUNTINFO_RDONLY) != 0;
+}
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_MOUNTTREE_H_ */
+
diff --git a/src/libs/uio/paths.c b/src/libs/uio/paths.c
new file mode 100644
index 0000000..f8411cd
--- /dev/null
+++ b/src/libs/uio/paths.c
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "paths.h"
+#include "uioport.h"
+#include "mem.h"
+
+static inline uio_PathComp *uio_PathComp_alloc(void);
+static inline void uio_PathComp_free(uio_PathComp *pathComp);
+
+// gets the first dir component of a path
+// sets '*start' to the start of the first component
+// sets '*end' to the end of the first component
+// if *start >= dirEnd, then the end has been reached.
+void
+getFirstPathComponent(const char *dir, const char *dirEnd,
+ const char **startComp, const char **endComp) {
+ assert(dir <= dirEnd);
+ *startComp = dir;
+ if (*startComp == dirEnd) {
+ *endComp = *startComp;
+ return;
+ }
+ *endComp = memchr(*startComp, '/', dirEnd - *startComp);
+ if (*endComp == NULL)
+ *endComp = dirEnd;
+}
+
+// gets the first dir component of a path
+// sets '*start' to the start of the first component
+// sets '*end' to the end of the first component
+// if **start == '\0', then the end has been reached.
+void
+getFirstPath0Component(const char *dir,
+ const char **startComp, const char **endComp) {
+ *startComp = dir;
+ if (**startComp == '\0') {
+ *endComp = *startComp;
+ return;
+ }
+ *endComp = strchr(*startComp, '/');
+ if (*endComp == NULL)
+ *endComp = *startComp + strlen(*startComp);
+}
+
+// gets the next component of a path
+// '*start' should be set to the start of the last component
+// '*end' should be set to the end of the last component
+// '*start' will be set to the start of the next component
+// '*end' will be set to the end of the next component
+// if *start >= dirEnd, then the end has been reached.
+void
+getNextPathComponent(const char *dirEnd,
+ const char **startComp, const char **endComp) {
+ assert(*endComp <= dirEnd);
+ if (*endComp == dirEnd) {
+ *startComp = *endComp;
+ return;
+ }
+ assert(**endComp == '/');
+ *startComp = *endComp + 1;
+ *endComp = memchr(*startComp, '/', dirEnd - *startComp);
+ if (*endComp == NULL)
+ *endComp = dirEnd;
+}
+
+// gets the next component of a path
+// '*start' should be set to the start of the last component
+// '*end' should be set to the end of the last component
+// '*start' will be set to the start of the next component
+// '*end' will be set to the end of the next component
+// if **start == '\0', then the end has been reached.
+void
+getNextPath0Component(const char **startComp, const char **endComp) {
+ if (**endComp == '\0') {
+ *startComp = *endComp;
+ return;
+ }
+ assert(**endComp == '/');
+ *startComp = *endComp + 1;
+ *endComp = strchr(*startComp, '/');
+ if (*endComp == NULL)
+ *endComp = *startComp + strlen(*startComp);
+}
+
+// if *end == dir, the beginning has been reached
+void
+getLastPathComponent(const char *dir, const char *endDir,
+ const char **startComp, const char **endComp) {
+ *endComp = endDir;
+// if (*(*endComp - 1) == '/')
+// (*endComp)--;
+ *startComp = *endComp;
+ // TODO: use memrchr where available
+ while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/')
+ (*startComp)--;
+}
+
+// if *end == dir, the beginning has been reached
+// pre: dir is \0-terminated
+void
+getLastPath0Component(const char *dir,
+ const char **startComp, const char **endComp) {
+ *endComp = dir + strlen(dir);
+// if (*(*endComp - 1) == '/')
+// (*endComp)--;
+ *startComp = *endComp;
+ // TODO: use memrchr where available
+ while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/')
+ (*startComp)--;
+}
+
+// if *end == dir, the beginning has been reached
+void
+getPreviousPathComponent(const char *dir,
+ const char **startComp, const char **endComp) {
+ if (*startComp == dir) {
+ *endComp = *startComp;
+ return;
+ }
+ *endComp = *startComp - 1;
+ *startComp = *endComp;
+ while ((*startComp) - 1 >= dir && *(*startComp - 1) != '/')
+ (*startComp)--;
+}
+
+// Combine two parts of a paths into a new path.
+// The new path starts with a '/' only when the first part does.
+// The first path may (but doesn't have to) end on a '/', or may be empty.
+// Pre: the second path doesn't start with a '/'
+char *
+joinPaths(const char *first, const char *second) {
+ char *result, *resPtr;
+ size_t firstLen, secondLen;
+
+ if (first[0] == '\0')
+ return uio_strdup(second);
+
+ firstLen = strlen(first);
+ if (first[firstLen - 1] == '/')
+ firstLen--;
+ secondLen = strlen(second);
+ result = uio_malloc(firstLen + secondLen + 2);
+ resPtr = result;
+
+ memcpy(resPtr, first, firstLen);
+ resPtr += firstLen;
+
+ *resPtr = '/';
+ resPtr++;
+
+ memcpy(resPtr, second, secondLen);
+ resPtr += secondLen;
+
+ *resPtr = '\0';
+ return result;
+}
+
+// Combine two parts of a paths into a new path.
+// The new path will always start with a '/'.
+// The first path may (but doesn't have to) end on a '/', or may be empty.
+// Pre: the second path doesn't start with a '/'
+char *
+joinPathsAbsolute(const char *first, const char *second) {
+ char *result, *resPtr;
+ size_t firstLen, secondLen;
+
+ if (first[0] == '\0') {
+ secondLen = strlen(second);
+ result = uio_malloc(secondLen + 2);
+ result[0] = '/';
+ memcpy(&result[1], second, secondLen);
+ result[secondLen + 1] = '\0';
+ return result;
+ }
+
+ firstLen = strlen(first);
+ if (first[firstLen - 1] == '/')
+ firstLen--;
+ secondLen = strlen(second);
+ result = uio_malloc(firstLen + secondLen + 3);
+ resPtr = result;
+
+ *resPtr = '/';
+ resPtr++;
+
+ memcpy(resPtr, first, firstLen);
+ resPtr += firstLen;
+
+ *resPtr = '/';
+ resPtr++;
+
+ memcpy(resPtr, second, secondLen);
+ resPtr += secondLen;
+
+ *resPtr = '\0';
+ return result;
+}
+
+// Returns 'false' if
+// - one of the path components is empty, or
+// - one of the path components is ".", or
+// - one of the path components is ".."
+// and 'true' otherwise.
+uio_bool
+validPathName(const char *path, size_t len) {
+ const char *pathEnd;
+ const char *start, *end;
+
+ pathEnd = path + len;
+ getFirstPathComponent(path, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.'))
+ return false;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ return true;
+}
+
+// returns 0 if the path is not a valid UNC path.
+// Does not skip trailing slashes.
+size_t
+uio_skipUNCServerShare(const char *inPath) {
+ const char *path = inPath;
+
+ // Skip the initial two backslashes.
+ if (path[0] != '\\' || path[1] != '\\')
+ return (size_t) 0;
+ path += 2;
+
+ // Skip the server part.
+ while (*path != '\\' && *path != '/') {
+ if (*path == '\0')
+ return (size_t) 0;
+ path++;
+ }
+
+ // Skip the seperator.
+ path++;
+
+ // Skip the share part.
+ while (*path != '\0' && *path != '\\' && *path != '/')
+ path++;
+
+ return (size_t) (path - inPath);
+}
+
+/**
+ * Find the server and share part of a Windows "Universal Naming Convention"
+ * path (a path of the form '\\server\share\path\file').
+ * The path must contain at least a server and share path to be valid.
+ * The initial two slashes must be backslashes, the other slashes may each
+ * be either a forward slash or a backslash.
+ *
+ * @param[in] inPath The path to parse.
+ * @param[out] outPath Will contain a newly allocated string (to be
+ * freed using uio_free(), containing the server and share part
+ * of inPath, separated by a backslash, or NULL if 'inPath' was
+ * not a valid UNC path.
+ * @param[out] outLen If not NULL on entry, it will contain the string
+ * length of '*outPath', or 0 if 'inPath' was not a valid UNC path.
+ *
+ * @returns The number of characters to add to 'inPath' to get to the first
+ * path component past the server and share part, or 0 if 'inPath'
+ * was not a valid UNC path.
+ */
+size_t
+uio_getUNCServerShare(const char *inPath, char **outPath, size_t *outLen) {
+ const char *ptr;
+ const char *server;
+ const char *serverEnd;
+ const char *share;
+ const char *shareEnd;
+ char *name;
+ char *nameEnd;
+ size_t nameLen;
+
+ ptr = inPath;
+
+ if (ptr[0] != '\\' || ptr[1] != '\\')
+ goto noMatch;
+
+ // Parse the server part.
+ server = ptr + 2;
+ serverEnd = server;
+ for (;;) {
+ if (*serverEnd == '\0')
+ goto noMatch;
+ if (isPathDelimiter(*serverEnd))
+ break;
+ serverEnd++;
+ }
+ if (serverEnd == server)
+ goto noMatch;
+
+ // Parse the share part.
+ share = serverEnd + 1;
+ shareEnd = share;
+ while (*shareEnd != '\0') {
+ if (isPathDelimiter(*shareEnd))
+ break;
+ serverEnd++;
+ }
+
+ // Skip any trailing path delimiters.
+ ptr = shareEnd;
+ while (isPathDelimiter(*ptr))
+ ptr++;
+
+ // Allocate a new string and fill it.
+ nameLen = (serverEnd - server) + (shareEnd - share) + 3;
+ name = uio_malloc(nameLen + 1);
+ nameEnd = name;
+ *(nameEnd++) = '\\';
+ *(nameEnd++) = '\\';
+ memcpy(nameEnd, server, serverEnd - server);
+ *(nameEnd++) = '\\';
+ memcpy(nameEnd, share, shareEnd - share);
+ *nameEnd = '\0';
+
+ *outPath = name;
+ if (outLen != NULL)
+ *outLen = nameLen;
+ return (size_t) (ptr - inPath);
+
+noMatch:
+ *outPath = NULL;
+ if (outLen != NULL)
+ *outLen = 0;
+ return (size_t) 0;
+}
+
+// Decomposes a path into its components.
+// If isAbsolute is not NULL, *isAbsolute will be set to true
+// iff the path is absolute.
+// As POSIX considers multiple consecutive slashes to be equivalent to
+// a single slash, so will uio (but not in the "\\MACHINE\share" part
+// of a Windows UNC path).
+int
+decomposePath(const char *path, uio_PathComp **pathComp,
+ uio_bool *isAbsolute) {
+ uio_PathComp *result;
+ uio_PathComp *last;
+ uio_PathComp **endResult = &result;
+ uio_bool absolute = false;
+ char *name;
+#ifdef HAVE_UNC_PATHS
+ size_t nameLen;
+#endif /* HAVE_UNC_PATHS */
+
+ if (path[0] == '\0') {
+ errno = ENOENT;
+ return -1;
+ }
+
+ last = NULL;
+#ifdef HAVE_UNC_PATHS
+ path += uio_getUNCServerShare(path, &name, &nameLen);
+ if (name != NULL) {
+ // UNC path
+ *endResult = uio_PathComp_new(name, nameLen, last);
+ last = *endResult;
+ endResult = &last->next;
+
+ absolute = true;
+ } else
+#endif /* HAVE_UNC_PATHS */
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(path[0]) && path[1] == ':') {
+ // DOS/Windows drive letter.
+ if (path[2] != '\0' && !isPathDelimiter(path[2])) {
+ errno = ENOENT;
+ return -1;
+ }
+ name = uio_memdup0(path, 2);
+ *endResult = uio_PathComp_new(name, 2, last);
+ last = *endResult;
+ endResult = &last->next;
+ absolute = true;
+ } else
+#endif /* HAVE_DRIVE_LETTERS */
+ {
+ if (isPathDelimiter(*path)) {
+ absolute = true;
+ do {
+ path++;
+ } while (isPathDelimiter(*path));
+ }
+ }
+
+ while (*path != '\0') {
+ const char *start = path;
+ while (*path != '\0' && !isPathDelimiter(*path))
+ path++;
+
+ name = uio_memdup0(path, path - start);
+ *endResult = uio_PathComp_new(name, path - start, last);
+ last = *endResult;
+ endResult = &last->next;
+
+ while (isPathDelimiter(*path))
+ path++;
+ }
+
+ *endResult = NULL;
+ *pathComp = result;
+ if (isAbsolute != NULL)
+ *isAbsolute = absolute;
+ return 0;
+}
+
+// Pre: pathComp forms a valid path for the platform.
+void
+composePath(const uio_PathComp *pathComp, uio_bool absolute,
+ char **path, size_t *pathLen) {
+ size_t len;
+ const uio_PathComp *ptr;
+ char *result;
+ char *pathPtr;
+
+ assert(pathComp != NULL);
+
+ // First determine how much space is required.
+ len = 0;
+ if (absolute)
+ len++;
+ ptr = pathComp;
+ while (ptr != NULL) {
+ len += ptr->nameLen;
+ ptr = ptr->next;
+ }
+
+ // Allocate the required space.
+ result = (char *) uio_malloc(len + 1);
+
+ // Fill the path.
+ pathPtr = result;
+ ptr = pathComp;
+ if (absolute) {
+#ifdef HAVE_UNC_PATHS
+ if (ptr->name[0] == '\\') {
+ // UNC path
+ assert(ptr->name[1] == '\\');
+ // Nothing to do.
+ } else
+#endif /* HAVE_UNC_PATHS */
+#ifdef HAVE_DRIVE_LETTERS
+ if (ptr->nameLen == 2 && ptr->name[1] == ':'
+ && isDriveLetter(ptr->name[0])) {
+ // Nothing to do.
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+ {
+ *(pathPtr++) = '/';
+ }
+ }
+
+ for (;;) {
+ memcpy(pathPtr, ptr->name, ptr->nameLen);
+ pathPtr += ptr->nameLen;
+
+ ptr = ptr->next;
+ if (ptr == NULL)
+ break;
+
+ *(pathPtr++) = '/';
+ }
+
+ *path = result;
+ *pathLen = len;
+}
+
+
+// *** uio_PathComp *** //
+
+static inline uio_PathComp *
+uio_PathComp_alloc(void) {
+ uio_PathComp *result = uio_malloc(sizeof (uio_PathComp));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PathComp, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PathComp_free(uio_PathComp *pathComp) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PathComp, (void *) pathComp);
+#endif
+ uio_free(pathComp);
+}
+
+// 'name' should be a null terminated string. It is stored in the PathComp,
+// no copy is made.
+// 'namelen' should be the length of 'name'
+uio_PathComp *
+uio_PathComp_new(char *name, size_t nameLen, uio_PathComp *upComp) {
+ uio_PathComp *result;
+
+ result = uio_PathComp_alloc();
+ result->name = name;
+ result->nameLen = nameLen;
+ result->up = upComp;
+ return result;
+}
+
+void
+uio_PathComp_delete(uio_PathComp *pathComp) {
+ uio_PathComp *next;
+
+ while (pathComp != NULL) {
+ next = pathComp->next;
+ uio_free(pathComp->name);
+ uio_PathComp_free(pathComp);
+ pathComp = next;
+ }
+}
+
+// Count the number of path components that 'comp' leads to.
+int
+uio_countPathComps(const uio_PathComp *comp) {
+ int count;
+
+ count = 0;
+ for (; comp != NULL; comp = comp->next)
+ count++;
+ return count;
+}
+
+uio_PathComp *
+uio_lastPathComp(uio_PathComp *comp) {
+ if (comp == NULL)
+ return NULL;
+
+ while (comp->next != NULL)
+ comp = comp->next;
+ return comp;
+}
+
+// make a list of uio_PathComps from a path string
+uio_PathComp *
+uio_makePathComps(const char *path, uio_PathComp *upComp) {
+ const char *start, *end;
+ char *str;
+ uio_PathComp *result;
+ uio_PathComp **compPtr; // Where to put the next PathComp
+
+ compPtr = &result;
+ getFirstPath0Component(path, &start, &end);
+ while (*start != '\0') {
+ str = uio_malloc(end - start + 1);
+ memcpy(str, start, end - start);
+ str[end - start] = '\0';
+
+ *compPtr = uio_PathComp_new(str, end - start, upComp);
+ upComp = *compPtr;
+ compPtr = &(*compPtr)->next;
+ getNextPath0Component(&start, &end);
+ }
+ *compPtr = NULL;
+ return result;
+}
+
+void
+uio_printPathComp(FILE *outStream, const uio_PathComp *comp) {
+ fprintf(outStream, "%s", comp->name);
+}
+
+void
+uio_printPathToComp(FILE *outStream, const uio_PathComp *comp) {
+ if (comp == NULL)
+ return;
+ uio_printPathToComp(outStream, comp->up);
+ fprintf(outStream, "/");
+ uio_printPathComp(outStream, comp);
+}
+
+
diff --git a/src/libs/uio/paths.h b/src/libs/uio/paths.h
new file mode 100644
index 0000000..bb5090d
--- /dev/null
+++ b/src/libs/uio/paths.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_PATHS_H_
+#define LIBS_UIO_PATHS_H_
+
+typedef struct uio_PathComp uio_PathComp;
+
+#include "types.h"
+#include "uioport.h"
+
+#include <stdio.h>
+
+struct uio_PathComp {
+ char *name;
+ // The name of this path component, 0-terminated
+ size_t nameLen;
+ // The length of the 'name' field, for fast lookups.
+ struct uio_PathComp *next;
+ // Next component in the path.
+ struct uio_PathComp *up;
+ // Previous component in the path.
+};
+
+void getFirstPathComponent(const char *dir, const char *dirEnd,
+ const char **startComp, const char **endComp);
+void getFirstPath0Component(const char *dir, const char **startComp,
+ const char **endComp);
+void getNextPathComponent(const char *dirEnd,
+ const char **startComp, const char **endComp);
+void getNextPath0Component(const char **startComp, const char **endComp);
+void getLastPathComponent(const char *dir, const char *dirEnd,
+ const char **startComp, const char **endComp);
+void getLastPath0Component(const char *dir, const char **startComp,
+ const char **endComp);
+void getPreviousPathComponent(const char *dir, const char **startComp,
+ const char **endComp);
+#define getPreviousPath0Component getPreviousPathComponent
+char *joinPaths(const char *first, const char *second);
+char *joinPathsAbsolute(const char *first, const char *second);
+
+uio_bool validPathName(const char *path, size_t len);
+size_t uio_skipUNCServerShare(const char *inPath);
+size_t uio_getUNCServerShare(const char *inPath, char **outPath,
+ size_t *outLen);
+
+#ifdef HAVE_DRIVE_LETTERS
+static inline int
+isDriveLetter(int c)
+{
+ return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
+}
+#endif /* HAVE_DRIVE_LETTERS */
+
+static inline int
+isPathDelimiter(int c)
+{
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ return c == '/' || c == '\\';
+#else
+ return c == '/';
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+}
+
+int decomposePath(const char *path, uio_PathComp **pathComp,
+ uio_bool *isAbsolute);
+void composePath(const uio_PathComp *pathComp, uio_bool absolute,
+ char **path, size_t *pathLen);
+uio_PathComp *uio_PathComp_new(char *name, size_t nameLen,
+ uio_PathComp *upComp);
+void uio_PathComp_delete(uio_PathComp *pathComp);
+int uio_countPathComps(const uio_PathComp *comp);
+uio_PathComp *uio_lastPathComp(uio_PathComp *comp);
+uio_PathComp *uio_makePathComps(const char *path, uio_PathComp *upComp);
+void uio_printPathComp(FILE *outStream, const uio_PathComp *comp);
+void uio_printPathToComp(FILE *outStream, const uio_PathComp *comp);
+
+#endif /* LIBS_UIO_PATHS_H_ */
+
diff --git a/src/libs/uio/physical.c b/src/libs/uio/physical.c
new file mode 100644
index 0000000..18f96d1
--- /dev/null
+++ b/src/libs/uio/physical.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "physical.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+#include "uioport.h"
+
+static inline uio_PRoot *uio_PRoot_alloc(void);
+static inline void uio_PRoot_free(uio_PRoot *pRoot);
+
+// NB: ref counter is not incremented
+uio_PDirHandle *
+uio_PRoot_getRootDirHandle(uio_PRoot *pRoot) {
+ return pRoot->rootDir;
+}
+
+void
+uio_PRoot_deletePRootExtra(uio_PRoot *pRoot) {
+ if (pRoot->extra == NULL)
+ return;
+ assert(pRoot->handler->deletePRootExtra != NULL);
+ pRoot->handler->deletePRootExtra(pRoot->extra);
+}
+
+// note: sets refMount count to 1
+// set handlerRef count to 0
+uio_PRoot *
+uio_PRoot_new(uio_PDirHandle *topDirHandle,
+ uio_FileSystemHandler *handler, uio_Handle *handle,
+ uio_PRootExtra extra, int flags) {
+ uio_PRoot *pRoot;
+
+ pRoot = uio_PRoot_alloc();
+ pRoot->mountRef = 1;
+ pRoot->handleRef = 0;
+ pRoot->rootDir = topDirHandle;
+ pRoot->handler = handler;
+ pRoot->handle = handle;
+ pRoot->extra = extra;
+ pRoot->flags = flags;
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+ pRoot->numCloseHandlers = 0;
+ pRoot->closeHandlers = NULL;
+#endif
+
+ return pRoot;
+}
+
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+// Closehandlers code disabled.
+// It was only meant for internal use, but I don't need it any more.
+// Keeping it around for a while until I'm confident I won't need it in the
+// future.
+
+void
+uio_PRoot_addCloseHandler(uio_PRoot *pRoot, void (*fun)(void *), void *arg) {
+ pRoot->numCloseHandlers++;
+ pRoot->closeHandlers = uio_realloc(pRoot->closeHandlers,
+ pRoot->numCloseHandlers * sizeof (uio_PRoot_CloseHandler));
+ pRoot->closeHandlers[pRoot->numCloseHandlers - 1].fun = fun;
+ pRoot->closeHandlers[pRoot->numCloseHandlers - 1].arg = arg;
+}
+
+void
+uio_PRoot_callCloseHandlers(uio_PRoot *pRoot) {
+ int i;
+
+ i = pRoot->numCloseHandlers;
+ while (i--) {
+ uio_PRoot_CloseHandler *closeHandler;
+
+ closeHandler = &pRoot->closeHandlers[i];
+ (closeHandler->fun)(closeHandler->arg);
+ }
+}
+
+void
+uio_PRoot_removeCloseHandlers(uio_PRoot *pRoot) {
+ pRoot->numCloseHandlers = 0;
+ if (pRoot->closeHandlers != NULL)
+ uio_free(pRoot->closeHandlers);
+ pRoot->closeHandlers = NULL;
+}
+#endif
+
+static inline void
+uio_PRoot_delete(uio_PRoot *pRoot) {
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+ uio_PRoot_callCloseHandlers(pRoot);
+ uio_PRoot_removeCloseHandlers(pRoot);
+#endif
+ assert(pRoot->handler->umount != NULL);
+ pRoot->handler->umount(pRoot);
+ if (pRoot->handle)
+ uio_Handle_unref(pRoot->handle);
+ uio_PRoot_deletePRootExtra(pRoot);
+ uio_PRoot_free(pRoot);
+}
+
+static inline uio_PRoot *
+uio_PRoot_alloc(void) {
+ uio_PRoot *result = uio_malloc(sizeof (uio_PRoot));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_PRoot, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_PRoot_free(uio_PRoot *pRoot) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_PRoot, (void *) pRoot);
+#endif
+ uio_free(pRoot);
+}
+
+void
+uio_PRoot_refHandle(uio_PRoot *pRoot) {
+ pRoot->handleRef++;
+}
+
+void
+uio_PRoot_unrefHandle(uio_PRoot *pRoot) {
+ assert(pRoot->handleRef > 0);
+ pRoot->handleRef--;
+ if (pRoot->handleRef == 0 && pRoot->mountRef == 0)
+ uio_PRoot_delete(pRoot);
+}
+
+void
+uio_PRoot_refMount(uio_PRoot *pRoot) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugRef(uio_PRoot, (void *) pRoot);
+#endif
+ pRoot->mountRef++;
+}
+
+void
+uio_PRoot_unrefMount(uio_PRoot *pRoot) {
+ assert(pRoot->mountRef > 0);
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugUnref(uio_PRoot, (void *) pRoot);
+#endif
+ pRoot->mountRef--;
+ if (pRoot->mountRef == 0 && pRoot->handleRef == 0)
+ uio_PRoot_delete(pRoot);
+}
+
+
diff --git a/src/libs/uio/physical.h b/src/libs/uio/physical.h
new file mode 100644
index 0000000..71bc8e6
--- /dev/null
+++ b/src/libs/uio/physical.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_PHYSICAL_H_
+#define LIBS_UIO_PHYSICAL_H_
+
+#ifndef uio_INTERNAL_PHYSICAL
+typedef void *uio_PRootExtra;
+typedef void *uio_NativeEntriesContext;
+#endif
+
+// 'forward' declarations
+typedef struct uio_PRoot uio_PRoot;
+typedef struct uio_PRoot_CloseHandler uio_PRoot_CloseHandler;
+
+
+#include "iointrn.h"
+#include "uioport.h"
+#include "fstypes.h"
+
+
+/*
+ * Represents the root of a physical directory structure.
+ */
+struct uio_PRoot {
+ int mountRef;
+ /* Number of times this structure is referenced from
+ * mount trees. */
+ int handleRef;
+ /* Number of file or directory handles that point inside the
+ * physical directory strucure of this pRoot.
+ */
+ struct uio_PDirHandle *rootDir;
+ struct uio_FileSystemHandler *handler;
+ /* How to handle files in this PRoot tree */
+ int flags;
+# define uio_PRoot_NOCACHE 0x0002
+ struct uio_Handle *handle;
+ /* The handle through which this PRoot is opened,
+ * this is NULL for the top PRoot */
+ // TODO: move this to extra?
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+ int numCloseHandlers;
+ uio_PRoot_CloseHandler *closeHandlers;
+#endif
+ uio_PRootExtra extra;
+ /* extra internal data for some filesystem types */
+};
+
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+struct uio_PRoot_CloseHandler {
+ void (*fun)(void *);
+ void *arg;
+};
+#endif
+
+void uio_PRoot_deletePRootExtra(uio_PRoot *pRoot);
+uio_PRoot *uio_PRoot_new(uio_PDirHandle *topDirHandle,
+ uio_FileSystemHandler *handler, uio_Handle *handle,
+ uio_PRootExtra extra, int flags);
+#ifdef uio_PROOT_HAVE_CLOSE_HANDLERS
+void uio_PRoot_addCloseHandler(uio_PRoot *pRoot, void (*fun)(void *),
+ void *arg);
+void uio_PRoot_callCloseHandlers(uio_PRoot *pRoot);
+void uio_PRoot_removeCloseHandlers(uio_PRoot *pRoot);
+#endif
+uio_PDirHandle *uio_PRoot_getRootDirHandle(uio_PRoot *pRoot);
+void uio_PRoot_refHandle(uio_PRoot *pRoot);
+void uio_PRoot_unrefHandle(uio_PRoot *pRoot);
+void uio_PRoot_refMount(uio_PRoot *pRoot);
+void uio_PRoot_unrefMount(uio_PRoot *pRoot);
+
+#endif /* LIBS_UIO_PHYSICAL_H_ */
+
+
diff --git a/src/libs/uio/stdio/Makeinfo b/src/libs/uio/stdio/Makeinfo
new file mode 100644
index 0000000..2d99c66
--- /dev/null
+++ b/src/libs/uio/stdio/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="stdio.c"
+uqm_HFILES="stdio.h"
diff --git a/src/libs/uio/stdio/stdio.c b/src/libs/uio/stdio/stdio.c
new file mode 100644
index 0000000..a4421d1
--- /dev/null
+++ b/src/libs/uio/stdio/stdio.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// The GPDir structures and functions are used for caching only.
+
+#ifdef __svr4__
+# define _POSIX_PTHREAD_SEMANTICS
+ // For the POSIX variant of readdir_r()
+#endif
+
+#include "./stdio.h"
+
+#ifdef WIN32
+# include <io.h>
+#else
+# include <sys/stat.h>
+# include <unistd.h>
+# include <dirent.h>
+#endif
+#include <stdio.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "../uioport.h"
+#include "../paths.h"
+#include "../mem.h"
+#include "../physical.h"
+#ifdef uio_MEM_DEBUG
+# include "../memdebug.h"
+#endif
+
+static inline uio_GPFile *stdio_addFile(uio_GPDir *gPDir,
+ const char *fileName);
+static inline uio_GPDir *stdio_addDir(uio_GPDir *gPDir, const char *dirName);
+static char *stdio_getPath(uio_GPDir *gPDir);
+static stdio_GPDirData *stdio_GPDirData_new(char *name, char *cachedPath,
+ uio_GPDir *upDir);
+static void stdio_GPDirData_delete(stdio_GPDirData *gPDirData);
+static inline stdio_GPDirData *stdio_GPDirData_alloc(void);
+static inline void stdio_GPDirData_free(stdio_GPDirData *gPDirData);
+static inline stdio_EntriesIterator *stdio_EntriesIterator_alloc(void);
+static inline void stdio_EntriesIterator_free(
+ stdio_EntriesIterator *iterator);
+
+uio_FileSystemHandler stdio_fileSystemHandler = {
+ /* .init = */ NULL,
+ /* .unInit = */ NULL,
+ /* .cleanup = */ NULL,
+
+ /* .mount = */ stdio_mount,
+ /* .umount = */ uio_GPRoot_umount,
+
+ /* .access = */ stdio_access,
+ /* .close = */ stdio_close,
+ /* .fstat = */ stdio_fstat,
+ /* .stat = */ stdio_stat,
+ /* .mkdir = */ stdio_mkdir,
+ /* .open = */ stdio_open,
+ /* .read = */ stdio_read,
+ /* .rename = */ stdio_rename,
+ /* .rmdir = */ stdio_rmdir,
+ /* .seek = */ stdio_seek,
+ /* .write = */ stdio_write,
+ /* .unlink = */ stdio_unlink,
+
+ /* .openEntries = */ stdio_openEntries,
+ /* .readEntries = */ stdio_readEntries,
+ /* .closeEntries = */ stdio_closeEntries,
+
+ /* .getPDirEntryHandle = */ stdio_getPDirEntryHandle,
+ /* .deletePRootExtra = */ uio_GPRoot_delete,
+ /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete,
+ /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete,
+};
+
+uio_GPRoot_Operations stdio_GPRootOperations = {
+ /* .fillGPDir = */ NULL,
+ /* .deleteGPRootExtra = */ NULL,
+ /* .deleteGPDirExtra = */ stdio_GPDirData_delete,
+ /* .deleteGPFileExtra = */ NULL,
+};
+
+
+void
+stdio_close(uio_Handle *handle) {
+ int fd;
+ int result;
+
+ fd = handle->native->fd;
+ uio_free(handle->native);
+
+ while (1) {
+ result = close(fd);
+ if (result == 0)
+ break;
+ if (errno != EINTR) {
+ fprintf(stderr, "Warning: Error while closing socket: %s\n",
+ strerror(errno));
+ break;
+ }
+ }
+}
+
+int
+stdio_access(uio_PDirHandle *pDirHandle, const char *name, int mode) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = access(path, mode);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+ return result;
+}
+
+int
+stdio_fstat(uio_Handle *handle, struct stat *statBuf) {
+ return fstat(handle->native->fd, statBuf);
+}
+
+int
+stdio_stat(uio_PDirHandle *pDirHandle, const char *name,
+ struct stat *statBuf) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = stat(path, statBuf);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+ return result;
+}
+
+uio_PDirHandle *
+stdio_mkdir(uio_PDirHandle *pDirHandle, const char *name, mode_t mode) {
+ char *path;
+ uio_GPDir *newGPDir;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (MKDIR(path, mode) == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return NULL;
+ }
+ uio_free(path);
+
+ newGPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(newGPDir);
+ return uio_PDirHandle_new(pDirHandle->pRoot, newGPDir);
+}
+
+/*
+ * Function name: stdio_open
+ * Description: open a file from a normal stdio environment
+ * Arguments: gPDir - the dir where to open the file
+ * file - the name of the file to open
+ * flags - flags, as to stdio open()
+ * mode - mode, as to stdio open()
+ * Returns: handle, for use in functions accessing the opened file.
+ * If failed, errno is set and handle is -1.
+ */
+uio_Handle *
+stdio_open(uio_PDirHandle *pDirHandle, const char *file, int flags,
+ mode_t mode) {
+ stdio_Handle *handle;
+ char *path;
+ int fd;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), file);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ fd = open(path, flags, mode);
+ if (fd == -1) {
+ int save_errno;
+
+ save_errno = errno;
+ uio_free(path);
+ errno = save_errno;
+ return NULL;
+ }
+ uio_free(path);
+
+#if 0
+ if (flags & O_CREAT) {
+ if (uio_GPDir_getGPDirEntry(pDirHandle->extra, file) == NULL)
+ stdio_addFile(pDirHandle->extra, file);
+ }
+#endif
+
+ handle = uio_malloc(sizeof (stdio_Handle));
+ handle->fd = fd;
+
+ return uio_Handle_new(pDirHandle->pRoot, handle, flags);
+}
+
+ssize_t
+stdio_read(uio_Handle *handle, void *buf, size_t count) {
+ return read(handle->native->fd, buf, count);
+}
+
+int
+stdio_rename(uio_PDirHandle *oldPDirHandle, const char *oldName,
+ uio_PDirHandle *newPDirHandle, const char *newName) {
+ char *newPath, *oldPath;
+ int result;
+
+ oldPath = joinPaths(stdio_getPath(oldPDirHandle->extra), oldName);
+ if (oldPath == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ newPath = joinPaths(stdio_getPath(newPDirHandle->extra), newName);
+ if (newPath == NULL) {
+ // errno is set
+ uio_free(oldPath);
+ return -1;
+ }
+
+ result = rename(oldPath, newPath);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(oldPath);
+ uio_free(newPath);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(oldPath);
+ uio_free(newPath);
+
+ {
+ // update the GPDir structure
+ uio_GPDirEntry *entry;
+
+ // TODO: add locking
+ entry = uio_GPDir_getGPDirEntry(oldPDirHandle->extra, oldName);
+ if (entry != NULL) {
+ uio_GPDirEntries_remove(oldPDirHandle->extra->entries, oldName);
+ uio_GPDirEntries_add(newPDirHandle->extra->entries, newName,
+ entry);
+ }
+ }
+
+ return result;
+}
+
+int
+stdio_rmdir(uio_PDirHandle *pDirHandle, const char *name) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = rmdir(path);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+
+ uio_GPDir_removeSubDir(pDirHandle->extra, name);
+
+ return result;
+}
+
+off_t
+stdio_seek(uio_Handle *handle, off_t offset, int whence) {
+ return lseek(handle->native->fd, offset, whence);
+}
+
+ssize_t
+stdio_write(uio_Handle *handle, const void *buf, size_t count) {
+ return write(handle->native->fd, buf, count);
+}
+
+int
+stdio_unlink(uio_PDirHandle *pDirHandle, const char *name) {
+ char *path;
+ int result;
+
+ path = joinPaths(stdio_getPath(pDirHandle->extra), name);
+ if (path == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ result = unlink(path);
+ if (result == -1) {
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return -1;
+ }
+
+ uio_free(path);
+
+ uio_GPDir_removeFile(pDirHandle->extra, name);
+
+ return result;
+}
+
+uio_PDirEntryHandle *
+stdio_getPDirEntryHandle(const uio_PDirHandle *pDirHandle, const char *name) {
+ uio_PDirEntryHandle *result;
+ const char *pathUpTo;
+ char *path;
+ struct stat statBuf;
+#ifdef HAVE_DRIVE_LETTERS
+ char driveName[3];
+#endif /* HAVE_DRIVE_LETTERS */
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (pDirHandle->extra->extra->upDir == NULL) {
+ // Top dir. Contains only drive letters and UNC \\server\share
+ // parts.
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(name[0]) && name[1] == ':' && name[2] == '\0') {
+ driveName[0] = tolower(name[0]);
+ driveName[1] = ':';
+ driveName[2] = '\0';
+ name = driveName;
+ } else
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef HAVE_UNC_PATHS
+ {
+ size_t uncLen;
+
+ uncLen = uio_skipUNCServerShare(name);
+ if (name[uncLen] != '\0') {
+ // 'name' contains neither a drive letter, nor the
+ // first part of a UNC path.
+ return NULL;
+ }
+ }
+#else /* !defined(HAVE_UNC_PATHS) */
+ {
+ // Make sure that there is an 'else' case if HAVE_DRIVE_LETTERS
+ // is defined.
+ }
+#endif /* HAVE_UNC_PATHS */
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+
+ result = uio_GPDir_getPDirEntryHandle(pDirHandle, name);
+ if (result != NULL)
+ return result;
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (pDirHandle->extra->extra->upDir == NULL) {
+ // Need to create a 'directory' for the drive letter or UNC
+ // "\\server\share" part.
+ // It's no problem if we happen to create a dir for a non-existing
+ // drive. It should just produce an empty dir.
+ uio_GPDir *gPDir;
+
+ gPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(gPDir);
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(
+ pDirHandle->pRoot, gPDir);
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+
+ pathUpTo = stdio_getPath(pDirHandle->extra);
+ if (pathUpTo == NULL) {
+ // errno is set
+ return NULL;
+ }
+ path = joinPaths(pathUpTo, name);
+ if (path == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (stat(path, &statBuf) == -1) {
+#ifdef __SYMBIAN32__
+ // XXX: HACK: If we don't have access to a directory, we can still
+ // have access to the underlying entries. We don't actually know
+ // whether the entry is a directory, but I know of no way to find
+ // out. We just pretend that it is; worst case, a file which we can't
+ // access shows up as a directory which we can't access.
+ if (errno == EACCES) {
+ statBuf.st_mode = S_IFDIR;
+ // Fake a directory; the other fields of the stat
+ // structure are unused.
+ } else
+#endif
+ {
+ // errno is set.
+ int savedErrno = errno;
+ uio_free(path);
+ errno = savedErrno;
+ return NULL;
+ }
+ }
+ uio_free(path);
+
+ if (S_ISREG(statBuf.st_mode)) {
+ uio_GPFile *gPFile;
+
+ gPFile = stdio_addFile(pDirHandle->extra, name);
+ uio_GPFile_ref(gPFile);
+ return (uio_PDirEntryHandle *) uio_PFileHandle_new(
+ pDirHandle->pRoot, gPFile);
+ } else if (S_ISDIR(statBuf.st_mode)) {
+ uio_GPDir *gPDir;
+
+ gPDir = stdio_addDir(pDirHandle->extra, name);
+ uio_GPDir_ref(gPDir);
+ return (uio_PDirEntryHandle *) uio_PDirHandle_new(
+ pDirHandle->pRoot, gPDir);
+ } else {
+#ifdef DEBUG
+ fprintf(stderr, "Warning: Attempt to access '%s' from '%s', "
+ "which is not a regular file, nor a directory.\n", name,
+ pathUpTo);
+#endif
+ return NULL;
+ }
+}
+
+uio_PRoot *
+stdio_mount(uio_Handle *handle, int flags) {
+ uio_PRoot *result;
+ stdio_GPDirData *extra;
+
+ assert (handle == NULL);
+ extra = stdio_GPDirData_new(
+ uio_strdup("") /* name */,
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ // Full paths start with a drive letter or \\server\share
+ uio_strdup("") /* cached path */,
+#else
+ uio_strdup("/") /* cached path */,
+#endif /* HAVE_DRIVE_LETTERS */
+ NULL /* parent dir */);
+
+ result = uio_GPRoot_makePRoot(
+ uio_getFileSystemHandler(uio_FSTYPE_STDIO), flags,
+ &stdio_GPRootOperations, NULL, uio_GPRoot_PERSISTENT,
+ handle, extra, 0);
+
+ uio_GPDir_setComplete(result->rootDir->extra, true);
+
+ return result;
+}
+
+#ifdef WIN32
+stdio_EntriesIterator *
+stdio_openEntries(uio_PDirHandle *pDirHandle) {
+ const char *dirPath;
+ char path[PATH_MAX];
+ char *pathEnd;
+ size_t dirPathLen;
+ stdio_EntriesIterator *iterator;
+
+// uio_GPDir_access(pDirHandle->extra);
+
+ dirPath = stdio_getPath(pDirHandle->extra);
+ if (dirPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ dirPathLen = strlen(dirPath);
+ if (dirPathLen > PATH_MAX - 3) {
+ // dirPath ++ '/' ++ '*' ++ '\0'
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ memcpy(path, dirPath, dirPathLen);
+ pathEnd = path + dirPathLen;
+ pathEnd[0] = '/';
+ pathEnd[1] = '*';
+ pathEnd[2] = '\0';
+ iterator = stdio_EntriesIterator_new(0);
+ iterator->dirHandle = _findfirst(path, &iterator->findData);
+ if (iterator->dirHandle == 1) {
+ if (errno != ENOENT) {
+ stdio_EntriesIterator_delete(iterator);
+ return NULL;
+ }
+ iterator->status = 1;
+ } else
+ iterator->status = 0;
+ return iterator;
+}
+#endif
+
+#ifndef WIN32
+stdio_EntriesIterator *
+stdio_openEntries(uio_PDirHandle *pDirHandle) {
+ const char *dirPath;
+ DIR *dirHandle;
+ stdio_EntriesIterator *result;
+
+// uio_GPDir_access(pDirHandle->extra);
+
+ dirPath = stdio_getPath(pDirHandle->extra);
+ if (dirPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ dirHandle = opendir(dirPath);
+ if (dirHandle == NULL) {
+ // errno is set;
+ return NULL;
+ }
+
+ result = stdio_EntriesIterator_new(dirHandle);
+ result->status = readdir_r(dirHandle, result->direntBuffer,
+ &result->entry);
+#ifndef WIN32
+# ifdef DEBUG
+ if (result->status != 0) {
+ fprintf(stderr, "Warning: readdir_r() failed: %s\n",
+ strerror(result->status));
+ }
+# endif
+#endif
+ return result;
+}
+#endif
+
+// the start of 'buf' will be filled with pointers to strings
+// those strings are stored elsewhere in buf.
+// The function returns the number of strings passed along, or -1 for error.
+// If there are no more entries, the last pointer will be NULL.
+// (this pointer counts towards the return value)
+int
+stdio_readEntries(stdio_EntriesIterator **iteratorPtr,
+ char *buf, size_t len) {
+ char *end;
+ char **start;
+ int num;
+ const char *name;
+ size_t nameLen;
+ stdio_EntriesIterator *iterator;
+
+ iterator = *iteratorPtr;
+
+ // buf will be filled like this:
+ // The start of buf will contain pointers to char *,
+ // the end will contain the actual char[] that those pointers point to.
+ // The start and the end will grow towards eachother.
+ start = (char **) buf;
+ end = buf + len;
+ num = 0;
+#ifdef WIN32
+ for (; iterator->status == 0;
+ iterator->status = _findnext(iterator->dirHandle,
+ &iterator->findData))
+#else
+ for (; iterator->status == 0 && iterator->entry != NULL;
+ iterator->status = readdir_r(iterator->dirHandle,
+ iterator->direntBuffer, &iterator->entry))
+#endif
+ {
+#ifdef WIN32
+ name = iterator->findData.name;
+#else
+ name = iterator->entry->d_name;
+#endif
+ if (name[0] == '.' &&
+ (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0'))) {
+ // skip directories "." and ".."
+ continue;
+ }
+ nameLen = strlen(name) + 1;
+
+ // Does this work with systems that need memory access to be
+ // aligned on a certain number of bytes?
+ if ((size_t) (sizeof (char *) + nameLen) >
+ (size_t) (end - (char *) start)) {
+ // Not enough room to fit the pointer (at the beginning) and
+ // the string (at the end).
+ return num;
+ }
+ end -= nameLen;
+ memcpy(end, name, nameLen);
+ *start = end;
+ start++;
+ num++;
+ }
+#ifndef WIN32
+# ifdef DEBUG
+ if (iterator->status != 0) {
+ fprintf(stderr, "Warning: readdir_r() failed: %s\n",
+ strerror(iterator->status));
+ }
+# endif
+#endif
+ if (sizeof (char *) > (size_t) (end - (char *) start)) {
+ // not enough room to fit the NULL pointer.
+ // It will have to be reported seperately the next time.
+ return num;
+ }
+ *start = NULL;
+ num++;
+ return num;
+}
+
+void
+stdio_closeEntries(stdio_EntriesIterator *iterator) {
+#ifdef WIN32
+ _findclose(iterator->dirHandle);
+#else
+ closedir(iterator->dirHandle);
+#endif
+ stdio_EntriesIterator_delete(iterator);
+}
+
+#ifdef WIN32
+stdio_EntriesIterator *
+stdio_EntriesIterator_new(long dirHandle) {
+ stdio_EntriesIterator *result;
+
+ result = stdio_EntriesIterator_alloc();
+ result->dirHandle = dirHandle;
+ return result;
+}
+#else
+stdio_EntriesIterator *
+stdio_EntriesIterator_new(DIR *dirHandle) {
+ stdio_EntriesIterator *result;
+ size_t bufferSize;
+
+ result = stdio_EntriesIterator_alloc();
+ result->dirHandle = dirHandle;
+
+ // Linux's and FreeBSD's struct dirent are defined with a
+ // maximum d_name field (NAME_MAX).
+ // However, POSIX doesn't require this, and in fact
+ // at least QNX defines struct dirent with an empty d_name field.
+ // Solaris defineds it with a d_name field of length 1.
+ // This should take care of it:
+ bufferSize = sizeof (struct dirent)
+ - sizeof (((struct dirent *) 0)->d_name) + (NAME_MAX + 1);
+ // Take the length of the dirent structure as it is defined,
+ // subtract the length of the d_name field, and add the length
+ // of the maximum length d_name field (NAME_MAX plus 1 for
+ // the '\0').
+ // XXX: Could this give problems with weird alignments?
+ result->direntBuffer = uio_malloc(bufferSize);
+ return result;
+}
+#endif
+
+void
+stdio_EntriesIterator_delete(stdio_EntriesIterator *iterator) {
+#ifndef WIN32
+ uio_free(iterator->direntBuffer);
+#endif
+ stdio_EntriesIterator_free(iterator);
+}
+
+static inline stdio_EntriesIterator *
+stdio_EntriesIterator_alloc(void) {
+ return uio_malloc(sizeof (stdio_EntriesIterator));
+}
+
+static inline void
+stdio_EntriesIterator_free(stdio_EntriesIterator *iterator) {
+ uio_free(iterator);
+}
+
+static inline uio_GPFile *
+stdio_addFile(uio_GPDir *gPDir, const char *fileName) {
+ uio_GPFile *file;
+
+ file = uio_GPFile_new(gPDir->pRoot, NULL,
+ uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags));
+ uio_GPDir_addFile(gPDir, fileName, file);
+ return file;
+}
+
+// called by fillGPDir when a subdir is found
+static inline uio_GPDir *
+stdio_addDir(uio_GPDir *gPDir, const char *dirName) {
+ uio_GPDir *subDir;
+
+ subDir = uio_GPDir_prepareSubDir(gPDir, dirName);
+ if (subDir->extra == NULL) {
+ // It's a new dir, we'll need to add our own data.
+ uio_GPDir_ref(gPDir);
+ subDir->extra = stdio_GPDirData_new(uio_strdup(dirName),
+ NULL, gPDir);
+ uio_GPDir_setComplete(subDir, true);
+ // fillPDir should not be called.
+ }
+ uio_GPDir_commitSubDir(gPDir, dirName, subDir);
+ return subDir;
+}
+
+// returns a pointer to gPDir->extra->cachedPath
+// pointer should not be stored, the memory it points to can be freed
+// lateron. TODO: not threadsafe.
+static char *
+stdio_getPath(uio_GPDir *gPDir) {
+ if (gPDir->extra->cachedPath == NULL) {
+ char *upPath;
+ size_t upPathLen, nameLen;
+
+ if (gPDir->extra->upDir == NULL) {
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ // Drive letter or UNC \\server\share still needs to follow.
+ gPDir->extra->cachedPath = uio_malloc(1);
+ gPDir->extra->cachedPath[0] = '\0';
+#else
+ gPDir->extra->cachedPath = uio_malloc(2);
+ gPDir->extra->cachedPath[0] = '/';
+ gPDir->extra->cachedPath[1] = '\0';
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+ return gPDir->extra->cachedPath;
+ }
+
+ upPath = stdio_getPath(gPDir->extra->upDir);
+ if (upPath == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+#if defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS)
+ if (upPath[0] == '\0') {
+ // The up dir is the root dir. Directly below the root dir are
+ // only dirs for drive letters and UNC \\share\server parts.
+ // No '/' needs to be attached.
+ gPDir->extra->cachedPath = uio_strdup(gPDir->extra->name);
+ return gPDir->extra->cachedPath;
+ }
+#endif /* defined(HAVE_DRIVE_LETTERS) || defined(HAVE_UNC_PATHS) */
+ upPathLen = strlen(upPath);
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS)
+ if (upPath[upPathLen - 1] == '/') {
+ // should only happen for "/"
+ upPathLen--;
+ }
+#endif /* !defined(HAVE_DRIVE_LETTERS) && !defined(HAVE_UNC_PATHS) */
+ nameLen = strlen(gPDir->extra->name);
+ if (upPathLen + nameLen + 1 >= PATH_MAX) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ gPDir->extra->cachedPath = uio_malloc(upPathLen + nameLen + 2);
+ memcpy(gPDir->extra->cachedPath, upPath, upPathLen);
+ gPDir->extra->cachedPath[upPathLen] = '/';
+ memcpy(gPDir->extra->cachedPath + upPathLen + 1,
+ gPDir->extra->name, nameLen);
+ gPDir->extra->cachedPath[upPathLen + nameLen + 1] = '\0';
+ }
+ return gPDir->extra->cachedPath;
+}
+
+static stdio_GPDirData *
+stdio_GPDirData_new(char *name, char *cachedPath, uio_GPDir *upDir) {
+ stdio_GPDirData *result;
+
+ result = stdio_GPDirData_alloc();
+ result->name = name;
+ result->cachedPath = cachedPath;
+ result->upDir = upDir;
+ return result;
+}
+
+static void
+stdio_GPDirData_delete(stdio_GPDirData *gPDirData) {
+ if (gPDirData->upDir != NULL)
+ uio_GPDir_unref(gPDirData->upDir);
+ stdio_GPDirData_free(gPDirData);
+}
+
+static inline stdio_GPDirData *
+stdio_GPDirData_alloc(void) {
+ stdio_GPDirData *result;
+
+ result = uio_malloc(sizeof (stdio_GPDirData));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(stdio_GPDirData, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+stdio_GPDirData_free(stdio_GPDirData *gPDirData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(stdio_GPDirData, (void *) gPDirData);
+#endif
+ uio_free(gPDirData->name);
+ if (gPDirData->cachedPath != NULL)
+ uio_free(gPDirData->cachedPath);
+ uio_free(gPDirData);
+}
+
+
diff --git a/src/libs/uio/stdio/stdio.h b/src/libs/uio/stdio/stdio.h
new file mode 100644
index 0000000..914a1d7
--- /dev/null
+++ b/src/libs/uio/stdio/stdio.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+typedef struct stdio_Handle *uio_NativeHandle;
+typedef void *uio_GPRootExtra;
+typedef struct stdio_GPDirData *uio_GPDirExtra;
+typedef void *uio_GPFileExtra;
+typedef struct stdio_EntriesIterator stdio_EntriesIterator;
+typedef stdio_EntriesIterator *uio_NativeEntriesContext;
+
+
+#define uio_INTERNAL_PHYSICAL
+
+#include "../gphys.h"
+#include "../iointrn.h"
+#include "../uioport.h"
+#include "../fstypes.h"
+#include "../physical.h"
+
+#include <sys/stat.h>
+#ifndef WIN32
+# include <dirent.h>
+#endif
+
+
+typedef struct stdio_GPDirData {
+ // The reason that names are stored is that in the system filesystem
+ // you need names to refer to files and directories.
+ // (you could keep a file descriptor to each one, but that would
+ // mean a lot of open file descriptors, and for some it won't even
+ // be enough).
+ // This is not needed for all filesystems; therefor this info is not
+ // in uio_GPDir itself.
+ // The reasons for including upDir here are similar.
+ char *name;
+ char *cachedPath;
+ uio_GPDir *upDir;
+} stdio_GPDirData;
+
+typedef struct stdio_Handle {
+ int fd;
+} stdio_Handle;
+
+#ifdef WIN32
+struct stdio_EntriesIterator {
+ long dirHandle;
+ struct _finddata_t findData;
+ int status;
+};
+#endif
+
+#ifndef WIN32
+struct stdio_EntriesIterator {
+ DIR *dirHandle;
+ struct dirent *entry;
+ struct dirent *direntBuffer;
+ int status;
+};
+#endif
+
+
+uio_PRoot *stdio_mount(uio_Handle *handle, int flags);
+int stdio_umount(uio_PRoot *);
+uio_PDirHandle *stdio_mkdir(uio_PDirHandle *pDirHandle, const char *name,
+ mode_t mode);
+uio_Handle *stdio_open(uio_PDirHandle *pDirHandle, const char *file, int flags,
+ mode_t mode);
+void stdio_close(uio_Handle *handle);
+int zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode);
+int stdio_access(uio_PDirHandle *pDirHandle, const char *name, int mode);
+int stdio_fstat(uio_Handle *handle, struct stat *statBuf);
+int stdio_stat(uio_PDirHandle *pDirHandle, const char *name,
+ struct stat *statBuf);
+ssize_t stdio_read(uio_Handle *handle, void *buf, size_t count);
+int stdio_rename(uio_PDirHandle *oldPDirHandle, const char *oldName,
+ uio_PDirHandle *newPDirHandle, const char *newName);
+int stdio_rmdir(uio_PDirHandle *pDirHandle, const char *name);
+off_t stdio_seek(uio_Handle *handle, off_t offset, int whence);
+ssize_t stdio_write(uio_Handle *handle, const void *buf, size_t count);
+int stdio_unlink(uio_PDirHandle *pDirHandle, const char *name);
+
+stdio_EntriesIterator *stdio_openEntries(uio_PDirHandle *pDirHandle);
+int stdio_readEntries(stdio_EntriesIterator **iterator,
+ char *buf, size_t len);
+void stdio_closeEntries(stdio_EntriesIterator *iterator);
+#ifdef WIN32
+stdio_EntriesIterator *stdio_EntriesIterator_new(long dirHandle);
+#else
+stdio_EntriesIterator *stdio_EntriesIterator_new(DIR *dirHandle);
+#endif
+void stdio_EntriesIterator_delete(stdio_EntriesIterator *iterator);
+uio_PDirEntryHandle *stdio_getPDirEntryHandle(
+ const uio_PDirHandle *pDirHandle, const char *name);
+
diff --git a/src/libs/uio/types.h b/src/libs/uio/types.h
new file mode 100644
index 0000000..b92f7a4
--- /dev/null
+++ b/src/libs/uio/types.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _uio_TYPES_H
+#define _uio_TYPES_H
+
+#include "config.h"
+
+// ISO C99 compatible boolean types. The ISO C99 standard defines:
+// - An object declared as type _Bool, large enough to store the values 0
+// and 1, the rank of which is less than the rank of all other standard
+// integer types.
+// - A macro "bool", which expands to "_Bool".
+// - A macro "true", which expands to the integer constant 1, suitable for
+// use in #if preprocessing directives.
+// - A macro "false", which expands to the integer constant 0, suitable for
+// use in #if preprocessing directives.
+// - A macro "__bool_true_false_are_defined", which expands to the integer
+// constant 1, suitable for use in #if preprocessing directives.
+#ifndef __bool_true_false_are_defined
+#undef bool
+#undef false
+#undef true
+#ifndef HAVE__BOOL
+typedef unsigned char _Bool;
+#endif /* HAVE_BOOL */
+#define bool _Bool
+#define true 1
+#define false 0
+#define __bool_true_false_are_defined
+#endif /* __bool_true_false_are_defined */
+
+typedef bool uio_bool;
+
+typedef unsigned char uio_uint8;
+typedef signed char uio_sint8;
+typedef unsigned short uio_uint16;
+typedef signed short uio_sint16;
+typedef unsigned int uio_uint32;
+typedef signed int uio_sint32;
+
+typedef unsigned long uio_uintptr;
+ // Needs to be adapted for 64 bits systems
+
+#endif /* _uio_TYPES_H */
+
+
diff --git a/src/libs/uio/uioport.h b/src/libs/uio/uioport.h
new file mode 100644
index 0000000..69d7e8e
--- /dev/null
+++ b/src/libs/uio/uioport.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UIOPORT_H_
+#define LIBS_UIO_UIOPORT_H_
+
+#ifdef _MSC_VER
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+
+// Compilation related
+#ifndef inline
+# ifdef _MSC_VER
+# define inline __inline
+# else
+# define inline __inline__
+# endif
+#endif
+
+// Paths
+#ifdef WIN32
+# include <stdlib.h>
+# define PATH_MAX _MAX_PATH
+# define NAME_MAX _MAX_FNAME
+ // _MAX_DIR and FILENAME_MAX could also be candidates.
+ // If anyone can tell me which one matches NAME_MAX, please
+ // let me know.
+#elif defined(_WIN32_WCE)
+# include <sys/syslimits.h>
+#else
+# include <limits.h>
+ /* PATH_MAX is per POSIX defined in <limits.h>, but:
+ * "A definition of one of the values from Table 2.6 shall bea
+ * omitted from <limits.h> on specific implementations where the
+ * corresponding value is equal to or greater than the
+ * stated minimum, but where the value can vary depending
+ * on the file to which it is applied. The actual value supported
+ * for a specific pathname shall be provided by the pathconf()
+ * function."
+ * _POSIX_NAME_MAX will provide a minimum (14).
+ * This is relevant (at least) for Solaris.
+ */
+# ifndef NAME_MAX
+# define NAME_MAX _POSIX_NAME_MAX
+# endif
+#endif
+
+// Variations in path handling
+#if defined(WIN32) || defined(__SYMBIAN32__)
+ // HAVE_DRIVE_LETTERS is defined to signify that DOS/Windows style drive
+ // letters are to be recognised on this platform.
+# define HAVE_DRIVE_LETTERS
+ // BACKSLASH_IS_PATH_SEPARATOR is defined to signify that the backslash
+ // character is to be recognised as a path separator on this platform.
+ // This does not affect the acceptance of forward slashes as path
+ // separators.
+# define BACKSLASH_IS_PATH_SEPARATOR
+#endif
+#if defined(WIN32)
+ // HAVE_UNC_PATHS is defined to signify that Universal Naming Convention
+ // style paths are to be recognised on this platform.
+# define HAVE_UNC_PATHS
+#endif
+
+// User ids
+#ifdef WIN32
+typedef short uid_t;
+typedef short gid_t;
+#endif
+
+// Some types
+#ifdef _MSC_VER
+typedef int ssize_t;
+typedef unsigned short mode_t;
+#endif
+
+// Directories
+#include <sys/stat.h>
+#ifdef WIN32
+# ifdef _MSC_VER
+# define MKDIR(name, mode) ((void) mode, _mkdir(name))
+# else
+# define MKDIR(name, mode) ((void) mode, mkdir(name))
+# endif
+#else
+# define MKDIR mkdir
+#endif
+#ifdef _MSC_VER
+# include <direct.h>
+# define chdir _chdir
+# define getcwd _getcwd
+# define chdir _chdir
+# define getcwd _getcwd
+# define access _access
+# define F_OK 0
+# define W_OK 2
+# define R_OK 4
+# define open _open
+# define read _read
+# define rmdir _rmdir
+# define lseek _lseek
+# define lstat _lstat
+# define fstat _fstat
+# define S_IRUSR S_IREAD
+# define S_IWUSR S_IWRITE
+# define S_IXUSR S_IEXEC
+# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
+# define S_IRGRP 0
+# define S_IWGRP 0
+# define S_IXGRP 0
+# define S_IROTH 0
+# define S_IWOTH 0
+# define S_IXOTH 0
+# define S_IRWXG 0
+# define S_IRWXO 0
+# define S_ISUID 0
+# define S_ISGID 0
+# define O_ACCMODE (O_RDONLY | O_WRONLY | O_RDWR)
+# define S_IFMT _S_IFMT
+# define S_IFREG _S_IFREG
+# define S_IFCHR _S_IFCHR
+# define S_IFDIR _S_IFDIR
+# define S_ISDIR(mode) (((mode) & _S_IFMT) == _S_IFDIR)
+# define S_ISREG(mode) (((mode) & _S_IFMT) == _S_IFREG)
+# define write _write
+# define stat _stat
+# define unlink _unlink
+#elif defined (__MINGW32__)
+# define S_IRGRP 0
+# define S_IWGRP 0
+# define S_IXGRP 0
+# define S_IROTH 0
+# define S_IWOTH 0
+# define S_IXOTH 0
+# define S_IRWXG 0
+# define S_IRWXO 0
+# define S_ISUID 0
+# define S_ISGID 0
+# define S_IFMT _S_IFMT
+# define S_IFREG _S_IFREG
+# define S_IFCHR _S_IFCHR
+# define S_IFDIR _S_IFDIR
+#endif
+#ifdef __SYMBIAN32__
+ // TODO: Symbian doesn't have readdir_r(). If uio is to be usable
+ // outside of uqm (which defines its own backup readdir_r()), an
+ // implementation of that function needs to be added to uio.
+# include <dirent.h>
+ int readdir_r (DIR *dirp, struct dirent *entry, struct dirent **result);
+#endif
+
+#endif /* LIBS_UIO_UIOPORT_H_ */
+
diff --git a/src/libs/uio/uiostream.c b/src/libs/uio/uiostream.c
new file mode 100644
index 0000000..eb101a5
--- /dev/null
+++ b/src/libs/uio/uiostream.c
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "uioport.h"
+#include "iointrn.h"
+#include "uiostream.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "uioutils.h"
+#include "utils.h"
+#ifdef uio_MEM_DEBUG
+# include "memdebug.h"
+#endif
+
+#define uio_Stream_BLOCK_SIZE 1024
+
+static inline uio_Stream *uio_Stream_new(uio_Handle *handle, int openFlags);
+static inline void uio_Stream_delete(uio_Stream *stream);
+static inline uio_Stream *uio_Stream_alloc(void);
+static inline void uio_Stream_free(uio_Stream *stream);
+#ifdef NDEBUG
+# define uio_assertReadSanity(stream)
+# define uio_assertWriteSanity(stream)
+#else
+static void uio_assertReadSanity(uio_Stream *stream);
+static void uio_assertWriteSanity(uio_Stream *stream);
+#endif
+static int uio_Stream_fillReadBuffer(uio_Stream *stream);
+static int uio_Stream_flushWriteBuffer(uio_Stream *stream);
+static void uio_Stream_discardReadBuffer(uio_Stream *stream);
+
+
+uio_Stream *
+uio_fopen(uio_DirHandle *dir, const char *path, const char *mode) {
+ int openFlags;
+ uio_Handle *handle;
+ uio_Stream *stream;
+ int i;
+
+ switch (*mode) {
+ case 'r':
+ openFlags = O_RDONLY;
+ break;
+ case 'w':
+ openFlags = O_WRONLY | O_CREAT | O_TRUNC;
+ break;
+ case 'a':
+ openFlags = O_WRONLY| O_CREAT | O_APPEND;
+ default:
+ errno = EINVAL;
+ fprintf(stderr, "Invalid mode string in call to uio_fopen().\n");
+ return NULL;
+ }
+ mode++;
+
+ // C'89 says 'b' may either be the second or the third character.
+ // If someone specifies both 'b' and 't', he/she is out of luck.
+ i = 2;
+ while (i-- && (*mode != '\0')) {
+ switch (*mode) {
+ case 'b':
+#ifdef WIN32
+ openFlags |= O_BINARY;
+#endif
+ break;
+ case 't':
+#ifdef WIN32
+ openFlags |= O_TEXT;
+#endif
+ break;
+ case '+':
+ openFlags = (openFlags & ~O_ACCMODE) | O_RDWR;
+ break;
+ default:
+ i = 0;
+ // leave the while loop
+ break;
+ }
+ mode++;
+ }
+
+ // Any characters in the mode string that might follow are ignored.
+
+ handle = uio_open(dir, path, openFlags, S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+ if (handle == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ stream = uio_Stream_new(handle, openFlags);
+ return stream;
+}
+
+int
+uio_fclose(uio_Stream *stream) {
+ if (stream->operation == uio_StreamOperation_write)
+ uio_Stream_flushWriteBuffer(stream);
+ uio_close(stream->handle);
+ uio_Stream_delete(stream);
+ return 0;
+}
+
+// "The file position indicator for the stream (if defined) is advanced by
+// the number of characters successfully read. If an error occurs, the
+// resulting value of the file position indicator for the stream is
+// indeterminate. If a partial element is read, its value is
+// indeterminate." (from POSIX for fread()).
+size_t
+uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
+ size_t bytesToRead;
+ size_t bytesRead;
+
+ bytesToRead = size * nmemb;
+ bytesRead = 0;
+
+ uio_assertReadSanity(stream);
+ stream->operation = uio_StreamOperation_read;
+
+ if (stream->dataEnd > stream->dataStart) {
+ // First use what's in the buffer.
+ size_t numRead;
+
+ numRead = minu(stream->dataEnd - stream->dataStart, bytesToRead);
+ memcpy(buf, stream->dataStart, numRead);
+ buf = (void *) ((char *) buf + numRead);
+ stream->dataStart += numRead;
+ bytesToRead -= numRead;
+ bytesRead += numRead;
+ }
+ if (bytesToRead == 0) {
+ // Done already
+ return nmemb;
+ }
+
+ {
+ // Read the rest directly into the caller's buffer.
+ ssize_t numRead;
+ numRead = uio_read(stream->handle, buf, bytesToRead);
+ if (numRead == -1) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ goto out;
+ }
+ bytesRead += numRead;
+ if ((size_t) numRead < bytesToRead) {
+ // End of file
+ stream->status = uio_Stream_STATUS_EOF;
+ stream->operation = uio_StreamOperation_none;
+ goto out;
+ }
+ }
+
+out:
+ if (bytesToRead == 0)
+ return nmemb;
+ return bytesRead / size;
+}
+
+char *
+uio_fgets(char *s, int size, uio_Stream *stream) {
+ int orgSize;
+ char *buf;
+
+ uio_assertReadSanity(stream);
+ stream->operation = uio_StreamOperation_read;
+
+ size--;
+ orgSize = size;
+ buf = s;
+ while (size > 0) {
+ size_t maxRead;
+ const char *newLinePos;
+
+ // Fill buffer if empty.
+ if (stream->dataStart == stream->dataEnd) {
+ if (uio_Stream_fillReadBuffer(stream) == -1) {
+ // errno is set
+ stream->status = uio_Stream_STATUS_ERROR;
+ return NULL;
+ }
+ if (stream->dataStart == stream->dataEnd) {
+ // End-of-file
+ stream->status = uio_Stream_STATUS_EOF;
+ stream->operation = uio_StreamOperation_none;
+ if (size == orgSize) {
+ // Nothing was read.
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ // Search in buffer
+ maxRead = minu(stream->dataEnd - stream->dataStart, size);
+ newLinePos = memchr(stream->dataStart, '\n', maxRead);
+ if (newLinePos != NULL) {
+ // Newline found.
+ maxRead = newLinePos + 1 - stream->dataStart;
+ memcpy(buf, stream->dataStart, maxRead);
+ stream->dataStart += maxRead;
+ buf[maxRead] = '\0';
+ return buf;
+ }
+ // No newline present.
+ memcpy(buf, stream->dataStart, maxRead);
+ stream->dataStart += maxRead;
+ buf += maxRead;
+ size -= maxRead;
+ }
+
+ *buf = '\0';
+ return s;
+}
+
+int
+uio_fgetc(uio_Stream *stream) {
+ int result;
+
+ uio_assertReadSanity(stream);
+ stream->operation = uio_StreamOperation_read;
+
+ if (stream->dataStart == stream->dataEnd) {
+ // Buffer is empty
+ if (uio_Stream_fillReadBuffer(stream) == -1) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ return (int) EOF;
+ }
+ if (stream->dataStart == stream->dataEnd) {
+ // End-of-file
+ stream->status = uio_Stream_STATUS_EOF;
+ stream->operation = uio_StreamOperation_none;
+ return (int) EOF;
+ }
+ }
+
+ result = (int) *((unsigned char *) stream->dataStart);
+ stream->dataStart++;
+ return result;
+}
+
+// Only one character pushback is guaranteed, just like with stdio ungetc().
+int
+uio_ungetc(int c, uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
+ assert(c >= 0 && c <= 255);
+
+ return (int) EOF;
+ // not implemented
+// return c;
+}
+
+// NB. POSIX allows errno to be set for vsprintf(), but does not require it:
+// "The value of errno may be set to nonzero by a library function call
+// whether or not there is an error, provided the use of errno is not
+// documented in the description of the function in this International
+// Standard." The latter is the case for vsprintf().
+int
+uio_vfprintf(uio_Stream *stream, const char *format, va_list args) {
+ // This could be done faster, but going through snprintf() is easiest,
+ // and is fast enough for now.
+ char *buf;
+ int putResult;
+ int savedErrno;
+
+ buf = uio_vasprintf(format, args);
+ if (buf == NULL) {
+ // errno may or may not be set
+ return -1;
+ }
+
+ putResult = uio_fputs(buf, stream);
+ savedErrno = errno;
+
+ uio_free(buf);
+
+ errno = savedErrno;
+ return putResult;
+}
+
+int
+uio_fprintf(uio_Stream *stream, const char *format, ...) {
+ va_list args;
+ int result;
+
+ va_start(args, format);
+ result = uio_vfprintf(stream, format, args);
+ va_end(args);
+
+ return result;
+}
+
+int
+uio_fputc(int c, uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
+ assert(c >= 0 && c <= 255);
+
+ uio_assertWriteSanity(stream);
+ stream->operation = uio_StreamOperation_write;
+
+ if (stream->dataEnd == stream->bufEnd) {
+ // The buffer is full. Flush it out.
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ // Error status (for ferror()) is set.
+ return EOF;
+ }
+ }
+
+ *(unsigned char *) stream->dataEnd = (unsigned char) c;
+ stream->dataEnd++;
+ return c;
+}
+
+int
+uio_fputs(const char *s, uio_Stream *stream) {
+ int result;
+
+ result = uio_fwrite(s, strlen(s), 1, stream);
+ if (result != 1)
+ return EOF;
+ return 0;
+}
+
+int
+uio_fseek(uio_Stream *stream, long offset, int whence) {
+ int newPos;
+
+ if (stream->operation == uio_StreamOperation_read) {
+ uio_Stream_discardReadBuffer(stream);
+ } else if (stream->operation == uio_StreamOperation_write) {
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+ assert(stream->dataStart == stream->buf);
+ assert(stream->dataEnd == stream->buf);
+ stream->operation = uio_StreamOperation_none;
+
+ newPos = uio_lseek(stream->handle, offset, whence);
+ if (newPos == -1) {
+ // errno is set
+ return -1;
+ }
+ stream->status = uio_Stream_STATUS_OK;
+ // Clear error or end-of-file flag.
+
+ return 0;
+}
+
+long
+uio_ftell(uio_Stream *stream) {
+ off_t newPos;
+
+ newPos = uio_lseek(stream->handle, 0, SEEK_CUR);
+ if (newPos == (off_t) -1) {
+ // errno is set
+ return (long) -1;
+ }
+
+ if (stream->operation == uio_StreamOperation_write) {
+ newPos += stream->dataEnd - stream->dataStart;
+ } else if (stream->operation == uio_StreamOperation_read) {
+ newPos -= stream->dataEnd - stream->dataStart;
+ }
+
+ return (long) newPos;
+}
+
+// If less that nmemb elements could be written, or an error occurs, the
+// file pointer is undefined. clearerr() followed by fseek() need to be
+// called before attempting to read or write again.
+// I don't have the C standard myself, but I suspect this is the official
+// behaviour for fread() and fwrite().
+size_t
+uio_fwrite(const void *buf, size_t size, size_t nmemb, uio_Stream *stream) {
+ ssize_t bytesToWrite;
+ ssize_t bytesWritten;
+
+ uio_assertWriteSanity(stream);
+ stream->operation = uio_StreamOperation_write;
+
+ // NB. If a file is opened in append mode, the file position indicator
+ // is moved to the end of the file before writing.
+ // We leave that up to the physical layer.
+
+ bytesToWrite = size * nmemb;
+ if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
+ // There's enough space in the write buffer to store everything.
+ memcpy(stream->dataEnd, buf, bytesToWrite);
+ stream->dataEnd += bytesToWrite;
+ return nmemb;
+ }
+
+ // Not enough space in the write buffer to write everything.
+ // Flush what's left in the write buffer first.
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ // Error status (for ferror()) is set.
+ return 0;
+ }
+
+ if (bytesToWrite < stream->bufEnd - stream->dataEnd) {
+ // The now empty write buffer is large enough to store everything.
+ memcpy(stream->dataEnd, buf, bytesToWrite);
+ stream->dataEnd += bytesToWrite;
+ return nmemb;
+ }
+
+ // There is more data to write than fits in the (empty) write buffer.
+ // The data is written directly, in its entirety, without going
+ // through the write buffer.
+ bytesWritten = uio_write(stream->handle, buf, bytesToWrite);
+ if (bytesWritten != bytesToWrite) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ if (bytesWritten == -1)
+ return 0;
+ }
+
+ if (bytesWritten == bytesToWrite)
+ return nmemb;
+ return (size_t) bytesWritten / size;
+}
+
+// NB: stdio fflush() accepts NULL to flush all streams. uio_flush() does
+// not.
+int
+uio_fflush(uio_Stream *stream) {
+ assert(stream != NULL);
+
+ if (stream->operation == uio_StreamOperation_write) {
+ if (uio_Stream_flushWriteBuffer(stream) == -1) {
+ // errno is set
+ return (int) EOF;
+ }
+ stream->operation = uio_StreamOperation_none;
+ }
+
+ return 0;
+}
+
+int
+uio_feof(uio_Stream *stream) {
+ return stream->status == uio_Stream_STATUS_EOF;
+}
+
+int
+uio_ferror(uio_Stream *stream) {
+ return stream->status == uio_Stream_STATUS_ERROR;
+}
+
+void
+uio_clearerr(uio_Stream *stream) {
+ stream->status = uio_Stream_STATUS_OK;
+}
+
+// Counterpart of fileno()
+uio_Handle *
+uio_streamHandle(uio_Stream *stream) {
+ return stream->handle;
+}
+
+#ifndef NDEBUG
+static void
+uio_assertReadSanity(uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_WRONLY);
+
+ if (stream->operation == uio_StreamOperation_write) {
+ // "[...] output shall not be directly followed by input without an
+ // intervening call to the fflush function or to a file positioning
+ // function (fseek, fsetpos, or rewind), and input shall not be
+ // directly followed by output without an intervening call to a file
+ // positioning function, unless the input operation encounters
+ // end-of-file." (POSIX, C)
+ fprintf(stderr, "Error: Reading on a file directly after writing, "
+ "without an intervening call to fflush() or a file "
+ "positioning function.\n");
+ abort();
+ }
+}
+#endif
+
+#ifndef NDEBUG
+static void
+uio_assertWriteSanity(uio_Stream *stream) {
+ assert((stream->openFlags & O_ACCMODE) != O_RDONLY);
+
+ if (stream->operation == uio_StreamOperation_read) {
+ // "[...] output shall not be directly followed by input without an
+ // intervening call to the fflush function or to a file positioning
+ // function (fseek, fsetpos, or rewind), and input shall not be
+ // directly followed by output without an intervening call to a file
+ // positioning function, unless the input operation encounters
+ // end-of-file." (POSIX, C)
+ fprintf(stderr, "Error: Writing on a file directly after reading, "
+ "without an intervening call to a file positioning "
+ "function.\n");
+ abort();
+ }
+ assert(stream->dataStart == stream->buf);
+}
+#endif
+
+static int
+uio_Stream_flushWriteBuffer(uio_Stream *stream) {
+ ssize_t bytesWritten;
+
+ assert(stream->operation == uio_StreamOperation_write);
+
+ bytesWritten = uio_write(stream->handle, stream->dataStart,
+ stream->dataEnd - stream->dataStart);
+ if (bytesWritten != stream->dataEnd - stream->dataStart) {
+ stream->status = uio_Stream_STATUS_ERROR;
+ return -1;
+ }
+ assert(stream->dataStart == stream->buf);
+ stream->dataEnd = stream->buf;
+
+ return 0;
+}
+
+static void
+uio_Stream_discardReadBuffer(uio_Stream *stream) {
+ assert(stream->operation == uio_StreamOperation_read);
+ stream->dataStart = stream->buf;
+ stream->dataEnd = stream->buf;
+ // TODO: when implementing pushback: throw away pushback buffer.
+}
+
+static int
+uio_Stream_fillReadBuffer(uio_Stream *stream) {
+ ssize_t numRead;
+
+ assert(stream->operation == uio_StreamOperation_read);
+
+ numRead = uio_read(stream->handle, stream->buf,
+ uio_Stream_BLOCK_SIZE);
+ if (numRead == -1)
+ return -1;
+ stream->dataStart = stream->buf;
+ stream->dataEnd = stream->buf + numRead;
+ return 0;
+}
+
+static inline uio_Stream *
+uio_Stream_new(uio_Handle *handle, int openFlags) {
+ uio_Stream *result;
+
+ result = uio_Stream_alloc();
+ result->handle = handle;
+ result->openFlags = openFlags;
+ result->status = uio_Stream_STATUS_OK;
+ result->operation = uio_StreamOperation_none;
+ result->buf = uio_malloc(uio_Stream_BLOCK_SIZE);
+ result->dataStart = result->buf;
+ result->dataEnd = result->buf;
+ result->bufEnd = result->buf + uio_Stream_BLOCK_SIZE;
+ return result;
+}
+
+static inline uio_Stream *
+uio_Stream_alloc(void) {
+ uio_Stream *result = uio_malloc(sizeof (uio_Stream));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(uio_Stream, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+uio_Stream_delete(uio_Stream *stream) {
+ uio_free(stream->buf);
+ uio_Stream_free(stream);
+}
+
+static inline void
+uio_Stream_free(uio_Stream *stream) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(uio_Stream, (void *) stream);
+#endif
+ uio_free(stream);
+}
+
diff --git a/src/libs/uio/uiostream.h b/src/libs/uio/uiostream.h
new file mode 100644
index 0000000..f65487e
--- /dev/null
+++ b/src/libs/uio/uiostream.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UIOSTREAM_H_
+#define LIBS_UIO_UIOSTREAM_H_
+
+
+typedef struct uio_Stream uio_Stream;
+
+#include "io.h"
+
+#include <stdarg.h>
+
+
+uio_Stream *uio_fopen(uio_DirHandle *dir, const char *path, const char *mode);
+int uio_fclose(uio_Stream *stream);
+size_t uio_fread(void *buf, size_t size, size_t nmemb, uio_Stream *stream);
+char *uio_fgets(char *buf, int size, uio_Stream *stream);
+int uio_fgetc(uio_Stream *stream);
+#define uio_getc uio_fgetc
+int uio_ungetc(int c, uio_Stream *stream);
+int uio_vfprintf(uio_Stream *stream, const char *format, va_list args);
+int uio_fprintf(uio_Stream *stream, const char *format, ...);
+int uio_fputc(int c, uio_Stream *stream);
+#define uio_putc uio_fputc
+int uio_fputs(const char *s, uio_Stream *stream);
+int uio_fseek(uio_Stream *stream, long offset, int whence);
+long uio_ftell(uio_Stream *stream);
+size_t uio_fwrite(const void *buf, size_t size, size_t nmemb,
+ uio_Stream *stream);
+int uio_fflush(uio_Stream *stream);
+int uio_feof(uio_Stream *stream);
+int uio_ferror(uio_Stream *stream);
+void uio_clearerr(uio_Stream *stream);
+uio_Handle *uio_streamHandle(uio_Stream *stream);
+
+
+/* *** Internal definitions follow *** */
+#ifdef uio_INTERNAL
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include "iointrn.h"
+
+typedef enum {
+ uio_StreamOperation_none,
+ uio_StreamOperation_read,
+ uio_StreamOperation_write
+} uio_StreamOperation;
+
+struct uio_Stream {
+ char *buf;
+ // Start of the buffer.
+ char *dataStart;
+ // Start of the part of the buffer that is in use.
+ char *dataEnd;
+ // Start of the unused part of the buffer.
+ char *bufEnd;
+ // End of the buffer.
+ // INV: buf <= dataStart <= dataEnd <= bufEnd
+ // INV: if 'operation == uio_StreamOperation_write' then buf == dataStart
+
+ uio_Handle *handle;
+ int status;
+#define uio_Stream_STATUS_OK 0
+#define uio_Stream_STATUS_EOF 1
+#define uio_Stream_STATUS_ERROR 2
+ uio_StreamOperation operation;
+ // What was the last action (reading or writing). This
+ // determines whether the buffer is a read or write buffer.
+ int openFlags;
+ // Flags used for opening the file.
+};
+
+
+#endif /* uio_INTERNAL */
+
+#endif /* LIBS_UIO_UIOSTREAM_H_ */
+
+
diff --git a/src/libs/uio/uioutils.c b/src/libs/uio/uioutils.c
new file mode 100644
index 0000000..fbe3cd4
--- /dev/null
+++ b/src/libs/uio/uioutils.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "uioutils.h"
+#include "mem.h"
+#include "paths.h"
+#include "uioport.h"
+
+/**
+ * Concatenate two strings into a newly allocated buffer.
+ *
+ * @param[in] first The first (left) string, '\0' terminated.
+ * @param[in] second The second (right) string, '\0' terminated.
+ *
+ * @returns A newly allocated string consisting of the concatenation of
+ * 'first' and 'second', to be freed using uio_free().
+ */
+char *
+strcata(const char *first, const char *second) {
+ char *result, *resPtr;
+ size_t firstLen, secondLen;
+
+ firstLen = strlen(first);
+ secondLen = strlen(second);
+ result = uio_malloc(firstLen + secondLen + 1);
+ resPtr = result;
+
+ memcpy(resPtr, first, firstLen);
+ resPtr += firstLen;
+
+ memcpy(resPtr, second, secondLen);
+ resPtr += secondLen;
+
+ *resPtr = '\0';
+ return result;
+}
+
+// returns a copy of a generic array 'array' with 'element' inserted in
+// position 'insertPos'
+void *
+insertArray(const void *array, size_t oldNumElements, int insertPos,
+ const void *element, size_t elementSize) {
+ void *newArray, *newArrayPtr;
+ const void *arrayPtr;
+ size_t preInsertSize;
+
+ newArray = uio_malloc((oldNumElements + 1) * elementSize);
+ preInsertSize = insertPos * elementSize;
+ memcpy(newArray, array, preInsertSize);
+ newArrayPtr = (char *) newArray + preInsertSize;
+ arrayPtr = (const char *) array + preInsertSize;
+ memcpy(newArrayPtr, element, elementSize);
+ newArrayPtr = (char *) newArrayPtr + elementSize;
+ memcpy(newArrayPtr, arrayPtr,
+ (oldNumElements - insertPos) * elementSize);
+ return newArray;
+}
+
+// returns a copy of a pointer array 'array' with 'element' inserted in
+// position 'insertPos'
+void **
+insertArrayPointer(const void **array, size_t oldNumElements, int insertPos,
+ const void *element) {
+ void **newArray, **newArrayPtr;
+ const void **arrayPtr;
+ size_t preInsertSize;
+
+ newArray = uio_malloc((oldNumElements + 1) * sizeof (void *));
+ preInsertSize = insertPos * sizeof (void *);
+ memcpy(newArray, array, preInsertSize);
+ newArrayPtr = newArray + insertPos;
+ arrayPtr = array + insertPos;
+ *newArrayPtr = unconst(element);
+ newArrayPtr++;
+ memcpy(newArrayPtr, arrayPtr,
+ (oldNumElements - insertPos) * sizeof (void *));
+ return newArray;
+}
+
+// returns a copy of a generic array 'array' with 'numExclude' elements,
+// starting from startpos, removed.
+void *
+excludeArray(const void *array, size_t oldNumElements, int startPos,
+ int numExclude, size_t elementSize) {
+ void *newArray, *newArrayPtr;
+ const void *arrayPtr;
+ size_t preExcludeSize;
+
+ newArray = uio_malloc((oldNumElements - numExclude) * elementSize);
+ preExcludeSize = startPos * elementSize;
+ memcpy(newArray, array, preExcludeSize);
+ newArrayPtr = (char *) newArray + preExcludeSize;
+ arrayPtr = (const char *) array +
+ (startPos + numExclude) * sizeof (elementSize);
+ memcpy(newArrayPtr, arrayPtr,
+ (oldNumElements - startPos - numExclude) * elementSize);
+ return newArray;
+}
+
+// returns a copy of a pointer array 'array' with 'numExclude' elements,
+// starting from startpos, removed.
+void **
+excludeArrayPointer(const void **array, size_t oldNumElements, int startPos,
+ int numExclude) {
+ void **newArray;
+
+ newArray = uio_malloc((oldNumElements - numExclude) * sizeof (void *));
+ memcpy(newArray, array, startPos * sizeof (void *));
+ memcpy(&newArray[startPos], &array[startPos + numExclude],
+ (oldNumElements - startPos - numExclude) * sizeof (void *));
+ return newArray;
+}
+
+// If the given DOS date/time is invalid, the result is unspecified,
+// but the function won't crash.
+time_t
+dosToUnixTime(uio_uint16 date, uio_uint16 tm) {
+ // DOS date has the following format:
+ // bits 0-4 specify the number of the day in the month (1-31).
+ // bits 5-8 specify the number of the month in the year (1-12).
+ // bits 9-15 specify the year number since 1980 (0-127)
+ // DOS time has the fillowing format:
+ // bits 0-4 specify the number of seconds/2 in the minute (0-29)
+ // (only accurate on 2 seconds)
+ // bits 5-10 specify the number of minutes in the hour (0-59)
+ // bits 11-15 specify the number of hours since midnight (0-23)
+
+ int year, month, day;
+ int hours, minutes, seconds;
+ long result;
+
+ static const int daysUntilMonth[] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+ 334, 334, 334, 334 };
+ // The last 4 entries are there so that there's no
+ // invalid memory access if the date is invalid.
+
+ year = date >> 9;
+ month = ((date >> 5) - 1) & 0x0f; // Number in [0..15]
+ day = (date - 1) & 0x1f; // Number in [0..31]
+ hours = tm >> 11;
+ minutes = (tm >> 5) & 0x3f;
+ seconds = (tm & 0x1f) * 2; // Even number in [0..62]
+
+ result = year * 365 + daysUntilMonth[month] + day;
+ // Count the (non-leap) days in all those years
+
+ // Add a leapday for each 4th year
+ if (year % 4 == 0 && month <= 2) {
+ // The given date is a leap-year but the leapday hasn't occured yet.
+ result += year / 4;
+ } else {
+ result += 1 + year / 4;
+ }
+ // result now is the number of days between 1980-01-01 and the given day.
+
+ // Add the days between 1970-01-01 and 1980-01-01
+ // (2 leapdays in this period)
+ result += 365 * 10 + 2;
+
+ result = (result * 24) + hours; // days to hours
+ result = (result * 60) + minutes; // hours to minutes
+ result = (result * 60) + seconds; // minutes to seconds
+
+ return (time_t) result;
+}
+
+char *
+dosToUnixPath(const char *path) {
+ const char *srcPtr;
+ char *result, *dstPtr;
+ size_t skip;
+
+ result = uio_malloc(strlen(path) + 1);
+ srcPtr = path;
+ dstPtr = result;
+
+ // A UNC path will look like this: "\\server\share/..."; the first two
+ // characters will be backslashes, and the separator between the server
+ // and the share too. The rest will be slashes.
+ // The goal is that at every forward slash, the path should be
+ // stat()'able.
+ skip = uio_skipUNCServerShare(srcPtr);
+ if (skip != 0) {
+ char *slash;
+ memcpy(dstPtr, srcPtr, skip);
+
+ slash = memchr(srcPtr + 2, '/', skip - 2);
+ if (slash != NULL)
+ *slash = '\\';
+
+ srcPtr += skip;
+ dstPtr += skip;
+ }
+
+ while (*srcPtr != '\0') {
+ if (*srcPtr == '\\') {
+ *dstPtr = '/';
+ } else
+ *dstPtr = *srcPtr;
+ srcPtr++;
+ dstPtr++;
+ }
+ *dstPtr = '\0';
+ return result;
+}
+
+
diff --git a/src/libs/uio/uioutils.h b/src/libs/uio/uioutils.h
new file mode 100644
index 0000000..6e04843
--- /dev/null
+++ b/src/libs/uio/uioutils.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UIOUTILS_H_
+#define LIBS_UIO_UIOUTILS_H_
+
+#include <time.h>
+
+#include "types.h"
+#include "uioport.h"
+
+char *strcata(const char *first, const char *second);
+void *insertArray(const void *array, size_t oldNumElements, int insertPos,
+ const void *element, size_t elementSize);
+void **insertArrayPointer(const void **array, size_t oldNumElements,
+ int insertPos, const void *element);
+void *excludeArray(const void *array, size_t oldNumElements, int startPos,
+ int numExclude, size_t elementSize);
+void **excludeArrayPointer(const void **array, size_t oldNumElements,
+ int startPos, int numExclude);
+time_t dosToUnixTime(uio_uint16 date, uio_uint16 tm);
+char *dosToUnixPath(const char *path);
+
+/* Sometimes you just have to remove a 'const'.
+ * (for instance, when implementing a function like strchr)
+ */
+static inline void *
+unconst(const void *arg) {
+ union {
+ void *c;
+ const void *cc;
+ } u;
+ u.cc = arg;
+ return u.c;
+}
+
+// byte1 is the lowest byte, byte4 the highest
+static inline uio_uint32
+makeUInt32(uio_uint8 byte1, uio_uint8 byte2, uio_uint8 byte3, uio_uint8 byte4) {
+ return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
+}
+
+static inline uio_uint16
+makeUInt16(uio_uint8 byte1, uio_uint8 byte2) {
+ return byte1 | (byte2 << 8);
+}
+
+static inline uio_sint32
+makeSInt32(uio_uint8 byte1, uio_uint8 byte2, uio_uint8 byte3, uio_uint8 byte4) {
+ return byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24);
+}
+
+static inline uio_sint16
+makeSInt16(uio_uint8 byte1, uio_uint8 byte2) {
+ return byte1 | (byte2 << 8);
+}
+
+static inline uio_bool
+isBitSet(uio_uint32 bitField, int bit) {
+ return ((bitField >> bit) & 1) == 1;
+}
+
+static inline int
+mins(int i1, int i2) {
+ return i1 <= i2 ? i1 : i2;
+}
+
+static inline unsigned int
+minu(unsigned int i1, unsigned int i2) {
+ return i1 <= i2 ? i1 : i2;
+}
+
+
+#endif /* LIBS_UIO_UIOUTILS_H_ */
+
diff --git a/src/libs/uio/utils.c b/src/libs/uio/utils.c
new file mode 100644
index 0000000..1f705bb
--- /dev/null
+++ b/src/libs/uio/utils.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <errno.h>
+#include <time.h>
+#include <stdio.h>
+#ifdef _MSC_VER
+# include <stdarg.h>
+#endif /* _MSC_VER */
+
+#include "iointrn.h"
+#include "ioaux.h"
+#include "utils.h"
+
+static int uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf);
+
+struct uio_StdioAccessHandle {
+ uio_DirHandle *tempRoot;
+ char *tempDirName;
+ uio_DirHandle *tempDir;
+ char *fileName;
+ char *stdioPath;
+};
+
+static inline uio_StdioAccessHandle *uio_StdioAccessHandle_new(
+ uio_DirHandle *tempRoot, char *tempDirName,
+ uio_DirHandle *tempDir, char *fileName,
+ char *stdioPath);
+static inline void uio_StdioAccessHandle_delete(
+ uio_StdioAccessHandle *handle);
+static inline uio_StdioAccessHandle *uio_StdioAccessHandle_alloc(void);
+static inline void uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle);
+
+/*
+ * Copy a file with path srcName to a file with name newName.
+ * If the destination already exists, the operation fails.
+ * Links are followed.
+ * Special files (fifos, char devices, block devices, etc) will be
+ * read as long as there is data available and the destination will be
+ * a regular file with that data.
+ * The new file will have the same permissions as the old.
+ * If an error occurs during copying, an attempt will be made to
+ * remove the copy.
+ */
+int
+uio_copyFile(uio_DirHandle *srcDir, const char *srcName,
+ uio_DirHandle *dstDir, const char *newName) {
+ uio_Handle *src, *dst;
+ struct stat sb;
+#define BUFSIZE 65536
+ uio_uint8 *buf, *bufPtr;
+ ssize_t numInBuf, numWritten;
+
+ src = uio_open(srcDir, srcName, O_RDONLY
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (src == NULL)
+ return -1;
+
+ if (uio_fstat(src, &sb) == -1)
+ return uio_copyError(src, NULL, NULL, NULL, NULL);
+
+ dst = uio_open(dstDir, newName, O_WRONLY | O_CREAT | O_EXCL
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (dst == NULL)
+ return uio_copyError(src, NULL, NULL, NULL, NULL);
+
+ buf = uio_malloc(BUFSIZE);
+ // This was originally a statically allocated buffer,
+ // but as this function might be run from a thread with
+ // a small Stack, this is better.
+ while (1) {
+ numInBuf = uio_read(src, buf, BUFSIZE);
+ if (numInBuf == -1) {
+ if (errno == EINTR)
+ continue;
+ return uio_copyError(src, dst, dstDir, newName, buf);
+ }
+ if (numInBuf == 0)
+ break;
+
+ bufPtr = buf;
+ do
+ {
+ numWritten = uio_write(dst, bufPtr, numInBuf);
+ if (numWritten == -1) {
+ if (errno == EINTR)
+ continue;
+ return uio_copyError(src, dst, dstDir, newName, buf);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ } while (numInBuf > 0);
+ }
+
+ uio_free(buf);
+ uio_close(src);
+ uio_close(dst);
+ errno = 0;
+ return 0;
+}
+
+/*
+ * Closes srcHandle if it's not -1.
+ * Closes dstHandle if it's not -1.
+ * Removes unlinkpath from the unlinkHandle dir if it's not NULL.
+ * Frees 'buf' if not NULL.
+ * Always returns -1.
+ * errno is what was before the call.
+ */
+static int
+uio_copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uio_uint8 *buf) {
+ int savedErrno;
+
+ savedErrno = errno;
+
+#ifdef DEBUG
+ fprintf(stderr, "Error while copying: %s\n", strerror(errno));
+#endif
+
+ if (srcHandle != NULL)
+ uio_close(srcHandle);
+
+ if (dstHandle != NULL)
+ uio_close(dstHandle);
+
+ if (unlinkPath != NULL)
+ uio_unlink(unlinkHandle, unlinkPath);
+
+ if (buf != NULL)
+ uio_free(buf);
+
+ errno = savedErrno;
+ return -1;
+}
+
+#define NUM_TEMP_RETRIES 16
+ // Retry this many times to create a temporary dir, before giving
+ // up. If undefined, keep trying indefinately.
+
+uio_StdioAccessHandle *
+uio_getStdioAccess(uio_DirHandle *dir, const char *path, int flags,
+ uio_DirHandle *tempDir) {
+ int res;
+ uio_MountHandle *mountHandle;
+ const char *name;
+ char *newPath;
+ char *tempDirName;
+ uio_DirHandle *newDir;
+ uio_FileSystemID fsID;
+
+ res = uio_getFileLocation(dir, path, flags, &mountHandle, &newPath);
+ if (res == -1) {
+ // errno is set
+ return NULL;
+ }
+
+ fsID = uio_getMountFileSystemType(mountHandle);
+ if (fsID == uio_FSTYPE_STDIO) {
+ // Current location is usable.
+ return uio_StdioAccessHandle_new(NULL, NULL, NULL, NULL, newPath);
+ }
+ uio_free(newPath);
+
+ {
+ uio_uint32 dirNum;
+ int i;
+
+ // Current location is not usable. Create a directory with a
+ // generated name, as a temporary location to store a copy of
+ // the file.
+ dirNum = (uio_uint32) time(NULL);
+ tempDirName = uio_malloc(sizeof "01234567");
+ for (i = 0; ; i++) {
+#ifdef NUM_TEMP_RETRIES
+ if (i >= NUM_TEMP_RETRIES) {
+ // Using ENOSPC to report that we couldn't create a
+ // temporary dir, getting EEXIST.
+ uio_free(tempDirName);
+ errno = ENOSPC;
+ return NULL;
+ }
+#endif
+
+ sprintf(tempDirName, "%08lx", (unsigned long) dirNum + i);
+
+ res = uio_mkdir(tempDir, tempDirName, 0700);
+ if (res == -1) {
+ int savedErrno;
+ if (errno == EEXIST)
+ continue;
+ savedErrno = errno;
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not create temporary dir: %s\n",
+ strerror(errno));
+#endif
+ uio_free(tempDirName);
+ errno = savedErrno;
+ return NULL;
+ }
+ break;
+ }
+
+ newDir = uio_openDirRelative(tempDir, tempDirName, 0);
+ if (newDir == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not open temporary dir: %s\n",
+ strerror(errno));
+#endif
+ res = uio_rmdir(tempDir, tempDirName);
+#ifdef DEBUG
+ if (res == -1)
+ fprintf(stderr, "Warning: Could not remove temporary dir: "
+ "%s.\n", strerror(errno));
+#endif
+ uio_free(tempDirName);
+ errno = EIO;
+ return NULL;
+ }
+
+ // Get the last component of path. This should be the file to
+ // access.
+ name = strrchr(path, '/');
+ if (name == NULL)
+ name = path;
+
+ // Copy the file
+ res = uio_copyFile(dir, path, newDir, name);
+ if (res == -1) {
+ int savedErrno = errno;
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not copy file to temporary dir: "
+ "%s\n", strerror(errno));
+#endif
+ uio_closeDir(newDir);
+ uio_free(tempDirName);
+ errno = savedErrno;
+ return NULL;
+ }
+ }
+
+ res = uio_getFileLocation(newDir, name, flags, &mountHandle, &newPath);
+ if (res == -1) {
+ int savedErrno = errno;
+ fprintf(stderr, "Error: uio_getStdioAccess: Could not get location "
+ "of temporary dir: %s.\n", strerror(errno));
+ uio_closeDir(newDir);
+ uio_free(tempDirName);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ fsID = uio_getMountFileSystemType(mountHandle);
+ if (fsID != uio_FSTYPE_STDIO) {
+ // Temp dir isn't on a stdio fs either.
+ fprintf(stderr, "Error: uio_getStdioAccess: Temporary file location "
+ "isn't on a stdio filesystem.\n");
+ uio_closeDir(newDir);
+ uio_free(tempDirName);
+ uio_free(newPath);
+// errno = EXDEV;
+ errno = EINVAL;
+ return NULL;
+ }
+
+ uio_DirHandle_ref(tempDir);
+ return uio_StdioAccessHandle_new(tempDir, tempDirName, newDir,
+ uio_strdup(name), newPath);
+}
+
+void
+uio_releaseStdioAccess(uio_StdioAccessHandle *handle) {
+ if (handle->tempDir != NULL) {
+ if (uio_unlink(handle->tempDir, handle->fileName) == -1) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not remove temporary file: "
+ "%s\n", strerror(errno));
+#endif
+ }
+
+ // Need to free this handle in advance. There should be no handles
+ // to a dir left when removing it.
+ uio_DirHandle_unref(handle->tempDir);
+ handle->tempDir = NULL;
+
+ if (uio_rmdir(handle->tempRoot, handle->tempDirName) == -1) {
+#ifdef DEBUG
+ fprintf(stderr, "Error: Could not remove temporary directory: "
+ "%s\n", strerror(errno));
+#endif
+ }
+ }
+
+ uio_StdioAccessHandle_delete(handle);
+}
+
+const char *
+uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle) {
+ return (const char *) handle->stdioPath;
+}
+
+// references to tempRoot and tempDir are not increased.
+// no copies of arguments are made.
+// By calling this function control of the values is transfered to
+// the handle.
+static inline uio_StdioAccessHandle *
+uio_StdioAccessHandle_new(
+ uio_DirHandle *tempRoot, char *tempDirName,
+ uio_DirHandle *tempDir, char *fileName, char *stdioPath) {
+ uio_StdioAccessHandle *result;
+
+ result = uio_StdioAccessHandle_alloc();
+ result->tempRoot = tempRoot;
+ result->tempDirName = tempDirName;
+ result->tempDir = tempDir;
+ result->fileName = fileName;
+ result->stdioPath = stdioPath;
+
+ return result;
+}
+
+static inline void
+uio_StdioAccessHandle_delete(uio_StdioAccessHandle *handle) {
+ if (handle->tempDir != NULL)
+ uio_DirHandle_unref(handle->tempDir);
+ if (handle->fileName != NULL)
+ uio_free(handle->fileName);
+ if (handle->tempRoot != NULL)
+ uio_DirHandle_unref(handle->tempRoot);
+ if (handle->tempDirName != NULL)
+ uio_free(handle->tempDirName);
+ uio_free(handle->stdioPath);
+ uio_StdioAccessHandle_free(handle);
+}
+
+static inline uio_StdioAccessHandle *
+uio_StdioAccessHandle_alloc(void) {
+ return uio_malloc(sizeof (uio_StdioAccessHandle));
+}
+
+static inline void
+uio_StdioAccessHandle_free(uio_StdioAccessHandle *handle) {
+ uio_free(handle);
+}
+
+#ifdef _MSC_VER
+# include <stdarg.h>
+#if 0 /* Unneeded for now */
+// MSVC does not have snprintf(). It does have a _snprintf(), but it does
+// not \0-terminate a truncated string as the C standard prescribes.
+static inline int
+snprintf(char *str, size_t size, const char *format, ...)
+{
+ int result;
+ va_list args;
+
+ va_start (args, format);
+ result = _vsnprintf (str, size, format, args);
+ if (str != NULL && size != 0)
+ str[size - 1] = '\0';
+ va_end (args);
+
+ return result;
+}
+#endif
+
+// MSVC does not have vsnprintf(). It does have a _vsnprintf(), but it does
+// not \0-terminate a truncated string as the C standard prescribes.
+static inline int
+vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+ int result = _vsnprintf (str, size, format, args);
+ if (str != NULL && size != 0)
+ str[size - 1] = '\0';
+ return result;
+}
+#endif /* _MSC_VER */
+
+// The result should be freed using uio_free().
+// NB. POSIX allows errno to be set for vsprintf(), but does not require it:
+// "The value of errno may be set to nonzero by a library function call
+// whether or not there is an error, provided the use of errno is not
+// documented in the description of the function in this International
+// Standard." The latter is the case for vsprintf().
+char *
+uio_vasprintf(const char *format, va_list args) {
+ // TODO: If there is a system vasprintf, use that.
+ // XXX: That would mean that the allocation would always go through
+ // malloc() or so, instead of uio_malloc(), which may not be
+ // desirable.
+
+ char *buf;
+ size_t bufSize = 128;
+ // Start with enough for one screen line, and a power of 2,
+ // which might give faster result with allocations.
+
+ buf = uio_malloc(bufSize);
+ if (buf == NULL) {
+ // errno is set.
+ return NULL;
+ }
+
+ for (;;) {
+ int printResult = vsnprintf(buf, bufSize, format, args);
+ if (printResult < 0) {
+ // This means the buffer was not large enough, but vsnprintf()
+ // does not give us any clue on how large it should be.
+ // Note that this does not happen with a C'99 compliant
+ // vsnprintf(), but it will happen on MS Windows, and on
+ // glibc before version 2.1.
+ bufSize *= 2;
+ } else if ((unsigned int) printResult >= bufSize) {
+ // The buffer was too small, but printResult contains the size
+ // that the buffer needs to be (excluding the '\0' character).
+ bufSize = printResult + 1;
+ } else {
+ // Success.
+ if ((unsigned int) printResult + 1 != bufSize) {
+ // Shorten the resulting buffer to the size that was
+ // actually needed.
+ char *newBuf = uio_realloc(buf, printResult + 1);
+ if (newBuf == NULL) {
+ // We could have returned the (overly large) original
+ // buffer, but the unused memory might not be
+ // acceptable, and the program would be likely to run
+ // into problems sooner or later anyhow.
+ int savedErrno = errno;
+ uio_free(buf);
+ errno = savedErrno;
+ return NULL;
+ }
+ return newBuf;
+ }
+
+ return buf;
+ }
+
+ {
+ char *newBuf = uio_realloc(buf, bufSize);
+ if (newBuf == NULL)
+ {
+ int savedErrno = errno;
+ uio_free(buf);
+ errno = savedErrno;
+ return NULL;
+ }
+ buf = newBuf;
+ }
+ }
+}
+
+// As uio_vasprintf(), but with an argument list.
+char *
+uio_asprintf(const char *format, ...) {
+ // TODO: If there is a system asprintf, use that.
+ // XXX: That would mean that the allocation would always go through
+ // malloc() or so, instead of uio_malloc(), which may not be
+ // desirable.
+
+ va_list args;
+ char *result;
+ int savedErrno;
+
+ va_start(args, format);
+ result = uio_vasprintf(format, args);
+ savedErrno = errno;
+ va_end(args);
+
+ errno = savedErrno;
+ return result;
+}
+
+
diff --git a/src/libs/uio/utils.h b/src/libs/uio/utils.h
new file mode 100644
index 0000000..313bf67
--- /dev/null
+++ b/src/libs/uio/utils.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIO_UTILS_H_
+#define LIBS_UIO_UTILS_H_
+
+#include <stdarg.h>
+
+#ifdef uio_INTERNAL
+typedef struct uio_StdioAccessHandle uio_StdioAccessHandle;
+#else
+typedef void uio_StdioAccessHandle;
+#endif
+
+int uio_copyFile(uio_DirHandle *srcDir, const char *srcName,
+ uio_DirHandle *dstDir, const char *newName);
+uio_StdioAccessHandle *uio_getStdioAccess(uio_DirHandle *dir,
+ const char *path, int flags, uio_DirHandle *tempDir);
+const char *uio_StdioAccessHandle_getPath(uio_StdioAccessHandle *handle);
+void uio_releaseStdioAccess(uio_StdioAccessHandle *handle);
+
+char *uio_vasprintf(const char *format, va_list args);
+char *uio_asprintf(const char *format, ...);
+
+#endif /* LIBS_UIO_UTILS_H_ */
+
diff --git a/src/libs/uio/zip/Makeinfo b/src/libs/uio/zip/Makeinfo
new file mode 100644
index 0000000..64ae8d5
--- /dev/null
+++ b/src/libs/uio/zip/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="zip.c"
+uqm_HFILES="zip.h"
diff --git a/src/libs/uio/zip/zip.c b/src/libs/uio/zip/zip.c
new file mode 100644
index 0000000..6da462b
--- /dev/null
+++ b/src/libs/uio/zip/zip.c
@@ -0,0 +1,1680 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * This file makes use of zlib (http://www.gzip.org/zlib/)
+ *
+ * References:
+ * The .zip format description from PKWare:
+ * http://www.pkware.com/products/enterprise/white_papers/appnote.html
+ * The .zip format description from InfoZip:
+ * ftp://ftp.info-zip.org/pub/infozip/doc/appnote-011203-iz.zip
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include "zip.h"
+#include "../physical.h"
+#include "../uioport.h"
+#include "../paths.h"
+#include "../uioutils.h"
+#ifdef uio_MEM_DEBUG
+# include "../memdebug.h"
+#endif
+
+
+#define DIR_STRUCTURE_READ_BUFSIZE 0x10000
+
+static int zip_badFile(zip_GPFileData *gPFileData, char *fileName);
+static int zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle);
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+static int zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle);
+static int zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos);
+#endif
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static off_t zip_findEndOfCentralDirectoryRecord(uio_Handle *handle,
+ uio_FileBlock *fileBlock);
+static int zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle);
+static int zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos);
+static int zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData,
+ uio_FileBlock *fileBlock, int pos);
+int zip_updateFileDataFromLocalHeader(uio_Handle *handle,
+ zip_GPFileData *gPFileData);
+#endif
+static int zip_fillDirStructureProcessExtraFields(
+ uio_FileBlock *fileBlock, off_t extraFieldLength,
+ zip_GPFileData *gPFileData, const char *path, off_t pos,
+ uio_bool central);
+static inline int zip_foundFile(uio_GPDir *gPDir, const char *path,
+ zip_GPFileData *gPFileData);
+static inline int zip_foundDir(uio_GPDir *gPDir, const char *dirName,
+ zip_GPDirData *gPDirData);
+static int zip_initZipStream(z_stream *zipStream);
+static int zip_unInitZipStream(z_stream *zipStream);
+static int zip_reInitZipStream(z_stream *zipStream);
+
+static voidpf zip_alloc(voidpf opaque, uInt items, uInt size);
+static void zip_free(voidpf opaque, voidpf address);
+
+static inline zip_GPFileData * zip_GPFileData_new(void);
+static inline void zip_GPFileData_delete(zip_GPFileData *gPFileData);
+static inline zip_GPFileData *zip_GPFileData_alloc(void);
+static inline void zip_GPFileData_free(zip_GPFileData *gPFileData);
+static inline void zip_GPDirData_delete(zip_GPDirData *gPDirData);
+static inline void zip_GPDirData_free(zip_GPDirData *gPDirData);
+
+static ssize_t zip_readStored(uio_Handle *handle, void *buf, size_t count);
+static ssize_t zip_readDeflated(uio_Handle *handle, void *buf, size_t count);
+static off_t zip_seekStored(uio_Handle *handle, off_t offset);
+static off_t zip_seekDeflated(uio_Handle *handle, off_t offset);
+
+uio_FileSystemHandler zip_fileSystemHandler = {
+ /* .init = */ NULL,
+ /* .unInit = */ NULL,
+ /* .cleanup = */ NULL,
+
+ /* .mount = */ zip_mount,
+ /* .umount = */ uio_GPRoot_umount,
+
+ /* .access = */ zip_access,
+ /* .close = */ zip_close,
+ /* .fstat = */ zip_fstat,
+ /* .stat = */ zip_stat,
+ /* .mkdir = */ NULL,
+ /* .open = */ zip_open,
+ /* .read = */ zip_read,
+ /* .rename = */ NULL,
+ /* .rmdir = */ NULL,
+ /* .seek = */ zip_seek,
+ /* .write = */ NULL,
+ /* .unlink = */ NULL,
+
+ /* .openEntries = */ uio_GPDir_openEntries,
+ /* .readEntries = */ uio_GPDir_readEntries,
+ /* .closeEntries = */ uio_GPDir_closeEntries,
+
+ /* .getPDirEntryHandle = */ uio_GPDir_getPDirEntryHandle,
+ /* .deletePRootExtra = */ uio_GPRoot_delete,
+ /* .deletePDirHandleExtra = */ uio_GPDirHandle_delete,
+ /* .deletePFileHandleExtra = */ uio_GPFileHandle_delete,
+};
+
+uio_GPRoot_Operations zip_GPRootOperations = {
+ /* .fillGPDir = */ NULL,
+ /* .deleteGPRootExtra = */ NULL,
+ /* .deleteGPDirExtra = */ zip_GPDirData_delete,
+ /* .deleteGPFileExtra = */ zip_GPFileData_delete,
+};
+
+
+#define NUM_COMPRESSION_METHODS 11
+/*
+ * [0] = stored uncompressed
+ * [1] = Shrunk
+ * [2] = Reduced with compression factor 1
+ * [3] = Reduced with compression factor 2
+ * [4] = Reduced with compression factor 3
+ * [5] = Reduced with compression factor 4
+ * [6] = Imploded
+ * [7] = Reserved for Tokenizing
+ * [8] = Deflated
+ * [9] = Deflate64
+ * [10] = "PKWARE Data Compression Library Imploding"
+ */
+static const uio_bool
+ zip_compressionMethodSupported[NUM_COMPRESSION_METHODS] = {
+ true, false, false, false, false, false, false, false,
+ true, false, false };
+
+typedef ssize_t (*zip_readFunctionType)(uio_Handle *handle,
+ void *buf, size_t count);
+zip_readFunctionType zip_readMethods[NUM_COMPRESSION_METHODS] = {
+ zip_readStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ zip_readDeflated, NULL, NULL
+};
+typedef off_t (*zip_seekFunctionType)(uio_Handle *handle, off_t offset);
+zip_seekFunctionType zip_seekMethods[NUM_COMPRESSION_METHODS] = {
+ zip_seekStored, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ zip_seekDeflated, NULL, NULL
+};
+
+
+typedef enum {
+ zip_OSType_FAT,
+ zip_OSType_Amiga,
+ zip_OSType_OpenVMS,
+ zip_OSType_UNIX,
+ zip_OSType_VMCMS,
+ zip_OSType_AtariST,
+ zip_OSType_HPFS,
+ zip_OSType_HFS,
+ zip_OSType_ZSystem,
+ zip_OSType_CPM,
+ zip_OSType_TOPS20,
+ zip_OSType_NTFS,
+ zip_OSType_QDOS,
+ zip_OSType_Acorn,
+ zip_OSType_VFAT,
+ zip_OSType_MVS,
+ zip_OSType_BeOS,
+ zip_OSType_Tandem,
+
+ zip_numOSTypes
+} zip_OSType;
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static mode_t zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes);
+#endif
+
+#define zip_INPUT_BUFFER_SIZE 0x10000
+ // TODO: make this configurable a la sysctl?
+#define zip_SEEK_BUFFER_SIZE zip_INPUT_BUFFER_SIZE
+
+
+void
+zip_close(uio_Handle *handle) {
+ zip_Handle *zip_handle;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_close - handle=%p\n", (void *) handle);
+#endif
+ zip_handle = handle->native;
+ uio_GPFile_unref(zip_handle->file);
+ zip_unInitZipStream(&zip_handle->zipStream);
+ uio_closeFileBlock(zip_handle->fileBlock);
+ uio_free(zip_handle);
+}
+
+static void
+zip_fillStat(struct stat *statBuf, const zip_GPFileData *gPFileData) {
+ memset(statBuf, '\0', sizeof (struct stat));
+ statBuf->st_size = gPFileData->uncompressedSize;
+ statBuf->st_uid = gPFileData->uid;
+ statBuf->st_gid = gPFileData->gid;
+ statBuf->st_mode = gPFileData->mode;
+ statBuf->st_atime = gPFileData->atime;
+ statBuf->st_mtime = gPFileData->mtime;
+ statBuf->st_ctime = gPFileData->ctime;
+}
+
+int
+zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode) {
+ errno = ENOSYS; // Not implemented.
+ (void) pDirHandle;
+ (void) name;
+ (void) mode;
+ return -1;
+
+#if 0
+ uio_GPDirEntry *entry;
+
+ if (name[0] == '.' && name[1] == '\0') {
+ entry = (uio_GPDirEntry *) pDirHandle->extra;
+ } else {
+ entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (entry == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (mode & R_OK)
+ {
+ // Read permission is always granted. Nothing to check here.
+ }
+
+ if (mode & W_OK) {
+ errno = EACCES;
+ return -1;
+ }
+
+ if (mode & X_OK) {
+ if (uio_GPDirEntry_isDir(entry)) {
+ // Search permission on directories is always granted.
+ } else {
+ // WORK
+#error
+ }
+ }
+
+ if (mode & F_OK) {
+ // WORK
+#error
+ }
+
+ return 0;
+#endif
+}
+
+int
+zip_fstat(uio_Handle *handle, struct stat *statBuf) {
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ if (handle->native->file->extra->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(handle->root->handle,
+ handle->native->file->extra) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+ zip_fillStat(statBuf, handle->native->file->extra);
+ return 0;
+}
+
+int
+zip_stat(uio_PDirHandle *pDirHandle, const char *name, struct stat *statBuf) {
+ uio_GPDirEntry *entry;
+
+ if (name[0] == '.' && name[1] == '\0') {
+ entry = (uio_GPDirEntry *) pDirHandle->extra;
+ } else {
+ entry = uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (entry == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (uio_GPDirEntry_isDir(entry) && entry->extra == NULL) {
+ // No information about this directory was stored.
+ // We'll have to make something up.
+ memset(statBuf, '\0', sizeof (struct stat));
+ statBuf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR |
+ S_IRGRP | S_IWGRP | S_IXOTH |
+ S_IROTH | S_IWOTH | S_IXOTH;
+ statBuf->st_uid = 0;
+ statBuf->st_gid = 0;
+ return 0;
+ }
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+#ifndef zip_INCOMPLETE_STAT
+ if (((zip_GPFileData *) entry->extra)->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle,
+ (zip_GPFileData *) entry->extra) == -1) {
+ // errno is set
+ return -1;
+ }
+ }
+#endif /* !defined(zip_INCOMPLETE_STAT) */
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+
+ zip_fillStat(statBuf, (zip_GPFileData *) entry->extra);
+ return 0;
+}
+
+/*
+ * Function name: zip_open
+ * Description: open a file in zip file
+ * Arguments: pDirHandle - handle to the dir where to open the file
+ * name - the name of the file to open
+ * flags - flags, as to stdio open()
+ * mode - mode, as to stdio open()
+ * Returns: handle, as from stdio open()
+ * If failed, errno is set and handle is -1.
+ */
+uio_Handle *
+zip_open(uio_PDirHandle *pDirHandle, const char *name, int flags,
+ mode_t mode) {
+ zip_Handle *handle;
+ uio_GPFile *gPFile;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_open - pDirHandle=%p name=%s flags=%d mode=0%o\n",
+ (void *) pDirHandle, name, flags, mode);
+#endif
+
+ if ((flags & O_ACCMODE) != O_RDONLY) {
+ errno = EACCES;
+ return NULL;
+ }
+
+ gPFile = (uio_GPFile *) uio_GPDir_getGPDirEntry(pDirHandle->extra, name);
+ if (gPFile == NULL) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ if (gPFile->extra->fileOffset == -1) {
+ // The local header wasn't read in yet.
+ if (zip_updateFileDataFromLocalHeader(pDirHandle->pRoot->handle,
+ gPFile->extra) == -1) {
+ // errno is set
+ return NULL;
+ }
+ }
+#endif
+
+ handle = uio_malloc(sizeof (zip_Handle));
+ uio_GPFile_ref(gPFile);
+ handle->file = gPFile;
+ handle->fileBlock = uio_openFileBlock2(pDirHandle->pRoot->handle,
+ gPFile->extra->fileOffset, gPFile->extra->compressedSize);
+ if (handle->fileBlock == NULL) {
+ // errno is set
+ return NULL;
+ }
+
+ if (zip_initZipStream(&handle->zipStream) == -1) {
+ uio_GPFile_unref(gPFile);
+ uio_closeFileBlock(handle->fileBlock);
+ return NULL;
+ }
+ handle->compressedOffset = 0;
+ handle->uncompressedOffset = 0;
+
+ (void) mode;
+ return uio_Handle_new(pDirHandle->pRoot, handle, flags);
+}
+
+ssize_t
+zip_read(uio_Handle *handle, void *buf, size_t count) {
+ ssize_t result;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_read - handle=%p buf=%p count=%d: ", (void *) handle,
+ (void *) buf, count);
+#endif
+ result = zip_readMethods[handle->native->file->extra->compressionMethod]
+ (handle, buf, count);
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "%d\n", result);
+#endif
+ return result;
+}
+
+static ssize_t
+zip_readStored(uio_Handle *handle, void *buf, size_t count) {
+ int numBytes;
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+ numBytes = uio_copyFileBlock(zipHandle->fileBlock,
+ zipHandle->uncompressedOffset, buf, count);
+ if (numBytes == -1) {
+ // errno is set
+ return -1;
+ }
+ zipHandle->uncompressedOffset += numBytes;
+ zipHandle->compressedOffset += numBytes;
+ return numBytes;
+}
+
+static ssize_t
+zip_readDeflated(uio_Handle *handle, void *buf, size_t count) {
+ zip_Handle *zipHandle;
+ int inflateResult;
+
+ zipHandle = handle->native;
+
+ if (count > ((zip_GPFileData *) (zipHandle->file->extra))->
+ uncompressedSize - zipHandle->zipStream.total_out)
+ count = ((zip_GPFileData *) (zipHandle->file->extra))->
+ uncompressedSize - zipHandle->zipStream.total_out;
+
+ zipHandle->zipStream.next_out = (Bytef *) buf;
+ zipHandle->zipStream.avail_out = count;
+ while (zipHandle->zipStream.avail_out > 0) {
+ if (zipHandle->zipStream.avail_in == 0) {
+ ssize_t numBytes;
+ numBytes = uio_accessFileBlock(zipHandle->fileBlock,
+ zipHandle->compressedOffset, zip_INPUT_BUFFER_SIZE,
+ (char **) &zipHandle->zipStream.next_in);
+ if (numBytes == -1) {
+ // errno is set
+ return -1;
+ }
+#if 0
+ if (numBytes == 0) {
+ if (zipHandle->uncompressedOffset !=
+ zipHandle->file->extra->uncompressedSize) {
+ // premature eof
+ errno = EIO;
+ return -1;
+ }
+ break;
+ }
+#endif
+ zipHandle->zipStream.avail_in = numBytes;
+ zipHandle->compressedOffset += numBytes;
+ }
+ inflateResult = inflate(&zipHandle->zipStream, Z_SYNC_FLUSH);
+ zipHandle->uncompressedOffset = zipHandle->zipStream.total_out;
+ if (inflateResult == Z_STREAM_END) {
+ // Everything is decompressed
+ break;
+ }
+ if (inflateResult != Z_OK) {
+ switch (inflateResult) {
+ case Z_VERSION_ERROR:
+ fprintf(stderr, "Error: Incompatible version problem for "
+ " decompression.\n");
+ break;
+ case Z_NEED_DICT:
+ fprintf(stderr, "Error: Decompressing requires "
+ "preset dictionary.\n");
+ break;
+ case Z_DATA_ERROR:
+ fprintf(stderr, "Error: Compressed file is corrupted.\n");
+ break;
+ case Z_STREAM_ERROR:
+ // This means zipHandle->zipStream is bad, which is
+ // most likely an error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ case Z_MEM_ERROR:
+ fprintf(stderr, "Error: Not enough memory available "
+ "while decompressing.\n");
+ break;
+ case Z_BUF_ERROR:
+ // No progress possible. Probably caused by premature
+ // end of input file.
+ fprintf(stderr, "Error: When decompressing: premature "
+ "end of input file.\n");
+ errno = EIO;
+ return -1;
+#if 0
+ // If this happens, either the input buffer is empty
+ // or the output buffer is full. This should not happen.
+ fprintf(stderr, "Fatal: internal error using zlib: "
+ " no progress is possible in decompression.\n");
+ abort();
+ break;
+#endif
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflate().\n");
+ abort();
+ }
+ if (zipHandle->zipStream.msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n",
+ zipHandle->zipStream.msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ }
+ return count - zipHandle->zipStream.avail_out;
+}
+
+off_t
+zip_seek(uio_Handle *handle, off_t offset, int whence) {
+ zip_Handle *zipHandle;
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "zip_seek - handle=%p offset=%d whence=%s\n",
+ (void *) handle, (int) offset,
+ whence == SEEK_SET ? "SEEK_SET" :
+ whence == SEEK_CUR ? "SEEK_CUR" :
+ whence == SEEK_END ? "SEEK_END" : "INVALID");
+#endif
+ zipHandle = handle->native;
+
+ assert(whence == SEEK_SET || whence == SEEK_CUR || whence == SEEK_END);
+ switch(whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += zipHandle->uncompressedOffset;
+ break;
+ case SEEK_END:
+ offset += zipHandle->file->extra->uncompressedSize;
+ break;
+ }
+ if (offset < 0) {
+ offset = 0;
+ } else if (offset > zipHandle->file->extra->uncompressedSize) {
+ offset = zipHandle->file->extra->uncompressedSize;
+ }
+ return zip_seekMethods[handle->native->file->extra->compressionMethod]
+ (handle, offset);
+}
+
+static off_t
+zip_seekStored(uio_Handle *handle, off_t offset) {
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+ if (offset > zipHandle->file->extra->uncompressedSize)
+ offset = zipHandle->file->extra->uncompressedSize;
+
+ zipHandle->compressedOffset = offset;
+ zipHandle->uncompressedOffset = offset;
+ return offset;
+}
+
+static off_t
+zip_seekDeflated(uio_Handle *handle, off_t offset) {
+ zip_Handle *zipHandle;
+
+ zipHandle = handle->native;
+
+ if (offset < zipHandle->uncompressedOffset) {
+ // The new offset is earlier than the current offset. We need to
+ // seek from the beginning.
+ if (zip_reInitZipStream(&zipHandle->zipStream) == -1) {
+ // Need to abort. Handle would get in an inconsistent state.
+ // Should not fail anyhow.
+ fprintf(stderr, "Fatal: Could not reinitialise zip stream: "
+ "%s.\n", strerror(errno));
+ abort();
+ }
+ zipHandle->compressedOffset = 0;
+ zipHandle->uncompressedOffset = 0;
+ }
+
+ if (offset == zipHandle->uncompressedOffset)
+ return offset;
+
+ // Seek from the current position.
+ {
+ char *buffer;
+ ssize_t numRead;
+ size_t toRead;
+
+ buffer = uio_malloc(zip_SEEK_BUFFER_SIZE);
+ toRead = offset - zipHandle->uncompressedOffset;
+ while (toRead > 0) {
+ numRead = zip_read(handle, buffer,
+ toRead < zip_SEEK_BUFFER_SIZE ?
+ toRead : zip_SEEK_BUFFER_SIZE);
+ if (numRead == -1) {
+ fprintf(stderr, "Warning: Could not read zipped file: %s\n",
+ strerror(errno));
+ break;
+ // The current location is returned.
+ }
+ toRead -= numRead;
+ }
+ uio_free(buffer);
+ }
+ return zipHandle->uncompressedOffset;
+}
+
+uio_PRoot *
+zip_mount(uio_Handle *handle, int flags) {
+ uio_PRoot *result;
+ uio_PDirHandle *rootDirHandle;
+
+ if ((flags & uio_MOUNT_RDONLY) != uio_MOUNT_RDONLY) {
+ errno = EACCES;
+ return NULL;
+ }
+
+ uio_Handle_ref(handle);
+ result = uio_GPRoot_makePRoot(
+ uio_getFileSystemHandler(uio_FSTYPE_ZIP), flags,
+ &zip_GPRootOperations, NULL, uio_GPRoot_PERSISTENT,
+ handle, NULL, uio_GPDir_COMPLETE);
+
+ rootDirHandle = uio_PRoot_getRootDirHandle(result);
+ if (zip_fillDirStructure(rootDirHandle->extra, handle) == -1) {
+ int savedErrno = errno;
+#ifdef DEBUG
+ fprintf(stderr, "Error: failed to read the zip directory "
+ "structure - %s.\n", strerror(errno));
+#endif
+ uio_GPRoot_umount(result);
+ errno = savedErrno;
+ return NULL;
+ }
+
+ return result;
+}
+
+static int
+zip_fillDirStructure(uio_GPDir *top, uio_Handle *handle) {
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ return zip_fillDirStructureCentral(top, handle);
+#endif
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+ return zip_fillDirStructureLocal(top, handle);
+#endif
+}
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+static int
+zip_fillDirStructureCentral(uio_GPDir *top, uio_Handle *handle) {
+ uio_FileBlock *fileBlock;
+ off_t pos;
+ char *buf;
+ ssize_t numBytes;
+ uio_uint16 numEntries;
+ // TODO: use numEntries to initialise the hash table
+ // to a smart size
+ off_t eocdr;
+ off_t startCentralDir;
+
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ goto err;
+ }
+
+ // first find the 'End of Central Directory Record'
+ eocdr = zip_findEndOfCentralDirectoryRecord(handle, fileBlock);
+ if (eocdr == -1) {
+ // errno is set
+ goto err;
+ }
+
+ numBytes = uio_accessFileBlock(fileBlock, eocdr, 22, &buf);
+ if (numBytes == -1) {
+ // errno is set
+ goto err;
+ }
+ if (numBytes != 22) {
+ errno = EIO;
+ goto err;
+ }
+
+ numEntries = makeUInt16(buf[10], buf[11]);
+ if (numEntries == 0xffff) {
+ fprintf(stderr, "Error: Zip64 .zip files are not supported.\n");
+ errno = ENOSYS;
+ goto err;
+ }
+
+ startCentralDir = makeUInt32(buf[16], buf[17], buf[18], buf[19]);
+
+ // Enable read-ahead buffering, for speed.
+ uio_setFileBlockUsageHint(fileBlock, uio_FB_USAGE_FORWARD,
+ DIR_STRUCTURE_READ_BUFSIZE);
+
+ pos = startCentralDir;
+ while (numEntries--) {
+ if (zip_fillDirStructureCentralProcessEntry(top, fileBlock, &pos)
+ == -1) {
+ // errno is set
+ goto err;
+ }
+ }
+
+ uio_closeFileBlock(fileBlock);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+
+ if (fileBlock != NULL)
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static int
+zip_fillDirStructureCentralProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos) {
+ char *buf;
+ zip_GPFileData *gPFileData;
+ ssize_t numBytes;
+
+ uio_uint32 signature;
+ uio_uint16 lastModTime;
+ uio_uint16 lastModDate;
+ uio_uint32 crc;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+ uio_uint16 fileCommentLength;
+ char *fileName;
+ zip_OSType creatorOS;
+
+ off_t nextEntryOffset;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 46, &buf);
+ if (numBytes != 46)
+ return zip_badFile(NULL, NULL);
+
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x02014b50) {
+ fprintf(stderr, "Error: Premature end of central directory.\n");
+ errno = EIO;
+ return -1;
+ }
+
+ gPFileData = zip_GPFileData_new();
+ creatorOS = (zip_OSType) buf[5];
+ gPFileData->compressionFlags = makeUInt16(buf[8], buf[9]);
+ gPFileData->compressionMethod = makeUInt16(buf[10], buf[11]);
+ lastModTime = makeUInt16(buf[12], buf[13]);
+ lastModDate = makeUInt16(buf[14], buf[15]);
+ gPFileData->atime = (time_t) 0;
+ gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime);
+ gPFileData->ctime = (time_t) 0;
+ crc = makeUInt32(buf[16], buf[17], buf[18], buf[19]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[20], buf[21], buf[22], buf[23]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[24], buf[25], buf[26], buf[27]);
+ fileNameLength = makeUInt16(buf[28], buf[29]);
+ extraFieldLength = makeUInt16(buf[30], buf[31]);
+ fileCommentLength = makeUInt16(buf[32], buf[33]);
+ gPFileData->uid = 0;
+ gPFileData->gid = 0;
+ gPFileData->mode = zip_makeFileMode(creatorOS,
+ makeUInt32(buf[38], buf[39], buf[40], buf[41]));
+
+ gPFileData->headerOffset =
+ (off_t) makeSInt32(buf[42], buf[43], buf[44], buf[45]);
+ gPFileData->fileOffset = (off_t) -1;
+
+ *pos += 46;
+ nextEntryOffset = *pos + fileNameLength + extraFieldLength +
+ fileCommentLength;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf);
+ if (numBytes != fileNameLength)
+ return zip_badFile(gPFileData, NULL);
+ fileName = uio_malloc(fileNameLength + 1);
+ memcpy(fileName, buf, fileNameLength);
+ fileName[fileNameLength] = '\0';
+ *pos += fileNameLength;
+
+ if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS ||
+ !zip_compressionMethodSupported[gPFileData->compressionMethod]) {
+ fprintf(stderr, "Warning: File '%s' is compressed with "
+ "unsupported method %d - skipped.\n", fileName,
+ gPFileData->compressionMethod);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (gPFileData->compressedSize == (off_t) 0xffffffff ||
+ gPFileData->uncompressedSize == (off_t) 0xffffffff ||
+ gPFileData->headerOffset < 0) {
+ fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (isBitSet(gPFileData->compressionFlags, 0)) {
+ fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, fileName, *pos, true)) {
+ case 0: // file is ok
+ break;
+ case 1: // file is not acceptable - skip file
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ case -1:
+ return zip_badFile(gPFileData, fileName);
+ }
+
+ *pos += extraFieldLength;
+
+ // If ctime or atime is 0, they will be filled in when the local
+ // file header is read.
+
+ if (S_ISREG(gPFileData->mode)) {
+ if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found file '%s'.\n", fileName);
+#endif
+ } else if (S_ISDIR(gPFileData->mode)) {
+ if (fileName[fileNameLength - 1] == '/')
+ fileName[fileNameLength - 1] = '\0';
+ if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ fprintf(stderr, "Warning: file '%s' already exists as a dir - "
+ "skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found dir '%s'.\n", fileName);
+#endif
+ } else {
+ fprintf(stderr, "Warning: '%s' is not a regular file, nor a "
+ "directory - skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+
+ uio_free(fileName);
+ return 0;
+}
+
+static off_t
+zip_findEndOfCentralDirectoryRecord(uio_Handle *handle,
+ uio_FileBlock *fileBlock) {
+ off_t fileSize;
+ off_t endPos, startPos;
+ char *buf, *bufPtr;
+ ssize_t bufLen;
+
+ struct stat statBuf;
+ if (uio_fstat(handle, &statBuf) == -1) {
+ // errno is set
+ return -1;
+ }
+ fileSize = statBuf.st_size;
+ startPos = fileSize - 0xffff - 22; // max comment and record size
+ if (startPos < 0)
+ startPos = 0;
+ endPos = fileSize - 22; // last position to be checked
+ bufLen = uio_accessFileBlock(fileBlock, startPos, endPos - startPos + 4,
+ &buf);
+ if (bufLen == -1) {
+ int savedErrno = errno;
+ fprintf(stderr, "Error: Read error while searching for "
+ "'end-of-central-directory record'.\n");
+ errno = savedErrno;
+ return -1;
+ }
+ if (bufLen != endPos - startPos + 4) {
+ fprintf(stderr, "Error: Read error while searching for "
+ "'end-of-central-directory record'.\n");
+ errno = EIO;
+ return -1;
+ }
+ bufPtr = buf + (endPos - startPos);
+ while (1) {
+ if (bufPtr < buf) {
+ fprintf(stderr, "Error: Zip file corrupt; could not find "
+ "'end-of-central-directory record'.\n");
+ errno = EIO;
+ return -1;
+ }
+ if (bufPtr[0] == 0x50 && bufPtr[1] == 0x4b && bufPtr[2] == 0x05 &&
+ bufPtr[3] == 0x06)
+ break;
+ bufPtr--;
+ }
+ return startPos + (bufPtr - buf);
+}
+
+static mode_t
+zip_makeFileMode(zip_OSType creatorOS, uio_uint32 modeBytes) {
+ switch (creatorOS) {
+ case zip_OSType_FAT:
+ case zip_OSType_NTFS:
+ case zip_OSType_VFAT: {
+ // Only the least signigicant byte is relevant.
+ mode_t mode;
+
+ if (modeBytes == 0) {
+ // File came from standard input
+ return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
+ S_IROTH | S_IWOTH;
+ }
+ if (modeBytes & 0x10) {
+ // Directory
+ mode = S_IFDIR | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP |
+ S_IROTH | S_IXOTH;
+ } else {
+ // Regular file
+ mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+ }
+ if (modeBytes & 0x01) {
+ // readonly
+ return mode;
+ } else {
+ // Write allowed
+ return mode | S_IWUSR | S_IWGRP | S_IWOTH;
+ }
+ }
+ case zip_OSType_UNIX:
+ return (mode_t) (modeBytes >> 16);
+ default:
+ fprintf(stderr, "Warning: file created by unknown OS.\n");
+ return S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ }
+}
+
+// If the data is read from the central directory, certain data
+// will need to be updated from the local directory when it is needed.
+// This function does that.
+// returns 0 for success, -1 for error (errno is set)
+// NB: Only the fields that may offer new information are checked.
+// the fields that were already read from the central directory
+// aren't verified.
+static int
+zip_updatePFileDataFromLocalFileHeader(zip_GPFileData *gPFileData,
+ uio_FileBlock *fileBlock, int pos) {
+ ssize_t numBytes;
+ char *buf;
+ uio_uint32 signature;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+
+ numBytes = uio_accessFileBlock(fileBlock, pos, 30, &buf);
+ if (numBytes != 30) {
+ errno = EIO;
+ return -1;
+ }
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x04034b50) {
+ errno = EIO;
+ return -1;
+ }
+ fileNameLength = makeUInt16(buf[26], buf[27]);
+ extraFieldLength = makeUInt16(buf[28], buf[29]);
+ pos += 30 + fileNameLength;
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, "<unknown>", pos, false)) {
+ case 0: // file is ok
+ break;
+ case 1:
+ // File is not acceptable (but according to the central header
+ // it was)
+ fprintf(stderr, "Warning: according to the central directory "
+ "of a zip file, some file inside is acceptable, "
+ "but according to the local header it isn't.\n");
+ errno = EIO;
+ return -1;
+ case -1:
+ errno = EIO;
+ return -1;
+ }
+ pos += extraFieldLength;
+ gPFileData->fileOffset = pos;
+ return 0;
+}
+#endif /* zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS */
+
+#if zip_USE_HEADERS == zip_USE_LOCAL_HEADERS
+static int
+zip_fillDirStructureLocal(uio_GPDir *top, uio_Handle *handle) {
+ uio_FileBlock *fileBlock;
+ off_t pos;
+ char *buf;
+ ssize_t numBytes;
+
+ pos = uio_lseek(handle, 0, SEEK_SET);
+ if (pos == -1) {
+ int savedErrno = errno;
+ errno = savedErrno;
+ return -1;
+ }
+ if (pos != 0) {
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+
+ // We read all the files from the beginning of the zip file to the end.
+ // (the directory record at the end of the file is ignored)
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ return -1;
+ }
+
+ pos = 0;
+ while (1) {
+ uio_uint32 signature;
+
+ numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf);
+ if (numBytes == -1)
+ goto err;
+ if (numBytes != 4)
+ break;
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x04034b50) {
+ // End of file data reached.
+ break;
+ }
+ pos += 4;
+ if (zip_fillDirStructureLocalProcessEntry(top, fileBlock, &pos) == -1)
+ goto err;
+ }
+
+ uio_closeFileBlock(fileBlock);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+}
+
+static int
+zip_fillDirStructureLocalProcessEntry(uio_GPDir *topGPDir,
+ uio_FileBlock *fileBlock, off_t *pos) {
+ char *buf;
+ zip_GPFileData *gPFileData;
+ ssize_t numBytes;
+
+ uio_uint16 lastModTime;
+ uio_uint16 lastModDate;
+ uio_uint32 crc;
+ uio_uint16 fileNameLength;
+ uio_uint16 extraFieldLength;
+ char *fileName;
+
+ off_t nextEntryOffset;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 26, &buf);
+ if (numBytes != 26)
+ return zip_badFile(NULL, NULL);
+
+ gPFileData = zip_GPFileData_new();
+ gPFileData->compressionFlags = makeUInt16(buf[2], buf[3]);
+ gPFileData->compressionMethod = makeUInt16(buf[4], buf[5]);
+ lastModTime = makeUInt16(buf[6], buf[7]);
+ lastModDate = makeUInt16(buf[8], buf[9]);
+ gPFileData->atime = (time_t) 0;
+ gPFileData->mtime = dosToUnixTime(lastModDate, lastModTime);
+ gPFileData->ctime = (time_t) 0;
+ gPFileData->uid = 0;
+ gPFileData->gid = 0;
+ gPFileData->mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (!isBitSet(gPFileData->compressionFlags, 3)) {
+ // If bit 3 is not set, this info will be in the data descriptor
+ // behind the file data.
+ crc = makeUInt32(buf[10], buf[11], buf[12], buf[13]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[14], buf[15], buf[16], buf[17]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[18], buf[19], buf[20], buf[21]);
+ }
+ fileNameLength = makeUInt16(buf[22], buf[23]);
+ extraFieldLength = makeUInt16(buf[24], buf[25]);
+ *pos += 26;
+ nextEntryOffset = *pos + fileNameLength + extraFieldLength +
+ gPFileData->compressedSize;
+ if (isBitSet(gPFileData->compressionFlags, 3)) {
+ // There's a data descriptor present behind the file data.
+ nextEntryOffset += 16;
+ }
+
+ if (gPFileData->compressionMethod >= NUM_COMPRESSION_METHODS ||
+ !zip_compressionMethodSupported[gPFileData->compressionMethod]) {
+ fprintf(stderr, "Warning: File '%s' is compressed with "
+ "unsupported method %d - skipped.\n", fileName,
+ gPFileData->compressionMethod);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (gPFileData->compressedSize == (off_t) 0xffffffff ||
+ gPFileData->uncompressedSize == (off_t) 0xffffffff) {
+ fprintf(stderr, "Warning: Skipping Zip64 file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ if (isBitSet(gPFileData->compressionFlags, 0)) {
+ fprintf(stderr, "Warning: Skipping encrypted file '%s'\n", fileName);
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ return 0;
+ }
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, fileNameLength, &buf);
+ if (numBytes != fileNameLength)
+ return zip_badFile(gPFileData, NULL);
+ *pos += fileNameLength;
+ if (buf[fileNameLength - 1] == '/') {
+ gPFileData->mode |= S_IFDIR;
+ fileNameLength--;
+ } else
+ gPFileData->mode |= S_IFREG;
+ fileName = uio_malloc(fileNameLength + 1);
+ memcpy(fileName, buf, fileNameLength);
+ fileName[fileNameLength] = '\0';
+
+ switch (zip_fillDirStructureProcessExtraFields(fileBlock,
+ extraFieldLength, gPFileData, fileName, *pos, false)) {
+ case 0: // file is ok
+ break;
+ case 1: // file is not acceptable - skip file
+ *pos = nextEntryOffset;
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ case -1:
+ return zip_badFile(gPFileData, fileName);
+ }
+ *pos += extraFieldLength;
+
+ gPFileData->fileOffset = *pos;
+
+ *pos += gPFileData->compressedSize;
+ if (isBitSet(gPFileData->compressionFlags, 3)) {
+ // Now comes a data descriptor.
+ // The PKWare version (which was never used) misses the signature.
+ // The InfoZip version is used below.
+ uio_uint32 signature;
+
+ numBytes = uio_accessFileBlock(fileBlock, *pos, 16, &buf);
+ if (numBytes != 16)
+ return zip_badFile(gPFileData, fileName);
+
+ signature = makeUInt32(buf[0], buf[1], buf[2], buf[3]);
+ if (signature != 0x08074b50)
+ return zip_badFile(gPFileData, fileName);
+ crc = makeUInt32(buf[4], buf[5], buf[6], buf[7]);
+ gPFileData->compressedSize =
+ makeUInt32(buf[8], buf[9], buf[10], buf[11]);
+ gPFileData->uncompressedSize =
+ makeUInt32(buf[12], buf[13], buf[14], buf[15]);
+ }
+
+ if (gPFileData->ctime == (time_t) 0)
+ gPFileData->ctime = gPFileData->mtime;
+
+ if (gPFileData->atime == (time_t) 0)
+ gPFileData->atime = gPFileData->mtime;
+
+ if (S_ISREG(gPFileData->mode)) {
+ if (zip_foundFile(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found file '%s'.\n", fileName);
+#endif
+ } else if (S_ISDIR(gPFileData->mode)) {
+ if (fileName[fileNameLength - 1] == '/')
+ fileName[fileNameLength - 1] = '\0';
+ if (zip_foundDir(topGPDir, fileName, gPFileData) == -1) {
+ if (errno == EISDIR) {
+ fprintf(stderr, "Warning: file '%s' already exists as a dir - "
+ "skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+ return zip_badFile(gPFileData, fileName);
+ }
+#if defined(DEBUG) && DEBUG > 1
+ fprintf(stderr, "Debug: Found dir '%s'.\n", fileName);
+#endif
+ } else {
+ fprintf(stderr, "Warning: '%s' is not a regular file, nor a "
+ "directory - skipped.\n", fileName);
+ zip_GPFileData_delete(gPFileData);
+ uio_free(fileName);
+ return 0;
+ }
+
+ uio_free(fileName);
+ return 0;
+}
+#endif /* zip_USE_HEADERS == zip_USE_LOCAL_HEADERS */
+
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+int
+zip_updateFileDataFromLocalHeader(uio_Handle *handle,
+ zip_GPFileData *gPFileData) {
+ uio_FileBlock *fileBlock;
+
+ fileBlock = uio_openFileBlock(handle);
+ if (fileBlock == NULL) {
+ // errno is set
+ return -1;
+ }
+ if (zip_updatePFileDataFromLocalFileHeader(gPFileData,
+ fileBlock, gPFileData->headerOffset) == -1) {
+ int savedErrno = errno;
+ uio_closeFileBlock(fileBlock);
+ errno = savedErrno;
+ return -1;
+ }
+ if (gPFileData->ctime == (time_t) 0)
+ gPFileData->ctime = gPFileData->mtime;
+ if (gPFileData->atime == (time_t) 0)
+ gPFileData->atime = gPFileData->mtime;
+ uio_closeFileBlock(fileBlock);
+ return 0;
+}
+#endif
+
+// If the zip file is bad, -1 is returned (no errno set!)
+// If the file in the zip file should be skipped, 1 is returned.
+// If the file in the zip file is ok, 0 is returned.
+static int
+zip_fillDirStructureProcessExtraFields(uio_FileBlock *fileBlock,
+ off_t extraFieldLength, zip_GPFileData *gPFileData,
+ const char *fileName, off_t pos, uio_bool central) {
+ off_t posEnd;
+ uio_uint16 headerID;
+ ssize_t dataSize;
+ ssize_t numBytes;
+ char *buf;
+
+ posEnd = pos + extraFieldLength;
+ while (pos < posEnd) {
+ numBytes = uio_accessFileBlock(fileBlock, pos, 4, &buf);
+ if (numBytes != 4)
+ return -1;
+ headerID = makeUInt16(buf[0], buf[1]);
+ dataSize = (ssize_t) makeUInt16(buf[2], buf[3]);
+ pos += 4;
+ numBytes = uio_accessFileBlock(fileBlock, pos, dataSize, &buf);
+ if (numBytes != dataSize)
+ return -1;
+ switch(headerID) {
+ case 0x000d: // 'Unix0'
+ // fallthrough
+ case 0x5855: // 'Unix1'
+ gPFileData->atime = (time_t) makeUInt32(
+ buf[0], buf[1], buf[2], buf[3]);
+ gPFileData->mtime = (time_t) makeUInt32(
+ buf[4], buf[5], buf[6], buf[7]);
+ if (central)
+ break;
+ if (dataSize > 8) {
+ gPFileData->uid = (uid_t) makeUInt16(buf[8], buf[9]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[10], buf[11]);
+ }
+ // Unix0 has an extra (ignored) field at the end.
+ break;
+ case 0x5455: { // 'time'
+ uio_uint8 flags;
+ const char *bufPtr;
+ flags = buf[0];
+ bufPtr = buf + 1;
+ if (isBitSet(flags, 0)) {
+ // modification time is present
+ gPFileData->mtime = (time_t) makeUInt32(
+ bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]);
+ bufPtr += 4;
+ }
+ // The flags field, even in the central header, specifies what
+ // is present in the local header.
+ // The central header only contains a field for the mtime
+ // when it is present in the local header too, and
+ // never contains fields for other times.
+ if (central)
+ break;
+ if (isBitSet(flags, 1)) {
+ // modification time is present
+ gPFileData->atime = (time_t) makeUInt32(
+ bufPtr[0], bufPtr[1], bufPtr[2], bufPtr[3]);
+ bufPtr += 4;
+ }
+ // Creation time and possible other times are skipped.
+ break;
+ }
+ case 0x7855: // 'Unix2'
+ if (central)
+ break;
+ gPFileData->uid = (uid_t) makeUInt16(buf[0], buf[1]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[2], buf[3]);
+ break;
+ case 0x756e: { // 'Unix3'
+ mode_t mode;
+ mode = (mode_t) makeUInt16(buf[4], buf[5]);
+ if (!S_ISREG(mode) && !S_ISDIR(mode)) {
+ fprintf(stderr, "Warning: Skipping '%s'; not a regular "
+ "file, nor a directory.\n", fileName);
+ return 1;
+ }
+ gPFileData->uid = (uid_t) makeUInt16(buf[10], buf[11]);
+ gPFileData->gid = (uid_t) makeUInt16(buf[12], buf[13]);
+ break;
+ }
+ default:
+#ifdef DEBUG
+ fprintf(stderr, "Debug: Extra field 0x%04x unsupported, "
+ "used for file '%s' - ignored.\n", headerID,
+ fileName);
+#endif
+ break;
+ } // switch
+ pos += dataSize;
+ } // while
+ if (pos != posEnd)
+ return -1;
+ return 0;
+}
+
+static int
+zip_badFile(zip_GPFileData *gPFileData, char *fileName) {
+ fprintf(stderr, "Error: Bad file format for .zip file.\n");
+ if (gPFileData != NULL)
+ zip_GPFileData_delete(gPFileData);
+ if (fileName != NULL)
+ uio_free(fileName);
+ errno = EINVAL; // Is this the best choice?
+ return -1;
+}
+
+static inline int
+zip_foundFile(uio_GPDir *gPDir, const char *path, zip_GPFileData *gPFileData) {
+ uio_GPFile *file;
+ size_t pathLen;
+ const char *rest;
+ const char *pathEnd;
+ const char *start, *end;
+ char *buf;
+
+ if (path[0] == '/')
+ path++;
+ pathLen = strlen(path);
+ if (path[pathLen - 1] == '/') {
+ fprintf(stderr, "Warning: '%s' is not a valid file name - skipped.\n",
+ path);
+ errno = EISDIR;
+ return -1;
+ }
+ pathEnd = path + pathLen;
+
+ switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) {
+ case 0:
+ // The entire path was matched. The last part was not supposed
+ // to be a dir.
+ fprintf(stderr, "Warning: '%s' already exists as a dir - "
+ "skipped.\n", path);
+ errno = EISDIR;
+ return -1;
+ case ENOTDIR:
+ fprintf(stderr, "Warning: A component to '%s' is not a "
+ "directory - file skipped.\n", path);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ break;
+ }
+
+ buf = uio_malloc(pathLen + 1);
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (1) {
+ uio_GPDir *newGPDir;
+
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.')) {
+ fprintf(stderr, "Warning: file '%s' has an invalid path - "
+ "skipped.\n", path);
+ uio_free(buf);
+ errno = EINVAL;
+ return -1;
+ }
+ if (end == pathEnd) {
+ // This is the last component; it should be the name of the dir.
+ rest = start;
+ break;
+ }
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ newGPDir = uio_GPDir_prepareSubDir(gPDir, buf);
+ newGPDir->flags |= uio_GPDir_COMPLETE;
+ // It will be complete when we're done adding
+ // all files, and it won't be used before that.
+ uio_GPDir_commitSubDir(gPDir, buf, newGPDir);
+
+ gPDir = newGPDir;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+
+ uio_free(buf);
+
+ file = uio_GPFile_new(gPDir->pRoot, (uio_GPFileExtra) gPFileData,
+ uio_gPFileFlagsFromPRootFlags(gPDir->pRoot->flags));
+ uio_GPDir_addFile(gPDir, rest, file);
+ return 0;
+}
+
+static inline int
+zip_foundDir(uio_GPDir *gPDir, const char *path, zip_GPDirData *gPDirData) {
+ size_t pathLen;
+ const char *pathEnd;
+ const char *rest;
+ const char *start, *end;
+ char *buf;
+
+ if (path[0] == '/')
+ path++;
+ pathLen = strlen(path);
+ pathEnd = path + pathLen;
+
+ switch (uio_walkGPPath(gPDir, path, pathLen, &gPDir, &rest)) {
+ case 0:
+ // The dir already exists. Only need to add gPDirData
+ if (gPDir->extra != NULL) {
+ fprintf(stderr, "Warning: '%s' is present more than once "
+ "in the zip file. The last entry will be used.\n",
+ path);
+ zip_GPDirData_delete(gPDir->extra);
+ }
+ gPDir->extra = gPDirData;
+ return 0;
+ case ENOTDIR:
+ fprintf(stderr, "Warning: A component of '%s' is not a "
+ "directory - file skipped.\n", path);
+ errno = ENOTDIR;
+ return -1;
+ case ENOENT:
+ break;
+ }
+
+ buf = uio_malloc(pathLen + 1);
+ getFirstPathComponent(rest, pathEnd, &start, &end);
+ while (start < pathEnd) {
+ uio_GPDir *newGPDir;
+
+ if (end == start || (end - start == 1 && start[0] == '.') ||
+ (end - start == 2 && start[0] == '.' && start[1] == '.')) {
+ fprintf(stderr, "Warning: directory '%s' has an invalid path - "
+ "skipped.\n", path);
+ uio_free(buf);
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ newGPDir = uio_GPDir_prepareSubDir(gPDir, buf);
+ newGPDir->flags |= uio_GPDir_COMPLETE;
+ // It will be complete when we're done adding
+ // all files, and it won't be used before that.
+ uio_GPDir_commitSubDir(gPDir, buf, newGPDir);
+
+ gPDir = newGPDir;
+ getNextPathComponent(pathEnd, &start, &end);
+ }
+ gPDir->extra = gPDirData;
+
+ uio_free(buf);
+ return 0;
+}
+
+static int
+zip_initZipStream(z_stream *zipStream) {
+ int retVal;
+
+ zipStream->next_in = Z_NULL;
+ zipStream->avail_in = 0;
+ zipStream->zalloc = zip_alloc;
+ zipStream->zfree = zip_free;
+ zipStream->opaque = NULL;
+ retVal = inflateInit2(zipStream, -MAX_WBITS);
+ // Negative window size means that no zlib header is present.
+ // This feature is undocumented in zlib, but it's used
+ // in the minizip program from the zlib contrib dir.
+ // The absolute value is used as real Window size.
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_MEM_ERROR:
+ fprintf(stderr, "Error: Not enough memory available for "
+ " decompression.\n");
+ break;
+ case Z_VERSION_ERROR:
+ fprintf(stderr, "Error: Incompatible version problem for "
+ " decompression.\n");
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateInit().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zip_unInitZipStream(z_stream *zipStream) {
+ int retVal;
+
+ retVal = inflateEnd(zipStream);
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_STREAM_ERROR:
+ // This means zipStream is bad, which is most likely an
+ // error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateEnd().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+static int
+zip_reInitZipStream(z_stream *zipStream) {
+ int retVal;
+
+ zipStream->next_in = Z_NULL;
+ zipStream->avail_in = 0;
+ retVal = inflateReset(zipStream);
+ if (retVal != Z_OK) {
+ switch (retVal) {
+ case Z_STREAM_ERROR:
+ // This means zipStream is bad, which is most likely an
+ // error in the code using zlib.
+ fprintf(stderr, "Fatal: internal error using zlib.\n");
+ abort();
+ break;
+ default:
+ fprintf(stderr, "Fatal: unknown error from inflateInit().\n");
+ abort();
+ }
+ if (zipStream->msg != NULL)
+ fprintf(stderr, "ZLib reports: %s\n", zipStream->msg);
+ errno = EIO;
+ // Using EIO to report an error in the backend.
+ return -1;
+ }
+ return 0;
+}
+
+
+// Used internally by zlib for allocating memory.
+static voidpf
+zip_alloc(voidpf opaque, uInt items, uInt size) {
+ (void) opaque;
+ return (voidpf) uio_calloc((size_t) items, (size_t) size);
+}
+
+// Used internally by zlib for freeing memory.
+static void
+zip_free(voidpf opaque, voidpf address) {
+ (void) opaque;
+ uio_free((void *) address);
+}
+
+static inline zip_GPFileData *
+zip_GPFileData_new(void) {
+ return zip_GPFileData_alloc();
+}
+
+static inline void
+zip_GPFileData_delete(zip_GPFileData *gPFileData) {
+ zip_GPFileData_free(gPFileData);
+}
+
+static inline zip_GPFileData *
+zip_GPFileData_alloc(void) {
+ zip_GPFileData *result = uio_malloc(sizeof (zip_GPFileData));
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugAlloc(zip_GPFileData, (void *) result);
+#endif
+ return result;
+}
+
+static inline void
+zip_GPFileData_free(zip_GPFileData *gPFileData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(zip_GPFileData, (void *) gPFileData);
+#endif
+ uio_free(gPFileData);
+}
+
+static inline void
+zip_GPDirData_delete(zip_GPDirData *gPDirData) {
+ zip_GPDirData_free(gPDirData);
+}
+
+static inline void
+zip_GPDirData_free(zip_GPDirData *gPDirData) {
+#ifdef uio_MEM_DEBUG
+ uio_MemDebug_debugFree(zip_GPFileData, (void *) gPDirData);
+#endif
+ uio_free(gPDirData);
+}
+
+
+
diff --git a/src/libs/uio/zip/zip.h b/src/libs/uio/zip/zip.h
new file mode 100644
index 0000000..4b2762f
--- /dev/null
+++ b/src/libs/uio/zip/zip.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+typedef struct zip_Handle *uio_NativeHandle;
+typedef void *uio_GPRootExtra;
+typedef struct zip_GPFileData *uio_GPFileExtra;
+typedef struct zip_GPFileData *uio_GPDirExtra;
+typedef struct uio_GPDirEntries_Iterator *uio_NativeEntriesContext;
+
+#define uio_INTERNAL_PHYSICAL
+
+#include "../gphys.h"
+#include "../iointrn.h"
+#include "../uioport.h"
+#include "../physical.h"
+#include "../types.h"
+#include "../fileblock.h"
+
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// zip_USE_HEADERS determinines what header for files within a .zip file
+// is used when building the directory structure.
+// Set to 'zip_USE_CENTRAL_HEADERS' to use the central directory header,
+// set to 'zip_USE_LOCAL_HEADERS' to use the local file header.
+// Central is highly adviced: it uses much less seeking, and hence is much
+// faster.
+#define zip_USE_HEADERS zip_USE_CENTRAL_HEADERS
+#define zip_USE_CENTRAL_HEADERS 1
+#define zip_USE_LOCAL_HEADERS 2
+
+#define zip_INCOMPLETE_STAT
+ // Ignored unless zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS.
+ // If defined, extra meta-data for files in the .zip archive
+ // isn't retrieved from the local file header when zip_stat()
+ // is called. The uid, gid, file mode, and file times may be
+ // inaccurate. The advantage is that a possibly costly seek and
+ // read can be avoided.
+
+typedef struct zip_GPFileData {
+ off_t compressedSize;
+ off_t uncompressedSize;
+ uio_uint16 compressionFlags;
+ uio_uint16 compressionMethod;
+#if zip_USE_HEADERS == zip_USE_CENTRAL_HEADERS
+ off_t headerOffset; // start of the local header for this file
+#endif
+ off_t fileOffset; // start of the compressed data in the .zip file
+ uid_t uid;
+ gid_t gid;
+ mode_t mode;
+ time_t atime; // access time
+ time_t mtime; // modification time
+ time_t ctime; // change time
+} zip_GPFileData;
+
+typedef zip_GPFileData zip_GPDirData;
+// TODO: some of the fields from zip_GPFileData are not needed for
+// directories. A few bytes could be saved here by making a seperate
+// structure.
+
+typedef struct zip_Handle {
+ uio_GPFile *file;
+ z_stream zipStream;
+ uio_FileBlock *fileBlock;
+ off_t uncompressedOffset;
+ // seek location in the uncompressed stream
+ off_t compressedOffset;
+ // seek location in the compressed stream, from the start
+ // of the compressed file
+} zip_Handle;
+
+
+uio_PRoot *zip_mount(uio_Handle *handle, int flags);
+int zip_umount(struct uio_PRoot *);
+uio_Handle *zip_open(uio_PDirHandle *pDirHandle, const char *file, int flags,
+ mode_t mode);
+void zip_close(uio_Handle *handle);
+int zip_access(uio_PDirHandle *pDirHandle, const char *name, int mode);
+int zip_fstat(uio_Handle *handle, struct stat *statBuf);
+int zip_stat(uio_PDirHandle *pDirHandle, const char *name,
+ struct stat *statBuf);
+ssize_t zip_read(uio_Handle *handle, void *buf, size_t count);
+off_t zip_seek(uio_Handle *handle, off_t offset, int whence);
+
+
+
+
diff --git a/src/libs/uioutils.h b/src/libs/uioutils.h
new file mode 100644
index 0000000..ec8a5a5
--- /dev/null
+++ b/src/libs/uioutils.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2003 Serge van den Boom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ * Nota bene: later versions of the GNU General Public License do not apply
+ * to this program.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef LIBS_UIOUTILS_H_
+#define LIBS_UIOUTILS_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "uio/utils.h"
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_UIOUTILS_H_ */
diff --git a/src/libs/unicode.h b/src/libs/unicode.h
new file mode 100644
index 0000000..922a3cc
--- /dev/null
+++ b/src/libs/unicode.h
@@ -0,0 +1,72 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UNICODE_H
+#define UNICODE_H
+
+#include "port.h"
+#include "types.h"
+ // for uint32
+#include <sys/types.h>
+ // For size_t
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef uint32 UniChar;
+
+#ifdef UNICODE_INTERNAL
+# define UNICODE_CHAR unsigned char
+#else
+# define UNICODE_CHAR char
+#endif
+
+UniChar getCharFromString(const UNICODE_CHAR **ptr);
+UniChar getCharFromStringN(const UNICODE_CHAR **ptr,
+ const UNICODE_CHAR *end);
+unsigned char *getLineFromString(const UNICODE_CHAR *start,
+ const UNICODE_CHAR **end, const UNICODE_CHAR **startNext);
+size_t utf8StringCount(const UNICODE_CHAR *start);
+size_t utf8StringCountN(const UNICODE_CHAR *start,
+ const UNICODE_CHAR *end);
+int utf8StringPos (const UNICODE_CHAR *pStr, UniChar ch);
+unsigned char *utf8StringCopy (UNICODE_CHAR *dst, size_t size,
+ const UNICODE_CHAR *src);
+int utf8StringCompare (const UNICODE_CHAR *str1, const UNICODE_CHAR *str2);
+UNICODE_CHAR *skipUTF8Chars(const UNICODE_CHAR *ptr, size_t num);
+size_t getUniCharFromString(UniChar *wstr, size_t maxcount,
+ const UNICODE_CHAR *start);
+size_t getUniCharFromStringN(UniChar *wstr, size_t maxcount,
+ const UNICODE_CHAR *start, const UNICODE_CHAR *end);
+int getStringFromChar(UNICODE_CHAR *ptr, size_t size, UniChar ch);
+size_t getStringFromWideN(UNICODE_CHAR *ptr, size_t size,
+ const UniChar *wstr, size_t count);
+size_t getStringFromWide(UNICODE_CHAR *ptr, size_t size,
+ const UniChar *wstr);
+int UniChar_isGraph(UniChar ch);
+int UniChar_isPrint(UniChar ch);
+UniChar UniChar_toUpper(UniChar ch);
+UniChar UniChar_toLower(UniChar ch);
+
+#undef UNICODE_CHAR
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UNICODE_H */
+
diff --git a/src/libs/video/Makeinfo b/src/libs/video/Makeinfo
new file mode 100644
index 0000000..1282e49
--- /dev/null
+++ b/src/libs/video/Makeinfo
@@ -0,0 +1,3 @@
+uqm_CFILES="vfileins.c vresins.c video.c videodec.c vidplayer.c dukvid.c \
+ legacyplayer.c"
+uqm_HFILES="dukvid.h videodec.h video.h vidintrn.h vidplayer.h"
diff --git a/src/libs/video/dukvid.c b/src/libs/video/dukvid.c
new file mode 100644
index 0000000..d9cb8fa
--- /dev/null
+++ b/src/libs/video/dukvid.c
@@ -0,0 +1,748 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* DUCK video player
+ *
+ * Status: fully functional
+ */
+
+#include "video.h"
+#include "dukvid.h"
+#include <stdio.h>
+#include <string.h>
+#include "libs/uio.h"
+#include "libs/memlib.h"
+#include "endian_uqm.h"
+
+#define THIS_PTR TFB_VideoDecoder* This
+
+static const char* dukv_GetName (void);
+static bool dukv_InitModule (int flags);
+static void dukv_TermModule (void);
+static uint32 dukv_GetStructSize (void);
+static int dukv_GetError (THIS_PTR);
+static bool dukv_Init (THIS_PTR, TFB_PixelFormat* fmt);
+static void dukv_Term (THIS_PTR);
+static bool dukv_Open (THIS_PTR, uio_DirHandle *dir, const char *filename);
+static void dukv_Close (THIS_PTR);
+static int dukv_DecodeNext (THIS_PTR);
+static uint32 dukv_SeekFrame (THIS_PTR, uint32 frame);
+static float dukv_SeekTime (THIS_PTR, float time);
+static uint32 dukv_GetFrame (THIS_PTR);
+static float dukv_GetTime (THIS_PTR);
+
+TFB_VideoDecoderFuncs dukv_DecoderVtbl =
+{
+ dukv_GetName,
+ dukv_InitModule,
+ dukv_TermModule,
+ dukv_GetStructSize,
+ dukv_GetError,
+ dukv_Init,
+ dukv_Term,
+ dukv_Open,
+ dukv_Close,
+ dukv_DecodeNext,
+ dukv_SeekFrame,
+ dukv_SeekTime,
+ dukv_GetFrame,
+ dukv_GetTime,
+};
+
+typedef struct tfb_duckvideoheader
+{
+ uint32 version;
+ uint32 scrn_x_ofs; // horz screen offset in pixels
+ uint32 scrn_y_ofs; // vert screen offset in pixels
+ uint16 wb, hb; // width + height in blocks
+ sint16 lumas[8]; // future luminance deltas
+ sint16 chromas[8]; // future chrominance deltas
+
+} TFB_DuckVideoHeader;
+
+#define NUM_VEC_ITEMS 0x010
+#define NUM_VECTORS 0x100
+
+typedef struct tfb_duckvideodeltas
+{
+ sint32 lumas[NUM_VECTORS][NUM_VEC_ITEMS];
+ sint32 chromas[NUM_VECTORS][NUM_VEC_ITEMS];
+
+} TFB_DuckVideoDeltas;
+
+// specific video decoder struct derived from TFB_VideoDecoder
+// the only sane way in C one can :)
+typedef struct tfb_duckvideodecoder
+{
+ // always the first member
+ TFB_VideoDecoder decoder;
+
+ sint32 last_error;
+ uio_DirHandle* basedir;
+ char* basename;
+ uio_Stream *stream;
+
+// loaded from disk
+ uint32* frames;
+ uint32 cframes;
+ uint32 iframe;
+ uint32 version;
+ uint32 wb, hb; // width, height in blocks
+
+// generated
+ TFB_DuckVideoDeltas d;
+
+ uint8* inbuf;
+ uint32* decbuf;
+
+} TFB_DuckVideoDecoder;
+
+#define DUCK_GENERAL_FPS 14.622f
+#define DUCK_MAX_FRAME_SIZE 0x8000U
+#define DUCK_END_OF_SEQUENCE 1
+
+static void
+dukv_DecodeFrame (uint8* src_p, uint32* dst_p, uint32 wb, uint32 hb,
+ TFB_DuckVideoDeltas* deltas)
+{
+ int iVec;
+ int iSeq;
+ uint32 x, y;
+ sint32 w;
+ uint32 *d_p0, *d_p1;
+ int i;
+
+ w = wb * 4;
+
+ iVec = *(src_p++);
+ iSeq = 0;
+
+ for (y = 0; y < hb; ++y)
+ {
+ sint32 accum0, accum1, corr, corr0, corr1, delta;
+ sint32 pix[4];
+
+ d_p0 = dst_p + y * w * 2;
+ d_p1 = d_p0 + w;
+
+ accum0 = 0;
+ accum1 = 0;
+ corr0 = 0;
+ corr1 = 0;
+
+ for (x = 0; x < wb; ++x)
+ {
+ if (y == 0)
+ {
+ pix[0] = pix[1] = pix[2] = pix[3] = 0;
+ }
+ else
+ {
+ uint32* p_p = d_p0 - w;
+ pix[0] = p_p[0];
+ pix[1] = p_p[1];
+ pix[2] = p_p[2];
+ pix[3] = p_p[3];
+ }
+
+ // start with chroma delta
+ delta = deltas->chromas[iVec][iSeq++];
+ iSeq++; // correctors ignored
+
+ accum0 += delta >> 1;
+ if (delta & 1)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ // line 0
+ for (i = 0; i < 4; ++i, ++d_p0)
+ {
+ delta = deltas->lumas[iVec][iSeq++];
+ corr = deltas->lumas[iVec][iSeq++];
+
+ accum0 += delta >> 1;
+ corr0 ^= corr;
+ pix[i] += accum0;
+ pix[i] ^= corr0;
+
+ if (delta & 1)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ *d_p0 = pix[i];
+ }
+
+ // line 1
+ for (i = 0; i < 4; ++i, ++d_p1)
+ {
+ delta = deltas->lumas[iVec][iSeq++];
+ corr = deltas->lumas[iVec][iSeq++];
+
+ accum1 += delta >> 1;
+ corr1 ^= corr;
+ pix[i] += accum1;
+ pix[i] ^= corr1;
+
+ if (delta & 1)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ *d_p1 = pix[i];
+ }
+ }
+ }
+}
+
+static void
+dukv_DecodeFrameV3 (uint8* src_p, uint32* dst_p, uint32 wb, uint32 hb,
+ TFB_DuckVideoDeltas* deltas)
+{
+ int iVec;
+ int iSeq;
+ uint32 x, y;
+ sint32 w;
+ uint32* d_p;
+ int i;
+
+ iVec = *(src_p++);
+ iSeq = 0;
+
+ hb *= 2;
+ w = wb * 4;
+
+ for (y = 0; y < hb; ++y)
+ {
+ sint32 accum, delta, pix;
+
+ d_p = dst_p + y * w;
+
+ accum = 0;
+
+ for (x = 0; x < wb; ++x)
+ {
+ // start with chroma delta
+ delta = deltas->chromas[iVec][iSeq];
+ iSeq += 2; // correctors ignored
+
+ accum += delta >> 1;
+
+ if (delta & DUCK_END_OF_SEQUENCE)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ for (i = 0; i < 4; ++i, ++d_p)
+ {
+ if (y == 0)
+ pix = 0;
+ else
+ pix = d_p[-w];
+
+ // get next luma delta
+ delta = deltas->lumas[iVec][iSeq];
+ iSeq += 2; // correctors ignored
+
+ accum += delta >> 1;
+ pix += accum;
+
+ if (delta & DUCK_END_OF_SEQUENCE)
+ {
+ iVec = *(src_p++);
+ iSeq = 0;
+ }
+
+ *d_p = pix;
+ }
+ }
+ }
+}
+
+static bool
+dukv_OpenStream (TFB_DuckVideoDecoder* dukv)
+{
+ char filename[280];
+
+ strcat (strcpy (filename, dukv->basename), ".duk");
+
+ return (dukv->stream =
+ uio_fopen (dukv->basedir, filename, "rb")) != NULL;
+}
+
+static bool
+dukv_ReadFrames (TFB_DuckVideoDecoder* dukv)
+{
+ char filename[280];
+ uint32 i;
+ uio_Stream *fp;
+
+ strcat (strcpy (filename, dukv->basename), ".frm");
+
+ if (!(fp = uio_fopen (dukv->basedir, filename, "rb")))
+ return false;
+
+ // get number of frames
+ uio_fseek (fp, 0, SEEK_END);
+ dukv->cframes = uio_ftell (fp) / sizeof (uint32);
+ uio_fseek (fp, 0, SEEK_SET);
+ dukv->frames = (uint32*) HMalloc (dukv->cframes * sizeof (uint32));
+
+ if (uio_fread (dukv->frames, sizeof (uint32), dukv->cframes,
+ fp) != dukv->cframes)
+ {
+ HFree (dukv->frames);
+ dukv->frames = 0;
+ return 0;
+ }
+ uio_fclose (fp);
+
+ for (i = 0; i < dukv->cframes; ++i)
+ dukv->frames[i] = UQM_SwapBE32 (dukv->frames[i]);
+
+ return true;
+}
+
+static bool
+dukv_ReadVectors (TFB_DuckVideoDecoder* dukv, uint8* vectors)
+{
+ uio_Stream *fp;
+ char filename[280];
+ int ret;
+
+ strcat (strcpy (filename, dukv->basename), ".tbl");
+
+ if (!(fp = uio_fopen (dukv->basedir, filename, "rb")))
+ return false;
+
+ ret = uio_fread (vectors, NUM_VEC_ITEMS, NUM_VECTORS, fp);
+ uio_fclose (fp);
+
+ return ret == NUM_VECTORS;
+}
+
+static bool
+dukv_ReadHeader (TFB_DuckVideoDecoder* dukv, sint32* pl, sint32* pc)
+{
+ uio_Stream *fp;
+ char filename[280];
+ int ret;
+ int i;
+ TFB_DuckVideoHeader hdr;
+
+ strcat (strcpy (filename, dukv->basename), ".hdr");
+
+ if (!(fp = uio_fopen (dukv->basedir, filename, "rb")))
+ return false;
+
+ ret = uio_fread (&hdr, sizeof (hdr), 1, fp);
+ uio_fclose (fp);
+ if (!ret)
+ return false;
+
+ dukv->version = UQM_SwapBE32 (hdr.version);
+ dukv->wb = UQM_SwapBE16 (hdr.wb);
+ dukv->hb = UQM_SwapBE16 (hdr.hb);
+
+ for (i = 0; i < 8; ++i)
+ {
+ pl[i] = (sint16) UQM_SwapBE16 (hdr.lumas[i]);
+ pc[i] = (sint16) UQM_SwapBE16 (hdr.chromas[i]);
+ }
+
+ dukv->decoder.w = dukv->wb * 4;
+ dukv->decoder.h = dukv->hb * 4;
+
+ return true;
+}
+
+static sint32
+dukv_make_delta (sint32* protos, bool is_chroma, int i1, int i2)
+{
+ sint32 d1, d2;
+
+ if (!is_chroma)
+ {
+ // 0x421 is (r,g,b)=(1,1,1) in 15bit pixel coding
+ d1 = (protos[i1] >> 1) * 0x421;
+ d2 = (protos[i2] >> 1) * 0x421;
+ return ((d1 << 16) + d2) << 1;
+ }
+ else
+ {
+ d1 = (protos[i1] << 10) + protos[i2];
+ return ((d1 << 16) + d1) << 1;
+ }
+}
+
+static sint32
+dukv_make_corr (sint32* protos, bool is_chroma, int i1, int i2)
+{
+ sint32 d1, d2;
+
+ if (!is_chroma)
+ {
+ d1 = (protos[i1] & 1) << 15;
+ d2 = (protos[i2] & 1) << 15;
+ return (d1 << 16) + d2;
+ }
+ else
+ {
+ return (i1 << 3) + i2;
+ }
+}
+
+static void
+dukv_DecodeVector (uint8* vec, sint32* p, bool is_chroma, sint32* deltas)
+{
+ int citems = vec[0];
+ int i;
+
+ for (i = 0; i < citems; i += 2, vec += 2, deltas += 2)
+ {
+ sint32 d = dukv_make_delta (p, is_chroma, vec[1], vec[2]);
+
+ if (i == citems - 2)
+ d |= DUCK_END_OF_SEQUENCE;
+
+ deltas[0] = d;
+ deltas[1] = dukv_make_corr (p, is_chroma, vec[1], vec[2]);
+ }
+
+}
+
+static void
+dukv_InitDeltas (TFB_DuckVideoDecoder* dukv, uint8* vectors,
+ sint32* pl, sint32* pc)
+{
+ int i;
+
+ for (i = 0; i < NUM_VECTORS; ++i)
+ {
+ uint8* vector = vectors + i * NUM_VEC_ITEMS;
+ dukv_DecodeVector (vector, pl, false, dukv->d.lumas[i]);
+ dukv_DecodeVector (vector, pc, true, dukv->d.chromas[i]);
+ }
+}
+
+static inline uint32
+dukv_PixelConv (uint16 pix, const TFB_PixelFormat* fmt)
+{
+ uint32 r, g, b;
+
+ r = (pix >> 7) & 0xf8;
+ g = (pix >> 2) & 0xf8;
+ b = (pix << 3) & 0xf8;
+
+ return
+ ((r >> fmt->Rloss) << fmt->Rshift) |
+ ((g >> fmt->Gloss) << fmt->Gshift) |
+ ((b >> fmt->Bloss) << fmt->Bshift);
+}
+
+static void
+dukv_RenderFrame (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ const TFB_PixelFormat* fmt = This->format;
+ uint32 h, x, y;
+ uint32* dec = dukv->decbuf;
+
+ h = dukv->decoder.h / 2;
+
+ // separate bpp versions for speed
+ switch (fmt->BytesPerPixel)
+ {
+ case 2:
+ {
+ for (y = 0; y < h; ++y)
+ {
+ uint16 *dst0, *dst1;
+
+ dst0 = (uint16*) This->callbacks.GetCanvasLine (This, y * 2);
+ dst1 = (uint16*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
+
+ for (x = 0; x < dukv->decoder.w; ++x, ++dec, ++dst0, ++dst1)
+ {
+ uint32 pair = *dec;
+ *dst0 = dukv_PixelConv ((uint16)(pair >> 16), fmt);
+ *dst1 = dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
+ }
+ }
+ break;
+ }
+ case 3:
+ {
+ for (y = 0; y < h; ++y)
+ {
+ uint8 *dst0, *dst1;
+
+ dst0 = (uint8*) This->callbacks.GetCanvasLine (This, y * 2);
+ dst1 = (uint8*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
+
+ for (x = 0; x < dukv->decoder.w;
+ ++x, ++dec, dst0 += 3, dst1 += 3)
+ {
+ uint32 pair = *dec;
+ *(uint32*)dst0 =
+ dukv_PixelConv ((uint16)(pair >> 16), fmt);
+ *(uint32*)dst1 =
+ dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
+ }
+ }
+ break;
+ }
+ case 4:
+ {
+ for (y = 0; y < h; ++y)
+ {
+ uint32 *dst0, *dst1;
+
+ dst0 = (uint32*) This->callbacks.GetCanvasLine (This, y * 2);
+ dst1 = (uint32*) This->callbacks.GetCanvasLine (This, y * 2 + 1);
+
+ for (x = 0; x < dukv->decoder.w; ++x, ++dec, ++dst0, ++dst1)
+ {
+ uint32 pair = *dec;
+ *dst0 = dukv_PixelConv ((uint16)(pair >> 16), fmt);
+ *dst1 = dukv_PixelConv ((uint16)(pair & 0xffff), fmt);
+ }
+ }
+ break;
+ }
+ default:
+ ;
+ }
+}
+
+static const char*
+dukv_GetName (void)
+{
+ return "DukVid";
+}
+
+static bool
+dukv_InitModule (int flags)
+{
+ // no flags are defined for now
+ return true;
+
+ (void)flags; // dodge compiler warning
+}
+
+static void
+dukv_TermModule (void)
+{
+ // do an extensive search on the word 'nothing'
+}
+
+static uint32
+dukv_GetStructSize (void)
+{
+ return sizeof (TFB_DuckVideoDecoder);
+}
+
+static int
+dukv_GetError (THIS_PTR)
+{
+ return This->error;
+}
+
+static bool
+dukv_Init (THIS_PTR, TFB_PixelFormat* fmt)
+{
+ This->format = fmt;
+ This->audio_synced = true;
+ return true;
+}
+
+static void
+dukv_Term (THIS_PTR)
+{
+ //TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ dukv_Close (This);
+}
+
+static bool
+dukv_Open (THIS_PTR, uio_DirHandle *dir, const char *filename)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ char* pext;
+ sint32 lumas[8], chromas[8];
+ uint8* vectors;
+
+ dukv->basedir = dir;
+ dukv->basename = HMalloc (strlen (filename) + 1);
+ strcpy (dukv->basename, filename);
+ pext = strrchr (dukv->basename, '.');
+ if (pext) // strip extension
+ *pext = 0;
+
+ vectors = HMalloc (NUM_VEC_ITEMS * NUM_VECTORS);
+
+ if (!dukv_OpenStream (dukv)
+ || !dukv_ReadFrames (dukv)
+ || !dukv_ReadHeader (dukv, lumas, chromas)
+ || !dukv_ReadVectors (dukv, vectors))
+ {
+ HFree (vectors);
+ dukv_Close (This);
+ dukv->last_error = dukve_BadFile;
+ return false;
+ }
+
+ dukv_InitDeltas (dukv, vectors, lumas, chromas);
+ HFree (vectors);
+
+ This->length = (float) dukv->cframes / DUCK_GENERAL_FPS;
+ This->frame_count = dukv->cframes;
+ This->interframe_wait = (uint32) (1000.0 / DUCK_GENERAL_FPS);
+
+ dukv->inbuf = HMalloc (DUCK_MAX_FRAME_SIZE);
+ dukv->decbuf = HMalloc (
+ dukv->decoder.w * dukv->decoder.h * sizeof (uint16));
+
+ return true;
+}
+
+static void
+dukv_Close (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ if (dukv->basename)
+ {
+ HFree (dukv->basename);
+ dukv->basename = NULL;
+ }
+ if (dukv->frames)
+ {
+ HFree (dukv->frames);
+ dukv->frames = NULL;
+ }
+ if (dukv->stream)
+ {
+ uio_fclose (dukv->stream);
+ dukv->stream = NULL;
+ }
+ if (dukv->inbuf)
+ {
+ HFree (dukv->inbuf);
+ dukv->inbuf = NULL;
+ }
+ if (dukv->decbuf)
+ {
+ HFree (dukv->decbuf);
+ dukv->decbuf = NULL;
+ }
+}
+
+static int
+dukv_DecodeNext (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ uint32 fh[2];
+ uint32 vofs;
+ uint32 vsize;
+ uint16 ver;
+
+ if (!dukv->stream || dukv->iframe >= dukv->cframes)
+ return 0;
+
+ uio_fseek (dukv->stream, dukv->frames[dukv->iframe], SEEK_SET);
+ if (uio_fread (&fh, sizeof (fh), 1, dukv->stream) != 1)
+ {
+ dukv->last_error = dukve_EOF;
+ return 0;
+ }
+
+ vofs = UQM_SwapBE32 (fh[0]);
+ vsize = UQM_SwapBE32 (fh[1]);
+ if (vsize > DUCK_MAX_FRAME_SIZE)
+ {
+ dukv->last_error = dukve_OutOfBuf;
+ return -1;
+ }
+
+ uio_fseek (dukv->stream, vofs, SEEK_CUR);
+ if (uio_fread (dukv->inbuf, 1, vsize, dukv->stream) != vsize)
+ {
+ dukv->last_error = dukve_EOF;
+ return 0;
+ }
+
+ ver = UQM_SwapBE16 (*(uint16*)dukv->inbuf);
+ if (ver == 0x0300)
+ dukv_DecodeFrameV3 (dukv->inbuf + 0x10, dukv->decbuf,
+ dukv->wb, dukv->hb, &dukv->d);
+ else
+ dukv_DecodeFrame (dukv->inbuf + 0x10, dukv->decbuf,
+ dukv->wb, dukv->hb, &dukv->d);
+
+ dukv->iframe++;
+
+ This->callbacks.BeginFrame (This);
+ dukv_RenderFrame (This);
+ This->callbacks.EndFrame (This);
+
+ if (!This->audio_synced)
+ This->callbacks.SetTimer (This, (uint32) (1000.0f / DUCK_GENERAL_FPS));
+
+ return 1;
+}
+
+static uint32
+dukv_SeekFrame (THIS_PTR, uint32 frame)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ if (frame > dukv->cframes)
+ frame = dukv->cframes; // EOS
+
+ return dukv->iframe = frame;
+}
+
+static float
+dukv_SeekTime (THIS_PTR, float time)
+{
+ //TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+ uint32 frame = (uint32) (time * DUCK_GENERAL_FPS);
+
+ // Note that DUCK_GENERAL_FPS is a float constant
+ return dukv_SeekFrame (This, frame) / DUCK_GENERAL_FPS;
+}
+
+static uint32
+dukv_GetFrame (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ return dukv->iframe;
+}
+
+static float
+dukv_GetTime (THIS_PTR)
+{
+ TFB_DuckVideoDecoder* dukv = (TFB_DuckVideoDecoder*) This;
+
+ return (float) dukv->iframe / DUCK_GENERAL_FPS;
+}
diff --git a/src/libs/video/dukvid.h b/src/libs/video/dukvid.h
new file mode 100644
index 0000000..a6d70b0
--- /dev/null
+++ b/src/libs/video/dukvid.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_VIDEO_DUKVID_H_
+#define LIBS_VIDEO_DUKVID_H_
+
+#include "libs/video/videodec.h"
+
+extern TFB_VideoDecoderFuncs dukv_DecoderVtbl;
+
+typedef enum
+{
+ // positive values are the same as in errno
+ dukve_None = 0,
+ dukve_Unknown = -1,
+ dukve_BadFile = -2,
+ dukve_BadArg = -3,
+ dukve_OutOfBuf = -4,
+ dukve_EOF = -5,
+ dukve_Other = -1000,
+} DukVid_Error;
+
+#endif // LIBS_VIDEO_DUKVID_H_
diff --git a/src/libs/video/legacyplayer.c b/src/libs/video/legacyplayer.c
new file mode 100644
index 0000000..5dfddad
--- /dev/null
+++ b/src/libs/video/legacyplayer.c
@@ -0,0 +1,81 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "vidintrn.h"
+#include "video.h"
+#include "vidplayer.h"
+#include "libs/memlib.h"
+
+
+LEGACY_VIDEO_REF
+PlayLegacyVideo (LEGACY_VIDEO vid)
+{
+ const char *name, *audname, *speechname;
+ uint32 loopframe;
+ LEGACY_VIDEO_REF ref;
+ VIDEO_TYPE type;
+
+ if (!vid)
+ return NULL;
+ ref = HCalloc (sizeof (*ref));
+ if (!ref)
+ return NULL;
+ name = vid->video;
+ audname = vid->audio;
+ speechname = vid->speech;
+ loopframe = vid->loop;
+
+ ref->vidref = LoadVideoFile (name);
+ if (!ref->vidref)
+ return NULL;
+ if (audname)
+ ref->audref = LoadMusicFile (audname);
+ if (speechname)
+ ref->speechref = LoadMusicFile (speechname);
+
+ type = VidPlayEx (ref->vidref, ref->audref, ref->speechref, loopframe);
+ if (type == NO_FMV)
+ { // Video failed to start
+ StopLegacyVideo (ref);
+ return NULL;
+ }
+
+ return ref;
+}
+
+void
+StopLegacyVideo (LEGACY_VIDEO_REF ref)
+{
+ if (!ref)
+ return;
+ VidStop ();
+
+ DestroyVideo (ref->vidref);
+ if (ref->speechref)
+ DestroyMusic (ref->speechref);
+ if (ref->audref)
+ DestroyMusic (ref->audref);
+
+ HFree (ref);
+}
+
+BOOLEAN
+PlayingLegacyVideo (LEGACY_VIDEO_REF ref)
+{
+ if (!ref)
+ return FALSE;
+ return TFB_VideoPlaying (ref->vidref);
+}
diff --git a/src/libs/video/vfileins.c b/src/libs/video/vfileins.c
new file mode 100644
index 0000000..26cb7c1
--- /dev/null
+++ b/src/libs/video/vfileins.c
@@ -0,0 +1,28 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "libs/vidlib.h"
+#include "video.h"
+
+VIDEO_REF
+LoadVideoFile (const char *pStr)
+{
+ return _init_video_file (pStr);
+}
+
+
diff --git a/src/libs/video/video.c b/src/libs/video/video.c
new file mode 100644
index 0000000..dd4cd46
--- /dev/null
+++ b/src/libs/video/video.c
@@ -0,0 +1,190 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "video.h"
+
+#include "vidintrn.h"
+#include "options.h"
+#include "vidplayer.h"
+#include "libs/memlib.h"
+#include "libs/sndlib.h"
+
+
+#define NULL_VIDEO_REF (0)
+static VIDEO_REF _cur_video = NULL_VIDEO_REF;
+static MUSIC_REF _cur_speech = 0;
+
+BOOLEAN
+InitVideoPlayer (BOOLEAN useCDROM)
+ //useCDROM doesn't really apply to us
+{
+ TFB_PixelFormat fmt;
+
+ TFB_DrawCanvas_GetScreenFormat (&fmt);
+ if (!VideoDecoder_Init (0, fmt.BitsPerPixel, fmt.Rmask,
+ fmt.Gmask, fmt.Bmask, 0))
+ return FALSE;
+
+ return TFB_InitVideoPlayer ();
+
+ (void)useCDROM; /* dodge compiler warning */
+}
+
+void
+UninitVideoPlayer (void)
+{
+ TFB_UninitVideoPlayer ();
+ VideoDecoder_Uninit ();
+}
+
+void
+VidStop (void)
+{
+ if (_cur_speech)
+ snd_StopSpeech ();
+ if (_cur_video)
+ TFB_StopVideo (_cur_video);
+ _cur_speech = 0;
+ _cur_video = NULL_VIDEO_REF;
+}
+
+VIDEO_REF
+VidPlaying (void)
+ // this should just probably return BOOLEAN
+{
+ if (!_cur_video)
+ return NULL_VIDEO_REF;
+
+ if (TFB_VideoPlaying (_cur_video))
+ return _cur_video;
+
+ return NULL_VIDEO_REF;
+}
+
+BOOLEAN
+VidProcessFrame (void)
+{
+ if (!_cur_video)
+ return FALSE;
+ return TFB_ProcessVideoFrame (_cur_video);
+}
+
+// return current video position in milliseconds
+DWORD
+VidGetPosition (void)
+{
+ if (!VidPlaying ())
+ return 0;
+ return TFB_GetVideoPosition (_cur_video);
+}
+
+BOOLEAN
+VidSeek (DWORD pos)
+ // pos in milliseconds
+{
+ if (!VidPlaying ())
+ return FALSE;
+ return TFB_SeekVideo (_cur_video, pos);
+}
+
+VIDEO_TYPE
+VidPlayEx (VIDEO_REF vid, MUSIC_REF AudRef, MUSIC_REF SpeechRef,
+ DWORD LoopFrame)
+{
+ VIDEO_TYPE ret;
+
+ if (!vid)
+ return NO_FMV;
+
+ if (AudRef)
+ {
+ if (vid->hAudio)
+ DestroyMusic (vid->hAudio);
+ vid->hAudio = AudRef;
+ vid->decoder->audio_synced = FALSE;
+ }
+
+ vid->loop_frame = LoopFrame;
+ vid->loop_to = 0;
+
+ if (_cur_speech)
+ snd_StopSpeech ();
+ if (_cur_video)
+ TFB_StopVideo (_cur_video);
+ _cur_speech = 0;
+ _cur_video = NULL_VIDEO_REF;
+
+ // play video in the center of the screen
+ if (TFB_PlayVideo (vid, (ScreenWidth - vid->w) / 2,
+ (ScreenHeight - vid->h) / 2))
+ {
+ _cur_video = vid;
+ ret = SOFTWARE_FMV;
+ if (SpeechRef)
+ {
+ snd_PlaySpeech (SpeechRef);
+ _cur_speech = SpeechRef;
+ }
+ }
+ else
+ {
+ ret = NO_FMV;
+ }
+
+ return ret;
+}
+
+VIDEO_TYPE
+VidPlay (VIDEO_REF VidRef)
+{
+ return VidPlayEx (VidRef, 0, 0, VID_NO_LOOP);
+}
+
+VIDEO_REF
+_init_video_file (const char *pStr)
+{
+ TFB_VideoClip* vid;
+ TFB_VideoDecoder* dec;
+
+ dec = VideoDecoder_Load (contentDir, pStr);
+ if (!dec)
+ return NULL_VIDEO_REF;
+
+ vid = HCalloc (sizeof (*vid));
+ vid->decoder = dec;
+ vid->length = dec->length;
+ vid->w = vid->decoder->w;
+ vid->h = vid->decoder->h;
+ vid->guard = CreateMutex ("video guard", SYNC_CLASS_VIDEO);
+
+ return (VIDEO_REF) vid;
+}
+
+BOOLEAN
+DestroyVideo (VIDEO_REF vid)
+{
+ if (!vid)
+ return FALSE;
+
+ // just some armouring; should already be stopped
+ TFB_StopVideo (vid);
+
+ VideoDecoder_Free (vid->decoder);
+ DestroyMutex (vid->guard);
+ HFree (vid);
+
+ return TRUE;
+}
diff --git a/src/libs/video/video.h b/src/libs/video/video.h
new file mode 100644
index 0000000..5e76aa4
--- /dev/null
+++ b/src/libs/video/video.h
@@ -0,0 +1,56 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_VIDEO_VIDEO_H_
+#define LIBS_VIDEO_VIDEO_H_
+
+#include "libs/vidlib.h"
+#include "libs/sndlib.h"
+#include "libs/graphics/tfb_draw.h"
+#include "types.h"
+#include "videodec.h"
+#include "libs/sound/sound.h"
+
+
+typedef struct tfb_videoclip
+{
+ TFB_VideoDecoder *decoder; // decoder to read from
+ float length; // total length of clip seconds
+ uint32 w, h;
+
+ // video player data
+ RECT dst_rect; // destination screen rect
+ RECT src_rect; // source rect
+ MUSIC_REF hAudio;
+ uint32 frame_time; // time when next frame should be rendered
+ TFB_Image* frame; // frame preped and optimized for rendering
+ uint32 cur_frame; // index of frame currently displayed
+ bool playing;
+ bool own_audio;
+ uint32 loop_frame; // frame index to loop from
+ uint32 loop_to; // frame index to loop to
+
+ Mutex guard;
+ uint32 want_frame; // audio-signaled desired frame index
+ int lag_cnt; // N of frames video is behind or ahead of audio
+
+ void* data; // user-defined data
+
+} TFB_VideoClip;
+
+extern VIDEO_REF _init_video_file(const char *pStr);
+
+#endif // LIBS_VIDEO_VIDEO_H_
diff --git a/src/libs/video/videodec.c b/src/libs/video/videodec.c
new file mode 100644
index 0000000..b99a442
--- /dev/null
+++ b/src/libs/video/videodec.c
@@ -0,0 +1,363 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include "video.h"
+#include "videodec.h"
+#include "dukvid.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+#define MAX_REG_DECODERS 31
+
+static bool vd_inited = false;
+static TFB_PixelFormat vd_vidfmt;
+static int vd_flags = 0;
+
+struct TFB_RegVideoDecoder
+{
+ bool builtin;
+ bool used; // ever used indicator
+ const char* ext;
+ const TFB_VideoDecoderFuncs* funcs;
+};
+static TFB_RegVideoDecoder vd_decoders[MAX_REG_DECODERS + 1] =
+{
+ {true, true, "duk", &dukv_DecoderVtbl},
+ {false, false, NULL, NULL}, // null term
+};
+
+static void vd_computeMasks (uint32 mask, DWORD* shift, DWORD* loss);
+
+const char*
+VideoDecoder_GetName (TFB_VideoDecoder *decoder)
+{
+ if (!decoder || !decoder->funcs)
+ return "(Null)";
+ return decoder->funcs->GetName ();
+}
+
+bool
+VideoDecoder_Init (int flags, int depth, uint32 Rmask, uint32 Gmask,
+ uint32 Bmask, uint32 Amask)
+{
+ TFB_RegVideoDecoder* info;
+
+ vd_inited = false;
+
+ if (depth < 15 || depth > 32)
+ {
+ log_add (log_Error, "VideoDecoder_Init: "
+ "Unsupported video depth %d", depth);
+ return false;
+ }
+
+ if ((Rmask & Gmask) || (Rmask & Bmask) || (Rmask & Amask) ||
+ (Gmask & Bmask) || (Gmask & Amask) || (Bmask & Amask))
+ {
+ log_add (log_Error, "VideoDecoder_Init: Invalid channel masks");
+ return false;
+ }
+
+ // BEGIN: adapted from SDL
+ vd_vidfmt.BitsPerPixel = depth;
+ vd_vidfmt.BytesPerPixel = (depth + 7) / 8;
+ vd_vidfmt.Rmask = Rmask;
+ vd_vidfmt.Gmask = Gmask;
+ vd_vidfmt.Bmask = Bmask;
+ vd_vidfmt.Amask = Amask;
+ vd_computeMasks (Rmask, &vd_vidfmt.Rshift, &vd_vidfmt.Rloss);
+ vd_computeMasks (Gmask, &vd_vidfmt.Gshift, &vd_vidfmt.Gloss);
+ vd_computeMasks (Bmask, &vd_vidfmt.Bshift, &vd_vidfmt.Bloss);
+ vd_computeMasks (Amask, &vd_vidfmt.Ashift, &vd_vidfmt.Aloss);
+ // END: adapted from SDL
+
+ // init built-in decoders
+ for (info = vd_decoders; info->ext; info++)
+ {
+ if (!info->funcs->InitModule (flags))
+ {
+ log_add (log_Error, "VideoDecoder_Init(): "
+ "%s video decoder init failed",
+ info->funcs->GetName ());
+ }
+ }
+
+ vd_flags = flags;
+ vd_inited = true;
+
+ return true;
+}
+
+void
+VideoDecoder_Uninit (void)
+{
+ TFB_RegVideoDecoder* info;
+
+ // uninit all decoders
+ // and unregister loaded decoders
+ for (info = vd_decoders; info->used; info++)
+ {
+ if (info->ext) // check if present
+ info->funcs->TermModule ();
+
+ if (!info->builtin)
+ {
+ info->used = false;
+ info->ext = NULL;
+ }
+ }
+
+ vd_inited = false;
+}
+
+TFB_RegVideoDecoder*
+VideoDecoder_Register (const char* fileext, TFB_VideoDecoderFuncs* decvtbl)
+{
+ TFB_RegVideoDecoder* info;
+ TFB_RegVideoDecoder* newslot = NULL;
+
+ if (!decvtbl)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): Null decoder table");
+ return NULL;
+ }
+ if (!fileext)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): Bad file type for %s",
+ decvtbl->GetName ());
+ return NULL;
+ }
+
+ // check if extension already registered
+ for (info = vd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, fileext) != 0);
+ ++info)
+ {
+ // and pick up an empty slot (where available)
+ if (!newslot && !info->ext)
+ newslot = info;
+ }
+
+ if (info >= vd_decoders + MAX_REG_DECODERS)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): Decoders limit reached");
+ return NULL;
+ }
+ else if (info->ext)
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): "
+ "'%s' decoder already registered (%s denied)",
+ fileext, decvtbl->GetName ());
+ return NULL;
+ }
+
+ if (!decvtbl->InitModule (vd_flags))
+ {
+ log_add (log_Warning, "VideoDecoder_Register(): %s decoder init failed",
+ decvtbl->GetName ());
+ return NULL;
+ }
+
+ if (!newslot)
+ {
+ newslot = info;
+ newslot->used = true;
+ // make next one a term
+ info[1].builtin = false;
+ info[1].used = false;
+ info[1].ext = NULL;
+ }
+
+ newslot->ext = fileext;
+ newslot->funcs = decvtbl;
+
+ return newslot;
+}
+
+void
+VideoDecoder_Unregister (TFB_RegVideoDecoder* regdec)
+{
+ if (regdec < vd_decoders || regdec >= vd_decoders + MAX_REG_DECODERS ||
+ !regdec->ext || !regdec->funcs)
+ {
+ log_add (log_Warning, "VideoDecoder_Unregister(): "
+ "Invalid or expired decoder passed");
+ return;
+ }
+
+ regdec->funcs->TermModule ();
+ regdec->ext = NULL;
+ regdec->funcs = NULL;
+}
+
+const TFB_VideoDecoderFuncs*
+VideoDecoder_Lookup (const char* fileext)
+{
+ TFB_RegVideoDecoder* info;
+
+ for (info = vd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, fileext) != 0);
+ ++info)
+ ;
+ return info->ext ? info->funcs : NULL;
+}
+
+TFB_VideoDecoder*
+VideoDecoder_Load (uio_DirHandle *dir, const char *filename)
+{
+ const char* pext;
+ TFB_RegVideoDecoder* info;
+ TFB_VideoDecoder* decoder;
+
+
+ if (!vd_inited)
+ return NULL;
+
+ pext = strrchr (filename, '.');
+ if (!pext)
+ {
+ log_add (log_Warning, "VideoDecoder_Load: Unknown file type");
+ return NULL;
+ }
+ ++pext;
+
+ for (info = vd_decoders; info->used &&
+ (!info->ext || strcmp (info->ext, pext) != 0);
+ ++info)
+ ;
+ if (!info->ext)
+ {
+ log_add (log_Warning, "VideoDecoder_Load: Unsupported file type");
+ return NULL;
+ }
+
+ decoder = HCalloc (info->funcs->GetStructSize ());
+ decoder->funcs = info->funcs;
+ if (!decoder->funcs->Init (decoder, &vd_vidfmt))
+ {
+ log_add (log_Warning, "VideoDecoder_Load: "
+ "Cannot init '%s' decoder, code %d",
+ decoder->funcs->GetName (),
+ decoder->funcs->GetError (decoder));
+ HFree (decoder);
+ return NULL;
+ }
+
+ decoder->dir = dir;
+ decoder->filename = (char *) HMalloc (strlen (filename) + 1);
+ strcpy (decoder->filename, filename);
+ decoder->error = VIDEODECODER_OK;
+
+ if (!decoder->funcs->Open (decoder, dir, filename))
+ {
+ log_add (log_Warning, "VideoDecoder_Load: "
+ "'%s' decoder did not load %s, code %d",
+ decoder->funcs->GetName (), filename,
+ decoder->funcs->GetError (decoder));
+
+ VideoDecoder_Free (decoder);
+ return NULL;
+ }
+
+ return decoder;
+}
+
+// return: >0 = OK, 0 = EOF, <0 = Error
+int
+VideoDecoder_Decode (TFB_VideoDecoder *decoder)
+{
+ int ret;
+
+ if (!decoder)
+ return 0;
+
+ decoder->cur_frame = decoder->funcs->GetFrame (decoder);
+ decoder->pos = decoder->funcs->GetTime (decoder);
+
+ ret = decoder->funcs->DecodeNext (decoder);
+ if (ret == 0)
+ decoder->error = VIDEODECODER_EOF;
+ else if (ret < 0)
+ decoder->error = VIDEODECODER_ERROR;
+ else
+ decoder->error = VIDEODECODER_OK;
+
+ return ret;
+}
+
+float
+VideoDecoder_Seek (TFB_VideoDecoder *decoder, float pos)
+{
+ if (!decoder)
+ return 0.0;
+
+ decoder->pos = decoder->funcs->SeekTime (decoder, pos);
+ decoder->cur_frame = decoder->funcs->GetFrame (decoder);
+
+ return decoder->pos;
+}
+
+uint32
+VideoDecoder_SeekFrame (TFB_VideoDecoder *decoder, uint32 frame)
+{
+ if (!decoder)
+ return 0;
+
+ decoder->cur_frame = decoder->funcs->SeekFrame (decoder, frame);
+ decoder->pos = decoder->funcs->GetTime (decoder);
+
+ return decoder->cur_frame;
+}
+
+void
+VideoDecoder_Rewind (TFB_VideoDecoder *decoder)
+{
+ if (!decoder)
+ return;
+
+ VideoDecoder_Seek (decoder, 0);
+}
+
+void
+VideoDecoder_Free (TFB_VideoDecoder *decoder)
+{
+ if (!decoder)
+ return;
+
+ decoder->funcs->Close (decoder);
+ decoder->funcs->Term (decoder);
+
+ HFree (decoder->filename);
+ HFree (decoder);
+}
+
+// BEGIN: adapted from SDL
+static void
+vd_computeMasks (uint32 mask, DWORD* shift, DWORD* loss)
+{
+ *shift = 0;
+ *loss = 8;
+ if (mask)
+ {
+ for (; !(mask & 1); mask >>= 1 )
+ ++*shift;
+
+ for (; (mask & 1); mask >>= 1 )
+ --*loss;
+ }
+}
+// END: adapted from SDL
diff --git a/src/libs/video/videodec.h b/src/libs/video/videodec.h
new file mode 100644
index 0000000..2d75c98
--- /dev/null
+++ b/src/libs/video/videodec.h
@@ -0,0 +1,124 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_VIDEO_VIDEODEC_H_
+#define LIBS_VIDEO_VIDEODEC_H_
+
+#include "libs/vidlib.h"
+#include "libs/video/video.h"
+#include "libs/reslib.h"
+
+// forward-declare
+typedef struct tfb_videodecoder TFB_VideoDecoder;
+
+#define THIS_PTR TFB_VideoDecoder*
+
+typedef struct tfb_videodecoderfunc
+{
+ const char* (* GetName) (void);
+ bool (* InitModule) (int flags);
+ void (* TermModule) (void);
+ uint32 (* GetStructSize) (void);
+ int (* GetError) (THIS_PTR);
+ bool (* Init) (THIS_PTR, TFB_PixelFormat* fmt);
+ void (* Term) (THIS_PTR);
+ bool (* Open) (THIS_PTR, uio_DirHandle *dir, const char *filename);
+ void (* Close) (THIS_PTR);
+ int (* DecodeNext) (THIS_PTR);
+ uint32 (* SeekFrame) (THIS_PTR, uint32 frame);
+ float (* SeekTime) (THIS_PTR, float time);
+ uint32 (* GetFrame) (THIS_PTR);
+ float (* GetTime) (THIS_PTR);
+
+} TFB_VideoDecoderFuncs;
+
+// decoder will call these to get info
+// from the player
+typedef struct tfb_videocallbacks
+{
+ // any decoder calls these
+ void (* BeginFrame) (THIS_PTR);
+ void (* EndFrame) (THIS_PTR);
+ void* (* GetCanvasLine) (THIS_PTR, uint32 line);
+ // non-audio-driven decoders call this to figure out
+ // when the next frame should be drawn
+ uint32 (* GetTicks) (THIS_PTR);
+ // non-audio-driven decoders call this to inform
+ // the player when the next frame should be drawn
+ bool (* SetTimer) (THIS_PTR, uint32 msecs);
+
+} TFB_VideoCallbacks;
+
+#undef THIS_PTR
+
+struct tfb_videodecoder
+{
+ // decoder virtual funcs - R/O
+ const TFB_VideoDecoderFuncs *funcs;
+ // video formats - R/O
+ const TFB_PixelFormat *format;
+ // decoder-set data - R/O
+ uint32 w, h;
+ float length; // total length in seconds
+ uint32 frame_count;
+ uint32 interframe_wait; // nominal interframe delay in msecs
+ bool audio_synced;
+ // decoder callbacks
+ TFB_VideoCallbacks callbacks;
+
+ // other - public
+ bool looping;
+ void* data; // user-defined data
+ // info - public R/O
+ sint32 error;
+ float pos; // position in seconds
+ uint32 cur_frame;
+
+ // semi-private
+ uio_DirHandle *dir;
+ char *filename;
+
+};
+
+// return values
+enum
+{
+ VIDEODECODER_OK,
+ VIDEODECODER_ERROR,
+ VIDEODECODER_EOF,
+};
+
+typedef struct TFB_RegVideoDecoder TFB_RegVideoDecoder;
+
+TFB_RegVideoDecoder* VideoDecoder_Register (const char* fileext,
+ TFB_VideoDecoderFuncs* decvtbl);
+void VideoDecoder_Unregister (TFB_RegVideoDecoder* regdec);
+const TFB_VideoDecoderFuncs* VideoDecoder_Lookup (const char* fileext);
+
+bool VideoDecoder_Init (int flags, int depth, uint32 Rmask, uint32 Gmask,
+ uint32 Bmask, uint32 Amask);
+void VideoDecoder_Uninit (void);
+TFB_VideoDecoder* VideoDecoder_Load (uio_DirHandle *dir,
+ const char *filename);
+int VideoDecoder_Decode (TFB_VideoDecoder *decoder);
+float VideoDecoder_Seek (TFB_VideoDecoder *decoder, float time_pos);
+uint32 VideoDecoder_SeekFrame (TFB_VideoDecoder *decoder, uint32 frame_pos);
+void VideoDecoder_Rewind (TFB_VideoDecoder *decoder);
+void VideoDecoder_Free (TFB_VideoDecoder *decoder);
+const char* VideoDecoder_GetName (TFB_VideoDecoder *decoder);
+
+
+#endif // LIBS_VIDEO_VIDEODEC_H_
diff --git a/src/libs/video/vidintrn.h b/src/libs/video/vidintrn.h
new file mode 100644
index 0000000..40a19e4
--- /dev/null
+++ b/src/libs/video/vidintrn.h
@@ -0,0 +1,41 @@
+// Copyright 2008 Michael Martin
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef VIDINTERN_H_
+#define VIDINTERN_H_
+
+#include "types.h"
+#include "libs/vidlib.h"
+#include "libs/threadlib.h"
+
+struct legacy_video_desc
+{
+ char *video, *audio, *speech;
+ uint32 loop;
+};
+
+typedef struct legacy_video_desc LEGACY_VIDEO_DESC;
+
+struct legacy_video_ref
+{
+ VIDEO_REF vidref;
+ MUSIC_REF audref;
+ MUSIC_REF speechref;
+};
+
+#endif
diff --git a/src/libs/video/vidplayer.c b/src/libs/video/vidplayer.c
new file mode 100644
index 0000000..09a506d
--- /dev/null
+++ b/src/libs/video/vidplayer.c
@@ -0,0 +1,481 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "vidplayer.h"
+
+#include "vidintrn.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/sndlib.h"
+
+// video callbacks
+static void vp_BeginFrame (TFB_VideoDecoder*);
+static void vp_EndFrame (TFB_VideoDecoder*);
+static void* vp_GetCanvasLine (TFB_VideoDecoder*, uint32 line);
+static uint32 vp_GetTicks (TFB_VideoDecoder*);
+static bool vp_SetTimer (TFB_VideoDecoder*, uint32 msecs);
+
+
+static const TFB_VideoCallbacks vp_DecoderCBs =
+{
+ vp_BeginFrame,
+ vp_EndFrame,
+ vp_GetCanvasLine,
+ vp_GetTicks,
+ vp_SetTimer
+};
+
+// audio stream callbacks
+static bool vp_AudioStart (TFB_SoundSample* sample);
+static void vp_AudioEnd (TFB_SoundSample* sample);
+static void vp_BufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag);
+static void vp_QueueBuffer (TFB_SoundSample* sample, audio_Object buffer);
+
+static const TFB_SoundCallbacks vp_AudioCBs =
+{
+ vp_AudioStart,
+ NULL,
+ vp_AudioEnd,
+ vp_BufferTag,
+ vp_QueueBuffer
+};
+
+
+bool
+TFB_InitVideoPlayer (void)
+{
+ // now just a stub
+ return true;
+}
+
+void
+TFB_UninitVideoPlayer (void)
+{
+ // now just a stub
+}
+
+static inline sint32
+msecToTimeCount (sint32 msec)
+{
+ return msec * ONE_SECOND / 1000;
+}
+
+// audio-synced video playback frame function
+// the frame rate and timing is dictated by the audio
+static bool
+processAudioSyncedFrame (VIDEO_REF vid)
+{
+#define MAX_FRAME_LAG 8
+#define LAG_FRACTION 6
+#define SYNC_BIAS 1 / 3
+ int ret;
+ uint32 want_frame;
+ uint32 prev_want_frame;
+ sint32 wait_msec;
+ CONTEXT oldContext;
+ TimeCount Now = GetTimeCounter ();
+
+ if (!vid->playing)
+ return false;
+
+ if (Now < vid->frame_time)
+ return true; // not time yet
+
+ LockMutex (vid->guard);
+ want_frame = vid->want_frame;
+ UnlockMutex (vid->guard);
+
+ if (want_frame >= vid->decoder->frame_count)
+ {
+ vid->playing = false;
+ return false;
+ }
+
+ // this works like so (audio-synced):
+ // 1. you call VideoDecoder_Seek() [when necessary] and
+ // VideoDecoder_Decode()
+ // 2. wait till it's time for this frame to be drawn
+ // the timeout is necessary because the audio signaling is not
+ // precise (see vp_AudioStart, vp_AudioEnd, vp_BufferTag)
+ // 3. output the frame; if the audio is behind, the lag counter
+ // goes up; if the video is behind, the lag counter goes down
+ // 4. set the next frame timeout; lag counter increases or
+ // decreases the timeout to allow audio or video to catch up
+ // 5. on a seek operation, the audio stream is moved to the
+ // correct position and then the audio signals the frame
+ // that should be rendered
+ // The system of timeouts and lag counts should make the video
+ // *relatively* smooth
+ //
+ prev_want_frame = vid->cur_frame - vid->lag_cnt;
+ if (want_frame > prev_want_frame - MAX_FRAME_LAG
+ && want_frame <= prev_want_frame + MAX_FRAME_LAG)
+ {
+ // we will draw the next frame right now, thus +1
+ vid->lag_cnt = vid->cur_frame + 1 - want_frame;
+ }
+ else
+ { // out of sequence frame, let's get it
+ vid->lag_cnt = 0;
+ vid->cur_frame = VideoDecoder_SeekFrame (vid->decoder, want_frame);
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret < 0)
+ { // decoder returned a failure
+ vid->playing = false;
+ return false;
+ }
+ }
+ vid->cur_frame = vid->decoder->cur_frame;
+
+ // draw the frame
+ // We have the cliprect precalculated and don't need the rest
+ oldContext = SetContext (NULL);
+ TFB_DrawScreen_Image (vid->frame,
+ vid->dst_rect.corner.x, vid->dst_rect.corner.y, 0, 0,
+ NULL, DRAW_REPLACE_MODE, TFB_SCREEN_MAIN);
+ SetContext (oldContext);
+ FlushGraphics (); // needed to prevent half-frame updates
+
+ // increase interframe with positive lag-count to allow audio to catch up
+ // decrease interframe with negative lag-count to allow video to catch up
+ wait_msec = vid->decoder->interframe_wait
+ - (int)vid->decoder->interframe_wait * SYNC_BIAS
+ + (int)vid->decoder->interframe_wait * vid->lag_cnt / LAG_FRACTION;
+ vid->frame_time = Now + msecToTimeCount (wait_msec);
+
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret < 0)
+ {
+ // TODO: decide what to do on error
+ }
+
+ return vid->playing;
+}
+
+// audio-independent video playback frame function
+// the frame rate and timing is dictated by the video decoder
+static bool
+processMuteFrame (VIDEO_REF vid)
+{
+ int ret;
+ TimeCount Now = GetTimeCounter ();
+
+ if (!vid->playing)
+ return false;
+
+ // this works like so:
+ // 1. you call VideoDecoder_Seek() [when necessary] and
+ // VideoDecoder_Decode()
+ // 2. the decoder calls back vp_GetTicks() and vp_SetTimer()
+ // to figure out and tell you when to render the frame
+ // being decoded
+ // On a seek operation, the decoder should reset its internal
+ // clock and call vp_GetTicks() again
+ //
+ if (Now >= vid->frame_time)
+ {
+ CONTEXT oldContext;
+
+ vid->cur_frame = vid->decoder->cur_frame;
+
+ // We have the cliprect precalculated and don't need the rest
+ oldContext = SetContext (NULL);
+ TFB_DrawScreen_Image (vid->frame,
+ vid->dst_rect.corner.x, vid->dst_rect.corner.y, 0, 0,
+ NULL, DRAW_REPLACE_MODE, TFB_SCREEN_MAIN);
+ SetContext (oldContext);
+ FlushGraphics (); // needed to prevent half-frame updates
+
+ if (vid->cur_frame == vid->loop_frame)
+ VideoDecoder_SeekFrame (vid->decoder, vid->loop_to);
+
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret <= 0)
+ vid->playing = false;
+ }
+
+ return vid->playing;
+}
+
+bool
+TFB_PlayVideo (VIDEO_REF vid, uint32 x, uint32 y)
+{
+ RECT scrn_r;
+ RECT clip_r = {{0, 0}, {vid->w, vid->h}};
+ RECT vid_r = {{0, 0}, {ScreenWidth, ScreenHeight}};
+ RECT dr = {{x, y}, {vid->w, vid->h}};
+ RECT sr;
+ bool loop_music = false;
+ int ret;
+
+ if (!vid)
+ return false;
+
+ // calculate the frame-source and screen-destination rects
+ GetContextClipRect (&scrn_r);
+ if (!BoxIntersect(&scrn_r, &vid_r, &scrn_r))
+ return false; // drawing outside visible
+
+ sr = dr;
+ sr.corner.x = -sr.corner.x;
+ sr.corner.y = -sr.corner.y;
+ if (!BoxIntersect (&clip_r, &sr, &sr))
+ return false; // drawing outside visible
+
+ dr.corner.x += scrn_r.corner.x;
+ dr.corner.y += scrn_r.corner.y;
+ if (!BoxIntersect (&scrn_r, &dr, &vid->dst_rect))
+ return false; // drawing outside visible
+
+ vid->src_rect = vid->dst_rect;
+ vid->src_rect.corner.x = sr.corner.x;
+ vid->src_rect.corner.y = sr.corner.y;
+
+ vid->decoder->callbacks = vp_DecoderCBs;
+ vid->decoder->data = vid;
+
+ vid->frame = TFB_DrawImage_CreateForScreen (vid->w, vid->h, FALSE);
+ vid->cur_frame = -1;
+ vid->want_frame = -1;
+
+ if (!vid->hAudio)
+ {
+ vid->hAudio = LoadMusicFile (vid->decoder->filename);
+ vid->own_audio = true;
+ }
+
+ if (vid->decoder->audio_synced)
+ {
+ if (!vid->hAudio)
+ {
+ log_add (log_Warning, "TFB_PlayVideo: "
+ "Cannot load sound-track for audio-synced video");
+ return false;
+ }
+
+ TFB_SetSoundSampleCallbacks (*vid->hAudio, &vp_AudioCBs);
+ TFB_SetSoundSampleData (*vid->hAudio, vid);
+ }
+
+ // get the first frame
+ ret = VideoDecoder_Decode (vid->decoder);
+ if (ret < 0)
+ return false;
+
+ vid->playing = true;
+
+ loop_music = !vid->decoder->audio_synced && vid->loop_frame != VID_NO_LOOP;
+ if (vid->hAudio)
+ PLRPlaySong (vid->hAudio, loop_music, 1);
+
+ if (vid->decoder->audio_synced)
+ {
+ // draw the first frame now
+ vid->frame_time = GetTimeCounter ();
+ }
+
+ return true;
+}
+
+void
+TFB_StopVideo (VIDEO_REF vid)
+{
+ if (!vid)
+ return;
+
+ vid->playing = false;
+
+ if (vid->hAudio)
+ {
+ PLRStop (vid->hAudio);
+ if (vid->own_audio)
+ {
+ DestroyMusic (vid->hAudio);
+ vid->hAudio = 0;
+ vid->own_audio = false;
+ }
+ }
+ if (vid->frame)
+ {
+ TFB_DrawScreen_DeleteImage (vid->frame);
+ vid->frame = NULL;
+ }
+}
+
+bool
+TFB_VideoPlaying (VIDEO_REF vid)
+{
+ if (!vid)
+ return false;
+
+ return vid->playing;
+}
+
+bool
+TFB_ProcessVideoFrame (VIDEO_REF vid)
+{
+ if (!vid)
+ return false;
+
+ if (vid->decoder->audio_synced)
+ return processAudioSyncedFrame (vid);
+ else
+ return processMuteFrame (vid);
+}
+
+uint32
+TFB_GetVideoPosition (VIDEO_REF vid)
+{
+ uint32 pos;
+
+ if (!TFB_VideoPlaying (vid))
+ return 0;
+
+ LockMutex (vid->guard);
+ pos = (uint32) (vid->decoder->pos * 1000);
+ UnlockMutex (vid->guard);
+
+ return pos;
+}
+
+bool
+TFB_SeekVideo (VIDEO_REF vid, uint32 pos)
+{
+ if (!TFB_VideoPlaying (vid))
+ return false;
+
+ if (vid->decoder->audio_synced)
+ {
+ PLRSeek (vid->hAudio, pos);
+ TaskSwitch ();
+ return true;
+ }
+ else
+ { // TODO: Non-a/s decoder seeking is not supported yet
+ // Decide what to do with these. Seeking this kind of
+ // video is trivial, but we may not want to do it.
+ // The only non-a/s videos right now are ship spins.
+ return false;
+ }
+}
+
+static void
+vp_BeginFrame (TFB_VideoDecoder* decoder)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (vid)
+ TFB_DrawCanvas_Lock (vid->frame->NormalImg);
+}
+
+static void
+vp_EndFrame (TFB_VideoDecoder* decoder)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (vid)
+ TFB_DrawCanvas_Unlock (vid->frame->NormalImg);
+}
+
+static void*
+vp_GetCanvasLine (TFB_VideoDecoder* decoder, uint32 line)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (!vid)
+ return NULL;
+
+ return TFB_DrawCanvas_GetLine (vid->frame->NormalImg, line);
+}
+
+static uint32
+vp_GetTicks (TFB_VideoDecoder* decoder)
+{
+ uint32 ctr = GetTimeCounter ();
+ return (ctr / ONE_SECOND) * 1000 + ((ctr % ONE_SECOND) * 1000) / ONE_SECOND;
+
+ (void)decoder; // gobble up compiler warning
+}
+
+static bool
+vp_SetTimer (TFB_VideoDecoder* decoder, uint32 msecs)
+{
+ TFB_VideoClip* vid = decoder->data;
+
+ if (!vid)
+ return false;
+
+ // time when next frame should be displayed
+ vid->frame_time = GetTimeCounter () + msecs * ONE_SECOND / 1000;
+ return true;
+}
+
+static bool
+vp_AudioStart (TFB_SoundSample* sample)
+{
+ TFB_VideoClip* vid = TFB_GetSoundSampleData (sample);
+ TFB_SoundDecoder *decoder;
+
+ assert (sizeof (intptr_t) >= sizeof (vid));
+ assert (vid != NULL);
+
+ decoder = TFB_GetSoundSampleDecoder (sample);
+
+ LockMutex (vid->guard);
+ vid->want_frame = SoundDecoder_GetFrame (decoder);
+ UnlockMutex (vid->guard);
+
+ return true;
+}
+
+static void
+vp_AudioEnd (TFB_SoundSample* sample)
+{
+ TFB_VideoClip* vid = TFB_GetSoundSampleData (sample);
+
+ assert (vid != NULL);
+
+ LockMutex (vid->guard);
+ vid->want_frame = vid->decoder->frame_count; // end it
+ UnlockMutex (vid->guard);
+}
+
+static void
+vp_BufferTag (TFB_SoundSample* sample, TFB_SoundTag* tag)
+{
+ TFB_VideoClip* vid = TFB_GetSoundSampleData (sample);
+ uint32 frame = (uint32) tag->data;
+
+ assert (sizeof (tag->data) >= sizeof (frame));
+ assert (vid != NULL);
+
+ LockMutex (vid->guard);
+ vid->want_frame = frame; // let it go!
+ UnlockMutex (vid->guard);
+}
+
+static void
+vp_QueueBuffer (TFB_SoundSample* sample, audio_Object buffer)
+{
+ //TFB_VideoClip* vid = (TFB_VideoClip*) TFB_GetSoundSampleData (sample);
+ TFB_SoundDecoder *decoder = TFB_GetSoundSampleDecoder (sample);
+
+ TFB_TagBuffer (sample, buffer,
+ (intptr_t) SoundDecoder_GetFrame (decoder));
+}
+
diff --git a/src/libs/video/vidplayer.h b/src/libs/video/vidplayer.h
new file mode 100644
index 0000000..78e4edb
--- /dev/null
+++ b/src/libs/video/vidplayer.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_VIDEO_VIDPLAYER_H_
+#define LIBS_VIDEO_VIDPLAYER_H_
+
+#include "video.h"
+
+extern bool TFB_InitVideoPlayer (void);
+extern void TFB_UninitVideoPlayer (void);
+extern bool TFB_PlayVideo (VIDEO_REF VidRef, uint32 x, uint32 y);
+extern void TFB_StopVideo (VIDEO_REF VidRef);
+extern bool TFB_VideoPlaying (VIDEO_REF VidRef);
+extern bool TFB_ProcessVideoFrame (VIDEO_REF vid);
+extern uint32 TFB_GetVideoPosition (VIDEO_REF VidRef);
+extern bool TFB_SeekVideo (VIDEO_REF VidRef, uint32 pos);
+
+#endif // LIBS_VIDEO_VIDPLAYER_H_
diff --git a/src/libs/video/vresins.c b/src/libs/video/vresins.c
new file mode 100644
index 0000000..dbb18e0
--- /dev/null
+++ b/src/libs/video/vresins.c
@@ -0,0 +1,186 @@
+// Copyright 2008 Michael Martin
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "vidintrn.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+static BOOLEAN
+FreeLegacyVideoData (void *data)
+{
+ LEGACY_VIDEO pLV;
+ if (!data)
+ return FALSE;
+
+ pLV = (LEGACY_VIDEO) data;
+ if (pLV->video)
+ HFree (pLV->video);
+ if (pLV->audio)
+ HFree (pLV->audio);
+ if (pLV->speech)
+ HFree (pLV->speech);
+ HFree (pLV);
+
+ return TRUE;
+}
+
+static void
+GetLegacyVideoData (const char *path, RESOURCE_DATA *resdata)
+{
+ void *result = NULL;
+ char paths[1024], *audio_path, *speech_path, *loop_str;
+ uint32 LoopFrame = VID_NO_LOOP;
+
+ /* Parse out the video components. */
+ strncpy (paths, path, 1023);
+ paths[1023] = '\0';
+ audio_path = strchr (paths, ':');
+ if (audio_path == NULL)
+ {
+ speech_path = NULL;
+ loop_str = NULL;
+ }
+ else
+ {
+ *audio_path = '\0';
+ audio_path++;
+
+ speech_path = strchr (audio_path, ':');
+ if (speech_path == NULL)
+ {
+ loop_str = NULL;
+ }
+ else
+ {
+ *speech_path = '\0';
+ speech_path++;
+
+ loop_str = strchr (speech_path, ':');
+ if (loop_str != NULL) {
+ *loop_str = '\0';
+ loop_str++;
+ }
+ }
+ }
+
+ log_add (log_Info, "\t'%s' -- video", paths);
+ if (audio_path)
+ log_add (log_Info, "\t'%s' -- audio", audio_path);
+ else
+ log_add (log_Info, "\tNo associated audio");
+ if (speech_path)
+ log_add (log_Info, "\t'%s' -- speech path", speech_path);
+ else
+ log_add (log_Info, "\tNo associated speech");
+ if (loop_str)
+ {
+ char *end;
+ LoopFrame = strtol (loop_str, &end, 10);
+ // We allow whitespace at the end, but nothing printable.
+ if (*end > 32) {
+ log_add (log_Warning, "Warning: Unparsable loop frame '%s'. Disabling loop.", loop_str);
+ LoopFrame = VID_NO_LOOP;
+ }
+ log_add (log_Info, "\tLoop frame is %u", LoopFrame);
+ }
+ else
+ log_add (log_Info, "\tNo specified loop frame");
+
+ result = HMalloc (sizeof (LEGACY_VIDEO_DESC));
+ if (result)
+ {
+ LEGACY_VIDEO pLV = (LEGACY_VIDEO) result;
+ int len;
+ pLV->video = NULL;
+ pLV->audio = NULL;
+ pLV->speech = NULL;
+ pLV->loop = LoopFrame;
+
+ len = strlen(paths)+1;
+ pLV->video = (char *)HMalloc (len);
+ if (!pLV->video)
+ {
+ log_add (log_Warning, "Warning: Couldn't allocate space for '%s'", paths);
+ goto err;
+ }
+ strncpy(pLV->video, paths, len);
+
+ if (audio_path)
+ {
+ len = strlen(audio_path)+1;
+ pLV->audio = (char *)HMalloc (len);
+ if (!pLV->audio)
+ {
+ log_add (log_Warning, "Warning: Couldn't allocate space for '%s'", audio_path);
+ goto err;
+ }
+ strncpy(pLV->audio, audio_path, len);
+ }
+
+ if (speech_path)
+ {
+ len = strlen(speech_path)+1;
+ pLV->speech = (char *)HMalloc (len);
+ if (!pLV->speech)
+ {
+ log_add (log_Warning, "Warning: Couldn't allocate space for '%s'", speech_path);
+ goto err;
+ }
+ strncpy(pLV->speech, speech_path, len);
+ }
+
+ resdata->ptr = result;
+ }
+ return;
+err:
+ if (result)
+ FreeLegacyVideoData ((LEGACY_VIDEO)result);
+
+ resdata->ptr = NULL;
+ return;
+}
+
+BOOLEAN
+InstallVideoResType (void)
+{
+ InstallResTypeVectors ("3DOVID", GetLegacyVideoData, FreeLegacyVideoData, NULL);
+ return TRUE;
+}
+
+LEGACY_VIDEO
+LoadLegacyVideoInstance (RESOURCE res)
+{
+ void *data;
+
+ data = res_GetResource (res);
+ if (data)
+ {
+ res_DetachResource (res);
+ }
+
+ return (LEGACY_VIDEO)data;
+}
+
+BOOLEAN
+DestroyLegacyVideo (LEGACY_VIDEO vid)
+{
+ return FreeLegacyVideoData (vid);
+}
diff --git a/src/libs/vidlib.h b/src/libs/vidlib.h
new file mode 100644
index 0000000..9c32d7c
--- /dev/null
+++ b/src/libs/vidlib.h
@@ -0,0 +1,68 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LIBS_VIDLIB_H_
+#define LIBS_VIDLIB_H_
+
+#include "libs/compiler.h"
+#include "libs/sndlib.h"
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ NO_FMV = 0,
+ HARDWARE_FMV,
+ SOFTWARE_FMV
+} VIDEO_TYPE;
+
+typedef struct tfb_videoclip *VIDEO_REF;
+typedef struct legacy_video_desc *LEGACY_VIDEO;
+typedef struct legacy_video_ref *LEGACY_VIDEO_REF;
+
+extern BOOLEAN InstallVideoResType (void);
+
+extern BOOLEAN InitVideoPlayer (BOOLEAN UseCDROM);
+extern void UninitVideoPlayer (void);
+
+extern VIDEO_REF LoadVideoFile (const char *pStr);
+extern BOOLEAN DestroyVideo (VIDEO_REF VidRef);
+extern VIDEO_TYPE VidPlay (VIDEO_REF VidRef);
+extern VIDEO_TYPE VidPlayEx (VIDEO_REF VidRef, MUSIC_REF AudRef,
+ MUSIC_REF SpeechRef, DWORD LoopFrame);
+#define VID_NO_LOOP (0U-1)
+extern void VidStop (void);
+extern VIDEO_REF VidPlaying (void);
+extern BOOLEAN VidProcessFrame (void);
+extern DWORD VidGetPosition (void); // position in milliseconds
+extern BOOLEAN VidSeek (DWORD pos); // position in milliseconds
+
+extern LEGACY_VIDEO LoadLegacyVideoInstance (RESOURCE res);
+extern BOOLEAN DestroyLegacyVideo (LEGACY_VIDEO vid);
+extern LEGACY_VIDEO_REF PlayLegacyVideo (LEGACY_VIDEO vid);
+extern void StopLegacyVideo (LEGACY_VIDEO_REF ref);
+extern BOOLEAN PlayingLegacyVideo (LEGACY_VIDEO_REF ref);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LIBS_VIDLIB_H_ */
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000..ee9a577
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,647 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Eventually this should include all configuration stuff,
+ * for now there's few options which indicate 3do/pc flavors.
+ */
+
+#include "options.h"
+
+#include "port.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/file.h"
+#include "config.h"
+#include "libs/compiler.h"
+#include "libs/uio.h"
+#include "libs/strlib.h"
+#include "libs/log.h"
+#include "libs/reslib.h"
+#include "libs/memlib.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+#ifdef __APPLE__
+# include <libgen.h>
+ /* for dirname() */
+#endif
+
+
+int optWhichCoarseScan;
+int optWhichMenu;
+int optWhichFonts;
+int optWhichIntro;
+int optWhichShield;
+int optSmoothScroll;
+int optMeleeScale;
+const char **optAddons;
+
+BOOLEAN opt3doMusic;
+BOOLEAN optRemixMusic;
+BOOLEAN optSpeech;
+BOOLEAN optSubtitles;
+BOOLEAN optStereoSFX;
+BOOLEAN optKeepAspectRatio;
+
+float optGamma;
+
+uio_DirHandle *contentDir;
+uio_DirHandle *configDir;
+uio_DirHandle *saveDir;
+uio_DirHandle *meleeDir;
+uio_MountHandle* contentMountHandle;
+
+char baseContentPath[PATH_MAX];
+
+extern uio_Repository *repository;
+extern uio_DirHandle *rootDir;
+
+INPUT_TEMPLATE input_templates[6];
+
+static const char *findFileInDirs (const char *locs[], int numLocs,
+ const char *file);
+static uio_MountHandle *mountContentDir (uio_Repository *repository,
+ const char *contentPath);
+static void mountAddonDir (uio_Repository *repository,
+ uio_MountHandle *contentMountHandle, const char *addonDirName);
+
+static void mountDirZips (uio_DirHandle *dirHandle, const char *mountPoint,
+ int relativeFlags, uio_MountHandle *relativeHandle);
+
+
+// Looks for a file 'file' in all 'numLocs' locations from 'locs'.
+// returns the first element from locs where 'file' is found.
+// If there is no matching location, NULL will be returned and
+// errno will be set to 'ENOENT'.
+// Entries from 'locs' that together with 'file' are longer than
+// PATH_MAX will be ignored, except for a warning given to stderr.
+static const char *
+findFileInDirs (const char *locs[], int numLocs, const char *file)
+{
+ int locI;
+ char path[PATH_MAX];
+ size_t fileLen;
+
+ fileLen = strlen (file);
+ for (locI = 0; locI < numLocs; locI++)
+ {
+ size_t locLen;
+ const char *loc;
+ bool needSlash;
+
+ loc = locs[locI];
+ locLen = strlen (loc);
+
+ needSlash = (locLen != 0 && loc[locLen - 1] != '/');
+ if (locLen + (needSlash ? 1 : 0) + fileLen + 1 >= sizeof path)
+ {
+ // This dir plus the file name is too long.
+ log_add (log_Warning, "Warning: path '%s' is ignored"
+ " because it is too long.", loc);
+ continue;
+ }
+
+ snprintf (path, sizeof path, "%s%s%s", loc, needSlash ? "/" : "",
+ file);
+ if (fileExists (path))
+ return loc;
+ }
+
+ // No matching location was found.
+ errno = ENOENT;
+ return NULL;
+}
+
+// contentDirName is an explicitely specified location for the content,
+// or NULL if none was explicitely specified.
+// execFile is the path to the uqm executable, as acquired through
+// main()'s argv[0].
+void
+prepareContentDir (const char *contentDirName, const char* addonDirName, const char *execFile)
+{
+ const char *testFile = "version";
+ const char *loc;
+
+ if (contentDirName == NULL)
+ {
+ // Try the default content locations.
+ const char *locs[] = {
+ CONTENTDIR, /* defined in config.h */
+ "",
+ "content",
+ "../../content" /* For running from MSVC */
+ };
+ loc = findFileInDirs (locs, sizeof locs / sizeof locs[0], testFile);
+
+#ifdef __APPLE__
+ /* On OSX, if the content can't be found in any of the static
+ * locations, attempt to look inside the application bundle,
+ * by looking relative to the location of the uqm executable. */
+ if (loc == NULL)
+ {
+ char *tempDir = (char *) HMalloc (PATH_MAX);
+ char *execFileDup;
+
+ /* dirname can modify its argument, so we need a local
+ * mutable copy of it. */
+ execFileDup = (char *) HMalloc (strlen (execFile) + 1);
+ strcpy (execFileDup, execFile);
+ snprintf (tempDir, PATH_MAX, "%s/../Resources/content",
+ dirname (execFileDup));
+ loc = findFileInDirs ((const char **) &tempDir, 1, testFile);
+ HFree (execFileDup);
+ HFree (tempDir);
+ }
+#endif
+ }
+ else
+ {
+ // Only use the explicitely supplied content dir.
+ loc = findFileInDirs (&contentDirName, 1, testFile);
+ }
+ if (loc == NULL)
+ {
+ log_add (log_Fatal, "Fatal error: Could not find content.");
+ exit (EXIT_FAILURE);
+ }
+
+ if (expandPath (baseContentPath, sizeof baseContentPath, loc,
+ EP_ALL_SYSTEM) == -1)
+ {
+ log_add (log_Fatal, "Fatal error: Could not expand path to content "
+ "directory: %s", strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ log_add (log_Debug, "Using '%s' as base content dir.", baseContentPath);
+ contentMountHandle = mountContentDir (repository, baseContentPath);
+
+ if (addonDirName)
+ log_add (log_Debug, "Using '%s' as addon dir.", addonDirName);
+ mountAddonDir (repository, contentMountHandle, addonDirName);
+
+#ifndef __APPLE__
+ (void) execFile;
+#endif
+}
+
+void
+prepareConfigDir (const char *configDirName) {
+ char buf[PATH_MAX];
+ static uio_AutoMount *autoMount[] = { NULL };
+ uio_MountHandle *contentHandle;
+
+ if (configDirName == NULL)
+ {
+ configDirName = getenv("UQM_CONFIG_DIR");
+
+ if (configDirName == NULL)
+ configDirName = CONFIGDIR;
+ }
+
+ if (expandPath (buf, PATH_MAX - 13, configDirName, EP_ALL_SYSTEM)
+ == -1)
+ {
+ // Doesn't have to be fatal, but might mess up things when saving
+ // config files.
+ log_add (log_Fatal, "Fatal error: Invalid path to config files.");
+ exit (EXIT_FAILURE);
+ }
+ configDirName = buf;
+
+ log_add (log_Debug, "Using config dir '%s'", configDirName);
+
+ // Set the environment variable UQM_CONFIG_DIR so UQM_MELEE_DIR
+ // and UQM_SAVE_DIR can refer to it.
+ setenv("UQM_CONFIG_DIR", configDirName, 1);
+
+ // Create the path upto the config dir, if not already existing.
+ if (mkdirhier (configDirName) == -1)
+ exit (EXIT_FAILURE);
+
+ contentHandle = uio_mountDir (repository, "/",
+ uio_FSTYPE_STDIO, NULL, NULL, configDirName, autoMount,
+ uio_MOUNT_TOP, NULL);
+ if (contentHandle == NULL)
+ {
+ log_add (log_Fatal, "Fatal error: Could not mount config dir: %s",
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ configDir = uio_openDir (repository, "/", 0);
+ if (configDir == NULL)
+ {
+ log_add (log_Fatal, "Fatal error: Could not open config dir: %s",
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+}
+
+void
+prepareSaveDir (void) {
+ char buf[PATH_MAX];
+ const char *saveDirName;
+
+ saveDirName = getenv("UQM_SAVE_DIR");
+ if (saveDirName == NULL)
+ saveDirName = SAVEDIR;
+
+ if (expandPath (buf, PATH_MAX - 13, saveDirName, EP_ALL_SYSTEM) == -1)
+ {
+ // Doesn't have to be fatal, but might mess up things when saving
+ // config files.
+ log_add (log_Fatal, "Fatal error: Invalid path to config files.");
+ exit (EXIT_FAILURE);
+ }
+
+ saveDirName = buf;
+ setenv("UQM_SAVE_DIR", saveDirName, 1);
+
+ // Create the path upto the save dir, if not already existing.
+ if (mkdirhier (saveDirName) == -1)
+ exit (EXIT_FAILURE);
+
+ log_add (log_Debug, "Saved games are kept in %s.", saveDirName);
+
+ saveDir = uio_openDirRelative (configDir, "save", 0);
+ // TODO: this doesn't work if the save dir is not
+ // "save" in the config dir.
+ if (saveDir == NULL)
+ {
+ log_add (log_Fatal, "Fatal error: Could not open save dir: %s",
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+}
+
+void
+prepareMeleeDir (void) {
+ char buf[PATH_MAX];
+ const char *meleeDirName;
+
+ meleeDirName = getenv("UQM_MELEE_DIR");
+ if (meleeDirName == NULL)
+ meleeDirName = MELEEDIR;
+
+ if (expandPath (buf, PATH_MAX - 13, meleeDirName, EP_ALL_SYSTEM) == -1)
+ {
+ // Doesn't have to be fatal, but might mess up things when saving
+ // config files.
+ log_add (log_Fatal, "Fatal error: Invalid path to config files.");
+ exit (EXIT_FAILURE);
+ }
+
+ meleeDirName = buf;
+ setenv("UQM_MELEE_DIR", meleeDirName, 1);
+
+ // Create the path upto the save dir, if not already existing.
+ if (mkdirhier (meleeDirName) == -1)
+ exit (EXIT_FAILURE);
+
+ meleeDir = uio_openDirRelative (configDir, "teams", 0);
+ // TODO: this doesn't work if the save dir is not
+ // "teams" in the config dir.
+ if (meleeDir == NULL)
+ {
+ log_add (log_Fatal, "Fatal error: Could not open melee teams dir: %s",
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+}
+
+static uio_MountHandle *
+mountContentDir (uio_Repository *repository, const char *contentPath)
+{
+ uio_DirHandle *packagesDir;
+ static uio_AutoMount *autoMount[] = { NULL };
+ uio_MountHandle *contentMountHandle;
+
+ contentMountHandle = uio_mountDir (repository, "/",
+ uio_FSTYPE_STDIO, NULL, NULL, contentPath, autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+ if (contentMountHandle == NULL)
+ {
+ log_add (log_Fatal, "Fatal error: Could not mount content dir: %s",
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ contentDir = uio_openDir (repository, "/", 0);
+ if (contentDir == NULL)
+ {
+ log_add (log_Fatal, "Fatal error: Could not open content dir: %s",
+ strerror (errno));
+ exit (EXIT_FAILURE);
+ }
+
+ packagesDir = uio_openDir (repository, "/packages", 0);
+ if (packagesDir != NULL)
+ {
+ mountDirZips (packagesDir, "/", uio_MOUNT_BELOW, contentMountHandle);
+ uio_closeDir (packagesDir);
+ }
+
+ return contentMountHandle;
+}
+
+static void
+mountAddonDir (uio_Repository *repository, uio_MountHandle *contentMountHandle,
+ const char *addonDirName)
+{
+ uio_DirHandle *addonsDir;
+ static uio_AutoMount *autoMount[] = { NULL };
+ uio_MountHandle *mountHandle;
+ uio_DirList *availableAddons;
+
+ if (addonDirName != NULL)
+ {
+ mountHandle = uio_mountDir (repository, "addons",
+ uio_FSTYPE_STDIO, NULL, NULL, addonDirName, autoMount,
+ uio_MOUNT_TOP | uio_MOUNT_RDONLY, NULL);
+ if (mountHandle == NULL)
+ {
+ log_add (log_Warning, "Warning: Could not mount addon directory: %s"
+ ";\n\t'--addon' options are ignored.", strerror (errno));
+ return;
+ }
+ }
+ else
+ {
+ mountHandle = contentMountHandle;
+ }
+
+ // NB: note the difference between addonsDir and addonDir.
+ // the former is the dir 'addons', the latter a directory
+ // in that dir.
+ addonsDir = uio_openDirRelative (contentDir, "addons", 0);
+ if (addonsDir == NULL)
+ { // No addon dir found.
+ log_add (log_Warning, "Warning: There's no 'addons' "
+ "directory in the 'content' directory;\n\t'--addon' "
+ "options are ignored.");
+ return;
+ }
+
+ mountDirZips (addonsDir, "addons", uio_MOUNT_BELOW, mountHandle);
+
+ availableAddons = uio_getDirList (addonsDir, "", "", match_MATCH_PREFIX);
+ if (availableAddons != NULL)
+ {
+ int i, count;
+
+ // count the actual addon dirs
+ count = 0;
+ for (i = 0; i < availableAddons->numNames; ++i)
+ {
+ struct stat sb;
+
+ if (availableAddons->names[i][0] == '.' ||
+ uio_stat (addonsDir, availableAddons->names[i], &sb) == -1
+ || !S_ISDIR (sb.st_mode))
+ { // this dir entry ignored
+ availableAddons->names[i] = NULL;
+ continue;
+ }
+ ++count;
+ }
+ log_add (log_Info, "%d available addon pack%s.", count,
+ count == 1 ? "" : "s");
+
+ count = 0;
+ for (i = 0; i < availableAddons->numNames; ++i)
+ {
+ char mountname[128];
+ uio_DirHandle *addonDir;
+ const char *addon = availableAddons->names[i];
+
+ if (!addon)
+ continue;
+
+ ++count;
+ log_add (log_Info, " %d. %s", count, addon);
+
+ snprintf (mountname, sizeof mountname, "addons/%s", addon);
+
+ addonDir = uio_openDirRelative (addonsDir, addon, 0);
+ if (addonDir == NULL)
+ {
+ log_add (log_Warning, "Warning: directory 'addons/%s' "
+ "not found; addon skipped.", addon);
+ continue;
+ }
+ mountDirZips (addonDir, mountname, uio_MOUNT_BELOW, mountHandle);
+ uio_closeDir (addonDir);
+ }
+ }
+ else
+ {
+ log_add (log_Info, "0 available addon packs.");
+ }
+
+ uio_DirList_free (availableAddons);
+ uio_closeDir (addonsDir);
+}
+
+static void
+mountDirZips (uio_DirHandle *dirHandle, const char *mountPoint,
+ int relativeFlags, uio_MountHandle *relativeHandle)
+{
+ static uio_AutoMount *autoMount[] = { NULL };
+ uio_DirList *dirList;
+
+ dirList = uio_getDirList (dirHandle, "", "\\.([zZ][iI][pP]|[uU][qQ][mM])$",
+ match_MATCH_REGEX);
+ if (dirList != NULL)
+ {
+ int i;
+
+ for (i = 0; i < dirList->numNames; i++)
+ {
+ if (uio_mountDir (repository, mountPoint, uio_FSTYPE_ZIP,
+ dirHandle, dirList->names[i], "/", autoMount,
+ relativeFlags | uio_MOUNT_RDONLY,
+ relativeHandle) == NULL)
+ {
+ log_add (log_Warning, "Warning: Could not mount '%s': %s.",
+ dirList->names[i], strerror (errno));
+ }
+ }
+ }
+ uio_DirList_free (dirList);
+}
+
+int
+loadIndices (uio_DirHandle *dir)
+{
+ uio_DirList *indices;
+ int numLoaded = 0;
+
+ indices = uio_getDirList (dir, "", "\\.[rR][mM][pP]$",
+ match_MATCH_REGEX);
+
+ if (indices != NULL)
+ {
+ int i;
+
+ for (i = 0; i < indices->numNames; i++)
+ {
+ log_add (log_Debug, "Loading resource index '%s'",
+ indices->names[i]);
+ LoadResourceIndex (dir, indices->names[i], NULL);
+ numLoaded++;
+ }
+ }
+ uio_DirList_free (indices);
+
+ /* Return the number of index files loaded. */
+ return numLoaded;
+}
+
+BOOLEAN
+loadAddon (const char *addon)
+{
+ uio_DirHandle *addonsDir, *addonDir;
+ int numLoaded;
+
+ addonsDir = uio_openDirRelative (contentDir, "addons", 0);
+ if (addonsDir == NULL)
+ {
+ // No addon dir found.
+ log_add (log_Warning, "Warning: There's no 'addons' "
+ "directory in the 'content' directory;\n\t'--addon' "
+ "options are ignored.");
+ return FALSE;
+ }
+ addonDir = uio_openDirRelative (addonsDir, addon, 0);
+ if (addonDir == NULL)
+ {
+ log_add (log_Warning, "Warning: Addon '%s' not found", addon);
+ uio_closeDir (addonsDir);
+ return FALSE;
+ }
+
+ numLoaded = loadIndices (addonDir);
+ if (!numLoaded)
+ {
+ log_add (log_Error, "No RMP index files were loaded for addon '%s'",
+ addon);
+ }
+
+ uio_closeDir (addonDir);
+ uio_closeDir (addonsDir);
+
+ return (numLoaded > 0);
+}
+
+void
+prepareShadowAddons (const char **addons)
+{
+ uio_DirHandle *addonsDir;
+ const char *shadowDirName = "shadow-content";
+
+ addonsDir = uio_openDirRelative (contentDir, "addons", 0);
+ // If anything fails here, it will fail again later, so
+ // we'll just keep quiet about it for now
+ if (addonsDir == NULL)
+ return;
+
+ for (; *addons != NULL; addons++)
+ {
+ const char *addon = *addons;
+ uio_DirHandle *addonDir;
+ uio_DirHandle *shadowDir;
+
+ addonDir = uio_openDirRelative (addonsDir, addon, 0);
+ if (addonDir == NULL)
+ continue;
+
+ // Mount addon's "shadow-content" on top of "/"
+ shadowDir = uio_openDirRelative (addonDir, shadowDirName, 0);
+ if (shadowDir)
+ {
+ log_add (log_Debug, "Mounting shadow content of '%s' addon", addon);
+ mountDirZips (shadowDir, "/", uio_MOUNT_ABOVE, contentMountHandle);
+ // Mount non-zipped shadow content
+ if (uio_transplantDir ("/", shadowDir, uio_MOUNT_RDONLY |
+ uio_MOUNT_ABOVE, contentMountHandle) == NULL)
+ {
+ log_add (log_Warning, "Warning: Could not mount shadow content"
+ " of '%s': %s.", addon, strerror (errno));
+ }
+
+ uio_closeDir (shadowDir);
+ }
+ uio_closeDir (addonDir);
+ }
+
+ uio_closeDir (addonsDir);
+}
+
+void
+prepareAddons (const char **addons)
+{
+ for (; *addons != NULL; addons++)
+ {
+ log_add (log_Info, "Loading addon '%s'", *addons);
+ if (!loadAddon (*addons))
+ {
+ // TODO: Should we do something like inform the user?
+ // Why simply refuse to load other addons?
+ // Maybe exit() to inform the user of the failure?
+ break;
+ }
+ }
+}
+
+void
+unprepareAllDirs (void)
+{
+ if (saveDir)
+ {
+ uio_closeDir (saveDir);
+ saveDir = 0;
+ }
+ if (meleeDir)
+ {
+ uio_closeDir (meleeDir);
+ meleeDir = 0;
+ }
+ if (contentDir)
+ {
+ uio_closeDir (contentDir);
+ contentDir = 0;
+ }
+ if (configDir)
+ {
+ uio_closeDir (configDir);
+ configDir = 0;
+ }
+}
+
+bool
+setGammaCorrection (float gamma)
+{
+ bool set = TFB_SetGamma (gamma);
+ if (set)
+ log_add (log_Info, "Gamma correction set to %.4f.", gamma);
+ else
+ log_add (log_Warning, "Unable to set gamma correction.");
+ return set;
+}
diff --git a/src/options.h b/src/options.h
new file mode 100644
index 0000000..67480a4
--- /dev/null
+++ b/src/options.h
@@ -0,0 +1,94 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Eventually this should include all configuration stuff,
+ * for now there's few options which indicate 3do/pc flavors.
+ */
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include "port.h"
+#include "libs/compiler.h"
+#include "libs/uio.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define OPT_3DO 0x01
+#define OPT_PC 0x02
+#define OPT_ALL 0xFF
+
+extern int optWhichCoarseScan;
+extern int optWhichMenu;
+extern int optWhichFonts;
+extern int optWhichIntro;
+extern int optWhichShield;
+extern int optSmoothScroll;
+extern int optMeleeScale;
+
+extern BOOLEAN opt3doMusic;
+extern BOOLEAN optRemixMusic;
+extern BOOLEAN optSpeech;
+extern BOOLEAN optSubtitles;
+extern BOOLEAN optStereoSFX;
+extern BOOLEAN optKeepAspectRatio;
+
+#define GAMMA_SCALE 1000
+extern float optGamma;
+
+extern uio_DirHandle *contentDir;
+extern uio_DirHandle *configDir;
+extern uio_DirHandle *saveDir;
+extern uio_DirHandle *meleeDir;
+extern char baseContentPath[PATH_MAX];
+
+extern const char **optAddons;
+
+/* These get edited by TEXTENTRY widgets, so they should have room to
+ * hold as much as one of them allows by default. */
+typedef struct _input_template {
+ char name[30];
+
+ /* This should eventually also hold things like Joystick Port
+ * and whether or not joysticks are enabled at all, and
+ * possibly the whole configuration scheme. If we do that, we
+ * can actually ditch much of VControl. */
+} INPUT_TEMPLATE;
+
+extern INPUT_TEMPLATE input_templates[6];
+
+void prepareContentDir (const char *contentDirName, const char *addonDirName, const char *execFile);
+void prepareConfigDir (const char *configDirName);
+void prepareMeleeDir (void);
+void prepareSaveDir (void);
+void prepareAddons (const char **addons);
+void prepareShadowAddons (const char **addons);
+void unprepareAllDirs (void);
+
+BOOLEAN loadAddon (const char *addon);
+int loadIndices (uio_DirHandle *baseDir);
+
+bool setGammaCorrection (float gamma);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/src/port.c b/src/port.c
new file mode 100644
index 0000000..dc25f16
--- /dev/null
+++ b/src/port.c
@@ -0,0 +1,145 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/*
+ * This file contains definitions that might be included in one system, but
+ * omited in another.
+ *
+ * Created by Serge van den Boom
+ */
+
+#include "port.h"
+
+#include <ctype.h>
+#include <errno.h>
+#ifdef _MSC_VER
+# include <stdarg.h>
+# include <stdio.h>
+#endif /* _MSC_VER */
+#include <stdlib.h>
+#include <string.h>
+#if !defined (_MSC_VER) && !defined (HAVE_READDIR_R)
+# include <dirent.h>
+#endif
+
+#ifndef HAVE_STRUPR
+char *
+strupr (char *str)
+{
+ char *ptr;
+
+ ptr = str;
+ while (*ptr)
+ {
+ *ptr = (char) toupper (*ptr);
+ ptr++;
+ }
+ return str;
+}
+#endif
+
+#ifndef HAVE_SETENV
+int
+setenv (const char *name, const char *value, int overwrite)
+{
+ char *string, *ptr;
+ size_t nameLen, valueLen;
+
+ if (!overwrite)
+ {
+ char *old;
+
+ old = getenv (name);
+ if (old != NULL)
+ return 0;
+ }
+
+ nameLen = strlen (name);
+ valueLen = strlen (value);
+
+ string = malloc (nameLen + valueLen + 2);
+ // "NAME=VALUE\0"
+ // putenv() does NOT make a copy, but uses the string passed.
+
+ ptr = string;
+
+ strcpy (string, name);
+ ptr += nameLen;
+
+ *ptr = '=';
+ ptr++;
+
+ strcpy (ptr, value);
+
+ return putenv (string);
+}
+#endif
+
+#if !defined (_MSC_VER) && !defined (HAVE_READDIR_R)
+// NB. This function calls readdir() directly, and as such has the same
+// reentrance issues as that function. For the purposes of UQM it will
+// do though.
+// Note the POSIX requires that "The pointer returned by readdir()
+// points to data which may be overwritten by another call to
+// readdir( ) on the same directory stream. This data is not
+// overwritten by another call to readdir() on a different directory
+// stream."
+// NB. This function makes an extra copy of the dirent and will hence be
+// slower than a direct call to readdir() or readdir_r().
+int
+readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
+ struct dirent *readdir_entry;
+
+ readdir_entry = readdir(dirp);
+ if (readdir_entry == NULL) {
+ *result = NULL;
+ return errno;
+ }
+
+ *entry = *readdir_entry;
+ *result = entry;
+ return 0;
+}
+#endif
+
+#ifdef _MSC_VER
+// MSVC does not have snprintf() and vsnprintf(). It does have a _snprintf()
+// and _vsnprintf(), but these do not terminate a truncated string as
+// the C standard prescribes.
+int
+snprintf(char *str, size_t size, const char *format, ...)
+{
+ int result;
+ va_list args;
+
+ va_start (args, format);
+ result = _vsnprintf (str, size, format, args);
+ if (str != NULL && size != 0)
+ str[size - 1] = '\0';
+ va_end (args);
+
+ return result;
+}
+
+int
+vsnprintf(char *str, size_t size, const char *format, va_list args)
+{
+ int result = _vsnprintf (str, size, format, args);
+ if (str != NULL && size != 0)
+ str[size - 1] = '\0';
+ return result;
+}
+#endif /* _MSC_VER */
+
diff --git a/src/port.h b/src/port.h
new file mode 100644
index 0000000..4dc56ee
--- /dev/null
+++ b/src/port.h
@@ -0,0 +1,554 @@
+#ifndef PORT_H_
+#define PORT_H_
+
+#include "config.h"
+
+#ifdef __MINGW32__
+// Microsoft Windows headers expect this to be set. The MSVC compiler sets
+// it, but MinGW doesn't.
+# if defined(_X86_)
+# define _M_IX86
+# elif defined(_IA64_)
+# define _M_IA64
+# elif defined(__amd64__)
+# define _M_AMD64
+# define _M_X64
+# elif defined(__m68k__)
+# define _68K_
+# elif defined(__ppc__)
+# define _M_PPC
+# endif
+#endif
+
+
+// Compilation related
+#ifdef _MSC_VER
+# define inline __inline
+#elif defined(__SYMBIAN32__)
+#else
+# define inline __inline__
+# ifdef __MINGW32__
+ // For when including Microsoft Windows header files.
+# define _inline inline
+# endif
+#endif
+
+
+// Compilation warnings:
+#ifdef _MSC_VER
+ // UQM uses a lot of functions that can be used unsafely, but it uses them
+ // in a safe way. The warnings about these functions however may drown out
+ // serious warnings, so we turn them off.
+# define _CRT_SECURE_NO_DEPRECATE
+
+ // Escalate some warnings we consider important
+ // "'operator' : 'identifier1' indirection to slightly different base
+ // types from 'identifier2'
+# pragma warning( 3 : 4057 )
+ // "unreferenced formal parameter"
+# pragma warning( 3 : 4100 )
+ // "'function' : unreferenced local function has been removed"
+# pragma warning( 3 : 4505 )
+ // "local variable 'name' may be used without having been initialized"
+# pragma warning( 3 : 4701 )
+
+ // Downgrade some warnings we consider unimportant
+ // "'operator' conversion from 'type1' to 'type2', possible loss of data"
+# pragma warning( 4 : 4244)
+#endif
+
+
+#ifdef _MSC_VER
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+// Using "HAVE_STRCASECMP_UQM" instead of "HAVE_STRCASECMP" as the latter
+// conflicts with SDL.
+#if !defined(HAVE_STRICMP) && !defined(HAVE_STRCASECMP_UQM)
+# error Neither stricmp() nor strcasecmp() is available.
+#elif !defined(HAVE_STRICMP)
+# define stricmp strcasecmp
+#elif !defined(HAVE_STRCASECMP_UQM)
+# define strcasecmp stricmp
+#else
+ // We should take care not to define anything if both strcasecmp() and
+ // stricmp() are defined, as one might exist as a macro to the other.
+#endif
+
+
+#ifndef HAVE_STRUPR
+#if defined(__cplusplus)
+extern "C" {
+#endif
+char *strupr (char *str);
+#if defined(__cplusplus)
+}
+#endif
+#endif
+
+#if !defined (_MSC_VER) && !defined (HAVE_READDIR_R)
+# include <dirent.h>
+#if defined(__cplusplus)
+extern "C" {
+#endif
+int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
+#if defined(__cplusplus)
+}
+#endif
+#endif
+
+// Directories
+#ifdef WIN32
+# include <stdlib.h>
+# define PATH_MAX _MAX_PATH
+# define NAME_MAX _MAX_FNAME
+ // _MAX_DIR and FILENAME_MAX could also be candidates.
+ // If anyone can tell me which one matches NAME_MAX, please
+ // let me know. - SvdB
+#elif defined(_WIN32_WCE)
+# include <sys/syslimits.h>
+#else
+# include <limits.h>
+ /* PATH_MAX is per POSIX defined in <limits.h>, but:
+ * "A definition of one of the values from Table 2.6 shall bea
+ * omitted from <limits.h> on specific implementations where the
+ * corresponding value is equal to or greater than the
+ * stated minimum, but where the value can vary depending
+ * on the file to which it is applied. The actual value supported
+ * for a specific pathname shall be provided by the pathconf()
+ * function."
+ * _POSIX_NAME_MAX will provide a minimum (14).
+ * This is relevant (at least) for Solaris.
+ */
+# ifndef NAME_MAX
+# define NAME_MAX _POSIX_NAME_MAX
+# endif
+#endif
+
+// Some types
+#ifdef _MSC_VER
+typedef int ssize_t;
+typedef unsigned short mode_t;
+#endif
+
+// Directories
+#include <sys/stat.h>
+#ifdef _MSC_VER
+# define MKDIR(name, mode) ((void) mode, _mkdir(name))
+#elif defined(__MINGW32__)
+# define MKDIR(name, mode) ((void) mode, mkdir(name))
+#else
+# define MKDIR mkdir
+#endif
+#ifdef _MSC_VER
+# include <direct.h>
+# define chdir _chdir
+# define getcwd _getcwd
+# define access _access
+# define F_OK 0
+# define W_OK 2
+# define R_OK 4
+# define open _open
+# define read _read
+//# define fstat _fstat
+# define write _write
+//# define stat _stat
+# define unlink _unlink
+#endif
+
+// Memory
+#ifdef WIN32
+# ifdef __MINGW32__
+# include <malloc.h>
+# elif defined (_MSC_VER)
+# define alloca _alloca
+# endif
+#elif defined(__linux__) || defined(__svr4__)
+# include <alloca.h>
+#endif
+
+// String formatting
+#ifdef _MSC_VER
+# include <stdarg.h>
+// Defined in port.c
+#if defined(__cplusplus)
+extern "C" {
+#endif
+int snprintf(char *str, size_t size, const char *format, ...);
+int vsnprintf(char *str, size_t size, const char *format, va_list args);
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _MSC_VER */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// setenv()
+#ifndef HAVE_SETENV
+int setenv (const char *name, const char *value, int overwrite);
+#endif
+
+#ifndef HAVE_WCHAR_T
+typedef unsigned short wchar_t;
+#endif
+
+#ifndef HAVE_WINT_T
+typedef unsigned int wint_t;
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#if defined (_MSC_VER) || defined(__MINGW32__)
+# define USE_WINSOCK
+#endif
+
+// errno error numbers. The values used don't matter, as long as they
+// don't conflict with existing errno error numbers.
+#ifdef PORT_WANT_ERRNO
+# ifdef USE_WINSOCK
+# include <errno.h>
+# ifndef E2BIG
+# define E2BIG 0x01000001
+# endif
+# ifndef EACCES
+# define EACCES 0x01000002
+# endif
+# ifndef EADDRINUSE
+# define EADDRINUSE 0x01000003
+# endif
+# ifndef EADDRNOTAVAIL
+# define EADDRNOTAVAIL 0x01000004
+# endif
+# ifndef EAFNOSUPPORT
+# define EAFNOSUPPORT 0x01000005
+# endif
+# ifndef EAGAIN
+# ifdef EWOULDBLOCK
+# define EAGAIN EWOULDBLOCK
+# else
+# define EAGAIN 0x01000006
+# endif
+# endif
+# ifndef EALREADY
+# define EALREADY 0x01000007
+# endif
+# ifndef EBADF
+# define EBADF 0x01000008
+# endif
+# ifndef EBADMSG
+# define EBADMSG 0x01000009
+# endif
+# ifndef EBUSY
+# define EBUSY 0x0100000a
+# endif
+# ifndef ECANCELED
+# define ECANCELED 0x0100000b
+# endif
+# ifndef ECHILD
+# define ECHILD 0x0100000c
+# endif
+# ifndef ECONNABORTED
+# define ECONNABORTED 0x0100000d
+# endif
+# ifndef ECONNREFUSED
+# define ECONNREFUSED 0x0100000e
+# endif
+# ifndef ECONNRESET
+# define ECONNRESET 0x0100000f
+# endif
+# ifndef EDEADLK
+# define EDEADLK 0x01000010
+# endif
+# ifndef EDESTADDRREQ
+# define EDESTADDRREQ 0x01000011
+# endif
+# ifndef EDOM
+# define EDOM 0x01000012
+# endif
+// Reserved in POSIX
+//# ifndef //EDQUOT
+//# define //EDQUOT 0x01000013
+//# endif
+# ifndef EEXIST
+# define EEXIST 0x01000014
+# endif
+# ifndef EFAULT
+# define EFAULT 0x01000015
+# endif
+# ifndef EFBIG
+# define EFBIG 0x01000016
+# endif
+# ifndef EHOSTUNREACH
+# define EHOSTUNREACH 0x01000017
+# endif
+# ifndef EIDRM
+# define EIDRM 0x01000018
+# endif
+# ifndef EILSEQ
+# define EILSEQ 0x01000019
+# endif
+# ifndef EINPROGRESS
+# define EINPROGRESS 0x0100001a
+# endif
+# ifndef EINTR
+# define EINTR 0x0100001b
+# endif
+# ifndef EINVAL
+# define EINVAL 0x0100001c
+# endif
+# ifndef EIO
+# define EIO 0x0100001d
+# endif
+# ifndef EISCONN
+# define EISCONN 0x0100001e
+# endif
+# ifndef EISDIR
+# define EISDIR 0x0100001f
+# endif
+# ifndef ELOOP
+# define ELOOP 0x01000020
+# endif
+# ifndef EMFILE
+# define EMFILE 0x01000021
+# endif
+# ifndef EMLINK
+# define EMLINK 0x01000022
+# endif
+# ifndef EMSGSIZE
+# define EMSGSIZE 0x01000023
+# endif
+// Reserved in POSIX
+//# ifndef //EMULTIHOP
+//# define //EMULTIHOP 0x01000024
+//# endif
+# ifndef ENAMETOOLONG
+# define ENAMETOOLONG 0x01000025
+# endif
+# ifndef ENETDOWN
+# define ENETDOWN 0x01000026
+# endif
+# ifndef ENETRESET
+# define ENETRESET 0x01000027
+# endif
+# ifndef ENETUNREACH
+# define ENETUNREACH 0x01000028
+# endif
+# ifndef ENFILE
+# define ENFILE 0x01000029
+# endif
+# ifndef ENOBUFS
+# define ENOBUFS 0x0100002a
+# endif
+# ifndef ENODATA
+# define ENODATA 0x0100002b
+# endif
+# ifndef ENODEV
+# define ENODEV 0x0100002c
+# endif
+# ifndef ENOENT
+# define ENOENT 0x0100002d
+# endif
+# ifndef ENOEXEC
+# define ENOEXEC 0x0100002e
+# endif
+# ifndef ENOLCK
+# define ENOLCK 0x0100002f
+# endif
+// Reserved in POSIX
+//# ifndef ENOLINK
+//# define ENOLINK 0x01000030
+//# endif
+# ifndef ENOMEM
+# define ENOMEM 0x01000031
+# endif
+# ifndef ENOMSG
+# define ENOMSG 0x01000032
+# endif
+# ifndef ENOPROTOOPT
+# define ENOPROTOOPT 0x01000033
+# endif
+# ifndef ENOSPC
+# define ENOSPC 0x01000034
+# endif
+# ifndef ENOSR
+# define ENOSR 0x01000035
+# endif
+# ifndef ENOSTR
+# define ENOSTR 0x01000036
+# endif
+# ifndef ENOSYS
+# define ENOSYS 0x01000037
+# endif
+# ifndef ENOTCONN
+# define ENOTCONN 0x01000038
+# endif
+# ifndef ENOTDIR
+# define ENOTDIR 0x01000039
+# endif
+# ifndef ENOTEMPTY
+# define ENOTEMPTY 0x0100003a
+# endif
+# ifndef ENOTSOCK
+# define ENOTSOCK 0x0100003b
+# endif
+# ifndef ENOTSUP
+# define ENOTSUP 0x0100003c
+# endif
+# ifndef ENOTTY
+# define ENOTTY 0x0100003d
+# endif
+# ifndef ENXIO
+# define ENXIO 0x0100003e
+# endif
+# ifndef EOPNOTSUPP
+# define EOPNOTSUPP 0x0100003f
+# endif
+# ifndef EOVERFLOW
+# define EOVERFLOW 0x01000040
+# endif
+# ifndef EPERM
+# define EPERM 0x01000041
+# endif
+# ifndef EPIPE
+# define EPIPE 0x01000042
+# endif
+# ifndef EPROTO
+# define EPROTO 0x01000043
+# endif
+# ifndef EPROTONOSUPPORT
+# define EPROTONOSUPPORT 0x01000044
+# endif
+# ifndef EPROTOTYPE
+# define EPROTOTYPE 0x01000045
+# endif
+# ifndef ERANGE
+# define ERANGE 0x01000046
+# endif
+# ifndef EROFS
+# define EROFS 0x01000047
+# endif
+# ifndef ESPIPE
+# define ESPIPE 0x01000048
+# endif
+# ifndef ESRCH
+# define ESRCH 0x01000049
+# endif
+// Reserved in POSIX
+//# ifndef //ESTALE
+//# define //ESTALE 0x0100004a
+//# endif
+# ifndef ETIME
+# define ETIME 0x0100004b
+# endif
+# ifndef ETIMEDOUT
+# define ETIMEDOUT 0x0100004c
+# endif
+# ifndef ETXTBSY
+# define ETXTBSY 0x0100004d
+# endif
+# ifndef EWOULDBLOCK
+# ifdef EAGAIN
+# define EWOULDBLOCK EAGAIN
+# else
+# define EWOULDBLOCK 0x0100004e
+# endif
+# endif
+# ifndef EXDEV
+# define EXDEV 0x0100004f
+# endif
+
+// Non-POSIX:
+# ifndef EHOSTDOWN
+# define EHOSTDOWN 0x01100001
+# endif
+# ifndef EPFNOSUPPORT
+# define EPFNOSUPPORT 0x01100002
+# endif
+# ifndef EPROCLIM
+# define EPROCLIM 0x01100003
+# endif
+# ifndef ESHUTDOWN
+# define ESHUTDOWN 0x01100004
+# endif
+# ifndef ESOCKTNOSUPPORT
+# define ESOCKTNOSUPPORT 0x01100005
+# endif
+# elif defined (__FreeBSD__) || defined (__OpenBSD__)
+# ifndef EBADMSG
+# define EBADMSG EIO
+# endif
+# endif /* defined (__FreeBSD__) || defined (__OpenBSD__) */
+#endif /* defined (PORT_WANT_ERRNO) */
+
+// Use SDL_INCLUDE to portably include the SDL files from the right location.
+// The SDL_DIR definition is provided by the build configuration.
+#define SDL_INCLUDE(file) #file
+
+// Mark a function as using printf-style function arguments, so that
+// extra consistency checks can be made by the compiler.
+// The first argument to PRINTF_FUNCTION and VPRINTF_FUNCTION is the
+// index of the format string argument, the second is the index of
+// the first argument which is specified in the format string (in the
+// case of PRINTF_FUNCTION) or of the va_list argument (in the case of
+// VPRINTF_FUNCTION).
+#ifdef __GNUC__
+# define PRINTF_FUNCTION(formatArg, firstArg) \
+ __attribute__((format(printf, formatArg, firstArg)))
+# define VPRINTF_FUNCTION(formatArg) \
+ __attribute__((format(printf, formatArg, 0)))
+#else
+# define PRINTF_FUNCTION(formatArg, firstArg)
+# define VPRINTF_FUNCTION(formatArg)
+#endif
+
+#if defined(__GNUC__)
+# define _NORETURN __attribute__((noreturn))
+#else
+# define _NORETURN
+#endif
+
+// Buffered vs. unbuffered logfile
+// stderr is normally unbuffered when connected to a terminal, but it
+// will be buffered when connected to a file, when a --logfile argument
+// is passed to uqm.
+// Unbuffered output is slower, which can be significant if much debug output
+// is requested, but after a crash occurs the logfile will still be up to
+// date.
+// On platforms where there is no console, having up-to-date log files
+// after a crash is valuable enough to make the logfile unbuffered by
+// default there.
+#if defined(_WIN32_WCE)
+# define UNBUFFERED_LOGFILE
+#endif
+
+// Variations in path handling
+#if defined(WIN32) || defined(__SYMBIAN32__)
+ // HAVE_DRIVE_LETTERS is defined to signify that DOS/Windows style drive
+ // letters are to be recognised on this platform.
+# define HAVE_DRIVE_LETTERS
+ // BACKSLASH_IS_PATH_SEPARATOR is defined to signify that the backslash
+ // character is to be recognised as a path separator on this platform.
+ // This does not affect the acceptance of forward slashes as path
+ // separators.
+# define BACKSLASH_IS_PATH_SEPARATOR
+#endif
+#if defined(WIN32)
+ // HAVE_UNC_PATHS is defined to signify that Universal Naming Convention
+ // style paths are to be recognised on this platform.
+# define HAVE_UNC_PATHS
+ // HAVE_CWD_PER_DRIVE is defined to signify that every drive has its own
+ // current working directory.
+# define HAVE_CWD_PER_DRIVE
+#endif
+// REJECT_DRIVE_PATH_WITHOUT_SLASH can also be defined, if paths of the form
+// "d:foo/bar" (without a slash after the drive letter) are to be rejected.
+
+#endif /* PORT_H_ */
+
diff --git a/src/regex/Makeinfo b/src/regex/Makeinfo
new file mode 100644
index 0000000..7102683
--- /dev/null
+++ b/src/regex/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="regex.c"
+uqm_HFILES="regex.h regex_internal.h"
diff --git a/src/regex/regcomp.ci b/src/regex/regcomp.ci
new file mode 100644
index 0000000..6803d73
--- /dev/null
+++ b/src/regex/regcomp.ci
@@ -0,0 +1,3931 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+ int length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+ const re_dfastate_t *init_state,
+ char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, int pat_len);
+static void init_word_char (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void optimize_utf8 (re_dfa_t *dfa);
+#endif
+struct subexp_optimize
+{
+ re_dfa_t *dfa;
+ re_token_t *nodes;
+ int no_sub, re_nsub;
+};
+static bin_tree_t *optimize_subexps (struct subexp_optimize *so,
+ bin_tree_t *node, int sidx, int depth);
+static reg_errcode_t analyze (re_dfa_t *dfa);
+static reg_errcode_t analyze_tree (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_first (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_next (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_epsdest (re_dfa_t *dfa, bin_tree_t *node);
+static reg_errcode_t duplicate_node_closure (re_dfa_t *dfa, int top_org_node,
+ int top_clone_node, int root_node,
+ unsigned int constraint);
+static reg_errcode_t duplicate_node (int *new_idx, re_dfa_t *dfa, int org_idx,
+ unsigned int constraint);
+static int search_duplicated_node (re_dfa_t *dfa, int org_node,
+ unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+ int node, int root);
+static void calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+ reg_syntax_t syntax);
+static void fetch_token (re_token_t *result, re_string_t *input,
+ reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax);
+static int peek_token_bracket (re_token_t *token, re_string_t *input,
+ reg_syntax_t syntax);
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+ re_token_t *token, reg_syntax_t syntax,
+ int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+ re_dfa_t *dfa, re_token_t *token,
+ reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+ re_token_t *token, reg_syntax_t syntax,
+ reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token, int token_len,
+ re_dfa_t *dfa,
+ reg_syntax_t syntax,
+ int accept_hyphen);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+ re_string_t *regexp,
+ re_token_t *token);
+#ifndef _LIBC
+# ifdef RE_ENABLE_I18N
+static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset, int *range_alloc,
+ bracket_elem_t *start_elem,
+ bracket_elem_t *end_elem);
+static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset,
+ int *coll_sym_alloc,
+ const unsigned char *name);
+# else /* not RE_ENABLE_I18N */
+static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
+ bracket_elem_t *start_elem,
+ bracket_elem_t *end_elem);
+static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
+ const unsigned char *name);
+# endif /* not RE_ENABLE_I18N */
+#endif /* not _LIBC */
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset,
+ int *equiv_class_alloc,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (unsigned RE_TRANSLATE_TYPE trans,
+ re_bitset_ptr_t sbcset,
+ re_charset_t *mbcset,
+ int *char_class_alloc,
+ const unsigned char *class_name,
+ reg_syntax_t syntax);
+#else /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+ const unsigned char *name);
+static reg_errcode_t build_charclass (unsigned RE_TRANSLATE_TYPE trans,
+ re_bitset_ptr_t sbcset,
+ const unsigned char *class_name,
+ reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
+ unsigned RE_TRANSLATE_TYPE trans,
+ const unsigned char *class_name,
+ const unsigned char *extra,
+ int non_match, reg_errcode_t *err);
+static bin_tree_t *create_tree (re_dfa_t *dfa,
+ bin_tree_t *left, bin_tree_t *right,
+ re_token_type_t type, int index);
+static bin_tree_t *re_dfa_add_tree_node (re_dfa_t *dfa,
+ bin_tree_t *left, bin_tree_t *right,
+ const re_token_t *token)
+ __attribute ((noinline));
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void mark_opt_subexp (const bin_tree_t *src, re_dfa_t *dfa);
+static void mark_opt_subexp_iter (const bin_tree_t *src, re_dfa_t *dfa, int idx);
+
+/* This table gives an error message for each of the error codes listed
+ in regex.h. Obviously the order here has to be same as there.
+ POSIX doesn't require that we do anything for REG_NOERROR,
+ but why not be nice? */
+
+const char __re_error_msgid[] attribute_hidden =
+ {
+#define REG_NOERROR_IDX 0
+ gettext_noop ("Success") /* REG_NOERROR */
+ "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+ gettext_noop ("No match") /* REG_NOMATCH */
+ "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+ gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+ "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+ gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+ "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+ gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+ "\0"
+#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+ gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+ "\0"
+#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+ gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+ "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+ gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+ "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+ gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+ "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+ gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+ "\0"
+#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+ gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+ "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+ gettext_noop ("Invalid range end") /* REG_ERANGE */
+ "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+ gettext_noop ("Memory exhausted") /* REG_ESPACE */
+ "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+ gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+ "\0"
+#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+ gettext_noop ("Premature end of regular expression") /* REG_EEND */
+ "\0"
+#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
+ gettext_noop ("Regular expression too big") /* REG_ESIZE */
+ "\0"
+#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
+ gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+ };
+
+const size_t __re_error_msgid_idx[] attribute_hidden =
+ {
+ REG_NOERROR_IDX,
+ REG_NOMATCH_IDX,
+ REG_BADPAT_IDX,
+ REG_ECOLLATE_IDX,
+ REG_ECTYPE_IDX,
+ REG_EESCAPE_IDX,
+ REG_ESUBREG_IDX,
+ REG_EBRACK_IDX,
+ REG_EPAREN_IDX,
+ REG_EBRACE_IDX,
+ REG_BADBR_IDX,
+ REG_ERANGE_IDX,
+ REG_ESPACE_IDX,
+ REG_BADRPT_IDX,
+ REG_EEND_IDX,
+ REG_ESIZE_IDX,
+ REG_ERPAREN_IDX
+ };
+
+/* Entry points for GNU code. */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+ compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+ Returns 0 if the pattern was valid, otherwise an error string.
+
+ Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+ are set in BUFP on entry. */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+ const char *pattern;
+ size_t length;
+ struct re_pattern_buffer *bufp;
+{
+ reg_errcode_t ret;
+
+ /* And GNU code determines whether or not to get register information
+ by passing null for the REGS argument to re_match, etc., not by
+ setting no_sub, unless RE_NO_SUB is set. */
+ bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
+
+ /* Match anchors at newline. */
+ bufp->newline_anchor = 1;
+
+ ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+ if (!ret)
+ return NULL;
+ return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
+ also be assigned to arbitrarily: each pattern buffer stores its own
+ syntax, so it can be changed between regex compilations. */
+/* This has no initializer because initialized variables in Emacs
+ become read-only after dumping. */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation. This provides
+ for compatibility for various utilities which historically have
+ different, incompatible syntaxes.
+
+ The argument SYNTAX is a bit mask comprised of the various bits
+ defined in regex.h. We return the old syntax. */
+
+reg_syntax_t
+re_set_syntax (syntax)
+ reg_syntax_t syntax;
+{
+ reg_syntax_t ret = re_syntax_options;
+
+ re_syntax_options = syntax;
+ return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (bufp)
+ struct re_pattern_buffer *bufp;
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ char *fastmap = bufp->fastmap;
+
+ memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+ re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+ if (dfa->init_state != dfa->init_state_word)
+ re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+ if (dfa->init_state != dfa->init_state_nl)
+ re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+ if (dfa->init_state != dfa->init_state_begbuf)
+ re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+ bufp->fastmap_accurate = 1;
+ return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+static inline void
+__attribute ((always_inline))
+re_set_fastmap (char *fastmap, int icase, int ch)
+{
+ fastmap[ch] = 1;
+ if (icase)
+ fastmap[tolower (ch)] = 1;
+}
+
+/* Helper function for re_compile_fastmap.
+ Compile fastmap for the initial_state INIT_STATE. */
+
+static void
+re_compile_fastmap_iter (bufp, init_state, fastmap)
+ regex_t *bufp;
+ const re_dfastate_t *init_state;
+ char *fastmap;
+{
+ re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+ int node_cnt;
+ int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE));
+ for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+ {
+ int node = init_state->nodes.elems[node_cnt];
+ re_token_type_t type = dfa->nodes[node].type;
+
+ if (type == CHARACTER)
+ {
+ re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+#ifdef RE_ENABLE_I18N
+ if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+ {
+ unsigned char *buf = alloca (dfa->mb_cur_max), *p;
+ wchar_t wc;
+ mbstate_t state;
+
+ p = buf;
+ *p++ = dfa->nodes[node].opr.c;
+ while (++node < dfa->nodes_len
+ && dfa->nodes[node].type == CHARACTER
+ && dfa->nodes[node].mb_partial)
+ *p++ = dfa->nodes[node].opr.c;
+ memset (&state, 0, sizeof (state));
+ if (mbrtowc (&wc, (const char *) buf, p - buf,
+ &state) == p - buf
+ && __wcrtomb ((char *) buf, towlower (wc), &state) > 0)
+ re_set_fastmap (fastmap, 0, buf[0]);
+ }
+#endif
+ }
+ else if (type == SIMPLE_BRACKET)
+ {
+ int i, j, ch;
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (dfa->nodes[node].opr.sbcset[i] & (1 << j))
+ re_set_fastmap (fastmap, icase, ch);
+ }
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET)
+ {
+ int i;
+ re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+ if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes
+ || cset->nranges || cset->nchar_classes)
+ {
+# ifdef _LIBC
+ if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+ {
+ /* In this case we want to catch the bytes which are
+ the first byte of any collation elements.
+ e.g. In da_DK, we want to catch 'a' since "aa"
+ is a valid collation element, and don't catch
+ 'b' since 'b' is the only collation element
+ which starts from 'b'. */
+ int j, ch;
+ const int32_t *table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (table[ch] < 0)
+ re_set_fastmap (fastmap, icase, ch);
+ }
+# else
+ if (dfa->mb_cur_max > 1)
+ for (i = 0; i < SBC_MAX; ++i)
+ if (__btowc (i) == WEOF)
+ re_set_fastmap (fastmap, icase, i);
+# endif /* not _LIBC */
+ }
+ for (i = 0; i < cset->nmbchars; ++i)
+ {
+ char buf[256];
+ mbstate_t state;
+ memset (&state, '\0', sizeof (state));
+ __wcrtomb (buf, cset->mbchars[i], &state);
+ re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+ if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+ {
+ __wcrtomb (buf, towlower (cset->mbchars[i]), &state);
+ re_set_fastmap (fastmap, 0, *(unsigned char *) buf);
+ }
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+ || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+ || type == END_OF_RE)
+ {
+ memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+ if (type == END_OF_RE)
+ bufp->can_be_null = 1;
+ return;
+ }
+ }
+}
+
+/* Entry point for POSIX code. */
+/* regcomp takes a regular expression as a string and compiles it.
+
+ PREG is a regex_t *. We do not expect any fields to be initialized,
+ since POSIX says we shouldn't. Thus, we set
+
+ `buffer' to the compiled pattern;
+ `used' to the length of the compiled pattern;
+ `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+ REG_EXTENDED bit in CFLAGS is set; otherwise, to
+ RE_SYNTAX_POSIX_BASIC;
+ `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+ `fastmap' to an allocated space for the fastmap;
+ `fastmap_accurate' to zero;
+ `re_nsub' to the number of subexpressions in PATTERN.
+
+ PATTERN is the address of the pattern string.
+
+ CFLAGS is a series of bits which affect compilation.
+
+ If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+ use POSIX basic syntax.
+
+ If REG_NEWLINE is set, then . and [^...] don't match newline.
+ Also, regexec will try a match beginning after every newline.
+
+ If REG_ICASE is set, then we considers upper- and lowercase
+ versions of letters to be equivalent when matching.
+
+ If REG_NOSUB is set, then when PREG is passed to regexec, that
+ routine will report only success or failure, and nothing about the
+ registers.
+
+ It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
+ the return codes and their meanings.) */
+
+int
+regcomp (preg, pattern, cflags)
+ regex_t *__restrict preg;
+ const char *__restrict pattern;
+ int cflags;
+{
+ reg_errcode_t ret;
+ reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+ : RE_SYNTAX_POSIX_BASIC);
+
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ preg->used = 0;
+
+ /* Try to allocate space for the fastmap. */
+ preg->fastmap = re_malloc (char, SBC_MAX);
+ if (BE (preg->fastmap == NULL, 0))
+ return REG_ESPACE;
+
+ syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+ /* If REG_NEWLINE is set, newlines are treated differently. */
+ if (cflags & REG_NEWLINE)
+ { /* REG_NEWLINE implies neither . nor [^...] match newline. */
+ syntax &= ~RE_DOT_NEWLINE;
+ syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+ /* It also changes the matching behavior. */
+ preg->newline_anchor = 1;
+ }
+ else
+ preg->newline_anchor = 0;
+ preg->no_sub = !!(cflags & REG_NOSUB);
+ preg->translate = NULL;
+
+ ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+ /* POSIX doesn't distinguish between an unmatched open-group and an
+ unmatched close-group: both are REG_EPAREN. */
+ if (ret == REG_ERPAREN)
+ ret = REG_EPAREN;
+
+ /* We have already checked preg->fastmap != NULL. */
+ if (BE (ret == REG_NOERROR, 1))
+ /* Compute the fastmap now, since regexec cannot modify the pattern
+ buffer. This function never fails in this implementation. */
+ (void) re_compile_fastmap (preg);
+ else
+ {
+ /* Some error occurred while compiling the expression. */
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+ }
+
+ return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+ from either regcomp or regexec. We don't use PREG here. */
+
+size_t
+regerror (err_code, preg, errbuf, errbuf_size)
+ int err_code;
+ const regex_t *preg;
+ char *errbuf;
+ size_t errbuf_size;
+{
+ const char *msg;
+ size_t msg_size;
+
+ if (BE (err_code < 0
+ || err_code >= (int) (sizeof (__re_error_msgid_idx)
+ / sizeof (__re_error_msgid_idx[0])), 0))
+ /* Only error codes returned by the rest of the code should be passed
+ to this routine. If we are given anything else, or if other regex
+ code generates an invalid error code, then the program has a bug.
+ Dump core so we can fix it. */
+ abort ();
+
+ msg = gettext (__re_error_msgid + __re_error_msgid_idx[err_code]);
+
+ msg_size = strlen (msg) + 1; /* Includes the null. */
+
+ if (BE (errbuf_size != 0, 1))
+ {
+ if (BE (msg_size > errbuf_size, 0))
+ {
+#if defined HAVE_MEMPCPY || defined _LIBC
+ *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
+#else
+ memcpy (errbuf, msg, errbuf_size - 1);
+ errbuf[errbuf_size - 1] = 0;
+#endif
+ }
+ else
+ memcpy (errbuf, msg, msg_size);
+ }
+
+ return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+ UTF-8 is used. Otherwise we would allocate memory just to initialize
+ it the same all the time. UTF-8 is the preferred encoding so this is
+ a worthwhile optimization. */
+static const bitset utf8_sb_map =
+{
+ /* Set the first 128 bits. */
+# if UINT_MAX == 0xffffffff
+ 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
+# else
+# error "Add case for new unsigned int size"
+# endif
+};
+#endif
+
+
+static void
+free_dfa_content (re_dfa_t *dfa)
+{
+ int i, j;
+
+ if (dfa->nodes)
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ re_token_t *node = dfa->nodes + i;
+#ifdef RE_ENABLE_I18N
+ if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+ free_charset (node->opr.mbcset);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+ re_free (node->opr.sbcset);
+ }
+ re_free (dfa->nexts);
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ if (dfa->eclosures != NULL)
+ re_node_set_free (dfa->eclosures + i);
+ if (dfa->inveclosures != NULL)
+ re_node_set_free (dfa->inveclosures + i);
+ if (dfa->edests != NULL)
+ re_node_set_free (dfa->edests + i);
+ }
+ re_free (dfa->edests);
+ re_free (dfa->eclosures);
+ re_free (dfa->inveclosures);
+ re_free (dfa->nodes);
+
+ if (dfa->state_table)
+ for (i = 0; i <= dfa->state_hash_mask; ++i)
+ {
+ struct re_state_table_entry *entry = dfa->state_table + i;
+ for (j = 0; j < entry->num; ++j)
+ {
+ re_dfastate_t *state = entry->array[j];
+ free_state (state);
+ }
+ re_free (entry->array);
+ }
+ re_free (dfa->state_table);
+#ifdef RE_ENABLE_I18N
+ if (dfa->sb_char != utf8_sb_map)
+ re_free (dfa->sb_char);
+#endif
+ re_free (dfa->subexp_map);
+#ifdef DEBUG
+ re_free (dfa->re_str);
+#endif
+
+ re_free (dfa);
+}
+
+
+/* Free dynamically allocated space used by PREG. */
+
+void
+regfree (preg)
+ regex_t *preg;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ if (BE (dfa != NULL, 1))
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+
+ re_free (preg->fastmap);
+ preg->fastmap = NULL;
+
+ re_free (preg->translate);
+ preg->translate = NULL;
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer. */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+ these names if they don't use our functions, and still use
+ regcomp/regexec above without link errors. */
+weak_function
+# endif
+re_comp (s)
+ const char *s;
+{
+ reg_errcode_t ret;
+ char *fastmap;
+
+ if (!s)
+ {
+ if (!re_comp_buf.buffer)
+ return gettext ("No previous regular expression");
+ return 0;
+ }
+
+ if (re_comp_buf.buffer)
+ {
+ fastmap = re_comp_buf.fastmap;
+ re_comp_buf.fastmap = NULL;
+ __regfree (&re_comp_buf);
+ memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
+ re_comp_buf.fastmap = fastmap;
+ }
+
+ if (re_comp_buf.fastmap == NULL)
+ {
+ re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+ if (re_comp_buf.fastmap == NULL)
+ return (char *) gettext (__re_error_msgid
+ + __re_error_msgid_idx[(int) REG_ESPACE]);
+ }
+
+ /* Since `re_exec' always passes NULL for the `regs' argument, we
+ don't need to initialize the pattern buffer fields which affect it. */
+
+ /* Match anchors at newlines. */
+ re_comp_buf.newline_anchor = 1;
+
+ ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+ if (!ret)
+ return NULL;
+
+ /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
+ return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+ __regfree (&re_comp_buf);
+}
+#endif
+
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point.
+ Compile the regular expression PATTERN, whose length is LENGTH.
+ SYNTAX indicate regular expression's syntax. */
+
+static reg_errcode_t
+re_compile_internal (preg, pattern, length, syntax)
+ regex_t *preg;
+ const char * pattern;
+ int length;
+ reg_syntax_t syntax;
+{
+ reg_errcode_t err = REG_NOERROR;
+ re_dfa_t *dfa;
+ re_string_t regexp;
+
+ /* Initialize the pattern buffer. */
+ preg->fastmap_accurate = 0;
+ preg->syntax = syntax;
+ preg->not_bol = preg->not_eol = 0;
+ preg->used = 0;
+ preg->re_nsub = 0;
+ preg->can_be_null = 0;
+ preg->regs_allocated = REGS_UNALLOCATED;
+
+ /* Initialize the dfa. */
+ dfa = (re_dfa_t *) preg->buffer;
+ if (BE (preg->allocated < sizeof (re_dfa_t), 0))
+ {
+ /* If zero allocated, but buffer is non-null, try to realloc
+ enough space. This loses if buffer's address is bogus, but
+ that is the user's responsibility. If ->buffer is NULL this
+ is a simple allocation. */
+ dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+ if (dfa == NULL)
+ return REG_ESPACE;
+ preg->allocated = sizeof (re_dfa_t);
+ preg->buffer = (unsigned char *) dfa;
+ }
+ preg->used = sizeof (re_dfa_t);
+
+ err = init_dfa (dfa, length);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ return err;
+ }
+#ifdef DEBUG
+ dfa->re_str = re_malloc (char, length + 1);
+ strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+ err = re_string_construct (&regexp, pattern, length, preg->translate,
+ syntax & RE_ICASE, dfa);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_compile_internal_free_return:
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ return err;
+ }
+
+ /* Parse the regular expression, and build a structure tree. */
+ preg->re_nsub = 0;
+ dfa->str_tree = parse (&regexp, preg, syntax, &err);
+ if (BE (dfa->str_tree == NULL, 0))
+ goto re_compile_internal_free_return;
+
+#ifdef RE_ENABLE_I18N
+ /* If possible, do searching in single byte encoding to speed things up. */
+ if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
+ optimize_utf8 (dfa);
+#endif
+
+ if (preg->re_nsub > 0)
+ {
+ struct subexp_optimize so;
+
+ so.dfa = dfa;
+ so.nodes = dfa->nodes;
+ so.no_sub = preg->no_sub;
+ so.re_nsub = preg->re_nsub;
+ dfa->str_tree = optimize_subexps (&so, dfa->str_tree, -1, 0);
+ }
+
+ /* Analyze the tree and collect information which is necessary to
+ create the dfa. */
+ err = analyze (dfa);
+ if (BE (err != REG_NOERROR, 0))
+ goto re_compile_internal_free_return;
+
+ /* Then create the initial state of the dfa. */
+ err = create_initial_state (dfa);
+
+ /* Release work areas. */
+ free_workarea_compile (preg);
+ re_string_destruct (&regexp);
+
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_dfa_content (dfa);
+ preg->buffer = NULL;
+ preg->allocated = 0;
+ }
+
+ return err;
+}
+
+/* Initialize DFA. We use the length of the regular expression PAT_LEN
+ as the initial length of some arrays. */
+
+static reg_errcode_t
+init_dfa (dfa, pat_len)
+ re_dfa_t *dfa;
+ int pat_len;
+{
+ int table_size;
+#ifndef _LIBC
+ char *codeset_name;
+#endif
+
+ memset (dfa, '\0', sizeof (re_dfa_t));
+
+ /* Force allocation of str_tree_storage the first time. */
+ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+
+ dfa->nodes_alloc = pat_len + 1;
+ dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+ dfa->states_alloc = pat_len + 1;
+
+ /* table_size = 2 ^ ceil(log pat_len) */
+ for (table_size = 1; table_size > 0; table_size <<= 1)
+ if (table_size > pat_len)
+ break;
+
+ dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+ dfa->state_hash_mask = table_size - 1;
+
+ dfa->mb_cur_max = MB_CUR_MAX;
+#ifdef _LIBC
+ if (dfa->mb_cur_max == 6
+ && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
+ dfa->is_utf8 = 1;
+ dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
+ != 0);
+#else
+# ifdef HAVE_LANGINFO_CODESET
+ codeset_name = nl_langinfo (CODESET);
+# else
+ codeset_name = getenv ("LC_ALL");
+ if (codeset_name == NULL || codeset_name[0] == '\0')
+ codeset_name = getenv ("LC_CTYPE");
+ if (codeset_name == NULL || codeset_name[0] == '\0')
+ codeset_name = getenv ("LANG");
+ if (codeset_name == NULL)
+ codeset_name = "";
+ else if (strchr (codeset_name, '.') != NULL)
+ codeset_name = strchr (codeset_name, '.') + 1;
+# endif
+
+ if (strcasecmp (codeset_name, "UTF-8") == 0
+ || strcasecmp (codeset_name, "UTF8") == 0)
+ dfa->is_utf8 = 1;
+
+ /* We check exhaustively in the loop below if this charset is a
+ superset of ASCII. */
+ dfa->map_notascii = 0;
+#endif
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ if (dfa->is_utf8)
+ dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+ else
+ {
+ int i, j, ch;
+
+ dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1);
+ if (BE (dfa->sb_char == NULL, 0))
+ return REG_ESPACE;
+
+ /* Clear all bits by, then set those corresponding to single
+ byte chars. */
+ bitset_empty (dfa->sb_char);
+
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ {
+ wchar_t wch = __btowc (ch);
+ if (wch != WEOF)
+ dfa->sb_char[i] |= 1 << j;
+# ifndef _LIBC
+ if (isascii (ch) && wch != (wchar_t) ch)
+ dfa->map_notascii = 1;
+# endif
+ }
+ }
+ }
+#endif
+
+ if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+ "word". In this case "word" means that it is the word construction
+ character used by some operators like "\<", "\>", etc. */
+
+static void
+init_word_char (dfa)
+ re_dfa_t *dfa;
+{
+ int i, j, ch;
+ dfa->word_ops_used = 1;
+ for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+ for (j = 0; j < UINT_BITS; ++j, ++ch)
+ if (isalnum (ch) || ch == '_')
+ dfa->word_char[i] |= 1 << j;
+}
+
+/* Free the work area which are only used while compiling. */
+
+static void
+free_workarea_compile (preg)
+ regex_t *preg;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_storage_t *storage, *next;
+ for (storage = dfa->str_tree_storage; storage; storage = next)
+ {
+ next = storage->next;
+ re_free (storage);
+ }
+ dfa->str_tree_storage = NULL;
+ dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+ dfa->str_tree = NULL;
+ re_free (dfa->org_indices);
+ dfa->org_indices = NULL;
+}
+
+/* Create initial states for all contexts. */
+
+static reg_errcode_t
+create_initial_state (dfa)
+ re_dfa_t *dfa;
+{
+ int first, i;
+ reg_errcode_t err;
+ re_node_set init_nodes;
+
+ /* Initial states have the epsilon closure of the node which is
+ the first node of the regular expression. */
+ first = dfa->str_tree->first;
+ dfa->init_node = first;
+ err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* The back-references which are in initial states can epsilon transit,
+ since in this case all of the subexpressions can be null.
+ Then we add epsilon closures of the nodes which are the next nodes of
+ the back-references. */
+ if (dfa->nbackref > 0)
+ for (i = 0; i < init_nodes.nelem; ++i)
+ {
+ int node_idx = init_nodes.elems[i];
+ re_token_type_t type = dfa->nodes[node_idx].type;
+
+ int clexp_idx;
+ if (type != OP_BACK_REF)
+ continue;
+ for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+ {
+ re_token_t *clexp_node;
+ clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+ if (clexp_node->type == OP_CLOSE_SUBEXP
+ && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
+ break;
+ }
+ if (clexp_idx == init_nodes.nelem)
+ continue;
+
+ if (type == OP_BACK_REF)
+ {
+ int dest_idx = dfa->edests[node_idx].elems[0];
+ if (!re_node_set_contains (&init_nodes, dest_idx))
+ {
+ re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx);
+ i = 0;
+ }
+ }
+ }
+
+ /* It must be the first time to invoke acquire_state. */
+ dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+ /* We don't check ERR here, since the initial state must not be NULL. */
+ if (BE (dfa->init_state == NULL, 0))
+ return err;
+ if (dfa->init_state->has_constraint)
+ {
+ dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_WORD);
+ dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+ CONTEXT_NEWLINE);
+ dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+ &init_nodes,
+ CONTEXT_NEWLINE
+ | CONTEXT_BEGBUF);
+ if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return err;
+ }
+ else
+ dfa->init_state_word = dfa->init_state_nl
+ = dfa->init_state_begbuf = dfa->init_state;
+
+ re_node_set_free (&init_nodes);
+ return REG_NOERROR;
+}
+
+#ifdef RE_ENABLE_I18N
+/* If it is possible to do searching in single byte encoding instead of UTF-8
+ to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
+ DFA nodes where needed. */
+
+static void
+optimize_utf8 (dfa)
+ re_dfa_t *dfa;
+{
+ int node, i, mb_chars = 0, has_period = 0;
+
+ for (node = 0; node < dfa->nodes_len; ++node)
+ switch (dfa->nodes[node].type)
+ {
+ case CHARACTER:
+ if (dfa->nodes[node].opr.c >= 0x80)
+ mb_chars = 1;
+ break;
+ case ANCHOR:
+ switch (dfa->nodes[node].opr.idx)
+ {
+ case LINE_FIRST:
+ case LINE_LAST:
+ case BUF_FIRST:
+ case BUF_LAST:
+ break;
+ default:
+ /* Word anchors etc. cannot be handled. */
+ return;
+ }
+ break;
+ case OP_PERIOD:
+ has_period = 1;
+ break;
+ case OP_BACK_REF:
+ case OP_ALT:
+ case END_OF_RE:
+ case OP_DUP_ASTERISK:
+ case OP_DUP_QUESTION:
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ break;
+ case SIMPLE_BRACKET:
+ /* Just double check. */
+ for (i = 0x80 / UINT_BITS; i < BITSET_UINTS; ++i)
+ if (dfa->nodes[node].opr.sbcset[i])
+ return;
+ break;
+ default:
+ return;
+ }
+
+ if (mb_chars || has_period)
+ for (node = 0; node < dfa->nodes_len; ++node)
+ {
+ if (dfa->nodes[node].type == CHARACTER
+ && dfa->nodes[node].opr.c >= 0x80)
+ dfa->nodes[node].mb_partial = 0;
+ else if (dfa->nodes[node].type == OP_PERIOD)
+ dfa->nodes[node].type = OP_UTF8_PERIOD;
+ }
+
+ /* The search can be in single byte locale. */
+ dfa->mb_cur_max = 1;
+ dfa->is_utf8 = 0;
+ dfa->has_mb_node = dfa->nbackref > 0 || has_period;
+}
+#endif
+
+static bin_tree_t *
+optimize_subexps (so, node, sidx, depth)
+ struct subexp_optimize *so;
+ bin_tree_t *node;
+ int sidx, depth;
+{
+ int idx, new_depth, new_sidx;
+ bin_tree_t *ret;
+ if (node == NULL)
+ return NULL;
+
+ new_depth = 0;
+ new_sidx = sidx;
+ if ((depth & 1) && node->type == CONCAT
+ && node->right && node->right->type == 0
+ && so->nodes[idx = node->right->node_idx].type == OP_CLOSE_SUBEXP)
+ {
+ new_depth = depth + 1;
+ if (new_depth == 2
+ || (so->nodes[idx].opr.idx < 8 * sizeof (so->dfa->used_bkref_map)
+ && so->dfa->used_bkref_map & (1 << so->nodes[idx].opr.idx)))
+ new_sidx = so->nodes[idx].opr.idx;
+ }
+ node->left = optimize_subexps (so, node->left, new_sidx, new_depth);
+ new_depth = (depth & 1) == 0 && node->type == CONCAT
+ && node->left && node->left->type == 0
+ && so->nodes[node->left->node_idx].type == OP_OPEN_SUBEXP
+ ? depth + 1 : 0;
+ node->right = optimize_subexps (so, node->right, sidx, new_depth);
+
+ if (node->type != CONCAT)
+ return node;
+ if ((depth & 1) == 0
+ && node->left
+ && node->left->type == 0
+ && so->nodes[idx = node->left->node_idx].type == OP_OPEN_SUBEXP)
+ ret = node->right;
+ else if ((depth & 1)
+ && node->right
+ && node->right->type == 0
+ && so->nodes[idx = node->right->node_idx].type == OP_CLOSE_SUBEXP)
+ ret = node->left;
+ else
+ return node;
+
+ if (so->nodes[idx].opr.idx < 8 * sizeof (so->dfa->used_bkref_map)
+ && so->dfa->used_bkref_map & (1 << so->nodes[idx].opr.idx))
+ return node;
+
+ if (!so->no_sub)
+ {
+ int i;
+
+ if (depth < 2)
+ return node;
+
+ if (so->dfa->subexp_map == NULL)
+ {
+ so->dfa->subexp_map = re_malloc (int, so->re_nsub);
+ if (so->dfa->subexp_map == NULL)
+ return node;
+
+ for (i = 0; i < so->re_nsub; i++)
+ so->dfa->subexp_map[i] = i;
+ }
+
+ i = so->nodes[idx].opr.idx;
+ assert (sidx < i);
+ so->dfa->subexp_map[i] = sidx;
+ }
+
+ so->nodes[idx].type = OP_DELETED_SUBEXP;
+ ret->parent = node->parent;
+ return ret;
+}
+
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+ "eclosure", and "inveclosure". */
+
+static reg_errcode_t
+analyze (dfa)
+ re_dfa_t *dfa;
+{
+ int i;
+ reg_errcode_t ret;
+
+ /* Allocate arrays. */
+ dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+ dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
+ dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+ if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
+ || dfa->eclosures == NULL || dfa->inveclosures == NULL, 0))
+ return REG_ESPACE;
+ /* Initialize them. */
+ for (i = 0; i < dfa->nodes_len; ++i)
+ {
+ dfa->nexts[i] = -1;
+ re_node_set_init_empty (dfa->edests + i);
+ re_node_set_init_empty (dfa->eclosures + i);
+ re_node_set_init_empty (dfa->inveclosures + i);
+ }
+
+ ret = analyze_tree (dfa, dfa->str_tree);
+ if (BE (ret == REG_NOERROR, 1))
+ {
+ ret = calc_eclosure (dfa);
+ if (ret == REG_NOERROR)
+ calc_inveclosure (dfa);
+ }
+ return ret;
+}
+
+/* Helper functions for analyze.
+ This function calculate "first", "next", and "edest" for the subtree
+ whose root is NODE. */
+
+static reg_errcode_t
+analyze_tree (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ reg_errcode_t ret;
+ if (node->first == -1)
+ calc_first (dfa, node);
+ if (node->next == -1)
+ calc_next (dfa, node);
+ calc_epsdest (dfa, node);
+
+ /* Calculate "first" etc. for the left child. */
+ if (node->left != NULL)
+ {
+ ret = analyze_tree (dfa, node->left);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ /* Calculate "first" etc. for the right child. */
+ if (node->right != NULL)
+ {
+ ret = analyze_tree (dfa, node->right);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ return REG_NOERROR;
+}
+
+/* Calculate "first" for the node NODE. */
+static void
+calc_first (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx, type;
+ idx = node->node_idx;
+ type = (node->type == 0) ? dfa->nodes[idx].type : node->type;
+
+ switch (type)
+ {
+#ifdef DEBUG
+ case OP_OPEN_BRACKET:
+ case OP_CLOSE_BRACKET:
+ case OP_OPEN_DUP_NUM:
+ case OP_CLOSE_DUP_NUM:
+ case OP_DUP_PLUS:
+ case OP_NON_MATCH_LIST:
+ case OP_OPEN_COLL_ELEM:
+ case OP_CLOSE_COLL_ELEM:
+ case OP_OPEN_EQUIV_CLASS:
+ case OP_CLOSE_EQUIV_CLASS:
+ case OP_OPEN_CHAR_CLASS:
+ case OP_CLOSE_CHAR_CLASS:
+ /* These must not appear here. */
+ assert (0);
+#endif
+ case END_OF_RE:
+ case CHARACTER:
+ case OP_PERIOD:
+ case OP_DUP_ASTERISK:
+ case OP_DUP_QUESTION:
+#ifdef RE_ENABLE_I18N
+ case OP_UTF8_PERIOD:
+ case COMPLEX_BRACKET:
+#endif /* RE_ENABLE_I18N */
+ case SIMPLE_BRACKET:
+ case OP_BACK_REF:
+ case ANCHOR:
+ case OP_OPEN_SUBEXP:
+ case OP_CLOSE_SUBEXP:
+ node->first = idx;
+ break;
+ case OP_ALT:
+ node->first = idx;
+ break;
+ /* else fall through */
+ default:
+#ifdef DEBUG
+ assert (node->left != NULL);
+#endif
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ node->first = node->left->first;
+ break;
+ }
+}
+
+/* Calculate "next" for the node NODE. */
+
+static void
+calc_next (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx, type;
+ bin_tree_t *parent = node->parent;
+ if (parent == NULL)
+ {
+ node->next = -1;
+ idx = node->node_idx;
+ if (node->type == 0)
+ dfa->nexts[idx] = node->next;
+ return;
+ }
+
+ idx = parent->node_idx;
+ type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type;
+
+ switch (type)
+ {
+ case OP_DUP_ASTERISK:
+ node->next = idx;
+ break;
+ case CONCAT:
+ if (parent->left == node)
+ {
+ if (parent->right->first == -1)
+ calc_first (dfa, parent->right);
+ node->next = parent->right->first;
+ break;
+ }
+ /* else fall through */
+ default:
+ if (parent->next == -1)
+ calc_next (dfa, parent);
+ node->next = parent->next;
+ break;
+ }
+ idx = node->node_idx;
+ if (node->type == 0)
+ dfa->nexts[idx] = node->next;
+}
+
+/* Calculate "edest" for the node NODE. */
+
+static void
+calc_epsdest (dfa, node)
+ re_dfa_t *dfa;
+ bin_tree_t *node;
+{
+ int idx;
+ idx = node->node_idx;
+ if (node->type == 0)
+ {
+ if (dfa->nodes[idx].type == OP_DUP_ASTERISK
+ || dfa->nodes[idx].type == OP_DUP_QUESTION)
+ {
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ if (node->next == -1)
+ calc_next (dfa, node);
+ re_node_set_init_2 (dfa->edests + idx, node->left->first,
+ node->next);
+ }
+ else if (dfa->nodes[idx].type == OP_ALT)
+ {
+ int left, right;
+ if (node->left != NULL)
+ {
+ if (node->left->first == -1)
+ calc_first (dfa, node->left);
+ left = node->left->first;
+ }
+ else
+ {
+ if (node->next == -1)
+ calc_next (dfa, node);
+ left = node->next;
+ }
+ if (node->right != NULL)
+ {
+ if (node->right->first == -1)
+ calc_first (dfa, node->right);
+ right = node->right->first;
+ }
+ else
+ {
+ if (node->next == -1)
+ calc_next (dfa, node);
+ right = node->next;
+ }
+ re_node_set_init_2 (dfa->edests + idx, left, right);
+ }
+ else if (dfa->nodes[idx].type == ANCHOR
+ || dfa->nodes[idx].type == OP_OPEN_SUBEXP
+ || dfa->nodes[idx].type == OP_CLOSE_SUBEXP
+ || dfa->nodes[idx].type == OP_BACK_REF)
+ re_node_set_init_1 (dfa->edests + idx, node->next);
+ else
+ assert (!IS_EPSILON_NODE (dfa->nodes[idx].type));
+ }
+}
+
+/* Duplicate the epsilon closure of the node ROOT_NODE.
+ Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
+ to their own constraint. */
+
+static reg_errcode_t
+duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
+ init_constraint)
+ re_dfa_t *dfa;
+ int top_org_node, top_clone_node, root_node;
+ unsigned int init_constraint;
+{
+ reg_errcode_t err;
+ int org_node, clone_node, ret;
+ unsigned int constraint = init_constraint;
+ for (org_node = top_org_node, clone_node = top_clone_node;;)
+ {
+ int org_dest, clone_dest;
+ if (dfa->nodes[org_node].type == OP_BACK_REF)
+ {
+ /* If the back reference epsilon-transit, its destination must
+ also have the constraint. Then duplicate the epsilon closure
+ of the destination of the back reference, and store it in
+ edests of the back reference. */
+ org_dest = dfa->nexts[org_node];
+ re_node_set_empty (dfa->edests + clone_node);
+ err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ dfa->nexts[clone_node] = dfa->nexts[org_node];
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+ else if (dfa->edests[org_node].nelem == 0)
+ {
+ /* In case of the node can't epsilon-transit, don't duplicate the
+ destination and store the original destination as the
+ destination of the node. */
+ dfa->nexts[clone_node] = dfa->nexts[org_node];
+ break;
+ }
+ else if (dfa->edests[org_node].nelem == 1)
+ {
+ /* In case of the node can epsilon-transit, and it has only one
+ destination. */
+ org_dest = dfa->edests[org_node].elems[0];
+ re_node_set_empty (dfa->edests + clone_node);
+ if (dfa->nodes[org_node].type == ANCHOR)
+ {
+ /* In case of the node has another constraint, append it. */
+ if (org_node == root_node && clone_node != org_node)
+ {
+ /* ...but if the node is root_node itself, it means the
+ epsilon closure have a loop, then tie it to the
+ destination of the root_node. */
+ ret = re_node_set_insert (dfa->edests + clone_node,
+ org_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ break;
+ }
+ constraint |= dfa->nodes[org_node].opr.ctx_type;
+ }
+ err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+ else /* dfa->edests[org_node].nelem == 2 */
+ {
+ /* In case of the node can epsilon-transit, and it has two
+ destinations. E.g. '|', '*', '+', '?'. */
+ org_dest = dfa->edests[org_node].elems[0];
+ re_node_set_empty (dfa->edests + clone_node);
+ /* Search for a duplicated node which satisfies the constraint. */
+ clone_dest = search_duplicated_node (dfa, org_dest, constraint);
+ if (clone_dest == -1)
+ {
+ /* There are no such a duplicated node, create a new one. */
+ err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ err = duplicate_node_closure (dfa, org_dest, clone_dest,
+ root_node, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ {
+ /* There are a duplicated node which satisfy the constraint,
+ use it to avoid infinite loop. */
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+
+ org_dest = dfa->edests[org_node].elems[1];
+ err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+ if (BE (ret < 0, 0))
+ return REG_ESPACE;
+ }
+ org_node = org_dest;
+ clone_node = clone_dest;
+ }
+ return REG_NOERROR;
+}
+
+/* Search for a node which is duplicated from the node ORG_NODE, and
+ satisfies the constraint CONSTRAINT. */
+
+static int
+search_duplicated_node (dfa, org_node, constraint)
+ re_dfa_t *dfa;
+ int org_node;
+ unsigned int constraint;
+{
+ int idx;
+ for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
+ {
+ if (org_node == dfa->org_indices[idx]
+ && constraint == dfa->nodes[idx].constraint)
+ return idx; /* Found. */
+ }
+ return -1; /* Not found. */
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+ The new index will be stored in NEW_IDX and return REG_NOERROR if succeeded,
+ otherwise return the error code. */
+
+static reg_errcode_t
+duplicate_node (new_idx, dfa, org_idx, constraint)
+ re_dfa_t *dfa;
+ int *new_idx, org_idx;
+ unsigned int constraint;
+{
+ int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx], 1);
+ if (BE (dup_idx == -1, 0))
+ return REG_ESPACE;
+ dfa->nodes[dup_idx].constraint = constraint;
+ if (dfa->nodes[org_idx].type == ANCHOR)
+ dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type;
+ dfa->nodes[dup_idx].duplicated = 1;
+ re_node_set_init_empty (dfa->edests + dup_idx);
+ re_node_set_init_empty (dfa->eclosures + dup_idx);
+ re_node_set_init_empty (dfa->inveclosures + dup_idx);
+
+ /* Store the index of the original node. */
+ dfa->org_indices[dup_idx] = org_idx;
+ *new_idx = dup_idx;
+ return REG_NOERROR;
+}
+
+static void
+calc_inveclosure (dfa)
+ re_dfa_t *dfa;
+{
+ int src, idx, dest;
+ for (src = 0; src < dfa->nodes_len; ++src)
+ {
+ if (dfa->nodes[src].type == OP_DELETED_SUBEXP)
+ continue;
+ for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+ {
+ dest = dfa->eclosures[src].elems[idx];
+ re_node_set_insert_last (dfa->inveclosures + dest, src);
+ }
+ }
+}
+
+/* Calculate "eclosure" for all the node in DFA. */
+
+static reg_errcode_t
+calc_eclosure (dfa)
+ re_dfa_t *dfa;
+{
+ int node_idx, incomplete;
+#ifdef DEBUG
+ assert (dfa->nodes_len > 0);
+#endif
+ incomplete = 0;
+ /* For each nodes, calculate epsilon closure. */
+ for (node_idx = 0; ; ++node_idx)
+ {
+ reg_errcode_t err;
+ re_node_set eclosure_elem;
+ if (node_idx == dfa->nodes_len)
+ {
+ if (!incomplete)
+ break;
+ incomplete = 0;
+ node_idx = 0;
+ }
+
+#ifdef DEBUG
+ assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+ if (dfa->nodes[node_idx].type == OP_DELETED_SUBEXP)
+ continue;
+
+ /* If we have already calculated, skip it. */
+ if (dfa->eclosures[node_idx].nelem != 0)
+ continue;
+ /* Calculate epsilon closure of `node_idx'. */
+ err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (dfa->eclosures[node_idx].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+ return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE. */
+
+static reg_errcode_t
+calc_eclosure_iter (new_set, dfa, node, root)
+ re_node_set *new_set;
+ re_dfa_t *dfa;
+ int node, root;
+{
+ reg_errcode_t err;
+ unsigned int constraint;
+ int i, incomplete;
+ re_node_set eclosure;
+ incomplete = 0;
+ err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* This indicates that we are calculating this node now.
+ We reference this value to avoid infinite loop. */
+ dfa->eclosures[node].nelem = -1;
+
+ constraint = ((dfa->nodes[node].type == ANCHOR)
+ ? dfa->nodes[node].opr.ctx_type : 0);
+ /* If the current node has constraints, duplicate all nodes.
+ Since they must inherit the constraints. */
+ if (constraint
+ && dfa->edests[node].nelem
+ && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+ {
+ int org_node, cur_node;
+ org_node = cur_node = node;
+ err = duplicate_node_closure (dfa, node, node, node, constraint);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* Expand each epsilon destination nodes. */
+ if (IS_EPSILON_NODE(dfa->nodes[node].type))
+ for (i = 0; i < dfa->edests[node].nelem; ++i)
+ {
+ re_node_set eclosure_elem;
+ int edest = dfa->edests[node].elems[i];
+ /* If calculating the epsilon closure of `edest' is in progress,
+ return intermediate result. */
+ if (dfa->eclosures[edest].nelem == -1)
+ {
+ incomplete = 1;
+ continue;
+ }
+ /* If we haven't calculated the epsilon closure of `edest' yet,
+ calculate now. Otherwise use calculated epsilon closure. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ eclosure_elem = dfa->eclosures[edest];
+ /* Merge the epsilon closure of `edest'. */
+ re_node_set_merge (&eclosure, &eclosure_elem);
+ /* If the epsilon closure of `edest' is incomplete,
+ the epsilon closure of this node is also incomplete. */
+ if (dfa->eclosures[edest].nelem == 0)
+ {
+ incomplete = 1;
+ re_node_set_free (&eclosure_elem);
+ }
+ }
+
+ /* Epsilon closures include itself. */
+ re_node_set_insert (&eclosure, node);
+ if (incomplete && !root)
+ dfa->eclosures[node].nelem = 0;
+ else
+ dfa->eclosures[node] = eclosure;
+ *new_set = eclosure;
+ return REG_NOERROR;
+}
+
+/* Functions for token which are used in the parser. */
+
+/* Fetch a token from INPUT.
+ We must not use this function inside bracket expressions. */
+
+static void
+fetch_token (result, input, syntax)
+ re_token_t *result;
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ re_string_skip_bytes (input, peek_token (result, input, syntax));
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function inside bracket expressions. */
+
+static int
+peek_token (token, input, syntax)
+ re_token_t *token;
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ unsigned char c;
+
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+ token->word_char = 0;
+#ifdef RE_ENABLE_I18N
+ token->mb_partial = 0;
+ if (input->mb_cur_max > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ token->mb_partial = 1;
+ return 1;
+ }
+#endif
+ if (c == '\\')
+ {
+ unsigned char c2;
+ if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+ {
+ token->type = BACK_SLASH;
+ return 1;
+ }
+
+ c2 = re_string_peek_byte_case (input, 1);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc = re_string_wchar_at (input,
+ re_string_cur_idx (input) + 1);
+ token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+ }
+ else
+#endif
+ token->word_char = IS_WORD_CHAR (c2) != 0;
+
+ switch (c2)
+ {
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '1': case '2': case '3': case '4': case '5':
+ case '6': case '7': case '8': case '9':
+ if (!(syntax & RE_NO_BK_REFS))
+ {
+ token->type = OP_BACK_REF;
+ token->opr.idx = c2 - '1';
+ }
+ break;
+ case '<':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_FIRST;
+ }
+ break;
+ case '>':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_LAST;
+ }
+ break;
+ case 'b':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = WORD_DELIM;
+ }
+ break;
+ case 'B':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = NOT_WORD_DELIM;
+ }
+ break;
+ case 'w':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_WORD;
+ break;
+ case 'W':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTWORD;
+ break;
+ case 's':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_SPACE;
+ break;
+ case 'S':
+ if (!(syntax & RE_NO_GNU_OPS))
+ token->type = OP_NOTSPACE;
+ break;
+ case '`':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = BUF_FIRST;
+ }
+ break;
+ case '\'':
+ if (!(syntax & RE_NO_GNU_OPS))
+ {
+ token->type = ANCHOR;
+ token->opr.ctx_type = BUF_LAST;
+ }
+ break;
+ case '(':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (!(syntax & RE_NO_BK_PARENS))
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ default:
+ break;
+ }
+ return 2;
+ }
+
+ token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+ token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+ }
+ else
+#endif
+ token->word_char = IS_WORD_CHAR (token->opr.c);
+
+ switch (c)
+ {
+ case '\n':
+ if (syntax & RE_NEWLINE_ALT)
+ token->type = OP_ALT;
+ break;
+ case '|':
+ if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+ token->type = OP_ALT;
+ break;
+ case '*':
+ token->type = OP_DUP_ASTERISK;
+ break;
+ case '+':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_PLUS;
+ break;
+ case '?':
+ if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+ token->type = OP_DUP_QUESTION;
+ break;
+ case '{':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_OPEN_DUP_NUM;
+ break;
+ case '}':
+ if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+ token->type = OP_CLOSE_DUP_NUM;
+ break;
+ case '(':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_OPEN_SUBEXP;
+ break;
+ case ')':
+ if (syntax & RE_NO_BK_PARENS)
+ token->type = OP_CLOSE_SUBEXP;
+ break;
+ case '[':
+ token->type = OP_OPEN_BRACKET;
+ break;
+ case '.':
+ token->type = OP_PERIOD;
+ break;
+ case '^':
+ if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
+ re_string_cur_idx (input) != 0)
+ {
+ char prev = re_string_peek_byte (input, -1);
+ if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.ctx_type = LINE_FIRST;
+ break;
+ case '$':
+ if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+ re_string_cur_idx (input) + 1 != re_string_length (input))
+ {
+ re_token_t next;
+ re_string_skip_bytes (input, 1);
+ peek_token (&next, input, syntax);
+ re_string_skip_bytes (input, -1);
+ if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+ break;
+ }
+ token->type = ANCHOR;
+ token->opr.ctx_type = LINE_LAST;
+ break;
+ default:
+ break;
+ }
+ return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+ We must not use this function out of bracket expressions. */
+
+static int
+peek_token_bracket (token, input, syntax)
+ re_token_t *token;
+ re_string_t *input;
+ reg_syntax_t syntax;
+{
+ unsigned char c;
+ if (re_string_eoi (input))
+ {
+ token->type = END_OF_RE;
+ return 0;
+ }
+ c = re_string_peek_byte (input, 0);
+ token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1 &&
+ !re_string_first_byte (input, re_string_cur_idx (input)))
+ {
+ token->type = CHARACTER;
+ return 1;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+ && re_string_cur_idx (input) + 1 < re_string_length (input))
+ {
+ /* In this case, '\' escape a character. */
+ unsigned char c2;
+ re_string_skip_bytes (input, 1);
+ c2 = re_string_peek_byte (input, 0);
+ token->opr.c = c2;
+ token->type = CHARACTER;
+ return 1;
+ }
+ if (c == '[') /* '[' is a special char in a bracket exps. */
+ {
+ unsigned char c2;
+ int token_len;
+ if (re_string_cur_idx (input) + 1 < re_string_length (input))
+ c2 = re_string_peek_byte (input, 1);
+ else
+ c2 = 0;
+ token->opr.c = c2;
+ token_len = 2;
+ switch (c2)
+ {
+ case '.':
+ token->type = OP_OPEN_COLL_ELEM;
+ break;
+ case '=':
+ token->type = OP_OPEN_EQUIV_CLASS;
+ break;
+ case ':':
+ if (syntax & RE_CHAR_CLASSES)
+ {
+ token->type = OP_OPEN_CHAR_CLASS;
+ break;
+ }
+ /* else fall through. */
+ default:
+ token->type = CHARACTER;
+ token->opr.c = c;
+ token_len = 1;
+ break;
+ }
+ return token_len;
+ }
+ switch (c)
+ {
+ case '-':
+ token->type = OP_CHARSET_RANGE;
+ break;
+ case ']':
+ token->type = OP_CLOSE_BRACKET;
+ break;
+ case '^':
+ token->type = OP_NON_MATCH_LIST;
+ break;
+ default:
+ token->type = CHARACTER;
+ }
+ return 1;
+}
+
+/* Functions for parser. */
+
+/* Entry point of the parser.
+ Parse the regular expression REGEXP and return the structure tree.
+ If an error is occured, ERR is set by error code, and return NULL.
+ This function build the following tree, from regular expression <reg_exp>:
+ CAT
+ / \
+ / \
+ <reg_exp> EOR
+
+ CAT means concatenation.
+ EOR means end of regular expression. */
+
+static bin_tree_t *
+parse (regexp, preg, syntax, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *eor, *root;
+ re_token_t current_token;
+ dfa->syntax = syntax;
+ fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+ tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ eor = re_dfa_add_tree_node (dfa, NULL, NULL, &current_token);
+ if (tree != NULL)
+ root = create_tree (dfa, tree, eor, CONCAT, 0);
+ else
+ root = eor;
+ if (BE (eor == NULL || root == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ return root;
+}
+
+/* This function build the following tree, from regular expression
+ <branch1>|<branch2>:
+ ALT
+ / \
+ / \
+ <branch1> <branch2>
+
+ ALT means alternative, which represents the operator `|'. */
+
+static bin_tree_t *
+parse_reg_exp (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *branch = NULL;
+ tree = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type == OP_ALT)
+ {
+ re_token_t alt_token = *token;
+ fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+ if (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ branch = parse_branch (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && branch == NULL, 0))
+ return NULL;
+ }
+ else
+ branch = NULL;
+ tree = re_dfa_add_tree_node (dfa, tree, branch, &alt_token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ dfa->has_plural_match = 1;
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ <exp1><exp2>:
+ CAT
+ / \
+ / \
+ <exp1> <exp2>
+
+ CAT means concatenation. */
+
+static bin_tree_t *
+parse_branch (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ bin_tree_t *tree, *exp;
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ tree = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+
+ while (token->type != OP_ALT && token->type != END_OF_RE
+ && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+ {
+ exp = parse_expression (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && exp == NULL, 0))
+ {
+ return NULL;
+ }
+ if (tree != NULL && exp != NULL)
+ {
+ tree = create_tree (dfa, tree, exp, CONCAT, 0);
+ if (tree == NULL)
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ else if (tree == NULL)
+ tree = exp;
+ /* Otherwise exp == NULL, we don't need to create new tree. */
+ }
+ return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+ *
+ |
+ a
+*/
+
+static bin_tree_t *
+parse_expression (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree;
+ switch (token->type)
+ {
+ case CHARACTER:
+ tree = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ while (!re_string_eoi (regexp)
+ && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+ {
+ bin_tree_t *mbc_remain;
+ fetch_token (token, regexp, syntax);
+ mbc_remain = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ tree = create_tree (dfa, tree, mbc_remain, CONCAT, 0);
+ if (BE (mbc_remain == NULL || tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ }
+#endif
+ break;
+ case OP_OPEN_SUBEXP:
+ tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_OPEN_BRACKET:
+ tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_BACK_REF:
+ if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
+ {
+ *err = REG_ESUBREG;
+ return NULL;
+ }
+ dfa->used_bkref_map |= 1 << token->opr.idx;
+ tree = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ ++dfa->nbackref;
+ dfa->has_mb_node = 1;
+ break;
+ case OP_OPEN_DUP_NUM:
+ if (syntax & RE_CONTEXT_INVALID_DUP)
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ /* FALLTHROUGH */
+ case OP_DUP_ASTERISK:
+ case OP_DUP_PLUS:
+ case OP_DUP_QUESTION:
+ if (syntax & RE_CONTEXT_INVALID_OPS)
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ else if (syntax & RE_CONTEXT_INDEP_OPS)
+ {
+ fetch_token (token, regexp, syntax);
+ return parse_expression (regexp, preg, token, syntax, nest, err);
+ }
+ /* else fall through */
+ case OP_CLOSE_SUBEXP:
+ if ((token->type == OP_CLOSE_SUBEXP) &&
+ !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+ {
+ *err = REG_ERPAREN;
+ return NULL;
+ }
+ /* else fall through */
+ case OP_CLOSE_DUP_NUM:
+ /* We treat it as a normal character. */
+
+ /* Then we can these characters as normal characters. */
+ token->type = CHARACTER;
+ /* mb_partial and word_char bits should be initialized already
+ by peek_token. */
+ tree = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ break;
+ case ANCHOR:
+ if ((token->opr.ctx_type
+ & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+ && dfa->word_ops_used == 0)
+ init_word_char (dfa);
+ if (token->opr.ctx_type == WORD_DELIM
+ || token->opr.ctx_type == NOT_WORD_DELIM)
+ {
+ bin_tree_t *tree_first, *tree_last;
+ if (token->opr.ctx_type == WORD_DELIM)
+ {
+ token->opr.ctx_type = WORD_FIRST;
+ tree_first = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ token->opr.ctx_type = WORD_LAST;
+ }
+ else
+ {
+ token->opr.ctx_type = INSIDE_WORD;
+ tree_first = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ token->opr.ctx_type = INSIDE_NOTWORD;
+ }
+ tree_last = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ token->type = OP_ALT;
+ tree = re_dfa_add_tree_node (dfa, tree_first, tree_last, token);
+ if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ else
+ {
+ tree = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ /* We must return here, since ANCHORs can't be followed
+ by repetition operators.
+ eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+ it must not be "<ANCHOR(^)><REPEAT(*)>". */
+ fetch_token (token, regexp, syntax);
+ return tree;
+ case OP_PERIOD:
+ tree = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ if (BE (tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ if (dfa->mb_cur_max > 1)
+ dfa->has_mb_node = 1;
+ break;
+ case OP_WORD:
+ case OP_NOTWORD:
+ tree = build_charclass_op (dfa, regexp->trans,
+ (const unsigned char *) "alnum",
+ (const unsigned char *) "_",
+ token->type == OP_NOTWORD, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_SPACE:
+ case OP_NOTSPACE:
+ tree = build_charclass_op (dfa, regexp->trans,
+ (const unsigned char *) "space",
+ (const unsigned char *) "",
+ token->type == OP_NOTSPACE, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ break;
+ case OP_ALT:
+ case END_OF_RE:
+ return NULL;
+ case BACK_SLASH:
+ *err = REG_EESCAPE;
+ return NULL;
+ default:
+ /* Must not happen? */
+#ifdef DEBUG
+ assert (0);
+#endif
+ return NULL;
+ }
+ fetch_token (token, regexp, syntax);
+
+ while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+ || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+ {
+ tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ /* In BRE consecutive duplications are not allowed. */
+ if ((syntax & RE_CONTEXT_INVALID_DUP)
+ && (token->type == OP_DUP_ASTERISK
+ || token->type == OP_OPEN_DUP_NUM))
+ {
+ *err = REG_BADRPT;
+ return NULL;
+ }
+ dfa->has_plural_match = 1;
+ }
+
+ return tree;
+}
+
+/* This function build the following tree, from regular expression
+ (<reg_exp>):
+ SUBEXP
+ |
+ <reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (regexp, preg, token, syntax, nest, err)
+ re_string_t *regexp;
+ regex_t *preg;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ int nest;
+ reg_errcode_t *err;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ bin_tree_t *tree, *left_par, *right_par;
+ size_t cur_nsub;
+ cur_nsub = preg->re_nsub++;
+
+ left_par = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ if (BE (left_par == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ dfa->nodes[left_par->node_idx].opr.idx = cur_nsub;
+ fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
+
+ /* The subexpression may be a null string. */
+ if (token->type == OP_CLOSE_SUBEXP)
+ tree = NULL;
+ else
+ {
+ tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+ if (BE (*err != REG_NOERROR && tree == NULL, 0))
+ return NULL;
+ }
+ if (BE (token->type != OP_CLOSE_SUBEXP, 0))
+ {
+ *err = REG_EPAREN;
+ return NULL;
+ }
+ right_par = re_dfa_add_tree_node (dfa, NULL, NULL, token);
+ dfa->completed_bkref_map |= 1 << cur_nsub;
+ tree = ((tree == NULL) ? right_par
+ : create_tree (dfa, tree, right_par, CONCAT, 0));
+ tree = create_tree (dfa, left_par, tree, CONCAT, 0);
+ if (BE (right_par == NULL || tree == NULL, 0))
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ dfa->nodes[right_par->node_idx].opr.idx = cur_nsub;
+
+ return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc. */
+
+static bin_tree_t *
+parse_dup_op (elem, regexp, dfa, token, syntax, err)
+ bin_tree_t *elem;
+ re_string_t *regexp;
+ re_dfa_t *dfa;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+ re_token_t dup_token;
+ bin_tree_t *tree = NULL, *old_tree = NULL;
+ int i, start, end, start_idx = re_string_cur_idx (regexp);
+ re_token_t start_token = *token;
+
+ if (token->type == OP_OPEN_DUP_NUM)
+ {
+ end = 0;
+ start = fetch_number (regexp, token, syntax);
+ if (start == -1)
+ {
+ if (token->type == CHARACTER && token->opr.c == ',')
+ start = 0; /* We treat "{,m}" as "{0,m}". */
+ else
+ {
+ *err = REG_BADBR; /* <re>{} is invalid. */
+ return NULL;
+ }
+ }
+ if (BE (start != -2, 1))
+ {
+ /* We treat "{n}" as "{n,n}". */
+ end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+ : ((token->type == CHARACTER && token->opr.c == ',')
+ ? fetch_number (regexp, token, syntax) : -2));
+ }
+ if (BE (start == -2 || end == -2, 0))
+ {
+ /* Invalid sequence. */
+ if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+ {
+ if (token->type == END_OF_RE)
+ *err = REG_EBRACE;
+ else
+ *err = REG_BADBR;
+
+ return NULL;
+ }
+
+ /* If the syntax bit is set, rollback. */
+ re_string_set_index (regexp, start_idx);
+ *token = start_token;
+ token->type = CHARACTER;
+ /* mb_partial and word_char bits should be already initialized by
+ peek_token. */
+ return elem;
+ }
+
+ if (BE (end != -1 && start > end, 0))
+ {
+ /* First number greater than second. */
+ *err = REG_BADBR;
+ return NULL;
+ }
+ }
+ else
+ {
+ start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+ end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
+ }
+
+ fetch_token (token, regexp, syntax);
+
+ /* Treat "<re>{0}*" etc. as "<re>{0}". */
+ if (BE (elem == NULL || (start == 0 && end == 0), 0))
+ return NULL;
+
+ /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */
+ if (BE (start > 0, 0))
+ {
+ tree = elem;
+ for (i = 2; i <= start; ++i)
+ {
+ elem = duplicate_tree (elem, dfa);
+ tree = create_tree (dfa, tree, elem, CONCAT, 0);
+ if (BE (elem == NULL || tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+
+ if (start == end)
+ return tree;
+
+ /* Duplicate ELEM before it is marked optional. */
+ elem = duplicate_tree (elem, dfa);
+ old_tree = tree;
+ }
+ else
+ old_tree = NULL;
+
+ mark_opt_subexp (elem, dfa);
+ dup_token.type = (end == -1 ? OP_DUP_ASTERISK : OP_DUP_QUESTION);
+ tree = re_dfa_add_tree_node (dfa, elem, NULL, &dup_token);
+ if (BE (tree == NULL, 0))
+ goto parse_dup_op_espace;
+
+ /* This loop is actually executed only when end != -1,
+ to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?... We have
+ already created the start+1-th copy. */
+ for (i = start + 2; i <= end; ++i)
+ {
+ elem = duplicate_tree (elem, dfa);
+ tree = create_tree (dfa, tree, elem, CONCAT, 0);
+ if (BE (elem == NULL || tree == NULL, 0))
+ goto parse_dup_op_espace;
+
+ tree = re_dfa_add_tree_node (dfa, tree, NULL, &dup_token);
+ if (BE (tree == NULL, 0))
+ goto parse_dup_op_espace;
+ }
+
+ if (old_tree)
+ tree = create_tree (dfa, old_tree, tree, CONCAT, 0);
+
+ return tree;
+
+ parse_dup_op_espace:
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+ I'm not sure, but maybe enough. */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+ /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+static reg_errcode_t
+# ifdef RE_ENABLE_I18N
+build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+ re_charset_t *mbcset;
+ int *range_alloc;
+# else /* not RE_ENABLE_I18N */
+build_range_exp (sbcset, start_elem, end_elem)
+# endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ bracket_elem_t *start_elem, *end_elem;
+{
+ unsigned int start_ch, end_ch;
+ /* Equivalence Classes and Character Classes can't be a range start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ /* We can handle no multi character collating elements without libc
+ support. */
+ if (BE ((start_elem->type == COLL_SYM
+ && strlen ((char *) start_elem->opr.name) > 1)
+ || (end_elem->type == COLL_SYM
+ && strlen ((char *) end_elem->opr.name) > 1), 0))
+ return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+ {
+ wchar_t wc, start_wc, end_wc;
+ wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+ start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+ start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+ ? __btowc (start_ch) : start_elem->opr.wch);
+ end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+ ? __btowc (end_ch) : end_elem->opr.wch);
+ if (start_wc == WEOF || end_wc == WEOF)
+ return REG_ECOLLATE;
+ cmp_buf[0] = start_wc;
+ cmp_buf[4] = end_wc;
+ if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+ return REG_ERANGE;
+
+ /* Got valid collation sequence values, add them as a new entry.
+ However, for !_LIBC we have no collation elements: if the
+ character set is single byte, the single byte character set
+ that we build below suffices. parse_bracket_exp passes
+ no MBCSET if dfa->mb_cur_max == 1. */
+ if (mbcset)
+ {
+ /* Check the space of the arrays. */
+ if (BE (*range_alloc == mbcset->nranges, 0))
+ {
+ /* There is not enough space, need realloc. */
+ wchar_t *new_array_start, *new_array_end;
+ int new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ /* Use realloc since mbcset->range_starts and mbcset->range_ends
+ are NULL if *range_alloc == 0. */
+ new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ mbcset->range_starts[mbcset->nranges] = start_wc;
+ mbcset->range_ends[mbcset->nranges++] = end_wc;
+ }
+
+ /* Build the table for single byte characters. */
+ for (wc = 0; wc < SBC_MAX; ++wc)
+ {
+ cmp_buf[2] = wc;
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ bitset_set (sbcset, wc);
+ }
+ }
+# else /* not RE_ENABLE_I18N */
+ {
+ unsigned int ch;
+ start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+ : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+ : 0));
+ end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+ : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+ : 0));
+ if (start_ch > end_ch)
+ return REG_ERANGE;
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ if (start_ch <= ch && ch <= end_ch)
+ bitset_set (sbcset, ch);
+ }
+# endif /* not RE_ENABLE_I18N */
+ return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument since we may update it. */
+
+static reg_errcode_t
+# ifdef RE_ENABLE_I18N
+build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+ re_charset_t *mbcset;
+ int *coll_sym_alloc;
+# else /* not RE_ENABLE_I18N */
+build_collating_symbol (sbcset, name)
+# endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ const unsigned char *name;
+{
+ size_t name_len = strlen ((const char *) name);
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+ "[[.a-a.]]" etc. */
+
+static bin_tree_t *
+parse_bracket_exp (regexp, dfa, token, syntax, err)
+ re_string_t *regexp;
+ re_dfa_t *dfa;
+ re_token_t *token;
+ reg_syntax_t syntax;
+ reg_errcode_t *err;
+{
+#ifdef _LIBC
+ const unsigned char *collseqmb;
+ const char *collseqwc;
+ uint32_t nrules;
+ int32_t table_size;
+ const int32_t *symb_table;
+ const unsigned char *extra;
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Seek the collating symbol entry correspondings to NAME.
+ Return the index of the symbol in the SYMB_TABLE. */
+
+ auto inline int32_t
+ __attribute ((always_inline))
+ seek_collating_symbol_entry (name, name_len)
+ const unsigned char *name;
+ size_t name_len;
+ {
+ int32_t hash = elem_hash ((const char *) name, name_len);
+ int32_t elem = hash % table_size;
+ int32_t second = hash % (table_size - 2);
+ while (symb_table[2 * elem] != 0)
+ {
+ /* First compare the hashing value. */
+ if (symb_table[2 * elem] == hash
+ /* Compare the length of the name. */
+ && name_len == extra[symb_table[2 * elem + 1]]
+ /* Compare the name. */
+ && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+ name_len) == 0)
+ {
+ /* Yep, this is the entry. */
+ break;
+ }
+
+ /* Next entry. */
+ elem += second;
+ }
+ return elem;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Look up the collation sequence value of BR_ELEM.
+ Return the value if succeeded, UINT_MAX otherwise. */
+
+ auto inline unsigned int
+ __attribute ((always_inline))
+ lookup_collation_sequence_value (br_elem)
+ bracket_elem_t *br_elem;
+ {
+ if (br_elem->type == SB_CHAR)
+ {
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ return collseqmb[br_elem->opr.ch];
+ else
+ {
+ wint_t wc = __btowc (br_elem->opr.ch);
+ return __collseq_table_lookup (collseqwc, wc);
+ }
+ }
+ else if (br_elem->type == MB_CHAR)
+ {
+ return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
+ }
+ else if (br_elem->type == COLL_SYM)
+ {
+ size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+ if (nrules != 0)
+ {
+ int32_t elem, idx;
+ elem = seek_collating_symbol_entry (br_elem->opr.name,
+ sym_name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ /* Skip the byte sequence of the collating element. */
+ idx += 1 + extra[idx];
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the multibyte collation sequence value. */
+ idx += sizeof (unsigned int);
+ /* Skip the wide char sequence of the collating element. */
+ idx += sizeof (unsigned int) *
+ (1 + *(unsigned int *) (extra + idx));
+ /* Return the collation sequence value. */
+ return *(unsigned int *) (extra + idx);
+ }
+ else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+ {
+ /* No valid character. Match it as a single byte
+ character. */
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ }
+ else if (sym_name_len == 1)
+ return collseqmb[br_elem->opr.name[0]];
+ }
+ return UINT_MAX;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the range expression which starts from START_ELEM, and ends
+ at END_ELEM. The result are written to MBCSET and SBCSET.
+ RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+ mbcset->range_ends, is a pointer argument sinse we may
+ update it. */
+
+ auto inline reg_errcode_t
+ __attribute ((always_inline))
+ build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+ re_charset_t *mbcset;
+ int *range_alloc;
+ re_bitset_ptr_t sbcset;
+ bracket_elem_t *start_elem, *end_elem;
+ {
+ unsigned int ch;
+ uint32_t start_collseq;
+ uint32_t end_collseq;
+
+ /* Equivalence Classes and Character Classes can't be a range
+ start/end. */
+ if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+ || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+ 0))
+ return REG_ERANGE;
+
+ start_collseq = lookup_collation_sequence_value (start_elem);
+ end_collseq = lookup_collation_sequence_value (end_elem);
+ /* Check start/end collation sequence values. */
+ if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+ return REG_ECOLLATE;
+ if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+ return REG_ERANGE;
+
+ /* Got valid collation sequence values, add them as a new entry.
+ However, if we have no collation elements, and the character set
+ is single byte, the single byte character set that we
+ build below suffices. */
+ if (nrules > 0 || dfa->mb_cur_max > 1)
+ {
+ /* Check the space of the arrays. */
+ if (BE (*range_alloc == mbcset->nranges, 0))
+ {
+ /* There is not enough space, need realloc. */
+ uint32_t *new_array_start;
+ uint32_t *new_array_end;
+ int new_nranges;
+
+ /* +1 in case of mbcset->nranges is 0. */
+ new_nranges = 2 * mbcset->nranges + 1;
+ new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+ new_nranges);
+ new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+ new_nranges);
+
+ if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+ return REG_ESPACE;
+
+ mbcset->range_starts = new_array_start;
+ mbcset->range_ends = new_array_end;
+ *range_alloc = new_nranges;
+ }
+
+ mbcset->range_starts[mbcset->nranges] = start_collseq;
+ mbcset->range_ends[mbcset->nranges++] = end_collseq;
+ }
+
+ /* Build the table for single byte characters. */
+ for (ch = 0; ch < SBC_MAX; ch++)
+ {
+ uint32_t ch_collseq;
+ /*
+ if (MB_CUR_MAX == 1)
+ */
+ if (nrules == 0)
+ ch_collseq = collseqmb[ch];
+ else
+ ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch));
+ if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+ bitset_set (sbcset, ch);
+ }
+ return REG_NOERROR;
+ }
+
+ /* Local function for parse_bracket_exp used in _LIBC environement.
+ Build the collating element which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+ pointer argument sinse we may update it. */
+
+ auto inline reg_errcode_t
+ __attribute ((always_inline))
+ build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+ re_charset_t *mbcset;
+ int *coll_sym_alloc;
+ re_bitset_ptr_t sbcset;
+ const unsigned char *name;
+ {
+ int32_t elem, idx;
+ size_t name_len = strlen ((const char *) name);
+ if (nrules != 0)
+ {
+ elem = seek_collating_symbol_entry (name, name_len);
+ if (symb_table[2 * elem] != 0)
+ {
+ /* We found the entry. */
+ idx = symb_table[2 * elem + 1];
+ /* Skip the name of collating element name. */
+ idx += 1 + extra[idx];
+ }
+ else if (symb_table[2 * elem] == 0 && name_len == 1)
+ {
+ /* No valid character, treat it as a normal
+ character. */
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ else
+ return REG_ECOLLATE;
+
+ /* Got valid collation sequence, add it as a new entry. */
+ /* Check the space of the arrays. */
+ if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->ncoll_syms is 0. */
+ int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+ /* Use realloc since mbcset->coll_syms is NULL
+ if *alloc == 0. */
+ int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+ new_coll_sym_alloc);
+ if (BE (new_coll_syms == NULL, 0))
+ return REG_ESPACE;
+ mbcset->coll_syms = new_coll_syms;
+ *coll_sym_alloc = new_coll_sym_alloc;
+ }
+ mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+ return REG_NOERROR;
+ }
+ else
+ {
+ if (BE (name_len != 1, 0))
+ return REG_ECOLLATE;
+ else
+ {
+ bitset_set (sbcset, name[0]);
+ return REG_NOERROR;
+ }
+ }
+ }
+#endif
+
+ re_token_t br_token;
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+ int equiv_class_alloc = 0, char_class_alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+ int non_match = 0;
+ bin_tree_t *work_tree;
+ int token_len;
+ int first_round = 1;
+#ifdef _LIBC
+ collseqmb = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules)
+ {
+ /*
+ if (MB_CUR_MAX > 1)
+ */
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+ symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_TABLEMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_SYMB_EXTRAMB);
+ }
+#endif
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+ if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_NON_MATCH_LIST)
+ {
+#ifdef RE_ENABLE_I18N
+ mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ non_match = 1;
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set (sbcset, '\0');
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_BADPAT;
+ goto parse_bracket_exp_free_return;
+ }
+ }
+
+ /* We treat the first ']' as a normal character. */
+ if (token->type == OP_CLOSE_BRACKET)
+ token->type = CHARACTER;
+
+ while (1)
+ {
+ bracket_elem_t start_elem, end_elem;
+ unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+ unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+ reg_errcode_t ret;
+ int token_len2 = 0, is_range_exp = 0;
+ re_token_t token2;
+
+ start_elem.opr.name = start_name_buf;
+ ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+ syntax, first_round);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+ first_round = 0;
+
+ /* Get information about the next token. We need it in any case. */
+ token_len = peek_token_bracket (token, regexp, syntax);
+
+ /* Do not check for ranges if we know they are not allowed. */
+ if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
+ {
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_CHARSET_RANGE)
+ {
+ re_string_skip_bytes (regexp, token_len); /* Skip '-'. */
+ token_len2 = peek_token_bracket (&token2, regexp, syntax);
+ if (BE (token2.type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token2.type == OP_CLOSE_BRACKET)
+ {
+ /* We treat the last '-' as a normal character. */
+ re_string_skip_bytes (regexp, -token_len);
+ token->type = CHARACTER;
+ }
+ else
+ is_range_exp = 1;
+ }
+ }
+
+ if (is_range_exp == 1)
+ {
+ end_elem.opr.name = end_name_buf;
+ ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+ dfa, syntax, 1);
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ *err = ret;
+ goto parse_bracket_exp_free_return;
+ }
+
+ token_len = peek_token_bracket (token, regexp, syntax);
+
+#ifdef _LIBC
+ *err = build_range_exp (sbcset, mbcset, &range_alloc,
+ &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
+ *err = build_range_exp (sbcset,
+ dfa->mb_cur_max > 1 ? mbcset : NULL,
+ &range_alloc, &start_elem, &end_elem);
+# else
+ *err = build_range_exp (sbcset, &start_elem, &end_elem);
+# endif
+#endif /* RE_ENABLE_I18N */
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ }
+ else
+ {
+ switch (start_elem.type)
+ {
+ case SB_CHAR:
+ bitset_set (sbcset, start_elem.opr.ch);
+ break;
+#ifdef RE_ENABLE_I18N
+ case MB_CHAR:
+ /* Check whether the array has enough space. */
+ if (BE (mbchar_alloc == mbcset->nmbchars, 0))
+ {
+ wchar_t *new_mbchars;
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nmbchars is 0. */
+ mbchar_alloc = 2 * mbcset->nmbchars + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+ mbchar_alloc);
+ if (BE (new_mbchars == NULL, 0))
+ goto parse_bracket_exp_espace;
+ mbcset->mbchars = new_mbchars;
+ }
+ mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+ break;
+#endif /* RE_ENABLE_I18N */
+ case EQUIV_CLASS:
+ *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case COLL_SYM:
+ *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &coll_sym_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ case CHAR_CLASS:
+ *err = build_charclass (regexp->trans, sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+ start_elem.opr.name, syntax);
+ if (BE (*err != REG_NOERROR, 0))
+ goto parse_bracket_exp_free_return;
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ if (BE (token->type == END_OF_RE, 0))
+ {
+ *err = REG_EBRACK;
+ goto parse_bracket_exp_free_return;
+ }
+ if (token->type == OP_CLOSE_BRACKET)
+ break;
+ }
+
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+
+ /* If it is non-matching list. */
+ if (non_match)
+ bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure only single byte characters are set. */
+ if (dfa->mb_cur_max > 1)
+ bitset_mask (sbcset, dfa->sb_char);
+#endif /* RE_ENABLE_I18N */
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ work_tree = re_dfa_add_tree_node (dfa, NULL, NULL, &br_token);
+ if (BE (work_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+
+#ifdef RE_ENABLE_I18N
+ if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+ || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
+ || mbcset->non_match)))
+ {
+ re_token_t alt_token;
+ bin_tree_t *mbc_tree;
+ int sbc_idx;
+ /* Build a tree for complex bracket. */
+ dfa->has_mb_node = 1;
+ for (sbc_idx = 0; sbc_idx < BITSET_UINTS; ++sbc_idx)
+ if (sbcset[sbc_idx])
+ break;
+ /* If there are no bits set in sbcset, there is no point
+ of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */
+ if (sbc_idx == BITSET_UINTS)
+ {
+ re_free (sbcset);
+ dfa->nodes[work_tree->node_idx].type = COMPLEX_BRACKET;
+ dfa->nodes[work_tree->node_idx].opr.mbcset = mbcset;
+ return work_tree;
+ }
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ mbc_tree = re_dfa_add_tree_node (dfa, NULL, NULL, &br_token);
+ if (BE (mbc_tree == NULL, 0))
+ goto parse_bracket_exp_espace;
+ /* Then join them by ALT node. */
+ alt_token.type = OP_ALT;
+ dfa->has_plural_match = 1;
+ work_tree = re_dfa_add_tree_node (dfa, work_tree, mbc_tree, &alt_token);
+ if (BE (mbc_tree != NULL, 1))
+ return work_tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return work_tree;
+ }
+#else /* not RE_ENABLE_I18N */
+ return work_tree;
+#endif /* not RE_ENABLE_I18N */
+
+ parse_bracket_exp_espace:
+ *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ return NULL;
+}
+
+/* Parse an element in the bracket expression. */
+
+static reg_errcode_t
+parse_bracket_element (elem, regexp, token, token_len, dfa, syntax,
+ accept_hyphen)
+ bracket_elem_t *elem;
+ re_string_t *regexp;
+ re_token_t *token;
+ int token_len;
+ re_dfa_t *dfa;
+ reg_syntax_t syntax;
+ int accept_hyphen;
+{
+#ifdef RE_ENABLE_I18N
+ int cur_char_size;
+ cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+ if (cur_char_size > 1)
+ {
+ elem->type = MB_CHAR;
+ elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+ re_string_skip_bytes (regexp, cur_char_size);
+ return REG_NOERROR;
+ }
+#endif /* RE_ENABLE_I18N */
+ re_string_skip_bytes (regexp, token_len); /* Skip a token. */
+ if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+ || token->type == OP_OPEN_EQUIV_CLASS)
+ return parse_bracket_symbol (elem, regexp, token);
+ if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
+ {
+ /* A '-' must only appear as anything but a range indicator before
+ the closing bracket. Everything else is an error. */
+ re_token_t token2;
+ (void) peek_token_bracket (&token2, regexp, syntax);
+ if (token2.type != OP_CLOSE_BRACKET)
+ /* The actual error value is not standardized since this whole
+ case is undefined. But ERANGE makes good sense. */
+ return REG_ERANGE;
+ }
+ elem->type = SB_CHAR;
+ elem->opr.ch = token->opr.c;
+ return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression. Bracket symbols are
+ such as [:<character_class>:], [.<collating_element>.], and
+ [=<equivalent_class>=]. */
+
+static reg_errcode_t
+parse_bracket_symbol (elem, regexp, token)
+ bracket_elem_t *elem;
+ re_string_t *regexp;
+ re_token_t *token;
+{
+ unsigned char ch, delim = token->opr.c;
+ int i = 0;
+ if (re_string_eoi(regexp))
+ return REG_EBRACK;
+ for (;; ++i)
+ {
+ if (i >= BRACKET_NAME_BUF_SIZE)
+ return REG_EBRACK;
+ if (token->type == OP_OPEN_CHAR_CLASS)
+ ch = re_string_fetch_byte_case (regexp);
+ else
+ ch = re_string_fetch_byte (regexp);
+ if (re_string_eoi(regexp))
+ return REG_EBRACK;
+ if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+ break;
+ elem->opr.name[i] = ch;
+ }
+ re_string_skip_bytes (regexp, 1);
+ elem->opr.name[i] = '\0';
+ switch (token->type)
+ {
+ case OP_OPEN_COLL_ELEM:
+ elem->type = COLL_SYM;
+ break;
+ case OP_OPEN_EQUIV_CLASS:
+ elem->type = EQUIV_CLASS;
+ break;
+ case OP_OPEN_CHAR_CLASS:
+ elem->type = CHAR_CLASS;
+ break;
+ default:
+ break;
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the equivalence class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (sbcset, mbcset, equiv_class_alloc, name)
+ re_charset_t *mbcset;
+ int *equiv_class_alloc;
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (sbcset, name)
+#endif /* not RE_ENABLE_I18N */
+ re_bitset_ptr_t sbcset;
+ const unsigned char *name;
+{
+#if defined _LIBC
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra, *cp;
+ unsigned char char_buf[2];
+ int32_t idx1, idx2;
+ unsigned int ch;
+ size_t len;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+ /* Calculate the index for equivalence class. */
+ cp = name;
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ idx1 = findidx (&cp);
+ if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+ /* This isn't a valid character. */
+ return REG_ECOLLATE;
+
+ /* Build single byte matcing table for this equivalence class. */
+ char_buf[1] = (unsigned char) '\0';
+ len = weights[idx1];
+ for (ch = 0; ch < SBC_MAX; ++ch)
+ {
+ char_buf[0] = ch;
+ cp = char_buf;
+ idx2 = findidx (&cp);
+/*
+ idx2 = table[ch];
+*/
+ if (idx2 == 0)
+ /* This isn't a valid character. */
+ continue;
+ if (len == weights[idx2])
+ {
+ int cnt = 0;
+ while (cnt <= len &&
+ weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt])
+ ++cnt;
+
+ if (cnt > len)
+ bitset_set (sbcset, ch);
+ }
+ }
+ /* Check whether the array has enough space. */
+ if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nequiv_classes is 0. */
+ int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+ /* Use realloc since the array is NULL if *alloc == 0. */
+ int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+ int32_t,
+ new_equiv_class_alloc);
+ if (BE (new_equiv_classes == NULL, 0))
+ return REG_ESPACE;
+ mbcset->equiv_classes = new_equiv_classes;
+ *equiv_class_alloc = new_equiv_class_alloc;
+ }
+ mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+ }
+ else
+#endif /* _LIBC */
+ {
+ if (BE (strlen ((const char *) name) != 1, 0))
+ return REG_ECOLLATE;
+ bitset_set (sbcset, *name);
+ }
+ return REG_NOERROR;
+}
+
+ /* Helper function for parse_bracket_exp.
+ Build the character class which is represented by NAME.
+ The result are written to MBCSET and SBCSET.
+ CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+ is a pointer argument sinse we may update it. */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (trans, sbcset, mbcset, char_class_alloc, class_name, syntax)
+ re_charset_t *mbcset;
+ int *char_class_alloc;
+#else /* not RE_ENABLE_I18N */
+build_charclass (trans, sbcset, class_name, syntax)
+#endif /* not RE_ENABLE_I18N */
+ unsigned RE_TRANSLATE_TYPE trans;
+ re_bitset_ptr_t sbcset;
+ const unsigned char *class_name;
+ reg_syntax_t syntax;
+{
+ int i;
+ const char *name = (const char *) class_name;
+
+ /* In case of REG_ICASE "upper" and "lower" match the both of
+ upper and lower cases. */
+ if ((syntax & RE_ICASE)
+ && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0))
+ name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+ /* Check the space of the arrays. */
+ if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
+ {
+ /* Not enough, realloc it. */
+ /* +1 in case of mbcset->nchar_classes is 0. */
+ int new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
+ /* Use realloc since array is NULL if *alloc == 0. */
+ wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+ new_char_class_alloc);
+ if (BE (new_char_classes == NULL, 0))
+ return REG_ESPACE;
+ mbcset->char_classes = new_char_classes;
+ *char_class_alloc = new_char_class_alloc;
+ }
+ mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func) \
+ for (i = 0; i < SBC_MAX; ++i) \
+ { \
+ if (ctype_func (i)) \
+ { \
+ int ch = trans ? trans[i] : i; \
+ bitset_set (sbcset, ch); \
+ } \
+ }
+
+ if (strcmp (name, "alnum") == 0)
+ BUILD_CHARCLASS_LOOP (isalnum)
+ else if (strcmp (name, "cntrl") == 0)
+ BUILD_CHARCLASS_LOOP (iscntrl)
+ else if (strcmp (name, "lower") == 0)
+ BUILD_CHARCLASS_LOOP (islower)
+ else if (strcmp (name, "space") == 0)
+ BUILD_CHARCLASS_LOOP (isspace)
+ else if (strcmp (name, "alpha") == 0)
+ BUILD_CHARCLASS_LOOP (isalpha)
+ else if (strcmp (name, "digit") == 0)
+ BUILD_CHARCLASS_LOOP (isdigit)
+ else if (strcmp (name, "print") == 0)
+ BUILD_CHARCLASS_LOOP (isprint)
+ else if (strcmp (name, "upper") == 0)
+ BUILD_CHARCLASS_LOOP (isupper)
+ else if (strcmp (name, "blank") == 0)
+ BUILD_CHARCLASS_LOOP (isblank)
+ else if (strcmp (name, "graph") == 0)
+ BUILD_CHARCLASS_LOOP (isgraph)
+ else if (strcmp (name, "punct") == 0)
+ BUILD_CHARCLASS_LOOP (ispunct)
+ else if (strcmp (name, "xdigit") == 0)
+ BUILD_CHARCLASS_LOOP (isxdigit)
+ else
+ return REG_ECTYPE;
+
+ return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_charclass_op (dfa, trans, class_name, extra, non_match, err)
+ re_dfa_t *dfa;
+ unsigned RE_TRANSLATE_TYPE trans;
+ const unsigned char *class_name;
+ const unsigned char *extra;
+ int non_match;
+ reg_errcode_t *err;
+{
+ re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset;
+ int alloc = 0;
+#endif /* not RE_ENABLE_I18N */
+ reg_errcode_t ret;
+ re_token_t br_token;
+ bin_tree_t *tree;
+
+ sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+#ifdef RE_ENABLE_I18N
+ mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+ if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+ if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+
+ if (non_match)
+ {
+#ifdef RE_ENABLE_I18N
+ /*
+ if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+ bitset_set(cset->sbcset, '\0');
+ */
+ mbcset->non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+ }
+
+ /* We don't care the syntax in this case. */
+ ret = build_charclass (trans, sbcset,
+#ifdef RE_ENABLE_I18N
+ mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+ class_name, 0);
+
+ if (BE (ret != REG_NOERROR, 0))
+ {
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = ret;
+ return NULL;
+ }
+ /* \w match '_' also. */
+ for (; *extra; extra++)
+ bitset_set (sbcset, *extra);
+
+ /* If it is non-matching list. */
+ if (non_match)
+ bitset_not (sbcset);
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure only single byte characters are set. */
+ if (dfa->mb_cur_max > 1)
+ bitset_mask (sbcset, dfa->sb_char);
+#endif
+
+ /* Build a tree for simple bracket. */
+ br_token.type = SIMPLE_BRACKET;
+ br_token.opr.sbcset = sbcset;
+ tree = re_dfa_add_tree_node (dfa, NULL, NULL, &br_token);
+ if (BE (tree == NULL, 0))
+ goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ re_token_t alt_token;
+ bin_tree_t *mbc_tree;
+ /* Build a tree for complex bracket. */
+ br_token.type = COMPLEX_BRACKET;
+ br_token.opr.mbcset = mbcset;
+ dfa->has_mb_node = 1;
+ mbc_tree = re_dfa_add_tree_node (dfa, NULL, NULL, &br_token);
+ if (BE (mbc_tree == NULL, 0))
+ goto build_word_op_espace;
+ /* Then join them by ALT node. */
+ alt_token.type = OP_ALT;
+ dfa->has_plural_match = 1;
+ tree = re_dfa_add_tree_node (dfa, tree, mbc_tree, &alt_token);
+ if (BE (mbc_tree != NULL, 1))
+ return tree;
+ }
+ else
+ {
+ free_charset (mbcset);
+ return tree;
+ }
+#else /* not RE_ENABLE_I18N */
+ return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+ re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+ free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+ *err = REG_ESPACE;
+ return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+ Fetch a number from `input', and return the number.
+ Return -1, if the number field is empty like "{,1}".
+ Return -2, If an error is occured. */
+
+static int
+fetch_number (input, token, syntax)
+ re_string_t *input;
+ re_token_t *token;
+ reg_syntax_t syntax;
+{
+ int num = -1;
+ unsigned char c;
+ while (1)
+ {
+ fetch_token (token, input, syntax);
+ c = token->opr.c;
+ if (BE (token->type == END_OF_RE, 0))
+ return -2;
+ if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+ break;
+ num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+ ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
+ num = (num > RE_DUP_MAX) ? -2 : num;
+ }
+ return num;
+}
+
+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+ re_free (cset->mbchars);
+# ifdef _LIBC
+ re_free (cset->coll_syms);
+ re_free (cset->equiv_classes);
+ re_free (cset->range_starts);
+ re_free (cset->range_ends);
+# endif
+ re_free (cset->char_classes);
+ re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Functions for binary tree operation. */
+
+/* Create a tree node. */
+
+static bin_tree_t *
+create_tree (dfa, left, right, type, index)
+ re_dfa_t *dfa;
+ bin_tree_t *left;
+ bin_tree_t *right;
+ re_token_type_t type;
+ int index;
+{
+ bin_tree_t *tree;
+ if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
+ {
+ bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
+
+ if (storage == NULL)
+ return NULL;
+ storage->next = dfa->str_tree_storage;
+ dfa->str_tree_storage = storage;
+ dfa->str_tree_storage_idx = 0;
+ }
+ tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
+
+ tree->parent = NULL;
+ tree->left = left;
+ tree->right = right;
+ tree->type = type;
+ tree->node_idx = index;
+ tree->first = -1;
+ tree->next = -1;
+ re_node_set_init_empty (&tree->eclosure);
+
+ if (left != NULL)
+ left->parent = tree;
+ if (right != NULL)
+ right->parent = tree;
+ return tree;
+}
+
+/* Create both a DFA node and a tree for it. */
+
+static bin_tree_t *
+re_dfa_add_tree_node (dfa, left, right, token)
+ re_dfa_t *dfa;
+ bin_tree_t *left;
+ bin_tree_t *right;
+ const re_token_t *token;
+{
+ int new_idx = re_dfa_add_node (dfa, *token, 0);
+
+ if (new_idx == -1)
+ return NULL;
+
+ return create_tree (dfa, left, right, 0, new_idx);
+}
+
+/* Mark the tree SRC as an optional subexpression. */
+
+static void
+mark_opt_subexp (src, dfa)
+ const bin_tree_t *src;
+ re_dfa_t *dfa;
+{
+ /* Pass an OPT_SUBEXP_IDX which is != 1 if the duplicated tree is
+ a subexpression. */
+ if (src->type == CONCAT
+ && src->left->type == NON_TYPE
+ && dfa->nodes[src->left->node_idx].type == OP_OPEN_SUBEXP)
+ mark_opt_subexp_iter (src, dfa, dfa->nodes[src->left->node_idx].opr.idx);
+}
+
+
+/* Recursive tree walker for mark_opt_subexp. */
+
+static void
+mark_opt_subexp_iter (src, dfa, idx)
+ const bin_tree_t *src;
+ re_dfa_t *dfa;
+ int idx;
+{
+ int node_idx;
+
+ if (src->type == NON_TYPE)
+ {
+ node_idx = src->node_idx;
+ if ((dfa->nodes[node_idx].type == OP_OPEN_SUBEXP
+ || dfa->nodes[node_idx].type == OP_CLOSE_SUBEXP)
+ && dfa->nodes[node_idx].opr.idx == idx)
+ dfa->nodes[node_idx].opt_subexp = 1;
+ }
+
+ if (src->left != NULL)
+ mark_opt_subexp_iter (src->left, dfa, idx);
+
+ if (src->right != NULL)
+ mark_opt_subexp_iter (src->right, dfa, idx);
+}
+
+
+/* Duplicate the node SRC, and return new node. */
+
+static bin_tree_t *
+duplicate_tree (src, dfa)
+ const bin_tree_t *src;
+ re_dfa_t *dfa;
+{
+ bin_tree_t *left = NULL, *right = NULL, *new_tree;
+ int new_node_idx;
+ /* Since node indies must be according to Post-order of the tree,
+ we must duplicate the left at first. */
+ if (src->left != NULL)
+ {
+ left = duplicate_tree (src->left, dfa);
+ if (left == NULL)
+ return NULL;
+ }
+
+ /* Secondaly, duplicate the right. */
+ if (src->right != NULL)
+ {
+ right = duplicate_tree (src->right, dfa);
+ if (right == NULL)
+ return NULL;
+ }
+
+ /* At last, duplicate itself. */
+ if (src->type == NON_TYPE)
+ {
+ new_node_idx = re_dfa_add_node (dfa, dfa->nodes[src->node_idx], 0);
+ dfa->nodes[new_node_idx].duplicated = 1;
+ if (BE (new_node_idx == -1, 0))
+ return NULL;
+ }
+ else
+ new_node_idx = src->type;
+
+ new_tree = create_tree (dfa, left, right, src->type, new_node_idx);
+ return new_tree;
+}
diff --git a/src/regex/regex.c b/src/regex/regex.c
new file mode 100644
index 0000000..c6094f1
--- /dev/null
+++ b/src/regex/regex.c
@@ -0,0 +1,99 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "port.h"
+
+#ifdef _AIX
+#pragma alloca
+#else
+# ifndef allocax /* predefined by HP cc +Olibcalls */
+# ifdef __GNUC__
+# define alloca(size) __builtin_alloca (size)
+# else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef __hpux
+ void *alloca ();
+# else
+# if !defined __OS2__ && !defined WIN32
+ char *alloca ();
+# else
+# include <malloc.h> /* OS/2 defines alloca in here */
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+
+#ifdef _LIBC
+/* We have to keep the namespace clean. */
+# define regfree(preg) __regfree (preg)
+# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+# define regerror(errcode, preg, errbuf, errbuf_size) \
+ __regerror(errcode, preg, errbuf, errbuf_size)
+# define re_set_registers(bu, re, nu, st, en) \
+ __re_set_registers (bu, re, nu, st, en)
+# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+ __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+# define re_match(bufp, string, size, pos, regs) \
+ __re_match (bufp, string, size, pos, regs)
+# define re_search(bufp, string, size, startpos, range, regs) \
+ __re_search (bufp, string, size, startpos, range, regs)
+# define re_compile_pattern(pattern, length, bufp) \
+ __re_compile_pattern (pattern, length, bufp)
+# define re_set_syntax(syntax) __re_set_syntax (syntax)
+# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+ __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+# include "../locale/localeinfo.h"
+#endif
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+ <regex.h>. */
+#include <sys/types.h>
+
+/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
+ GNU regex allows. Include it before <regex.h>, which correctly
+ #undefs RE_DUP_MAX and sets it to the right value. */
+#include <limits.h>
+
+#include <regex.h>
+#include "regex_internal.h"
+
+#include "regex_internal.ci"
+#include "regcomp.ci"
+#include "regexec.ci"
+
+/* Binary backward compatibility. */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
+int re_max_failures = 2000;
+# endif
+#endif
diff --git a/src/regex/regex.h b/src/regex/regex.h
new file mode 100644
index 0000000..b2d9a62
--- /dev/null
+++ b/src/regex/regex.h
@@ -0,0 +1,593 @@
+/* Definitions for data structures and routines for the regular
+ expression library.
+ Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003
+ Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _REGEX_H
+#define _REGEX_H 1
+
+#include <sys/types.h>
+
+/* Allow the use in C++ code. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+ <regex.h>. */
+
+#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+ should be there. */
+# include <stddef.h>
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+ wide enough to hold a value of a pointer. For most ANSI compilers
+ ptrdiff_t and size_t should be likely OK. Still size of these two
+ types is 2 for Microsoft C. Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+ recognize. The set/not-set meanings are chosen so that Emacs syntax
+ remains the value 0. The bits are given in alphabetical order, and
+ the definitions shifted by one from the previous bit; thus, when we
+ add or remove a bit, only one other definition need change. */
+typedef unsigned long int reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+ If set, then such a \ quotes the following character. */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+ literals.
+ If set, then \+ and \? are operators and + and ? are literals. */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported. They are:
+ [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
+ [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+ If not set, then character classes are not supported. */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+ expressions, of course).
+ If this bit is not set, then it depends:
+ ^ is an anchor if it is at the beginning of a regular
+ expression or after an open-group or an alternation operator;
+ $ is an anchor if it is at the end of a regular expression, or
+ before a close-group or an alternation operator.
+
+ This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+ POSIX draft 11.2 says that * etc. in leading positions is undefined.
+ We already implemented a previous draft which made those constructs
+ invalid, though, so we haven't changed the code back. */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+ regardless of where they are in the pattern.
+ If this bit is not set, then special characters are special only in
+ some contexts; otherwise they are ordinary. Specifically,
+ * + ? and intervals are only special when not after the beginning,
+ open-group, or alternation operator. */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+ immediately after an alternation or begin-group operator. */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+ If not set, then it doesn't. */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+ If not set, then it does. */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+ If not set, they do. */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+ interval, depending on RE_NO_BK_BRACES.
+ If not set, \{, \}, {, and } are literals. */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+ If not set, they are. */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+ If not set, newline is literal. */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+ are literals.
+ If not set, then `\{...\}' defines an interval. */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+ If not set, \(...\) defines a group, and ( and ) are literals. */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+ If not set, then \<digit> is a back-reference. */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+ If not set, then \| is an alternation operator, and | is literal. */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+ than the starting range point, as in [z-a], is invalid.
+ If not set, then when ending range point collates higher than the
+ starting range point, the range is ignored. */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+ If not set, then an unmatched ) is invalid. */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+ without further backtracking. */
+#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+ If not set, then the GNU regex operators are recognized. */
+#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, turn on internal regex debugging.
+ If not set, and debugging was on, turn it off.
+ This only works if regex.c is compiled -DDEBUG.
+ We define this bit always, so that all that's needed to turn on
+ debugging is to recompile regex.c; the calling code can always have
+ this bit set, and it won't affect anything in the normal case. */
+#define RE_DEBUG (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+ a string of ordinary characters. For example, the ERE 'a{1' is
+ treated as 'a\{1'. */
+#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+ for ^, because it is difficult to scan the regex backwards to find
+ whether ^ should be special. */
+#define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in an bre or
+ immediately after an alternation or begin-group operator. */
+#define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+ re_compile_pattern. */
+#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+ some interfaces). When a regexp is compiled, the syntax used is
+ stored in the pattern buffer, so changing this does not affect
+ already-compiled regexps. */
+extern reg_syntax_t re_syntax_options;
+
+/* Define combinations of the above bits for the standard possibilities.
+ (The [[[ comments delimit what gets put into the Texinfo file, so
+ don't delete them!) */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK \
+ (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
+ | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GNU_AWK \
+ ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \
+ & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS \
+ | RE_CONTEXT_INVALID_OPS ))
+
+#define RE_SYNTAX_POSIX_AWK \
+ (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
+ | RE_INTERVALS | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GREP \
+ (RE_BK_PLUS_QM | RE_CHAR_CLASSES \
+ | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
+ | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP \
+ (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
+ | RE_NEWLINE_ALT | RE_NO_BK_PARENS \
+ | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP \
+ (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \
+ | RE_INVALID_INTERVAL_ORD)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax. */
+#define _RE_SYNTAX_POSIX_COMMON \
+ (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
+ | RE_INTERVALS | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+ RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
+ isn't minimal, since other operators, such as \`, aren't disabled. */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
+ (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_VBAR \
+ | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+ removed and RE_NO_BK_REFS is added. */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
+ (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
+ | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
+ | RE_NO_BK_PARENS | RE_NO_BK_REFS \
+ | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+
+/* Maximum number of duplicates an interval can allow. Some systems
+ (erroneously) define this in other header files, but we want our
+ value, so remove any previous define. */
+#ifdef RE_DUP_MAX
+# undef RE_DUP_MAX
+#endif
+/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */
+#define RE_DUP_MAX (0x7fff)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp'). */
+
+/* If this bit is set, then use extended regular expression syntax.
+ If not set, then use basic regular expression syntax. */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+ If not set, then case is significant. */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+ characters in the string.
+ If not set, then anchors do match at newlines. */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+ If not set, then returns differ between not matching and errors. */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec). */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+ the beginning of the string (presumably because it's not the
+ beginning of a line).
+ If not set, then the beginning-of-line operator does match the
+ beginning of the string. */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line. */
+#define REG_NOTEOL (1 << 1)
+
+/* Use PMATCH[0] to delimit the start and end of the search in the
+ buffer. */
+#define REG_STARTEND (1 << 2)
+
+
+/* If any error codes are removed, changed, or added, update the
+ `re_error_msg' table in regex.c. */
+typedef enum
+{
+#ifdef _XOPEN_SOURCE
+ REG_ENOSYS = -1, /* This will never happen for this implementation. */
+#endif
+
+ REG_NOERROR = 0, /* Success. */
+ REG_NOMATCH, /* Didn't find a match (for regexec). */
+
+ /* POSIX regcomp return error codes. (In the order listed in the
+ standard.) */
+ REG_BADPAT, /* Invalid pattern. */
+ REG_ECOLLATE, /* Inalid collating element. */
+ REG_ECTYPE, /* Invalid character class name. */
+ REG_EESCAPE, /* Trailing backslash. */
+ REG_ESUBREG, /* Invalid back reference. */
+ REG_EBRACK, /* Unmatched left bracket. */
+ REG_EPAREN, /* Parenthesis imbalance. */
+ REG_EBRACE, /* Unmatched \{. */
+ REG_BADBR, /* Invalid contents of \{\}. */
+ REG_ERANGE, /* Invalid range end. */
+ REG_ESPACE, /* Ran out of memory. */
+ REG_BADRPT, /* No preceding re for repetition op. */
+
+ /* Error codes we've added. */
+ REG_EEND, /* Premature end. */
+ REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
+ REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
+} reg_errcode_t;
+
+/* This data structure represents a compiled pattern. Before calling
+ the pattern compiler, the fields `buffer', `allocated', `fastmap',
+ `translate', and `no_sub' can be set. After the pattern has been
+ compiled, the `re_nsub' field is available. All other fields are
+ private to the regex routines. */
+
+#ifndef RE_TRANSLATE_TYPE
+# define RE_TRANSLATE_TYPE char *
+#endif
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+ /* Space that holds the compiled pattern. It is declared as
+ `unsigned char *' because its elements are
+ sometimes used as array indexes. */
+ unsigned char *buffer;
+
+ /* Number of bytes to which `buffer' points. */
+ unsigned long int allocated;
+
+ /* Number of bytes actually used in `buffer'. */
+ unsigned long int used;
+
+ /* Syntax setting with which the pattern was compiled. */
+ reg_syntax_t syntax;
+
+ /* Pointer to a fastmap, if any, otherwise zero. re_search uses
+ the fastmap, if there is one, to skip over impossible
+ starting points for matches. */
+ char *fastmap;
+
+ /* Either a translate table to apply to all characters before
+ comparing them, or zero for no translation. The translation
+ is applied to a pattern when it is compiled and to a string
+ when it is matched. */
+ RE_TRANSLATE_TYPE translate;
+
+ /* Number of subexpressions found by the compiler. */
+ size_t re_nsub;
+
+ /* Zero if this pattern cannot match the empty string, one else.
+ Well, in truth it's used only in `re_search_2', to see
+ whether or not we should use the fastmap, so we don't set
+ this absolutely perfectly; see `re_compile_fastmap' (the
+ `duplicate' case). */
+ unsigned can_be_null : 1;
+
+ /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+ for `max (RE_NREGS, re_nsub + 1)' groups.
+ If REGS_REALLOCATE, reallocate space if necessary.
+ If REGS_FIXED, use what's there. */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+ unsigned regs_allocated : 2;
+
+ /* Set to zero when `regex_compile' compiles a pattern; set to one
+ by `re_compile_fastmap' if it updates the fastmap. */
+ unsigned fastmap_accurate : 1;
+
+ /* If set, `re_match_2' does not return information about
+ subexpressions. */
+ unsigned no_sub : 1;
+
+ /* If set, a beginning-of-line anchor doesn't match at the
+ beginning of the string. */
+ unsigned not_bol : 1;
+
+ /* Similarly for an end-of-line anchor. */
+ unsigned not_eol : 1;
+
+ /* If true, an anchor at a newline matches. */
+ unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+
+/* Type for byte offsets within the string. POSIX mandates this. */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in. See
+ regex.texinfo for a full description of what registers match. */
+struct re_registers
+{
+ unsigned num_regs;
+ regoff_t *start;
+ regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+ `re_match_2' returns information about at least this many registers
+ the first time a `regs' structure is passed. */
+#ifndef RE_NREGS
+# define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers. Aside from the different names than
+ `re_registers', POSIX uses an array of structures, instead of a
+ structure of arrays. */
+typedef struct
+{
+ regoff_t rm_so; /* Byte offset from string's start to substring's start. */
+ regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
+} regmatch_t;
+
+/* Declarations for routines. */
+
+/* To avoid duplicating every routine declaration -- once with a
+ prototype (if we are ANSI), and once without (if we aren't) -- we
+ use the following macro to declare argument types. This
+ unfortunately clutters up the declarations a bit, but I think it's
+ worth it. */
+
+#if __STDC__
+
+# define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+# define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+ You can also simply assign to the `re_syntax_options' variable. */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+ and syntax given by the global `re_syntax_options', into the buffer
+ BUFFER. Return NULL if successful, and an error string if not. */
+extern const char *re_compile_pattern
+ _RE_ARGS ((const char *pattern, size_t length,
+ struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+ accelerate searches. Return 0 if successful and -2 if was an
+ internal error. */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+ compiled into BUFFER. Start searching at position START, for RANGE
+ characters. Return the starting position of the match, -1 for no
+ match, or -2 for an internal error. Also return register
+ information in REGS (if REGS and BUFFER->no_sub are nonzero). */
+extern int re_search
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+ STRING2. Also, stop searching at index START + STOP. */
+extern int re_search_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+ in BUFFER matched, starting at position START. */
+extern int re_match
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+ int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
+extern int re_match_2
+ _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+ int length1, const char *string2, int length2,
+ int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using BUFFER and REGS will use this memory
+ for recording register information. STARTS and ENDS must be
+ allocated with malloc, and must each be at least `NUM_REGS * sizeof
+ (regoff_t)' bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+extern void re_set_registers
+ _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+ unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+# ifndef _CRAY
+/* 4.2 bsd compatibility. */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+# endif
+#endif
+
+/* GCC 2.95 and later have "__restrict"; C99 compilers have
+ "restrict", and "configure" may have defined "restrict". */
+#ifndef __restrict
+# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
+# if defined restrict || 199901L <= __STDC_VERSION__
+# define __restrict restrict
+# else
+# define __restrict
+# endif
+# endif
+#endif
+/* gcc 3.1 and up support the [restrict] syntax. */
+#ifndef __restrict_arr
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+# define __restrict_arr __restrict
+# else
+# define __restrict_arr
+# endif
+#endif
+
+/* POSIX compatibility. */
+extern int regcomp _RE_ARGS ((regex_t *__restrict __preg,
+ const char *__restrict __pattern,
+ int __cflags));
+
+extern int regexec _RE_ARGS ((const regex_t *__restrict __preg,
+ const char *__restrict __string, size_t __nmatch,
+ regmatch_t __pmatch[__restrict_arr],
+ int __eflags));
+
+extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg,
+ char *__errbuf, size_t __errbuf_size));
+
+extern void regfree _RE_ARGS ((regex_t *__preg));
+
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* regex.h */
+
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/src/regex/regex_internal.ci b/src/regex/regex_internal.ci
new file mode 100644
index 0000000..001b50b
--- /dev/null
+++ b/src/regex/regex_internal.ci
@@ -0,0 +1,1673 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+static void re_string_construct_common (const char *str, int len,
+ re_string_t *pstr,
+ RE_TRANSLATE_TYPE trans, int icase,
+ const re_dfa_t *dfa) internal_function;
+#ifdef RE_ENABLE_I18N
+static int re_string_skip_chars (re_string_t *pstr, int new_raw_idx,
+ wint_t *last_wc) internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t register_state (re_dfa_t *dfa, re_dfastate_t *newstate,
+ unsigned int hash) internal_function;
+static re_dfastate_t *create_ci_newstate (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int hash) internal_function;
+static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context,
+ unsigned int hash) internal_function;
+static unsigned int inline calc_state_hash (const re_node_set *nodes,
+ unsigned int context) internal_function;
+
+/* Functions for string operation. */
+
+/* This function allocate the buffers. It is necessary to call
+ re_string_reconstruct before using the object. */
+
+static reg_errcode_t
+re_string_allocate (pstr, str, len, init_len, trans, icase, dfa)
+ re_string_t *pstr;
+ const char *str;
+ int len, init_len, icase;
+ RE_TRANSLATE_TYPE trans;
+ const re_dfa_t *dfa;
+{
+ reg_errcode_t ret;
+ int init_buf_len;
+
+ /* Ensure at least one character fits into the buffers. */
+ if (init_len < dfa->mb_cur_max)
+ init_len = dfa->mb_cur_max;
+ init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+ re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+ ret = re_string_realloc_buffers (pstr, init_buf_len);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ pstr->word_char = dfa->word_char;
+ pstr->word_ops_used = dfa->word_ops_used;
+ pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+ pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
+ pstr->valid_raw_len = pstr->valid_len;
+ return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them. */
+
+static reg_errcode_t
+re_string_construct (pstr, str, len, trans, icase, dfa)
+ re_string_t *pstr;
+ const char *str;
+ int len, icase;
+ RE_TRANSLATE_TYPE trans;
+ const re_dfa_t *dfa;
+{
+ reg_errcode_t ret;
+ memset (pstr, '\0', sizeof (re_string_t));
+ re_string_construct_common (str, len, pstr, trans, icase, dfa);
+
+ if (len > 0)
+ {
+ ret = re_string_realloc_buffers (pstr, len + 1);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+
+ if (icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ {
+ while (1)
+ {
+ ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ if (pstr->valid_raw_len >= len)
+ break;
+ if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
+ break;
+ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (trans != NULL)
+ re_string_translate_buffer (pstr);
+ else
+ {
+ pstr->valid_len = pstr->bufs_len;
+ pstr->valid_raw_len = pstr->bufs_len;
+ }
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct. */
+
+static reg_errcode_t
+re_string_realloc_buffers (pstr, new_buf_len)
+ re_string_t *pstr;
+ int new_buf_len;
+{
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ wint_t *new_array = re_realloc (pstr->wcs, wint_t, new_buf_len);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ pstr->wcs = new_array;
+ if (pstr->offsets != NULL)
+ {
+ int *new_array = re_realloc (pstr->offsets, int, new_buf_len);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ pstr->offsets = new_array;
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ if (pstr->mbs_allocated)
+ {
+ unsigned char *new_array = re_realloc (pstr->mbs, unsigned char,
+ new_buf_len);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ pstr->mbs = new_array;
+ }
+ pstr->bufs_len = new_buf_len;
+ return REG_NOERROR;
+}
+
+
+static void
+re_string_construct_common (str, len, pstr, trans, icase, dfa)
+ const char *str;
+ int len;
+ re_string_t *pstr;
+ RE_TRANSLATE_TYPE trans;
+ int icase;
+ const re_dfa_t *dfa;
+{
+ pstr->raw_mbs = (const unsigned char *) str;
+ pstr->len = len;
+ pstr->raw_len = len;
+ pstr->trans = (unsigned RE_TRANSLATE_TYPE) trans;
+ pstr->icase = icase ? 1 : 0;
+ pstr->mbs_allocated = (trans != NULL || icase);
+ pstr->mb_cur_max = dfa->mb_cur_max;
+ pstr->is_utf8 = dfa->is_utf8;
+ pstr->map_notascii = dfa->map_notascii;
+ pstr->stop = pstr->len;
+ pstr->raw_stop = pstr->stop;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+ If the byte sequence of the string are:
+ <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+ Then wide character buffer will be:
+ <wc1> , WEOF , <wc2> , WEOF , <wc3>
+ We use WEOF for padding, they indicate that the position isn't
+ a first byte of a multibyte character.
+
+ Note that this function assumes PSTR->VALID_LEN elements are already
+ built and starts from PSTR->VALID_LEN. */
+
+static void
+build_wcs_buffer (pstr)
+ re_string_t *pstr;
+{
+#ifdef _LIBC
+ unsigned char buf[pstr->mb_cur_max];
+#else
+ unsigned char buf[64];
+#endif
+ mbstate_t prev_st;
+ int byte_idx, end_idx, mbclen, remain_len;
+
+ /* Build the buffers from pstr->valid_len to either pstr->len or
+ pstr->bufs_len. */
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+ for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ const char *p;
+
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ /* Apply the translation if we need. */
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i, ch;
+
+ for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+ {
+ ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
+ buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
+ }
+ p = (const char *) buf;
+ }
+ else
+ p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
+ mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2, 0))
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a singlebyte character. */
+ mbclen = 1;
+ wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ if (BE (pstr->trans != NULL, 0))
+ wc = pstr->trans[wc];
+ pstr->cur_state = prev_st;
+ }
+
+ /* Write wide character and padding. */
+ pstr->wcs[byte_idx++] = wc;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+ but for REG_ICASE. */
+
+static int
+build_wcs_upper_buffer (pstr)
+ re_string_t *pstr;
+{
+ mbstate_t prev_st;
+ int src_idx, byte_idx, end_idx, mbclen, remain_len;
+#ifdef _LIBC
+ unsigned char buf[pstr->mb_cur_max];
+#else
+ unsigned char buf[64];
+#endif
+
+ byte_idx = pstr->valid_len;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ /* The following optimization assumes that ASCII characters can be
+ mapped to wide characters with a simple cast. */
+ if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
+ {
+ while (byte_idx < end_idx)
+ {
+ wchar_t wc;
+
+ if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
+ && mbsinit (&pstr->cur_state))
+ {
+ /* In case of a singlebyte character. */
+ pstr->mbs[byte_idx]
+ = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
+ /* The next step uses the assumption that wchar_t is encoded
+ ASCII-safe: all ASCII values can be converted like this. */
+ pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
+ ++byte_idx;
+ continue;
+ }
+
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ mbclen = mbrtowc (&wc,
+ ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+ + byte_idx), remain_len, &pstr->cur_state);
+ if (BE (mbclen > 0, 1))
+ {
+ wchar_t wcu = wc;
+ if (iswlower (wc))
+ {
+ int mbcdlen;
+
+ wcu = towupper (wc);
+ mbcdlen = wcrtomb (buf, wcu, &prev_st);
+ if (BE (mbclen == mbcdlen, 1))
+ memcpy (pstr->mbs + byte_idx, buf, mbclen);
+ else
+ {
+ src_idx = byte_idx;
+ goto offsets_needed;
+ }
+ }
+ else
+ memcpy (pstr->mbs + byte_idx,
+ pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+ pstr->wcs[byte_idx++] = wcu;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ else if (mbclen == (size_t) -1 || mbclen == 0)
+ {
+ /* It is an invalid character or '\0'. Just use the byte. */
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+ pstr->mbs[byte_idx] = ch;
+ /* And also cast it to wide char. */
+ pstr->wcs[byte_idx++] = (wchar_t) ch;
+ if (BE (mbclen == (size_t) -1, 0))
+ pstr->cur_state = prev_st;
+ }
+ else
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = byte_idx;
+ return REG_NOERROR;
+ }
+ else
+ for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
+ {
+ wchar_t wc;
+ const char *p;
+ offsets_needed:
+ remain_len = end_idx - byte_idx;
+ prev_st = pstr->cur_state;
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i, ch;
+
+ for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+ {
+ ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
+ buf[i] = pstr->trans[ch];
+ }
+ p = (const char *) buf;
+ }
+ else
+ p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
+ mbclen = mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+ if (BE (mbclen > 0, 1))
+ {
+ wchar_t wcu = wc;
+ if (iswlower (wc))
+ {
+ int mbcdlen;
+
+ wcu = towupper (wc);
+ mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st);
+ if (BE (mbclen == mbcdlen, 1))
+ memcpy (pstr->mbs + byte_idx, buf, mbclen);
+ else
+ {
+ int i;
+
+ if (byte_idx + mbcdlen > pstr->bufs_len)
+ {
+ pstr->cur_state = prev_st;
+ break;
+ }
+
+ if (pstr->offsets == NULL)
+ {
+ pstr->offsets = re_malloc (int, pstr->bufs_len);
+
+ if (pstr->offsets == NULL)
+ return REG_ESPACE;
+ }
+ if (!pstr->offsets_needed)
+ {
+ for (i = 0; i < byte_idx; ++i)
+ pstr->offsets[i] = i;
+ pstr->offsets_needed = 1;
+ }
+
+ memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
+ pstr->wcs[byte_idx] = wcu;
+ pstr->offsets[byte_idx] = src_idx;
+ for (i = 1; i < mbcdlen; ++i)
+ {
+ pstr->offsets[byte_idx + i]
+ = src_idx + (i < mbclen ? i : mbclen - 1);
+ pstr->wcs[byte_idx + i] = WEOF;
+ }
+ pstr->len += mbcdlen - mbclen;
+ if (pstr->raw_stop > src_idx)
+ pstr->stop += mbcdlen - mbclen;
+ end_idx = (pstr->bufs_len > pstr->len)
+ ? pstr->len : pstr->bufs_len;
+ byte_idx += mbcdlen;
+ src_idx += mbclen;
+ continue;
+ }
+ }
+ else
+ memcpy (pstr->mbs + byte_idx, p, mbclen);
+
+ if (BE (pstr->offsets_needed != 0, 0))
+ {
+ int i;
+ for (i = 0; i < mbclen; ++i)
+ pstr->offsets[byte_idx + i] = src_idx + i;
+ }
+ src_idx += mbclen;
+
+ pstr->wcs[byte_idx++] = wcu;
+ /* Write paddings. */
+ for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+ pstr->wcs[byte_idx++] = WEOF;
+ }
+ else if (mbclen == (size_t) -1 || mbclen == 0)
+ {
+ /* It is an invalid character or '\0'. Just use the byte. */
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
+
+ if (BE (pstr->trans != NULL, 0))
+ ch = pstr->trans [ch];
+ pstr->mbs[byte_idx] = ch;
+
+ if (BE (pstr->offsets_needed != 0, 0))
+ pstr->offsets[byte_idx] = src_idx;
+ ++src_idx;
+
+ /* And also cast it to wide char. */
+ pstr->wcs[byte_idx++] = (wchar_t) ch;
+ if (BE (mbclen == (size_t) -1, 0))
+ pstr->cur_state = prev_st;
+ }
+ else
+ {
+ /* The buffer doesn't have enough space, finish to build. */
+ pstr->cur_state = prev_st;
+ break;
+ }
+ }
+ pstr->valid_len = byte_idx;
+ pstr->valid_raw_len = src_idx;
+ return REG_NOERROR;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+ Return the index. */
+
+static int
+re_string_skip_chars (pstr, new_raw_idx, last_wc)
+ re_string_t *pstr;
+ int new_raw_idx;
+ wint_t *last_wc;
+{
+ mbstate_t prev_st;
+ int rawbuf_idx, mbclen;
+ wchar_t wc = 0;
+
+ /* Skip the characters which are not necessary to check. */
+ for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
+ rawbuf_idx < new_raw_idx;)
+ {
+ int remain_len;
+ remain_len = pstr->len - rawbuf_idx;
+ prev_st = pstr->cur_state;
+ mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx,
+ remain_len, &pstr->cur_state);
+ if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+ {
+ /* We treat these cases as a singlebyte character. */
+ mbclen = 1;
+ pstr->cur_state = prev_st;
+ }
+ /* Then proceed the next character. */
+ rawbuf_idx += mbclen;
+ }
+ *last_wc = (wint_t) wc;
+ return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+ This function is used in case of REG_ICASE. */
+
+static void
+build_upper_buffer (pstr)
+ re_string_t *pstr;
+{
+ int char_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+ if (BE (pstr->trans != NULL, 0))
+ ch = pstr->trans[ch];
+ if (islower (ch))
+ pstr->mbs[char_idx] = toupper (ch);
+ else
+ pstr->mbs[char_idx] = ch;
+ }
+ pstr->valid_len = char_idx;
+ pstr->valid_raw_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR. */
+
+static void
+re_string_translate_buffer (pstr)
+ re_string_t *pstr;
+{
+ int buf_idx, end_idx;
+ end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+ for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+ {
+ int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+ pstr->mbs[buf_idx] = pstr->trans[ch];
+ }
+
+ pstr->valid_len = buf_idx;
+ pstr->valid_raw_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+ Concretely, convert to wide character in case of pstr->mb_cur_max > 1,
+ convert to upper case in case of REG_ICASE, apply translation. */
+
+static reg_errcode_t
+re_string_reconstruct (pstr, idx, eflags)
+ re_string_t *pstr;
+ int idx, eflags;
+{
+ int offset = idx - pstr->raw_mbs_idx;
+ if (BE (offset < 0, 0))
+ {
+ /* Reset buffer. */
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+ pstr->len = pstr->raw_len;
+ pstr->stop = pstr->raw_stop;
+ pstr->valid_len = 0;
+ pstr->raw_mbs_idx = 0;
+ pstr->valid_raw_len = 0;
+ pstr->offsets_needed = 0;
+ pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+ if (!pstr->mbs_allocated)
+ pstr->mbs = (unsigned char *) pstr->raw_mbs;
+ offset = idx;
+ }
+
+ if (BE (offset != 0, 1))
+ {
+ /* Are the characters which are already checked remain? */
+ if (BE (offset < pstr->valid_raw_len, 1)
+#ifdef RE_ENABLE_I18N
+ /* Handling this would enlarge the code too much.
+ Accept a slowdown in that case. */
+ && pstr->offsets_needed == 0
+#endif
+ )
+ {
+ /* Yes, move them to the front of the buffer. */
+ pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags);
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ memmove (pstr->wcs, pstr->wcs + offset,
+ (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+ if (BE (pstr->mbs_allocated, 0))
+ memmove (pstr->mbs, pstr->mbs + offset,
+ pstr->valid_len - offset);
+ pstr->valid_len -= offset;
+ pstr->valid_raw_len -= offset;
+#if DEBUG
+ assert (pstr->valid_len > 0);
+#endif
+ }
+ else
+ {
+ /* No, skip all characters until IDX. */
+#ifdef RE_ENABLE_I18N
+ if (BE (pstr->offsets_needed, 0))
+ {
+ pstr->len = pstr->raw_len - idx + offset;
+ pstr->stop = pstr->raw_stop - idx + offset;
+ pstr->offsets_needed = 0;
+ }
+#endif
+ pstr->valid_len = 0;
+ pstr->valid_raw_len = 0;
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ int wcs_idx;
+ wint_t wc = WEOF;
+
+ if (pstr->is_utf8)
+ {
+ const unsigned char *raw, *p, *q, *end;
+
+ /* Special case UTF-8. Multi-byte chars start with any
+ byte other than 0x80 - 0xbf. */
+ raw = pstr->raw_mbs + pstr->raw_mbs_idx;
+ end = raw + (offset - pstr->mb_cur_max);
+ for (p = raw + offset - 1; p >= end; --p)
+ if ((*p & 0xc0) != 0x80)
+ {
+ mbstate_t cur_state;
+ wchar_t wc2;
+ int mlen = raw + pstr->len - p;
+ unsigned char buf[6];
+
+ q = p;
+ if (BE (pstr->trans != NULL, 0))
+ {
+ int i = mlen < 6 ? mlen : 6;
+ while (--i >= 0)
+ buf[i] = pstr->trans[p[i]];
+ q = buf;
+ }
+ /* XXX Don't use mbrtowc, we know which conversion
+ to use (UTF-8 -> UCS4). */
+ memset (&cur_state, 0, sizeof (cur_state));
+ mlen = mbrtowc (&wc2, p, mlen, &cur_state)
+ - (raw + offset - p);
+ if (mlen >= 0)
+ {
+ memset (&pstr->cur_state, '\0',
+ sizeof (mbstate_t));
+ pstr->valid_len = mlen;
+ wc = wc2;
+ }
+ break;
+ }
+ }
+
+ if (wc == WEOF)
+ pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+ if (BE (pstr->valid_len, 0))
+ {
+ for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+ pstr->wcs[wcs_idx] = WEOF;
+ if (pstr->mbs_allocated)
+ memset (pstr->mbs, 255, pstr->valid_len);
+ }
+ pstr->valid_raw_len = pstr->valid_len;
+ pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
+ && IS_WIDE_WORD_CHAR (wc))
+ ? CONTEXT_WORD
+ : ((IS_WIDE_NEWLINE (wc)
+ && pstr->newline_anchor)
+ ? CONTEXT_NEWLINE : 0));
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+ if (pstr->trans)
+ c = pstr->trans[c];
+ pstr->tip_context = (bitset_contain (pstr->word_char, c)
+ ? CONTEXT_WORD
+ : ((IS_NEWLINE (c) && pstr->newline_anchor)
+ ? CONTEXT_NEWLINE : 0));
+ }
+ }
+ if (!BE (pstr->mbs_allocated, 0))
+ pstr->mbs += offset;
+ }
+ pstr->raw_mbs_idx = idx;
+ pstr->len -= offset;
+ pstr->stop -= offset;
+
+ /* Then build the buffers. */
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ if (pstr->icase)
+ {
+ int ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ else
+ build_wcs_buffer (pstr);
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ if (BE (pstr->mbs_allocated, 0))
+ {
+ if (pstr->icase)
+ build_upper_buffer (pstr);
+ else if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ }
+ else
+ pstr->valid_len = pstr->len;
+
+ pstr->cur_idx = 0;
+ return REG_NOERROR;
+}
+
+static unsigned char
+re_string_peek_byte_case (pstr, idx)
+ const re_string_t *pstr;
+ int idx;
+{
+ int ch, off;
+
+ /* Handle the common (easiest) cases first. */
+ if (BE (!pstr->mbs_allocated, 1))
+ return re_string_peek_byte (pstr, idx);
+
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1
+ && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
+ return re_string_peek_byte (pstr, idx);
+#endif
+
+ off = pstr->cur_idx + idx;
+#ifdef RE_ENABLE_I18N
+ if (pstr->offsets_needed)
+ off = pstr->offsets[off];
+#endif
+
+ ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+#ifdef RE_ENABLE_I18N
+ /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
+ this function returns CAPITAL LETTER I instead of first byte of
+ DOTLESS SMALL LETTER I. The latter would confuse the parser,
+ since peek_byte_case doesn't advance cur_idx in any way. */
+ if (pstr->offsets_needed && !isascii (ch))
+ return re_string_peek_byte (pstr, idx);
+#endif
+
+ return ch;
+}
+
+static unsigned char
+re_string_fetch_byte_case (pstr)
+ re_string_t *pstr;
+{
+ if (BE (!pstr->mbs_allocated, 1))
+ return re_string_fetch_byte (pstr);
+
+#ifdef RE_ENABLE_I18N
+ if (pstr->offsets_needed)
+ {
+ int off, ch;
+
+ /* For tr_TR.UTF-8 [[:islower:]] there is
+ [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip
+ in that case the whole multi-byte character and return
+ the original letter. On the other side, with
+ [[: DOTLESS SMALL LETTER I return [[:I, as doing
+ anything else would complicate things too much. */
+
+ if (!re_string_first_byte (pstr, pstr->cur_idx))
+ return re_string_fetch_byte (pstr);
+
+ off = pstr->offsets[pstr->cur_idx];
+ ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+ if (! isascii (ch))
+ return re_string_fetch_byte (pstr);
+
+ re_string_skip_bytes (pstr,
+ re_string_char_size_at (pstr, pstr->cur_idx));
+ return ch;
+ }
+#endif
+
+ return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
+}
+
+static void
+re_string_destruct (pstr)
+ re_string_t *pstr;
+{
+#ifdef RE_ENABLE_I18N
+ re_free (pstr->wcs);
+ re_free (pstr->offsets);
+#endif /* RE_ENABLE_I18N */
+ if (pstr->mbs_allocated)
+ re_free (pstr->mbs);
+}
+
+/* Return the context at IDX in INPUT. */
+
+static unsigned int
+re_string_context_at (input, idx, eflags)
+ const re_string_t *input;
+ int idx, eflags;
+{
+ int c;
+ if (BE (idx < 0, 0))
+ /* In this case, we use the value stored in input->tip_context,
+ since we can't know the character in input->mbs[-1] here. */
+ return input->tip_context;
+ if (BE (idx == input->len, 0))
+ return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+ : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+#ifdef RE_ENABLE_I18N
+ if (input->mb_cur_max > 1)
+ {
+ wint_t wc;
+ int wc_idx = idx;
+ while(input->wcs[wc_idx] == WEOF)
+ {
+#ifdef DEBUG
+ /* It must not happen. */
+ assert (wc_idx >= 0);
+#endif
+ --wc_idx;
+ if (wc_idx < 0)
+ return input->tip_context;
+ }
+ wc = input->wcs[wc_idx];
+ if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
+ return CONTEXT_WORD;
+ return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
+ ? CONTEXT_NEWLINE : 0);
+ }
+ else
+#endif
+ {
+ c = re_string_byte_at (input, idx);
+ if (bitset_contain (input->word_char, c))
+ return CONTEXT_WORD;
+ return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
+ }
+}
+
+/* Functions for set operation. */
+
+static reg_errcode_t
+re_node_set_alloc (set, size)
+ re_node_set *set;
+ int size;
+{
+ set->alloc = size;
+ set->nelem = 0;
+ set->elems = re_malloc (int, size);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_1 (set, elem)
+ re_node_set *set;
+ int elem;
+{
+ set->alloc = 1;
+ set->nelem = 1;
+ set->elems = re_malloc (int, 1);
+ if (BE (set->elems == NULL, 0))
+ {
+ set->alloc = set->nelem = 0;
+ return REG_ESPACE;
+ }
+ set->elems[0] = elem;
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_2 (set, elem1, elem2)
+ re_node_set *set;
+ int elem1, elem2;
+{
+ set->alloc = 2;
+ set->elems = re_malloc (int, 2);
+ if (BE (set->elems == NULL, 0))
+ return REG_ESPACE;
+ if (elem1 == elem2)
+ {
+ set->nelem = 1;
+ set->elems[0] = elem1;
+ }
+ else
+ {
+ set->nelem = 2;
+ if (elem1 < elem2)
+ {
+ set->elems[0] = elem1;
+ set->elems[1] = elem2;
+ }
+ else
+ {
+ set->elems[0] = elem2;
+ set->elems[1] = elem1;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_copy (dest, src)
+ re_node_set *dest;
+ const re_node_set *src;
+{
+ dest->nelem = src->nelem;
+ if (src->nelem > 0)
+ {
+ dest->alloc = dest->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ {
+ dest->alloc = dest->nelem = 0;
+ return REG_ESPACE;
+ }
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+ }
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+ Note: We assume dest->elems is NULL, when dest->alloc is 0. */
+
+static reg_errcode_t
+re_node_set_add_intersect (dest, src1, src2)
+ re_node_set *dest;
+ const re_node_set *src1, *src2;
+{
+ int i1, i2, is, id, delta, sbase;
+ if (src1->nelem == 0 || src2->nelem == 0)
+ return REG_NOERROR;
+
+ /* We need dest->nelem + 2 * elems_in_intersection; this is a
+ conservative estimate. */
+ if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+ {
+ int new_alloc = src1->nelem + src2->nelem + dest->alloc;
+ int *new_elems = re_realloc (dest->elems, int, new_alloc);
+ if (BE (new_elems == NULL, 0))
+ return REG_ESPACE;
+ dest->elems = new_elems;
+ dest->alloc = new_alloc;
+ }
+
+ /* Find the items in the intersection of SRC1 and SRC2, and copy
+ into the top of DEST those that are not already in DEST itself. */
+ sbase = dest->nelem + src1->nelem + src2->nelem;
+ i1 = src1->nelem - 1;
+ i2 = src2->nelem - 1;
+ id = dest->nelem - 1;
+ for (;;)
+ {
+ if (src1->elems[i1] == src2->elems[i2])
+ {
+ /* Try to find the item in DEST. Maybe we could binary search? */
+ while (id >= 0 && dest->elems[id] > src1->elems[i1])
+ --id;
+
+ if (id < 0 || dest->elems[id] != src1->elems[i1])
+ dest->elems[--sbase] = src1->elems[i1];
+
+ if (--i1 < 0 || --i2 < 0)
+ break;
+ }
+
+ /* Lower the highest of the two items. */
+ else if (src1->elems[i1] < src2->elems[i2])
+ {
+ if (--i2 < 0)
+ break;
+ }
+ else
+ {
+ if (--i1 < 0)
+ break;
+ }
+ }
+
+ id = dest->nelem - 1;
+ is = dest->nelem + src1->nelem + src2->nelem - 1;
+ delta = is - sbase + 1;
+
+ /* Now copy. When DELTA becomes zero, the remaining
+ DEST elements are already in place; this is more or
+ less the same loop that is in re_node_set_merge. */
+ dest->nelem += delta;
+ if (delta > 0 && id >= 0)
+ for (;;)
+ {
+ if (dest->elems[is] > dest->elems[id])
+ {
+ /* Copy from the top. */
+ dest->elems[id + delta--] = dest->elems[is--];
+ if (delta == 0)
+ break;
+ }
+ else
+ {
+ /* Slide from the bottom. */
+ dest->elems[id + delta] = dest->elems[id];
+ if (--id < 0)
+ break;
+ }
+ }
+
+ /* Copy remaining SRC elements. */
+ memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int));
+
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+re_node_set_init_union (dest, src1, src2)
+ re_node_set *dest;
+ const re_node_set *src1, *src2;
+{
+ int i1, i2, id;
+ if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+ {
+ dest->alloc = src1->nelem + src2->nelem;
+ dest->elems = re_malloc (int, dest->alloc);
+ if (BE (dest->elems == NULL, 0))
+ return REG_ESPACE;
+ }
+ else
+ {
+ if (src1 != NULL && src1->nelem > 0)
+ return re_node_set_init_copy (dest, src1);
+ else if (src2 != NULL && src2->nelem > 0)
+ return re_node_set_init_copy (dest, src2);
+ else
+ re_node_set_init_empty (dest);
+ return REG_NOERROR;
+ }
+ for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+ {
+ if (src1->elems[i1] > src2->elems[i2])
+ {
+ dest->elems[id++] = src2->elems[i2++];
+ continue;
+ }
+ if (src1->elems[i1] == src2->elems[i2])
+ ++i2;
+ dest->elems[id++] = src1->elems[i1++];
+ }
+ if (i1 < src1->nelem)
+ {
+ memcpy (dest->elems + id, src1->elems + i1,
+ (src1->nelem - i1) * sizeof (int));
+ id += src1->nelem - i1;
+ }
+ else if (i2 < src2->nelem)
+ {
+ memcpy (dest->elems + id, src2->elems + i2,
+ (src2->nelem - i2) * sizeof (int));
+ id += src2->nelem - i2;
+ }
+ dest->nelem = id;
+ return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+ DEST. Return value indicate the error code or REG_NOERROR if succeeded. */
+
+static reg_errcode_t
+re_node_set_merge (dest, src)
+ re_node_set *dest;
+ const re_node_set *src;
+{
+ int is, id, sbase, delta;
+ if (src == NULL || src->nelem == 0)
+ return REG_NOERROR;
+ if (dest->alloc < 2 * src->nelem + dest->nelem)
+ {
+ int new_alloc = 2 * (src->nelem + dest->alloc);
+ int *new_buffer = re_realloc (dest->elems, int, new_alloc);
+ if (BE (new_buffer == NULL, 0))
+ return REG_ESPACE;
+ dest->elems = new_buffer;
+ dest->alloc = new_alloc;
+ }
+
+ if (BE (dest->nelem == 0, 0))
+ {
+ dest->nelem = src->nelem;
+ memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+ return REG_NOERROR;
+ }
+
+ /* Copy into the top of DEST the items of SRC that are not
+ found in DEST. Maybe we could binary search in DEST? */
+ for (sbase = dest->nelem + 2 * src->nelem,
+ is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
+ {
+ if (dest->elems[id] == src->elems[is])
+ is--, id--;
+ else if (dest->elems[id] < src->elems[is])
+ dest->elems[--sbase] = src->elems[is--];
+ else /* if (dest->elems[id] > src->elems[is]) */
+ --id;
+ }
+
+ if (is >= 0)
+ {
+ /* If DEST is exhausted, the remaining items of SRC must be unique. */
+ sbase -= is + 1;
+ memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int));
+ }
+
+ id = dest->nelem - 1;
+ is = dest->nelem + 2 * src->nelem - 1;
+ delta = is - sbase + 1;
+ if (delta == 0)
+ return REG_NOERROR;
+
+ /* Now copy. When DELTA becomes zero, the remaining
+ DEST elements are already in place. */
+ dest->nelem += delta;
+ for (;;)
+ {
+ if (dest->elems[is] > dest->elems[id])
+ {
+ /* Copy from the top. */
+ dest->elems[id + delta--] = dest->elems[is--];
+ if (delta == 0)
+ break;
+ }
+ else
+ {
+ /* Slide from the bottom. */
+ dest->elems[id + delta] = dest->elems[id];
+ if (--id < 0)
+ {
+ /* Copy remaining SRC elements. */
+ memcpy (dest->elems, dest->elems + sbase,
+ delta * sizeof (int));
+ break;
+ }
+ }
+ }
+
+ return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ SET should not already have ELEM.
+ return -1 if an error is occured, return 1 otherwise. */
+
+static int
+re_node_set_insert (set, elem)
+ re_node_set *set;
+ int elem;
+{
+ int idx;
+ /* In case the set is empty. */
+ if (set->alloc == 0)
+ {
+ if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
+ return 1;
+ else
+ return -1;
+ }
+
+ if (BE (set->nelem, 0) == 0)
+ {
+ /* We already guaranteed above that set->alloc != 0. */
+ set->elems[0] = elem;
+ ++set->nelem;
+ return 1;
+ }
+
+ /* Realloc if we need. */
+ if (set->alloc == set->nelem)
+ {
+ int *new_array;
+ set->alloc = set->alloc * 2;
+ new_array = re_realloc (set->elems, int, set->alloc);
+ if (BE (new_array == NULL, 0))
+ return -1;
+ set->elems = new_array;
+ }
+
+ /* Move the elements which follows the new element. Test the
+ first element separately to skip a check in the inner loop. */
+ if (elem < set->elems[0])
+ {
+ idx = 0;
+ for (idx = set->nelem; idx > 0; idx--)
+ set->elems[idx] = set->elems[idx - 1];
+ }
+ else
+ {
+ for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
+ set->elems[idx] = set->elems[idx - 1];
+ }
+
+ /* Insert the new element. */
+ set->elems[idx] = elem;
+ ++set->nelem;
+ return 1;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+ SET should not already have any element greater than or equal to ELEM.
+ Return -1 if an error is occured, return 1 otherwise. */
+
+static int
+re_node_set_insert_last (set, elem)
+ re_node_set *set;
+ int elem;
+{
+ /* Realloc if we need. */
+ if (set->alloc == set->nelem)
+ {
+ int *new_array;
+ set->alloc = (set->alloc + 1) * 2;
+ new_array = re_realloc (set->elems, int, set->alloc);
+ if (BE (new_array == NULL, 0))
+ return -1;
+ set->elems = new_array;
+ }
+
+ /* Insert the new element. */
+ set->elems[set->nelem++] = elem;
+ return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+ return 1 if SET1 and SET2 are equivalent, return 0 otherwise. */
+
+static int
+re_node_set_compare (set1, set2)
+ const re_node_set *set1, *set2;
+{
+ int i;
+ if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+ return 0;
+ for (i = set1->nelem ; --i >= 0 ; )
+ if (set1->elems[i] != set2->elems[i])
+ return 0;
+ return 1;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */
+
+static int
+re_node_set_contains (set, elem)
+ const re_node_set *set;
+ int elem;
+{
+ unsigned int idx, right, mid;
+ if (set->nelem <= 0)
+ return 0;
+
+ /* Binary search the element. */
+ idx = 0;
+ right = set->nelem - 1;
+ while (idx < right)
+ {
+ mid = (idx + right) / 2;
+ if (set->elems[mid] < elem)
+ idx = mid + 1;
+ else
+ right = mid;
+ }
+ return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+re_node_set_remove_at (set, idx)
+ re_node_set *set;
+ int idx;
+{
+ if (idx < 0 || idx >= set->nelem)
+ return;
+ --set->nelem;
+ for (; idx < set->nelem; idx++)
+ set->elems[idx] = set->elems[idx + 1];
+}
+
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+ Or return -1, if an error will be occured. */
+
+static int
+re_dfa_add_node (dfa, token, mode)
+ re_dfa_t *dfa;
+ re_token_t token;
+ int mode;
+{
+ if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
+ {
+ int new_nodes_alloc = dfa->nodes_alloc * 2;
+ re_token_t *new_array = re_realloc (dfa->nodes, re_token_t,
+ new_nodes_alloc);
+ if (BE (new_array == NULL, 0))
+ return -1;
+ dfa->nodes = new_array;
+ if (mode)
+ {
+ int *new_nexts, *new_indices;
+ re_node_set *new_edests, *new_eclosures, *new_inveclosures;
+
+ new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc);
+ new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc);
+ new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+ new_eclosures = re_realloc (dfa->eclosures, re_node_set,
+ new_nodes_alloc);
+ new_inveclosures = re_realloc (dfa->inveclosures, re_node_set,
+ new_nodes_alloc);
+ if (BE (new_nexts == NULL || new_indices == NULL
+ || new_edests == NULL || new_eclosures == NULL
+ || new_inveclosures == NULL, 0))
+ return -1;
+ dfa->nexts = new_nexts;
+ dfa->org_indices = new_indices;
+ dfa->edests = new_edests;
+ dfa->eclosures = new_eclosures;
+ dfa->inveclosures = new_inveclosures;
+ }
+ dfa->nodes_alloc = new_nodes_alloc;
+ }
+ dfa->nodes[dfa->nodes_len] = token;
+ dfa->nodes[dfa->nodes_len].opt_subexp = 0;
+ dfa->nodes[dfa->nodes_len].duplicated = 0;
+ dfa->nodes[dfa->nodes_len].constraint = 0;
+ return dfa->nodes_len++;
+}
+
+static unsigned int inline
+calc_state_hash (nodes, context)
+ const re_node_set *nodes;
+ unsigned int context;
+{
+ unsigned int hash = nodes->nelem + context;
+ int i;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ hash += nodes->elems[i];
+ return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t*
+re_acquire_state (err, dfa, nodes)
+ reg_errcode_t *err;
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+{
+ unsigned int hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ int i;
+ if (BE (nodes->nelem == 0, 0))
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, 0);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (hash != state->hash)
+ continue;
+ if (re_node_set_compare (&state->nodes, nodes))
+ return state;
+ }
+
+ /* There are no appropriate state in the dfa, create the new one. */
+ new_state = create_ci_newstate (dfa, nodes, hash);
+ if (BE (new_state != NULL, 1))
+ return new_state;
+ else
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+ whose context is equivalent to CONTEXT.
+ Return the pointer to the state, if we found it in the DFA.
+ Otherwise create the new one and return it. In case of an error
+ return NULL and set the error code in ERR.
+ Note: - We assume NULL as the invalid state, then it is possible that
+ return value is NULL and ERR is REG_NOERROR.
+ - We never return non-NULL value in case of any errors, it is for
+ optimization. */
+
+static re_dfastate_t*
+re_acquire_state_context (err, dfa, nodes, context)
+ reg_errcode_t *err;
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int context;
+{
+ unsigned int hash;
+ re_dfastate_t *new_state;
+ struct re_state_table_entry *spot;
+ int i;
+ if (nodes->nelem == 0)
+ {
+ *err = REG_NOERROR;
+ return NULL;
+ }
+ hash = calc_state_hash (nodes, context);
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+ for (i = 0 ; i < spot->num ; i++)
+ {
+ re_dfastate_t *state = spot->array[i];
+ if (state->hash == hash
+ && state->context == context
+ && re_node_set_compare (state->entrance_nodes, nodes))
+ return state;
+ }
+ /* There are no appropriate state in `dfa', create the new one. */
+ new_state = create_cd_newstate (dfa, nodes, context, hash);
+ if (BE (new_state != NULL, 1))
+ return new_state;
+ else
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+}
+
+/* Finish initialization of the new state NEWSTATE, and using its hash value
+ HASH put in the appropriate bucket of DFA's state table. Return value
+ indicates the error code if failed. */
+
+static reg_errcode_t
+register_state (dfa, newstate, hash)
+ re_dfa_t *dfa;
+ re_dfastate_t *newstate;
+ unsigned int hash;
+{
+ struct re_state_table_entry *spot;
+ reg_errcode_t err;
+ int i;
+
+ newstate->hash = hash;
+ err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ESPACE;
+ for (i = 0; i < newstate->nodes.nelem; i++)
+ {
+ int elem = newstate->nodes.elems[i];
+ if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
+ re_node_set_insert_last (&newstate->non_eps_nodes, elem);
+ }
+
+ spot = dfa->state_table + (hash & dfa->state_hash_mask);
+ if (BE (spot->alloc <= spot->num, 0))
+ {
+ int new_alloc = 2 * spot->num + 2;
+ re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
+ new_alloc);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ spot->array = new_array;
+ spot->alloc = new_alloc;
+ }
+ spot->array[spot->num++] = newstate;
+ return REG_NOERROR;
+}
+
+/* Create the new state which is independ of contexts.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+create_ci_newstate (dfa, nodes, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int hash;
+{
+ int i;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ err = re_node_set_init_copy (&newstate->nodes, nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (newstate);
+ return NULL;
+ }
+
+ newstate->entrance_nodes = &newstate->nodes;
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (type == CHARACTER && !node->constraint)
+ continue;
+
+ /* If the state has the halt node, the state is a halt state. */
+ else if (type == END_OF_RE)
+ newstate->halt = 1;
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET
+ || type == OP_UTF8_PERIOD
+ || (type == OP_PERIOD && dfa->mb_cur_max > 1))
+ newstate->accept_mb = 1;
+#endif /* RE_ENABLE_I18N */
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR || node->constraint)
+ newstate->has_constraint = 1;
+ }
+ err = register_state (dfa, newstate, hash);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_state (newstate);
+ newstate = NULL;
+ }
+ return newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+ Return the new state if succeeded, otherwise return NULL. */
+
+static re_dfastate_t *
+create_cd_newstate (dfa, nodes, context, hash)
+ re_dfa_t *dfa;
+ const re_node_set *nodes;
+ unsigned int context, hash;
+{
+ int i, nctx_nodes = 0;
+ reg_errcode_t err;
+ re_dfastate_t *newstate;
+
+ newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+ if (BE (newstate == NULL, 0))
+ return NULL;
+ err = re_node_set_init_copy (&newstate->nodes, nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_free (newstate);
+ return NULL;
+ }
+
+ newstate->context = context;
+ newstate->entrance_nodes = &newstate->nodes;
+
+ for (i = 0 ; i < nodes->nelem ; i++)
+ {
+ unsigned int constraint = 0;
+ re_token_t *node = dfa->nodes + nodes->elems[i];
+ re_token_type_t type = node->type;
+ if (node->constraint)
+ constraint = node->constraint;
+
+ if (type == CHARACTER && !constraint)
+ continue;
+ /* If the state has the halt node, the state is a halt state. */
+ else if (type == END_OF_RE)
+ newstate->halt = 1;
+#ifdef RE_ENABLE_I18N
+ else if (type == COMPLEX_BRACKET
+ || type == OP_UTF8_PERIOD
+ || (type == OP_PERIOD && dfa->mb_cur_max > 1))
+ newstate->accept_mb = 1;
+#endif /* RE_ENABLE_I18N */
+ else if (type == OP_BACK_REF)
+ newstate->has_backref = 1;
+ else if (type == ANCHOR)
+ constraint = node->opr.ctx_type;
+
+ if (constraint)
+ {
+ if (newstate->entrance_nodes == &newstate->nodes)
+ {
+ newstate->entrance_nodes = re_malloc (re_node_set, 1);
+ if (BE (newstate->entrance_nodes == NULL, 0))
+ {
+ free_state (newstate);
+ return NULL;
+ }
+ re_node_set_init_copy (newstate->entrance_nodes, nodes);
+ nctx_nodes = 0;
+ newstate->has_constraint = 1;
+ }
+
+ if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+ {
+ re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+ ++nctx_nodes;
+ }
+ }
+ }
+ err = register_state (dfa, newstate, hash);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ free_state (newstate);
+ newstate = NULL;
+ }
+ return newstate;
+}
+
+static void
+free_state (state)
+ re_dfastate_t *state;
+{
+ re_node_set_free (&state->non_eps_nodes);
+ re_node_set_free (&state->inveclosure);
+ if (state->entrance_nodes != &state->nodes)
+ {
+ re_node_set_free (state->entrance_nodes);
+ re_free (state->entrance_nodes);
+ }
+ re_node_set_free (&state->nodes);
+ re_free (state->trtable);
+ re_free (state);
+}
diff --git a/src/regex/regex_internal.h b/src/regex/regex_internal.h
new file mode 100644
index 0000000..18865a7
--- /dev/null
+++ b/src/regex/regex_internal.h
@@ -0,0 +1,801 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
+# include <langinfo.h>
+#endif
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+
+/* In case that the system doesn't have isblank(). */
+#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+# define _RE_DEFINE_LOCALE_FUNCTIONS 1
+# include <locale/localeinfo.h>
+# include <locale/elem-hash.h>
+# include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages. */
+#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+# undef gettext
+# define gettext(msgid) \
+ INTUSE(__dcgettext) (INTUSE(_libc_intl_domainname), msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+ strings. */
+# define gettext_noop(String) String
+#endif
+
+#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC
+# define RE_ENABLE_I18N
+#endif
+
+#if __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# define inline
+#endif
+
+/* Number of bits in a byte. */
+#define BYTE_BITS 8
+/* Number of single byte character. */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline. */
+#define NEWLINE_CHAR '\n'
+#define WIDE_NEWLINE_CHAR L'\n'
+
+/* Rename to standard API for using out of glibc. */
+#ifndef _LIBC
+# define __wctype wctype
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mempcpy mempcpy
+# define __wcrtomb wcrtomb
+# define __regfree regfree
+# define attribute_hidden
+#endif /* not _LIBC */
+
+#ifdef __GNUC__
+# define __attribute(arg) __attribute__ (arg)
+#else
+# define __attribute(arg)
+#endif
+
+extern const char __re_error_msgid[] attribute_hidden;
+extern const size_t __re_error_msgid_idx[] attribute_hidden;
+
+/* Number of bits in an unsinged int. */
+#define UINT_BITS (sizeof (unsigned int) * BYTE_BITS)
+/* Number of unsigned int in an bit_set. */
+#define BITSET_UINTS ((SBC_MAX + UINT_BITS - 1) / UINT_BITS)
+typedef unsigned int bitset[BITSET_UINTS];
+typedef unsigned int *re_bitset_ptr_t;
+typedef const unsigned int *re_const_bitset_ptr_t;
+
+#define bitset_set(set,i) (set[i / UINT_BITS] |= 1 << i % UINT_BITS)
+#define bitset_clear(set,i) (set[i / UINT_BITS] &= ~(1 << i % UINT_BITS))
+#define bitset_contain(set,i) (set[i / UINT_BITS] & (1 << i % UINT_BITS))
+#define bitset_empty(set) memset (set, 0, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_set_all(set) \
+ memset (set, 255, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_copy(dest,src) \
+ memcpy (dest, src, sizeof (unsigned int) * BITSET_UINTS)
+static inline void bitset_not (bitset set);
+static inline void bitset_merge (bitset dest, const bitset src);
+static inline void bitset_not_merge (bitset dest, const bitset src);
+static inline void bitset_mask (bitset dest, const bitset src);
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define WORD_DELIM_CONSTRAINT 0x0100
+#define NOT_WORD_DELIM_CONSTRAINT 0x0200
+
+typedef enum
+{
+ INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+ WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+ LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+ LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+ BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+ BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+ WORD_DELIM = WORD_DELIM_CONSTRAINT,
+ NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+ int alloc;
+ int nelem;
+ int *elems;
+} re_node_set;
+
+typedef enum
+{
+ NON_TYPE = 0,
+
+ /* Node type, These are used by token, node, tree. */
+ CHARACTER = 1,
+ END_OF_RE = 2,
+ SIMPLE_BRACKET = 3,
+ OP_BACK_REF = 4,
+ OP_PERIOD = 5,
+#ifdef RE_ENABLE_I18N
+ COMPLEX_BRACKET = 6,
+ OP_UTF8_PERIOD = 7,
+#endif /* RE_ENABLE_I18N */
+
+ /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
+ when the debugger shows values of this enum type. */
+#define EPSILON_BIT 8
+ OP_OPEN_SUBEXP = EPSILON_BIT | 0,
+ OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
+ OP_ALT = EPSILON_BIT | 2,
+ OP_DUP_ASTERISK = EPSILON_BIT | 3,
+ OP_DUP_PLUS = EPSILON_BIT | 4,
+ OP_DUP_QUESTION = EPSILON_BIT | 5,
+ ANCHOR = EPSILON_BIT | 6,
+ OP_DELETED_SUBEXP = EPSILON_BIT | 7,
+
+ /* Tree type, these are used only by tree. */
+ CONCAT = 16,
+
+ /* Token type, these are used only by token. */
+ OP_OPEN_BRACKET = 17,
+ OP_CLOSE_BRACKET,
+ OP_CHARSET_RANGE,
+ OP_OPEN_DUP_NUM,
+ OP_CLOSE_DUP_NUM,
+ OP_NON_MATCH_LIST,
+ OP_OPEN_COLL_ELEM,
+ OP_CLOSE_COLL_ELEM,
+ OP_OPEN_EQUIV_CLASS,
+ OP_CLOSE_EQUIV_CLASS,
+ OP_OPEN_CHAR_CLASS,
+ OP_CLOSE_CHAR_CLASS,
+ OP_WORD,
+ OP_NOTWORD,
+ OP_SPACE,
+ OP_NOTSPACE,
+ BACK_SLASH
+
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+ /* Multibyte characters. */
+ wchar_t *mbchars;
+
+ /* Collating symbols. */
+# ifdef _LIBC
+ int32_t *coll_syms;
+# endif
+
+ /* Equivalence classes. */
+# ifdef _LIBC
+ int32_t *equiv_classes;
+# endif
+
+ /* Range expressions. */
+# ifdef _LIBC
+ uint32_t *range_starts;
+ uint32_t *range_ends;
+# else /* not _LIBC */
+ wchar_t *range_starts;
+ wchar_t *range_ends;
+# endif /* not _LIBC */
+
+ /* Character classes. */
+ wctype_t *char_classes;
+
+ /* If this character set is the non-matching list. */
+ unsigned int non_match : 1;
+
+ /* # of multibyte characters. */
+ int nmbchars;
+
+ /* # of collating symbols. */
+ int ncoll_syms;
+
+ /* # of equivalence classes. */
+ int nequiv_classes;
+
+ /* # of range expressions. */
+ int nranges;
+
+ /* # of character classes. */
+ int nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+ union
+ {
+ unsigned char c; /* for CHARACTER */
+ re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+ re_charset_t *mbcset; /* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+ int idx; /* for BACK_REF */
+ re_context_type ctx_type; /* for ANCHOR */
+ } opr;
+#if __GNUC__ >= 2
+ re_token_type_t type : 8;
+#else
+ re_token_type_t type;
+#endif
+ unsigned int constraint : 10; /* context constraint */
+ unsigned int duplicated : 1;
+ unsigned int opt_subexp : 1;
+#ifdef RE_ENABLE_I18N
+ /* These 2 bits can be moved into the union if needed (e.g. if running out
+ of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */
+ unsigned int mb_partial : 1;
+#endif
+ unsigned int word_char : 1;
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
+#define ACCEPT_MB_NODE(type) \
+ ((type) >= OP_PERIOD && (type) <= OP_UTF8_PERIOD)
+
+struct re_string_t
+{
+ /* Indicate the raw buffer which is the original string passed as an
+ argument of regexec(), re_search(), etc.. */
+ const unsigned char *raw_mbs;
+ /* Store the multibyte string. In case of "case insensitive mode" like
+ REG_ICASE, upper cases of the string are stored, otherwise MBS points
+ the same address that RAW_MBS points. */
+ unsigned char *mbs;
+#ifdef RE_ENABLE_I18N
+ /* Store the wide character string which is corresponding to MBS. */
+ wint_t *wcs;
+ int *offsets;
+ mbstate_t cur_state;
+#endif
+ /* Index in RAW_MBS. Each character mbs[i] corresponds to
+ raw_mbs[raw_mbs_idx + i]. */
+ int raw_mbs_idx;
+ /* The length of the valid characters in the buffers. */
+ int valid_len;
+ /* The corresponding number of bytes in raw_mbs array. */
+ int valid_raw_len;
+ /* The length of the buffers MBS and WCS. */
+ int bufs_len;
+ /* The index in MBS, which is updated by re_string_fetch_byte. */
+ int cur_idx;
+ /* length of RAW_MBS array. */
+ int raw_len;
+ /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */
+ int len;
+ /* End of the buffer may be shorter than its length in the cases such
+ as re_match_2, re_search_2. Then, we use STOP for end of the buffer
+ instead of LEN. */
+ int raw_stop;
+ /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */
+ int stop;
+
+ /* The context of mbs[0]. We store the context independently, since
+ the context of mbs[0] may be different from raw_mbs[0], which is
+ the beginning of the input string. */
+ unsigned int tip_context;
+ /* The translation passed as a part of an argument of re_compile_pattern. */
+ unsigned RE_TRANSLATE_TYPE trans;
+ /* Copy of re_dfa_t's word_char. */
+ re_const_bitset_ptr_t word_char;
+ /* 1 if REG_ICASE. */
+ unsigned char icase;
+ unsigned char is_utf8;
+ unsigned char map_notascii;
+ unsigned char mbs_allocated;
+ unsigned char offsets_needed;
+ unsigned char newline_anchor;
+ unsigned char word_ops_used;
+ int mb_cur_max;
+};
+typedef struct re_string_t re_string_t;
+
+
+struct re_dfa_t;
+typedef struct re_dfa_t re_dfa_t;
+
+#ifndef _LIBC
+# ifdef __i386__
+# define internal_function __attribute ((regparm (3), stdcall))
+# else
+# define internal_function
+# endif
+#endif
+
+#ifndef RE_NO_INTERNAL_PROTOTYPES
+static reg_errcode_t re_string_allocate (re_string_t *pstr, const char *str,
+ int len, int init_len,
+ RE_TRANSLATE_TYPE trans, int icase,
+ const re_dfa_t *dfa)
+ internal_function;
+static reg_errcode_t re_string_construct (re_string_t *pstr, const char *str,
+ int len, RE_TRANSLATE_TYPE trans,
+ int icase, const re_dfa_t *dfa)
+ internal_function;
+static reg_errcode_t re_string_reconstruct (re_string_t *pstr, int idx,
+ int eflags) internal_function;
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+ int new_buf_len)
+ internal_function;
+# ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr) internal_function;
+static int build_wcs_upper_buffer (re_string_t *pstr) internal_function;
+# endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr) internal_function;
+static void re_string_translate_buffer (re_string_t *pstr) internal_function;
+static void re_string_destruct (re_string_t *pstr) internal_function;
+# ifdef RE_ENABLE_I18N
+static int re_string_elem_size_at (const re_string_t *pstr, int idx)
+ internal_function __attribute ((pure));
+static inline int re_string_char_size_at (const re_string_t *pstr, int idx)
+ internal_function __attribute ((pure));
+static inline wint_t re_string_wchar_at (const re_string_t *pstr, int idx)
+ internal_function __attribute ((pure));
+# endif /* RE_ENABLE_I18N */
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+ int eflags)
+ internal_function __attribute ((pure));
+static unsigned char re_string_peek_byte_case (const re_string_t *pstr,
+ int idx)
+ internal_function __attribute ((pure));
+static unsigned char re_string_fetch_byte_case (re_string_t *pstr)
+ internal_function __attribute ((pure));
+#endif
+#define re_string_peek_byte(pstr, offset) \
+ ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+ ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+ ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+ ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
+ || (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+ struct bin_tree_t *parent;
+ struct bin_tree_t *left;
+ struct bin_tree_t *right;
+
+ /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+ Otherwise `type' indicate the type of this node. */
+ re_token_type_t type;
+ int node_idx;
+
+ int first;
+ int next;
+ re_node_set eclosure;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+#define BIN_TREE_STORAGE_SIZE \
+ ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
+
+struct bin_tree_storage_t
+{
+ struct bin_tree_storage_t *next;
+ bin_tree_t data[BIN_TREE_STORAGE_SIZE];
+};
+typedef struct bin_tree_storage_t bin_tree_storage_t;
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+ || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+ || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+ || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+ unsigned int hash;
+ re_node_set nodes;
+ re_node_set non_eps_nodes;
+ re_node_set inveclosure;
+ re_node_set *entrance_nodes;
+ struct re_dfastate_t **trtable;
+ unsigned int context : 4;
+ unsigned int halt : 1;
+ /* If this state can accept `multi byte'.
+ Note that we refer to multibyte characters, and multi character
+ collating elements as `multi byte'. */
+ unsigned int accept_mb : 1;
+ /* If this state has backreference node(s). */
+ unsigned int has_backref : 1;
+ unsigned int has_constraint : 1;
+ unsigned int word_trtable : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+struct re_state_table_entry
+{
+ int num;
+ int alloc;
+ re_dfastate_t **array;
+};
+
+/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */
+
+typedef struct
+{
+ int next_idx;
+ int alloc;
+ re_dfastate_t **array;
+} state_array_t;
+
+/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */
+
+typedef struct
+{
+ int node;
+ int str_idx; /* The position NODE match at. */
+ state_array_t path;
+} re_sub_match_last_t;
+
+/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
+ And information about the node, whose type is OP_CLOSE_SUBEXP,
+ corresponding to NODE is stored in LASTS. */
+
+typedef struct
+{
+ int str_idx;
+ int node;
+ int next_last_offset;
+ state_array_t *path;
+ int alasts; /* Allocation size of LASTS. */
+ int nlasts; /* The number of LASTS. */
+ re_sub_match_last_t **lasts;
+} re_sub_match_top_t;
+
+struct re_backref_cache_entry
+{
+ int node;
+ int str_idx;
+ int subexp_from;
+ int subexp_to;
+ char more;
+ char unused;
+ unsigned short int eps_reachable_subexps_map;
+};
+
+typedef struct
+{
+ /* The string object corresponding to the input string. */
+ re_string_t input;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+ re_dfa_t *const dfa;
+#else
+ re_dfa_t *dfa;
+#endif
+ /* EFLAGS of the argument of regexec. */
+ int eflags;
+ /* Where the matching ends. */
+ int match_last;
+ int last_node;
+ /* The state log used by the matcher. */
+ re_dfastate_t **state_log;
+ int state_log_top;
+ /* Back reference cache. */
+ int nbkref_ents;
+ int abkref_ents;
+ struct re_backref_cache_entry *bkref_ents;
+ int max_mb_elem_len;
+ int nsub_tops;
+ int asub_tops;
+ re_sub_match_top_t **sub_tops;
+} re_match_context_t;
+
+typedef struct
+{
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **limited_states;
+ int last_node;
+ int last_str_idx;
+ re_node_set limits;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+ int idx;
+ int node;
+ regmatch_t *regs;
+ re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+ int num;
+ int alloc;
+ struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+ re_token_t *nodes;
+ int nodes_alloc;
+ int nodes_len;
+ int *nexts;
+ int *org_indices;
+ re_node_set *edests;
+ re_node_set *eclosures;
+ re_node_set *inveclosures;
+ struct re_state_table_entry *state_table;
+ re_dfastate_t *init_state;
+ re_dfastate_t *init_state_word;
+ re_dfastate_t *init_state_nl;
+ re_dfastate_t *init_state_begbuf;
+ bin_tree_t *str_tree;
+ bin_tree_storage_t *str_tree_storage;
+ re_bitset_ptr_t sb_char;
+ int str_tree_storage_idx;
+
+ /* number of subexpressions `re_nsub' is in regex_t. */
+ unsigned int state_hash_mask;
+ int states_alloc;
+ int init_node;
+ int nbackref; /* The number of backreference in this dfa. */
+
+ /* Bitmap expressing which backreference is used. */
+ unsigned int used_bkref_map;
+ unsigned int completed_bkref_map;
+
+ unsigned int has_plural_match : 1;
+ /* If this dfa has "multibyte node", which is a backreference or
+ a node which can accept multibyte character or multi character
+ collating element. */
+ unsigned int has_mb_node : 1;
+ unsigned int is_utf8 : 1;
+ unsigned int map_notascii : 1;
+ unsigned int word_ops_used : 1;
+ int mb_cur_max;
+ bitset word_char;
+ reg_syntax_t syntax;
+ int *subexp_map;
+#ifdef DEBUG
+ char* re_str;
+#endif
+};
+
+#ifndef RE_NO_INTERNAL_PROTOTYPES
+static reg_errcode_t re_node_set_alloc (re_node_set *set, int size) internal_function;
+static reg_errcode_t re_node_set_init_1 (re_node_set *set, int elem) internal_function;
+static reg_errcode_t re_node_set_init_2 (re_node_set *set, int elem1,
+ int elem2) internal_function;
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+static reg_errcode_t re_node_set_init_copy (re_node_set *dest,
+ const re_node_set *src) internal_function;
+static reg_errcode_t re_node_set_add_intersect (re_node_set *dest,
+ const re_node_set *src1,
+ const re_node_set *src2) internal_function;
+static reg_errcode_t re_node_set_init_union (re_node_set *dest,
+ const re_node_set *src1,
+ const re_node_set *src2) internal_function;
+static reg_errcode_t re_node_set_merge (re_node_set *dest,
+ const re_node_set *src) internal_function;
+static int re_node_set_insert (re_node_set *set, int elem) internal_function;
+static int re_node_set_insert_last (re_node_set *set,
+ int elem) internal_function;
+static int re_node_set_compare (const re_node_set *set1,
+ const re_node_set *set2)
+ internal_function __attribute ((pure));
+static int re_node_set_contains (const re_node_set *set, int elem)
+ internal_function __attribute ((pure));
+static void re_node_set_remove_at (re_node_set *set, int idx) internal_function;
+#define re_node_set_remove(set,id) \
+ (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+static int re_dfa_add_node (re_dfa_t *dfa, re_token_t token, int mode) internal_function;
+static re_dfastate_t *re_acquire_state (reg_errcode_t *err, re_dfa_t *dfa,
+ const re_node_set *nodes) internal_function;
+static re_dfastate_t *re_acquire_state_context (reg_errcode_t *err,
+ re_dfa_t *dfa,
+ const re_node_set *nodes,
+ unsigned int context) internal_function;
+static void free_state (re_dfastate_t *state) internal_function;
+#endif
+
+
+typedef enum
+{
+ SB_CHAR,
+ MB_CHAR,
+ EQUIV_CLASS,
+ COLL_SYM,
+ CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+ bracket_elem_type type;
+ union
+ {
+ unsigned char ch;
+ unsigned char *name;
+ wchar_t wch;
+ } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation. */
+static inline void
+bitset_not (bitset set)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+ set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (bitset dest, const bitset src)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+ dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_not_merge (bitset dest, const bitset src)
+{
+ int i;
+ for (i = 0; i < BITSET_UINTS; ++i)
+ dest[i] |= ~src[i];
+}
+
+static inline void
+bitset_mask (bitset dest, const bitset src)
+{
+ int bitset_i;
+ for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+ dest[bitset_i] &= src[bitset_i];
+}
+
+#if defined RE_ENABLE_I18N && !defined RE_NO_INTERNAL_PROTOTYPES
+/* Inline functions for re_string. */
+static inline int
+internal_function
+re_string_char_size_at (const re_string_t *pstr, int idx)
+{
+ int byte_idx;
+ if (pstr->mb_cur_max == 1)
+ return 1;
+ for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
+ if (pstr->wcs[idx + byte_idx] != WEOF)
+ break;
+ return byte_idx;
+}
+
+static inline wint_t
+internal_function
+re_string_wchar_at (const re_string_t *pstr, int idx)
+{
+ if (pstr->mb_cur_max == 1)
+ return (wint_t) pstr->mbs[idx];
+ return (wint_t) pstr->wcs[idx];
+}
+
+static int
+internal_function
+re_string_elem_size_at (const re_string_t *pstr, int idx)
+{
+#ifdef _LIBC
+ const unsigned char *p, *extra;
+ const int32_t *table, *indirect;
+ int32_t tmp;
+# include <locale/weight.h>
+ uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+ if (nrules != 0)
+ {
+ table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+ _NL_COLLATE_INDIRECTMB);
+ p = pstr->mbs + idx;
+ tmp = findidx (&p);
+ return p - pstr->mbs - idx;
+ }
+ else
+#endif /* _LIBC */
+ return 1;
+}
+#endif /* RE_ENABLE_I18N */
+
+#endif /* _REGEX_INTERNAL_H */
diff --git a/src/regex/regexec.ci b/src/regex/regexec.ci
new file mode 100644
index 0000000..e7461f4
--- /dev/null
+++ b/src/regex/regexec.ci
@@ -0,0 +1,4325 @@
+/* Extended regular expression matching and search library.
+ Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+ int n) internal_function;
+static void match_ctx_clean (re_match_context_t *mctx) internal_function;
+static void match_ctx_free (re_match_context_t *cache) internal_function;
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
+ int str_idx, int from, int to)
+ internal_function;
+static int search_cur_bkref_entry (re_match_context_t *mctx, int str_idx)
+ internal_function;
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
+ int str_idx) internal_function;
+static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
+ int node, int str_idx)
+ internal_function;
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+ re_dfastate_t **limited_sts, int last_node,
+ int last_str_idx)
+ internal_function;
+static reg_errcode_t re_search_internal (const regex_t *preg,
+ const char *string, int length,
+ int start, int range, int stop,
+ size_t nmatch, regmatch_t pmatch[],
+ int eflags) internal_function;
+static int re_search_2_stub (struct re_pattern_buffer *bufp,
+ const char *string1, int length1,
+ const char *string2, int length2,
+ int start, int range, struct re_registers *regs,
+ int stop, int ret_len) internal_function;
+static int re_search_stub (struct re_pattern_buffer *bufp,
+ const char *string, int length, int start,
+ int range, int stop, struct re_registers *regs,
+ int ret_len) internal_function;
+static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+ int nregs, int regs_allocated) internal_function;
+static inline re_dfastate_t *acquire_init_state_context
+ (reg_errcode_t *err, const re_match_context_t *mctx, int idx)
+ __attribute ((always_inline)) internal_function;
+static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx)
+ internal_function;
+static int check_matching (re_match_context_t *mctx, int fl_longest_match,
+ int *p_match_first)
+ internal_function;
+static int check_halt_node_context (const re_dfa_t *dfa, int node,
+ unsigned int context) internal_function;
+static int check_halt_state_context (const re_match_context_t *mctx,
+ const re_dfastate_t *state, int idx)
+ internal_function;
+static void update_regs (re_dfa_t *dfa, regmatch_t *pmatch,
+ regmatch_t *prev_idx_match, int cur_node,
+ int cur_idx, int nmatch) internal_function;
+static int proceed_next_node (const re_match_context_t *mctx,
+ int nregs, regmatch_t *regs,
+ int *pidx, int node, re_node_set *eps_via_nodes,
+ struct re_fail_stack_t *fs) internal_function;
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+ int str_idx, int dest_node, int nregs,
+ regmatch_t *regs,
+ re_node_set *eps_via_nodes) internal_function;
+static int pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+ regmatch_t *regs, re_node_set *eps_via_nodes) internal_function;
+static reg_errcode_t set_regs (const regex_t *preg,
+ const re_match_context_t *mctx,
+ size_t nmatch, regmatch_t *pmatch,
+ int fl_backtrack) internal_function;
+static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) internal_function;
+
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int node_idx, int str_idx, int max_str_idx) internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (re_match_context_t *mctx,
+ re_sift_context_t *sctx) internal_function;
+static reg_errcode_t build_sifted_states (re_match_context_t *mctx,
+ re_sift_context_t *sctx, int str_idx,
+ re_node_set *cur_dest) internal_function;
+static reg_errcode_t update_cur_sifted_state (re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int str_idx,
+ re_node_set *dest_nodes) internal_function;
+static reg_errcode_t add_epsilon_src_nodes (re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates) internal_function;
+static reg_errcode_t sub_epsilon_src_nodes (re_dfa_t *dfa, int node,
+ re_node_set *dest_nodes,
+ const re_node_set *and_nodes) internal_function;
+static int check_dst_limits (re_match_context_t *mctx, re_node_set *limits,
+ int dst_node, int dst_idx, int src_node,
+ int src_idx) internal_function;
+static int check_dst_limits_calc_pos_1 (re_match_context_t *mctx,
+ int boundaries, int subexp_idx,
+ int from_node, int bkref_idx) internal_function;
+static int check_dst_limits_calc_pos (re_match_context_t *mctx,
+ int limit, int subexp_idx,
+ int node, int str_idx,
+ int bkref_idx) internal_function;
+static reg_errcode_t check_subexp_limits (re_dfa_t *dfa,
+ re_node_set *dest_nodes,
+ const re_node_set *candidates,
+ re_node_set *limits,
+ struct re_backref_cache_entry *bkref_ents,
+ int str_idx) internal_function;
+static reg_errcode_t sift_states_bkref (re_match_context_t *mctx,
+ re_sift_context_t *sctx,
+ int str_idx, const re_node_set *candidates) internal_function;
+static reg_errcode_t clean_state_log_if_needed (re_match_context_t *mctx,
+ int next_state_log_idx) internal_function;
+static reg_errcode_t merge_state_array (re_dfa_t *dfa, re_dfastate_t **dst,
+ re_dfastate_t **src, int num) internal_function;
+static re_dfastate_t *find_recover_state (reg_errcode_t *err,
+ re_match_context_t *mctx) internal_function;
+static re_dfastate_t *transit_state (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *state) internal_function;
+static re_dfastate_t *merge_state_with_log (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *next_state) internal_function;
+static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx,
+ re_node_set *cur_nodes,
+ int str_idx) internal_function;
+#if 0
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err,
+ re_match_context_t *mctx,
+ re_dfastate_t *pstate) internal_function;
+#endif
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
+ re_dfastate_t *pstate) internal_function;
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
+ const re_node_set *nodes) internal_function;
+static reg_errcode_t get_subexp (re_match_context_t *mctx,
+ int bkref_node, int bkref_str_idx) internal_function;
+static reg_errcode_t get_subexp_sub (re_match_context_t *mctx,
+ const re_sub_match_top_t *sub_top,
+ re_sub_match_last_t *sub_last,
+ int bkref_node, int bkref_str) internal_function;
+static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+ int subexp_idx, int type) internal_function;
+static reg_errcode_t check_arrival (re_match_context_t *mctx,
+ state_array_t *path, int top_node,
+ int top_str, int last_node, int last_str,
+ int type) internal_function;
+static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx,
+ int str_idx,
+ re_node_set *cur_nodes,
+ re_node_set *next_nodes) internal_function;
+static reg_errcode_t check_arrival_expand_ecl (re_dfa_t *dfa,
+ re_node_set *cur_nodes,
+ int ex_subexp, int type) internal_function;
+static reg_errcode_t check_arrival_expand_ecl_sub (re_dfa_t *dfa,
+ re_node_set *dst_nodes,
+ int target, int ex_subexp,
+ int type) internal_function;
+static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx,
+ re_node_set *cur_nodes, int cur_str,
+ int subexp_num, int type) internal_function;
+static re_dfastate_t **build_trtable (re_dfa_t *dfa,
+ re_dfastate_t *state) internal_function;
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (re_dfa_t *dfa, int node_idx,
+ const re_string_t *input, int idx) internal_function;
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+ size_t name_len) internal_function;
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static int group_nodes_into_DFAstates (re_dfa_t *dfa,
+ const re_dfastate_t *state,
+ re_node_set *states_node,
+ bitset *states_ch) internal_function;
+static int check_node_accept (const re_match_context_t *mctx,
+ const re_token_t *node, int idx) internal_function;
+static reg_errcode_t extend_buffers (re_match_context_t *mctx) internal_function;
+
+/* Entry point for POSIX code. */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+ string STRING.
+
+ If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+ `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at
+ least NMATCH elements, and we set them to the offsets of the
+ corresponding matched substrings.
+
+ EFLAGS specifies `execution flags' which affect matching: if
+ REG_NOTBOL is set, then ^ does not match at the beginning of the
+ string; if REG_NOTEOL is set, then $ does not match at the end.
+
+ We return 0 if we find a match and REG_NOMATCH if not. */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+ const regex_t *__restrict preg;
+ const char *__restrict string;
+ size_t nmatch;
+ regmatch_t pmatch[];
+ int eflags;
+{
+ reg_errcode_t err;
+ int start, length;
+
+ if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
+ return REG_BADPAT;
+
+ if (eflags & REG_STARTEND)
+ {
+ start = pmatch[0].rm_so;
+ length = pmatch[0].rm_eo;
+ }
+ else
+ {
+ start = 0;
+ length = strlen (string);
+ }
+ if (preg->no_sub)
+ err = re_search_internal (preg, string, length, start, length - start,
+ length, 0, NULL, eflags);
+ else
+ err = re_search_internal (preg, string, length, start, length - start,
+ length, nmatch, pmatch, eflags);
+ return err != REG_NOERROR;
+}
+
+#ifdef _LIBC
+# include <shlib-compat.h>
+versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+__typeof__ (__regexec) __compat_regexec;
+
+int
+attribute_compat_text_section
+__compat_regexec (const regex_t *__restrict preg,
+ const char *__restrict string, size_t nmatch,
+ regmatch_t pmatch[], int eflags)
+{
+ return regexec (preg, string, nmatch, pmatch,
+ eflags & (REG_NOTBOL | REG_NOTEOL));
+}
+compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
+# endif
+#endif
+
+/* Entry points for GNU code. */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+ The former two functions operate on STRING with length LENGTH,
+ while the later two operate on concatenation of STRING1 and STRING2
+ with lengths LENGTH1 and LENGTH2, respectively.
+
+ re_match() matches the compiled pattern in BUFP against the string,
+ starting at index START.
+
+ re_search() first tries matching at index START, then it tries to match
+ starting from index START + 1, and so on. The last start position tried
+ is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same
+ way as re_match().)
+
+ The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+ the first STOP characters of the concatenation of the strings should be
+ concerned.
+
+ If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+ and all groups is stroed in REGS. (For the "_2" variants, the offsets are
+ computed relative to the concatenation, not relative to the individual
+ strings.)
+
+ On success, re_match* functions return the length of the match, re_search*
+ return the position of the start of the match. Return value -1 means no
+ match was found and -2 indicates an internal error. */
+
+int
+re_match (bufp, string, length, start, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int length, start;
+ struct re_registers *regs;
+{
+ return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+int
+re_search (bufp, string, length, start, range, regs)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int length, start, range;
+ struct re_registers *regs;
+{
+ return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+int
+re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int length1, length2, start, stop;
+ struct re_registers *regs;
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, 0, regs, stop, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+int
+re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int length1, length2, start, range, stop;
+ struct re_registers *regs;
+{
+ return re_search_2_stub (bufp, string1, length1, string2, length2,
+ start, range, regs, stop, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static int
+re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs,
+ stop, ret_len)
+ struct re_pattern_buffer *bufp;
+ const char *string1, *string2;
+ int length1, length2, start, range, stop, ret_len;
+ struct re_registers *regs;
+{
+ const char *str;
+ int rval;
+ int len = length1 + length2;
+ int free_str = 0;
+
+ if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+ return -2;
+
+ /* Concatenate the strings. */
+ if (length2 > 0)
+ if (length1 > 0)
+ {
+ char *s = re_malloc (char, len);
+
+ if (BE (s == NULL, 0))
+ return -2;
+ memcpy (s, string1, length1);
+ memcpy (s + length1, string2, length2);
+ str = s;
+ free_str = 1;
+ }
+ else
+ str = string2;
+ else
+ str = string1;
+
+ rval = re_search_stub (bufp, str, len, start, range, stop, regs,
+ ret_len);
+ if (free_str)
+ re_free ((char *) str);
+ return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+ Additional parameters:
+ If RET_LEN is nonzero the length of the match is returned (re_match style);
+ otherwise the position of the match is returned. */
+
+static int
+re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
+ struct re_pattern_buffer *bufp;
+ const char *string;
+ int length, start, range, stop, ret_len;
+ struct re_registers *regs;
+{
+ reg_errcode_t result;
+ regmatch_t *pmatch;
+ int nregs, rval;
+ int eflags = 0;
+
+ /* Check for out-of-range. */
+ if (BE (start < 0 || start > length, 0))
+ return -1;
+ if (BE (start + range > length, 0))
+ range = length - start;
+ else if (BE (start + range < 0, 0))
+ range = -start;
+
+ eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+ eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+ /* Compile fastmap if we haven't yet. */
+ if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+ re_compile_fastmap (bufp);
+
+ if (BE (bufp->no_sub, 0))
+ regs = NULL;
+
+ /* We need at least 1 register. */
+ if (regs == NULL)
+ nregs = 1;
+ else if (BE (bufp->regs_allocated == REGS_FIXED &&
+ regs->num_regs < bufp->re_nsub + 1, 0))
+ {
+ nregs = regs->num_regs;
+ if (BE (nregs < 1, 0))
+ {
+ /* Nothing can be copied to regs. */
+ regs = NULL;
+ nregs = 1;
+ }
+ }
+ else
+ nregs = bufp->re_nsub + 1;
+ pmatch = re_malloc (regmatch_t, nregs);
+ if (BE (pmatch == NULL, 0))
+ return -2;
+
+ result = re_search_internal (bufp, string, length, start, range, stop,
+ nregs, pmatch, eflags);
+
+ rval = 0;
+
+ /* I hope we needn't fill ther regs with -1's when no match was found. */
+ if (result != REG_NOERROR)
+ rval = -1;
+ else if (regs != NULL)
+ {
+ /* If caller wants register contents data back, copy them. */
+ bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+ bufp->regs_allocated);
+ if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+ rval = -2;
+ }
+
+ if (BE (rval == 0, 1))
+ {
+ if (ret_len)
+ {
+ assert (pmatch[0].rm_so == start);
+ rval = pmatch[0].rm_eo - start;
+ }
+ else
+ rval = pmatch[0].rm_so;
+ }
+ re_free (pmatch);
+ return rval;
+}
+
+static unsigned
+re_copy_regs (regs, pmatch, nregs, regs_allocated)
+ struct re_registers *regs;
+ regmatch_t *pmatch;
+ int nregs, regs_allocated;
+{
+ int rval = REGS_REALLOCATE;
+ int i;
+ int need_regs = nregs + 1;
+ /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+ uses. */
+
+ /* Have the register data arrays been allocated? */
+ if (regs_allocated == REGS_UNALLOCATED)
+ { /* No. So allocate them with malloc. */
+ regs->start = re_malloc (regoff_t, need_regs);
+ regs->end = re_malloc (regoff_t, need_regs);
+ if (BE (regs->start == NULL, 0) || BE (regs->end == NULL, 0))
+ return REGS_UNALLOCATED;
+ regs->num_regs = need_regs;
+ }
+ else if (regs_allocated == REGS_REALLOCATE)
+ { /* Yes. If we need more elements than were already
+ allocated, reallocate them. If we need fewer, just
+ leave it alone. */
+ if (BE (need_regs > regs->num_regs, 0))
+ {
+ regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs);
+ regoff_t *new_end = re_realloc (regs->end, regoff_t, need_regs);
+ if (BE (new_start == NULL, 0) || BE (new_end == NULL, 0))
+ return REGS_UNALLOCATED;
+ regs->start = new_start;
+ regs->end = new_end;
+ regs->num_regs = need_regs;
+ }
+ }
+ else
+ {
+ assert (regs_allocated == REGS_FIXED);
+ /* This function may not be called with REGS_FIXED and nregs too big. */
+ assert (regs->num_regs >= nregs);
+ rval = REGS_FIXED;
+ }
+
+ /* Copy the regs. */
+ for (i = 0; i < nregs; ++i)
+ {
+ regs->start[i] = pmatch[i].rm_so;
+ regs->end[i] = pmatch[i].rm_eo;
+ }
+ for ( ; i < regs->num_regs; ++i)
+ regs->start[i] = regs->end[i] = -1;
+
+ return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+ ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use
+ this memory for recording register information. STARTS and ENDS
+ must be allocated using the malloc library routine, and must each
+ be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+ If NUM_REGS == 0, then subsequent matches should allocate their own
+ register data.
+
+ Unless this function is called, the first search or match using
+ PATTERN_BUFFER will allocate its own register data, without
+ freeing the old data. */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+ struct re_pattern_buffer *bufp;
+ struct re_registers *regs;
+ unsigned num_regs;
+ regoff_t *starts, *ends;
+{
+ if (num_regs)
+ {
+ bufp->regs_allocated = REGS_REALLOCATE;
+ regs->num_regs = num_regs;
+ regs->start = starts;
+ regs->end = ends;
+ }
+ else
+ {
+ bufp->regs_allocated = REGS_UNALLOCATED;
+ regs->num_regs = 0;
+ regs->start = regs->end = (regoff_t *) 0;
+ }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+
+/* Entry points compatible with 4.2 BSD regex library. We don't define
+ them unless specifically requested. */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+ const char *s;
+{
+ return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+
+/* Internal entry point. */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+ length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same
+ mingings with regexec. START, and RANGE have the same meanings
+ with re_search.
+ Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+ otherwise return the error code.
+ Note: We assume front end functions already check ranges.
+ (START + RANGE >= 0 && START + RANGE <= LENGTH) */
+
+static reg_errcode_t
+re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
+ eflags)
+ const regex_t *preg;
+ const char *string;
+ int length, start, range, stop, eflags;
+ size_t nmatch;
+ regmatch_t pmatch[];
+{
+ reg_errcode_t err;
+ re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+ int left_lim, right_lim, incr;
+ int fl_longest_match, match_first, match_kind, match_last = -1;
+ int sb, ch;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+ re_match_context_t mctx = { .dfa = dfa };
+#else
+ re_match_context_t mctx;
+#endif
+ char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate
+ && range && !preg->can_be_null) ? preg->fastmap : NULL;
+ unsigned RE_TRANSLATE_TYPE t = (unsigned RE_TRANSLATE_TYPE) preg->translate;
+
+#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
+ memset (&mctx, '\0', sizeof (re_match_context_t));
+ mctx.dfa = dfa;
+#endif
+
+ /* Check if the DFA haven't been compiled. */
+ if (BE (preg->used == 0 || dfa->init_state == NULL
+ || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+ || dfa->init_state_begbuf == NULL, 0))
+ return REG_NOMATCH;
+
+#ifdef DEBUG
+ /* We assume front-end functions already check them. */
+ assert (start + range >= 0 && start + range <= length);
+#endif
+
+ /* If initial states with non-begbuf contexts have no elements,
+ the regex must be anchored. If preg->newline_anchor is set,
+ we'll never use init_state_nl, so do not check it. */
+ if (dfa->init_state->nodes.nelem == 0
+ && dfa->init_state_word->nodes.nelem == 0
+ && (dfa->init_state_nl->nodes.nelem == 0
+ || !preg->newline_anchor))
+ {
+ if (start != 0 && start + range != 0)
+ return REG_NOMATCH;
+ start = range = 0;
+ }
+
+ /* We must check the longest matching, if nmatch > 0. */
+ fl_longest_match = (nmatch != 0 || dfa->nbackref);
+
+ err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1,
+ preg->translate, preg->syntax & RE_ICASE, dfa);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ mctx.input.stop = stop;
+ mctx.input.raw_stop = stop;
+ mctx.input.newline_anchor = preg->newline_anchor;
+
+ err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* We will log all the DFA states through which the dfa pass,
+ if nmatch > 1, or this dfa has "multibyte node", which is a
+ back-reference or a node which can accept multibyte character or
+ multi character collating element. */
+ if (nmatch > 1 || dfa->has_mb_node)
+ {
+ mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
+ if (BE (mctx.state_log == NULL, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ }
+ else
+ mctx.state_log = NULL;
+
+ match_first = start;
+ mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+ : CONTEXT_NEWLINE | CONTEXT_BEGBUF;
+
+ /* Check incrementally whether of not the input string match. */
+ incr = (range < 0) ? -1 : 1;
+ left_lim = (range < 0) ? start + range : start;
+ right_lim = (range < 0) ? start : start + range;
+ sb = dfa->mb_cur_max == 1;
+ match_kind =
+ (fastmap
+ ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0)
+ | (range >= 0 ? 2 : 0)
+ | (t != NULL ? 1 : 0))
+ : 8);
+
+ for (;; match_first += incr)
+ {
+ err = REG_NOMATCH;
+ if (match_first < left_lim || right_lim < match_first)
+ goto free_return;
+
+ /* Advance as rapidly as possible through the string, until we
+ find a plausible place to start matching. This may be done
+ with varying efficiency, so there are various possibilities:
+ only the most common of them are specialized, in order to
+ save on code size. We use a switch statement for speed. */
+ switch (match_kind)
+ {
+ case 8:
+ /* No fastmap. */
+ break;
+
+ case 7:
+ /* Fastmap with single-byte translation, match forward. */
+ while (BE (match_first < right_lim, 1)
+ && !fastmap[t[(unsigned char) string[match_first]]])
+ ++match_first;
+ goto forward_match_found_start_or_reached_end;
+
+ case 6:
+ /* Fastmap without translation, match forward. */
+ while (BE (match_first < right_lim, 1)
+ && !fastmap[(unsigned char) string[match_first]])
+ ++match_first;
+
+ forward_match_found_start_or_reached_end:
+ if (BE (match_first == right_lim, 0))
+ {
+ ch = match_first >= length
+ ? 0 : (unsigned char) string[match_first];
+ if (!fastmap[t ? t[ch] : ch])
+ goto free_return;
+ }
+ break;
+
+ case 4:
+ case 5:
+ /* Fastmap without multi-byte translation, match backwards. */
+ while (match_first >= left_lim)
+ {
+ ch = match_first >= length
+ ? 0 : (unsigned char) string[match_first];
+ if (fastmap[t ? t[ch] : ch])
+ break;
+ --match_first;
+ }
+ if (match_first < left_lim)
+ goto free_return;
+ break;
+
+ default:
+ /* In this case, we can't determine easily the current byte,
+ since it might be a component byte of a multibyte
+ character. Then we use the constructed buffer instead. */
+ for (;;)
+ {
+ /* If MATCH_FIRST is out of the valid range, reconstruct the
+ buffers. */
+ unsigned int offset = match_first - mctx.input.raw_mbs_idx;
+ if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0))
+ {
+ err = re_string_reconstruct (&mctx.input, match_first,
+ eflags);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ offset = match_first - mctx.input.raw_mbs_idx;
+ }
+ /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+ Note that MATCH_FIRST must not be smaller than 0. */
+ ch = (match_first >= length
+ ? 0 : re_string_byte_at (&mctx.input, offset));
+ if (fastmap[ch])
+ break;
+ match_first += incr;
+ if (match_first < left_lim || match_first > right_lim)
+ {
+ err = REG_NOMATCH;
+ goto free_return;
+ }
+ }
+ break;
+ }
+
+ /* Reconstruct the buffers so that the matcher can assume that
+ the matching starts from the beginning of the buffer. */
+ err = re_string_reconstruct (&mctx.input, match_first, eflags);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+#ifdef RE_ENABLE_I18N
+ /* Don't consider this char as a possible match start if it part,
+ yet isn't the head, of a multibyte character. */
+ if (!sb && !re_string_first_byte (&mctx.input, 0))
+ continue;
+#endif
+
+ /* It seems to be appropriate one, then use the matcher. */
+ /* We assume that the matching starts from 0. */
+ mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+ match_last = check_matching (&mctx, fl_longest_match,
+ range >= 0 ? &match_first : NULL);
+ if (match_last != -1)
+ {
+ if (BE (match_last == -2, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ else
+ {
+ mctx.match_last = match_last;
+ if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
+ {
+ re_dfastate_t *pstate = mctx.state_log[match_last];
+ mctx.last_node = check_halt_state_context (&mctx, pstate,
+ match_last);
+ }
+ if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+ || dfa->nbackref)
+ {
+ err = prune_impossible_nodes (&mctx);
+ if (err == REG_NOERROR)
+ break;
+ if (BE (err != REG_NOMATCH, 0))
+ goto free_return;
+ match_last = -1;
+ }
+ else
+ break; /* We found a match. */
+ }
+ }
+
+ match_ctx_clean (&mctx);
+ }
+
+#ifdef DEBUG
+ assert (match_last != -1);
+ assert (err == REG_NOERROR);
+#endif
+
+ /* Set pmatch[] if we need. */
+ if (nmatch > 0)
+ {
+ int reg_idx;
+
+ /* Initialize registers. */
+ for (reg_idx = 1; reg_idx < nmatch; ++reg_idx)
+ pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+ /* Set the points where matching start/end. */
+ pmatch[0].rm_so = 0;
+ pmatch[0].rm_eo = mctx.match_last;
+
+ if (!preg->no_sub && nmatch > 1)
+ {
+ err = set_regs (preg, &mctx, nmatch, pmatch,
+ dfa->has_plural_match && dfa->nbackref > 0);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+
+ /* At last, add the offset to the each registers, since we slided
+ the buffers so that we could assume that the matching starts
+ from 0. */
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so != -1)
+ {
+#ifdef RE_ENABLE_I18N
+ if (BE (mctx.input.offsets_needed != 0, 0))
+ {
+ if (pmatch[reg_idx].rm_so == mctx.input.valid_len)
+ pmatch[reg_idx].rm_so += mctx.input.valid_raw_len - mctx.input.valid_len;
+ else
+ pmatch[reg_idx].rm_so = mctx.input.offsets[pmatch[reg_idx].rm_so];
+ if (pmatch[reg_idx].rm_eo == mctx.input.valid_len)
+ pmatch[reg_idx].rm_eo += mctx.input.valid_raw_len - mctx.input.valid_len;
+ else
+ pmatch[reg_idx].rm_eo = mctx.input.offsets[pmatch[reg_idx].rm_eo];
+ }
+#else
+ assert (mctx.input.offsets_needed == 0);
+#endif
+ pmatch[reg_idx].rm_so += match_first;
+ pmatch[reg_idx].rm_eo += match_first;
+ }
+
+ if (dfa->subexp_map)
+ for (reg_idx = 0;
+ reg_idx + 1 < nmatch && reg_idx < preg->re_nsub;
+ reg_idx++)
+ if (dfa->subexp_map[reg_idx] != reg_idx)
+ {
+ pmatch[reg_idx + 1].rm_so
+ = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so;
+ pmatch[reg_idx + 1].rm_eo
+ = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo;
+ }
+ }
+
+ free_return:
+ re_free (mctx.state_log);
+ if (dfa->nbackref)
+ match_ctx_free (&mctx);
+ re_string_destruct (&mctx.input);
+ return err;
+}
+
+static reg_errcode_t
+prune_impossible_nodes (mctx)
+ re_match_context_t *mctx;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int halt_node, match_last;
+ reg_errcode_t ret;
+ re_dfastate_t **sifted_states;
+ re_dfastate_t **lim_states = NULL;
+ re_sift_context_t sctx;
+#ifdef DEBUG
+ assert (mctx->state_log != NULL);
+#endif
+ match_last = mctx->match_last;
+ halt_node = mctx->last_node;
+ sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+ if (BE (sifted_states == NULL, 0))
+ {
+ ret = REG_ESPACE;
+ goto free_return;
+ }
+ if (dfa->nbackref)
+ {
+ lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+ if (BE (lim_states == NULL, 0))
+ {
+ ret = REG_ESPACE;
+ goto free_return;
+ }
+ while (1)
+ {
+ memset (lim_states, '\0',
+ sizeof (re_dfastate_t *) * (match_last + 1));
+ sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+ match_last);
+ ret = sift_states_backward (mctx, &sctx);
+ re_node_set_free (&sctx.limits);
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ if (sifted_states[0] != NULL || lim_states[0] != NULL)
+ break;
+ do
+ {
+ --match_last;
+ if (match_last < 0)
+ {
+ ret = REG_NOMATCH;
+ goto free_return;
+ }
+ } while (mctx->state_log[match_last] == NULL
+ || !mctx->state_log[match_last]->halt);
+ halt_node = check_halt_state_context (mctx,
+ mctx->state_log[match_last],
+ match_last);
+ }
+ ret = merge_state_array (dfa, sifted_states, lim_states,
+ match_last + 1);
+ re_free (lim_states);
+ lim_states = NULL;
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ }
+ else
+ {
+ sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last);
+ ret = sift_states_backward (mctx, &sctx);
+ re_node_set_free (&sctx.limits);
+ if (BE (ret != REG_NOERROR, 0))
+ goto free_return;
+ }
+ re_free (mctx->state_log);
+ mctx->state_log = sifted_states;
+ sifted_states = NULL;
+ mctx->last_node = halt_node;
+ mctx->match_last = match_last;
+ ret = REG_NOERROR;
+ free_return:
+ re_free (sifted_states);
+ re_free (lim_states);
+ return ret;
+}
+
+/* Acquire an initial state and return it.
+ We must select appropriate initial state depending on the context,
+ since initial states may have constraints like "\<", "^", etc.. */
+
+static inline re_dfastate_t *
+acquire_init_state_context (err, mctx, idx)
+ reg_errcode_t *err;
+ const re_match_context_t *mctx;
+ int idx;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ if (dfa->init_state->has_constraint)
+ {
+ unsigned int context;
+ context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags);
+ if (IS_WORD_CONTEXT (context))
+ return dfa->init_state_word;
+ else if (IS_ORDINARY_CONTEXT (context))
+ return dfa->init_state;
+ else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_begbuf;
+ else if (IS_NEWLINE_CONTEXT (context))
+ return dfa->init_state_nl;
+ else if (IS_BEGBUF_CONTEXT (context))
+ {
+ /* It is relatively rare case, then calculate on demand. */
+ return re_acquire_state_context (err, dfa,
+ dfa->init_state->entrance_nodes,
+ context);
+ }
+ else
+ /* Must not happen? */
+ return dfa->init_state;
+ }
+ else
+ return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+ and return the index where the matching end, return -1 if not match,
+ or return -2 in case of an error.
+ FL_LONGEST_MATCH means we want the POSIX longest matching.
+ If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
+ next place where we may want to try matching.
+ Note that the matcher assume that the maching starts from the current
+ index of the buffer. */
+
+static int
+check_matching (mctx, fl_longest_match, p_match_first)
+ re_match_context_t *mctx;
+ int fl_longest_match;
+ int *p_match_first;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int match = 0;
+ int match_last = -1;
+ int cur_str_idx = re_string_cur_idx (&mctx->input);
+ re_dfastate_t *cur_state;
+ int at_init_state = p_match_first != NULL;
+ int next_start_idx = cur_str_idx;
+
+ err = REG_NOERROR;
+ cur_state = acquire_init_state_context (&err, mctx, cur_str_idx);
+ /* An initial state must not be NULL (invalid). */
+ if (BE (cur_state == NULL, 0))
+ {
+ assert (err == REG_ESPACE);
+ return -2;
+ }
+
+ if (mctx->state_log != NULL)
+ {
+ mctx->state_log[cur_str_idx] = cur_state;
+
+ /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
+ later. E.g. Processing back references. */
+ if (BE (dfa->nbackref, 0))
+ {
+ at_init_state = 0;
+ err = check_subexp_matching_top (mctx, &cur_state->nodes, 0);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (cur_state->has_backref)
+ {
+ err = transit_state_bkref (mctx, &cur_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+
+ /* If the RE accepts NULL string. */
+ if (BE (cur_state->halt, 0))
+ {
+ if (!cur_state->has_constraint
+ || check_halt_state_context (mctx, cur_state, cur_str_idx))
+ {
+ if (!fl_longest_match)
+ return cur_str_idx;
+ else
+ {
+ match_last = cur_str_idx;
+ match = 1;
+ }
+ }
+ }
+
+ while (!re_string_eoi (&mctx->input))
+ {
+ re_dfastate_t *old_state = cur_state;
+ int next_char_idx = re_string_cur_idx (&mctx->input) + 1;
+
+ if (BE (next_char_idx >= mctx->input.bufs_len, 0)
+ || (BE (next_char_idx >= mctx->input.valid_len, 0)
+ && mctx->input.valid_len < mctx->input.len))
+ {
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ assert (err == REG_ESPACE);
+ return -2;
+ }
+ }
+
+ cur_state = transit_state (&err, mctx, cur_state);
+ if (mctx->state_log != NULL)
+ cur_state = merge_state_with_log (&err, mctx, cur_state);
+
+ if (cur_state == NULL)
+ {
+ /* Reached the invalid state or an error. Try to recover a valid
+ state using the state log, if available and if we have not
+ already found a valid (even if not the longest) match. */
+ if (BE (err != REG_NOERROR, 0))
+ return -2;
+
+ if (mctx->state_log == NULL
+ || (match && !fl_longest_match)
+ || (cur_state = find_recover_state (&err, mctx)) == NULL)
+ break;
+ }
+
+ if (BE (at_init_state, 0))
+ {
+ if (old_state == cur_state)
+ next_start_idx = next_char_idx;
+ else
+ at_init_state = 0;
+ }
+
+ if (cur_state->halt)
+ {
+ /* Reached a halt state.
+ Check the halt state can satisfy the current context. */
+ if (!cur_state->has_constraint
+ || check_halt_state_context (mctx, cur_state,
+ re_string_cur_idx (&mctx->input)))
+ {
+ /* We found an appropriate halt state. */
+ match_last = re_string_cur_idx (&mctx->input);
+ match = 1;
+
+ /* We found a match, do not modify match_first below. */
+ p_match_first = NULL;
+ if (!fl_longest_match)
+ break;
+ }
+ }
+ }
+
+ if (p_match_first)
+ *p_match_first += next_start_idx;
+
+ return match_last;
+}
+
+/* Check NODE match the current context. */
+
+static int check_halt_node_context (dfa, node, context)
+ const re_dfa_t *dfa;
+ int node;
+ unsigned int context;
+{
+ re_token_type_t type = dfa->nodes[node].type;
+ unsigned int constraint = dfa->nodes[node].constraint;
+ if (type != END_OF_RE)
+ return 0;
+ if (!constraint)
+ return 1;
+ if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
+ return 0;
+ return 1;
+}
+
+/* Check the halt state STATE match the current context.
+ Return 0 if not match, if the node, STATE has, is a halt node and
+ match the context, return the node. */
+
+static int
+check_halt_state_context (mctx, state, idx)
+ const re_match_context_t *mctx;
+ const re_dfastate_t *state;
+ int idx;
+{
+ int i;
+ unsigned int context;
+#ifdef DEBUG
+ assert (state->halt);
+#endif
+ context = re_string_context_at (&mctx->input, idx, mctx->eflags);
+ for (i = 0; i < state->nodes.nelem; ++i)
+ if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context))
+ return state->nodes.elems[i];
+ return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+ corresponding to the DFA).
+ Return the destination node, and update EPS_VIA_NODES, return -1 in case
+ of errors. */
+
+static int
+proceed_next_node (mctx, nregs, regs, pidx, node, eps_via_nodes, fs)
+ const re_match_context_t *mctx;
+ regmatch_t *regs;
+ int nregs, *pidx, node;
+ re_node_set *eps_via_nodes;
+ struct re_fail_stack_t *fs;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int i, err, dest_node;
+ dest_node = -1;
+ if (IS_EPSILON_NODE (dfa->nodes[node].type))
+ {
+ re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
+ re_node_set *edests = &dfa->edests[node];
+ int dest_node;
+ err = re_node_set_insert (eps_via_nodes, node);
+ if (BE (err < 0, 0))
+ return -2;
+ /* Pick up a valid destination, or return -1 if none is found. */
+ for (dest_node = -1, i = 0; i < edests->nelem; ++i)
+ {
+ int candidate = edests->elems[i];
+ if (!re_node_set_contains (cur_nodes, candidate))
+ continue;
+ if (dest_node == -1)
+ dest_node = candidate;
+
+ else
+ {
+ /* In order to avoid infinite loop like "(a*)*", return the second
+ epsilon-transition if the first was already considered. */
+ if (re_node_set_contains (eps_via_nodes, dest_node))
+ return candidate;
+
+ /* Otherwise, push the second epsilon-transition on the fail stack. */
+ else if (fs != NULL
+ && push_fail_stack (fs, *pidx, candidate, nregs, regs,
+ eps_via_nodes))
+ return -2;
+
+ /* We know we are going to exit. */
+ break;
+ }
+ }
+ return dest_node;
+ }
+ else
+ {
+ int naccepted = 0;
+ re_token_type_t type = dfa->nodes[node].type;
+
+#ifdef RE_ENABLE_I18N
+ if (ACCEPT_MB_NODE (type))
+ naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx);
+ else
+#endif /* RE_ENABLE_I18N */
+ if (type == OP_BACK_REF)
+ {
+ int subexp_idx = dfa->nodes[node].opr.idx + 1;
+ naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+ if (fs != NULL)
+ {
+ if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+ return -1;
+ else if (naccepted)
+ {
+ char *buf = (char *) re_string_get_buffer (&mctx->input);
+ if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+ naccepted) != 0)
+ return -1;
+ }
+ }
+
+ if (naccepted == 0)
+ {
+ err = re_node_set_insert (eps_via_nodes, node);
+ if (BE (err < 0, 0))
+ return -2;
+ dest_node = dfa->edests[node].elems[0];
+ if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node))
+ return dest_node;
+ }
+ }
+
+ if (naccepted != 0
+ || check_node_accept (mctx, dfa->nodes + node, *pidx))
+ {
+ dest_node = dfa->nexts[node];
+ *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+ if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+ || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+ dest_node)))
+ return -1;
+ re_node_set_empty (eps_via_nodes);
+ return dest_node;
+ }
+ }
+ return -1;
+}
+
+static reg_errcode_t
+push_fail_stack (fs, str_idx, dest_node, nregs, regs, eps_via_nodes)
+ struct re_fail_stack_t *fs;
+ int str_idx, dest_node, nregs;
+ regmatch_t *regs;
+ re_node_set *eps_via_nodes;
+{
+ reg_errcode_t err;
+ int num = fs->num++;
+ if (fs->num == fs->alloc)
+ {
+ struct re_fail_stack_ent_t *new_array;
+ new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+ * fs->alloc * 2));
+ if (new_array == NULL)
+ return REG_ESPACE;
+ fs->alloc *= 2;
+ fs->stack = new_array;
+ }
+ fs->stack[num].idx = str_idx;
+ fs->stack[num].node = dest_node;
+ fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+ if (fs->stack[num].regs == NULL)
+ return REG_ESPACE;
+ memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+ err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+ return err;
+}
+
+static int
+pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes)
+ struct re_fail_stack_t *fs;
+ int *pidx, nregs;
+ regmatch_t *regs;
+ re_node_set *eps_via_nodes;
+{
+ int num = --fs->num;
+ assert (num >= 0);
+ *pidx = fs->stack[num].idx;
+ memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+ re_node_set_free (eps_via_nodes);
+ re_free (fs->stack[num].regs);
+ *eps_via_nodes = fs->stack[num].eps_via_nodes;
+ return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+ PMATCH.
+ Note: We assume that pmatch[0] is already set, and
+ pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */
+
+static reg_errcode_t
+set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
+ const regex_t *preg;
+ const re_match_context_t *mctx;
+ size_t nmatch;
+ regmatch_t *pmatch;
+ int fl_backtrack;
+{
+ re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+ int idx, cur_node, real_nmatch;
+ re_node_set eps_via_nodes;
+ struct re_fail_stack_t *fs;
+ struct re_fail_stack_t fs_body = { 0, 2, NULL };
+ regmatch_t *prev_idx_match;
+
+#ifdef DEBUG
+ assert (nmatch > 1);
+ assert (mctx->state_log != NULL);
+#endif
+ if (fl_backtrack)
+ {
+ fs = &fs_body;
+ fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+ if (fs->stack == NULL)
+ return REG_ESPACE;
+ }
+ else
+ fs = NULL;
+
+ cur_node = dfa->init_node;
+ real_nmatch = (nmatch <= preg->re_nsub) ? nmatch : preg->re_nsub + 1;
+ re_node_set_init_empty (&eps_via_nodes);
+
+ prev_idx_match = (regmatch_t *) alloca (sizeof (regmatch_t) * real_nmatch);
+ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * real_nmatch);
+
+ for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+ {
+ update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, real_nmatch);
+
+ if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+ {
+ int reg_idx;
+ if (fs)
+ {
+ for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+ if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+ break;
+ if (reg_idx == nmatch)
+ {
+ re_node_set_free (&eps_via_nodes);
+ return free_fail_stack_return (fs);
+ }
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ }
+ else
+ {
+ re_node_set_free (&eps_via_nodes);
+ return REG_NOERROR;
+ }
+ }
+
+ /* Proceed to next node. */
+ cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node,
+ &eps_via_nodes, fs);
+
+ if (BE (cur_node < 0, 0))
+ {
+ if (BE (cur_node == -2, 0))
+ {
+ re_node_set_free (&eps_via_nodes);
+ free_fail_stack_return (fs);
+ return REG_ESPACE;
+ }
+ if (fs)
+ cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+ &eps_via_nodes);
+ else
+ {
+ re_node_set_free (&eps_via_nodes);
+ return REG_NOMATCH;
+ }
+ }
+ }
+ re_node_set_free (&eps_via_nodes);
+ return free_fail_stack_return (fs);
+}
+
+static reg_errcode_t
+free_fail_stack_return (fs)
+ struct re_fail_stack_t *fs;
+{
+ if (fs)
+ {
+ int fs_idx;
+ for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
+ {
+ re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
+ re_free (fs->stack[fs_idx].regs);
+ }
+ re_free (fs->stack);
+ }
+ return REG_NOERROR;
+}
+
+static void
+update_regs (dfa, pmatch, prev_idx_match, cur_node, cur_idx, nmatch)
+ re_dfa_t *dfa;
+ regmatch_t *pmatch, *prev_idx_match;
+ int cur_node, cur_idx, nmatch;
+{
+ int type = dfa->nodes[cur_node].type;
+ if (type == OP_OPEN_SUBEXP)
+ {
+ int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+
+ /* We are at the first node of this sub expression. */
+ if (reg_num < nmatch)
+ {
+ pmatch[reg_num].rm_so = cur_idx;
+ pmatch[reg_num].rm_eo = -1;
+ }
+ }
+ else if (type == OP_CLOSE_SUBEXP)
+ {
+ int reg_num = dfa->nodes[cur_node].opr.idx + 1;
+ if (reg_num < nmatch)
+ {
+ /* We are at the last node of this sub expression. */
+ if (pmatch[reg_num].rm_so < cur_idx)
+ {
+ pmatch[reg_num].rm_eo = cur_idx;
+ /* This is a non-empty match or we are not inside an optional
+ subexpression. Accept this right away. */
+ memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+ }
+ else
+ {
+ if (dfa->nodes[cur_node].opt_subexp
+ && prev_idx_match[reg_num].rm_so != -1)
+ /* We transited through an empty match for an optional
+ subexpression, like (a?)*, and this is not the subexp's
+ first match. Copy back the old content of the registers
+ so that matches of an inner subexpression are undone as
+ well, like in ((a?))*. */
+ memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch);
+ else
+ /* We completed a subexpression, but it may be part of
+ an optional one, so do not update PREV_IDX_MATCH. */
+ pmatch[reg_num].rm_eo = cur_idx;
+ }
+ }
+ }
+}
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+ and sift the nodes in each states according to the following rules.
+ Updated state_log will be wrote to STATE_LOG.
+
+ Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+ 1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+ If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+ the LAST_NODE, we throw away the node `a'.
+ 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+ string `s' and transit to `b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+ away the node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+ thrown away, we throw away the node `a'.
+ 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b':
+ i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+ node `a'.
+ ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away,
+ we throw away the node `a'. */
+
+#define STATE_NODE_CONTAINS(state,node) \
+ ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+sift_states_backward (mctx, sctx)
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+{
+ reg_errcode_t err;
+ int null_cnt = 0;
+ int str_idx = sctx->last_str_idx;
+ re_node_set cur_dest;
+
+#ifdef DEBUG
+ assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+
+ /* Build sifted state_log[str_idx]. It has the nodes which can epsilon
+ transit to the last_node and the last_node itself. */
+ err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* Then check each states in the state_log. */
+ while (str_idx > 0)
+ {
+ /* Update counters. */
+ null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+ if (null_cnt > mctx->max_mb_elem_len)
+ {
+ memset (sctx->sifted_states, '\0',
+ sizeof (re_dfastate_t *) * str_idx);
+ re_node_set_free (&cur_dest);
+ return REG_NOERROR;
+ }
+ re_node_set_empty (&cur_dest);
+ --str_idx;
+
+ if (mctx->state_log[str_idx])
+ {
+ err = build_sifted_states (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+
+ /* Add all the nodes which satisfy the following conditions:
+ - It can epsilon transit to a node in CUR_DEST.
+ - It is in CUR_SRC.
+ And update state_log. */
+ err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ err = REG_NOERROR;
+ free_return:
+ re_node_set_free (&cur_dest);
+ return err;
+}
+
+static reg_errcode_t
+build_sifted_states (mctx, sctx, str_idx, cur_dest)
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int str_idx;
+ re_node_set *cur_dest;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes;
+ int i;
+
+ /* Then build the next sifted state.
+ We build the next sifted state on `cur_dest', and update
+ `sifted_states[str_idx]' with `cur_dest'.
+ Note:
+ `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+ `cur_src' points the node_set of the old `state_log[str_idx]'
+ (with the epsilon nodes pre-filtered out). */
+ for (i = 0; i < cur_src->nelem; i++)
+ {
+ int prev_node = cur_src->elems[i];
+ int naccepted = 0;
+ int ret;
+
+#if defined DEBUG || defined RE_ENABLE_I18N
+ re_token_type_t type = dfa->nodes[prev_node].type;
+#endif
+#ifdef DEBUG
+ assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+ /* If the node may accept `multi byte'. */
+ if (ACCEPT_MB_NODE (type))
+ naccepted = sift_states_iter_mb (mctx, sctx, prev_node,
+ str_idx, sctx->last_str_idx);
+#endif /* RE_ENABLE_I18N */
+
+ /* We don't check backreferences here.
+ See update_cur_sifted_state(). */
+ if (!naccepted
+ && check_node_accept (mctx, dfa->nodes + prev_node, str_idx)
+ && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+ dfa->nexts[prev_node]))
+ naccepted = 1;
+
+ if (naccepted == 0)
+ continue;
+
+ if (sctx->limits.nelem)
+ {
+ int to_idx = str_idx + naccepted;
+ if (check_dst_limits (mctx, &sctx->limits,
+ dfa->nexts[prev_node], to_idx,
+ prev_node, str_idx))
+ continue;
+ }
+ ret = re_node_set_insert (cur_dest, prev_node);
+ if (BE (ret == -1, 0))
+ return REG_ESPACE;
+ }
+
+ return REG_NOERROR;
+}
+
+/* Helper functions. */
+
+static reg_errcode_t
+clean_state_log_if_needed (mctx, next_state_log_idx)
+ re_match_context_t *mctx;
+ int next_state_log_idx;
+{
+ int top = mctx->state_log_top;
+
+ if (next_state_log_idx >= mctx->input.bufs_len
+ || (next_state_log_idx >= mctx->input.valid_len
+ && mctx->input.valid_len < mctx->input.len))
+ {
+ reg_errcode_t err;
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (top < next_state_log_idx)
+ {
+ memset (mctx->state_log + top + 1, '\0',
+ sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+ mctx->state_log_top = next_state_log_idx;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+merge_state_array (dfa, dst, src, num)
+ re_dfa_t *dfa;
+ re_dfastate_t **dst;
+ re_dfastate_t **src;
+ int num;
+{
+ int st_idx;
+ reg_errcode_t err;
+ for (st_idx = 0; st_idx < num; ++st_idx)
+ {
+ if (dst[st_idx] == NULL)
+ dst[st_idx] = src[st_idx];
+ else if (src[st_idx] != NULL)
+ {
+ re_node_set merged_set;
+ err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+ &src[st_idx]->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+ re_node_set_free (&merged_set);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+update_cur_sifted_state (mctx, sctx, str_idx, dest_nodes)
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int str_idx;
+ re_node_set *dest_nodes;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ const re_node_set *candidates;
+ candidates = ((mctx->state_log[str_idx] == NULL) ? NULL
+ : &mctx->state_log[str_idx]->nodes);
+
+ if (dest_nodes->nelem == 0)
+ sctx->sifted_states[str_idx] = NULL;
+ else
+ {
+ if (candidates)
+ {
+ /* At first, add the nodes which can epsilon transit to a node in
+ DEST_NODE. */
+ err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ /* Then, check the limitations in the current sift_context. */
+ if (sctx->limits.nelem)
+ {
+ err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+ mctx->bkref_ents, str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+
+ sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (candidates && mctx->state_log[str_idx]->has_backref)
+ {
+ err = sift_states_bkref (mctx, sctx, str_idx, candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+add_epsilon_src_nodes (dfa, dest_nodes, candidates)
+ re_dfa_t *dfa;
+ re_node_set *dest_nodes;
+ const re_node_set *candidates;
+{
+ reg_errcode_t err = REG_NOERROR;
+ int i;
+
+ re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ if (!state->inveclosure.alloc)
+ {
+ err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return REG_ESPACE;
+ for (i = 0; i < dest_nodes->nelem; i++)
+ re_node_set_merge (&state->inveclosure,
+ dfa->inveclosures + dest_nodes->elems[i]);
+ }
+ return re_node_set_add_intersect (dest_nodes, candidates,
+ &state->inveclosure);
+}
+
+static reg_errcode_t
+sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates)
+ re_dfa_t *dfa;
+ int node;
+ re_node_set *dest_nodes;
+ const re_node_set *candidates;
+{
+ int ecl_idx;
+ reg_errcode_t err;
+ re_node_set *inv_eclosure = dfa->inveclosures + node;
+ re_node_set except_nodes;
+ re_node_set_init_empty (&except_nodes);
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ int cur_node = inv_eclosure->elems[ecl_idx];
+ if (cur_node == node)
+ continue;
+ if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
+ {
+ int edst1 = dfa->edests[cur_node].elems[0];
+ int edst2 = ((dfa->edests[cur_node].nelem > 1)
+ ? dfa->edests[cur_node].elems[1] : -1);
+ if ((!re_node_set_contains (inv_eclosure, edst1)
+ && re_node_set_contains (dest_nodes, edst1))
+ || (edst2 > 0
+ && !re_node_set_contains (inv_eclosure, edst2)
+ && re_node_set_contains (dest_nodes, edst2)))
+ {
+ err = re_node_set_add_intersect (&except_nodes, candidates,
+ dfa->inveclosures + cur_node);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&except_nodes);
+ return err;
+ }
+ }
+ }
+ }
+ for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+ {
+ int cur_node = inv_eclosure->elems[ecl_idx];
+ if (!re_node_set_contains (&except_nodes, cur_node))
+ {
+ int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+ re_node_set_remove_at (dest_nodes, idx);
+ }
+ }
+ re_node_set_free (&except_nodes);
+ return REG_NOERROR;
+}
+
+static int
+check_dst_limits (mctx, limits, dst_node, dst_idx, src_node, src_idx)
+ re_match_context_t *mctx;
+ re_node_set *limits;
+ int dst_node, dst_idx, src_node, src_idx;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int lim_idx, src_pos, dst_pos;
+
+ int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx);
+ int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx);
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ int subexp_idx;
+ struct re_backref_cache_entry *ent;
+ ent = mctx->bkref_ents + limits->elems[lim_idx];
+ subexp_idx = dfa->nodes[ent->node].opr.idx;
+
+ dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+ subexp_idx, dst_node, dst_idx,
+ dst_bkref_idx);
+ src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+ subexp_idx, src_node, src_idx,
+ src_bkref_idx);
+
+ /* In case of:
+ <src> <dst> ( <subexp> )
+ ( <subexp> ) <src> <dst>
+ ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */
+ if (src_pos == dst_pos)
+ continue; /* This is unrelated limitation. */
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, from_node, bkref_idx)
+ re_match_context_t *mctx;
+ int boundaries, subexp_idx, from_node, bkref_idx;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ re_node_set *eclosures = dfa->eclosures + from_node;
+ int node_idx;
+
+ /* Else, we are on the boundary: examine the nodes on the epsilon
+ closure. */
+ for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+ {
+ int node = eclosures->elems[node_idx];
+ switch (dfa->nodes[node].type)
+ {
+ case OP_BACK_REF:
+ if (bkref_idx != -1)
+ {
+ struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx;
+ do
+ {
+ int dst, cpos;
+
+ if (ent->node != node)
+ continue;
+
+ if (subexp_idx <= 8 * sizeof (ent->eps_reachable_subexps_map)
+ && !(ent->eps_reachable_subexps_map & (1 << subexp_idx)))
+ continue;
+
+ /* Recurse trying to reach the OP_OPEN_SUBEXP and
+ OP_CLOSE_SUBEXP cases below. But, if the
+ destination node is the same node as the source
+ node, don't recurse because it would cause an
+ infinite loop: a regex that exhibits this behavior
+ is ()\1*\1* */
+ dst = dfa->edests[node].elems[0];
+ if (dst == from_node)
+ {
+ if (boundaries & 1)
+ return -1;
+ else /* if (boundaries & 2) */
+ return 0;
+ }
+
+ cpos =
+ check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+ dst, bkref_idx);
+ if (cpos == -1 /* && (boundaries & 1) */)
+ return -1;
+ if (cpos == 0 && (boundaries & 2))
+ return 0;
+
+ ent->eps_reachable_subexps_map &= ~(1 << subexp_idx);
+ }
+ while (ent++->more);
+ }
+ break;
+
+ case OP_OPEN_SUBEXP:
+ if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx)
+ return -1;
+ break;
+
+ case OP_CLOSE_SUBEXP:
+ if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx)
+ return 0;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return (boundaries & 2) ? 1 : 0;
+}
+
+static int
+check_dst_limits_calc_pos (mctx, limit, subexp_idx, from_node, str_idx, bkref_idx)
+ re_match_context_t *mctx;
+ int limit, subexp_idx, from_node, str_idx, bkref_idx;
+{
+ struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+ int boundaries;
+
+ /* If we are outside the range of the subexpression, return -1 or 1. */
+ if (str_idx < lim->subexp_from)
+ return -1;
+
+ if (lim->subexp_to < str_idx)
+ return 1;
+
+ /* If we are within the subexpression, return 0. */
+ boundaries = (str_idx == lim->subexp_from);
+ boundaries |= (str_idx == lim->subexp_to) << 1;
+ if (boundaries == 0)
+ return 0;
+
+ /* Else, examine epsilon closure. */
+ return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+ from_node, bkref_idx);
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+ which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx)
+ re_dfa_t *dfa;
+ re_node_set *dest_nodes;
+ const re_node_set *candidates;
+ re_node_set *limits;
+ struct re_backref_cache_entry *bkref_ents;
+ int str_idx;
+{
+ reg_errcode_t err;
+ int node_idx, lim_idx;
+
+ for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+ {
+ int subexp_idx;
+ struct re_backref_cache_entry *ent;
+ ent = bkref_ents + limits->elems[lim_idx];
+
+ if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+ continue; /* This is unrelated limitation. */
+
+ subexp_idx = dfa->nodes[ent->node].opr.idx;
+ if (ent->subexp_to == str_idx)
+ {
+ int ops_node = -1;
+ int cls_node = -1;
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_OPEN_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ ops_node = node;
+ else if (type == OP_CLOSE_SUBEXP
+ && subexp_idx == dfa->nodes[node].opr.idx)
+ cls_node = node;
+ }
+
+ /* Check the limitation of the open subexpression. */
+ /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */
+ if (ops_node >= 0)
+ {
+ err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ /* Check the limitation of the close subexpression. */
+ if (cls_node >= 0)
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ if (!re_node_set_contains (dfa->inveclosures + node,
+ cls_node)
+ && !re_node_set_contains (dfa->eclosures + node,
+ cls_node))
+ {
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ --node_idx;
+ }
+ }
+ }
+ else /* (ent->subexp_to != str_idx) */
+ {
+ for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+ {
+ int node = dest_nodes->elems[node_idx];
+ re_token_type_t type = dfa->nodes[node].type;
+ if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+ {
+ if (subexp_idx != dfa->nodes[node].opr.idx)
+ continue;
+ /* It is against this limitation.
+ Remove it form the current sifted state. */
+ err = sub_epsilon_src_nodes (dfa, node, dest_nodes,
+ candidates);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ }
+ }
+ return REG_NOERROR;
+}
+
+static reg_errcode_t
+sift_states_bkref (mctx, sctx, str_idx, candidates)
+ re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int str_idx;
+ const re_node_set *candidates;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int node_idx, node;
+ re_sift_context_t local_sctx;
+ int first_idx = search_cur_bkref_entry (mctx, str_idx);
+
+ if (first_idx == -1)
+ return REG_NOERROR;
+
+ local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */
+
+ for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+ {
+ int enabled_idx;
+ re_token_type_t type;
+ struct re_backref_cache_entry *entry;
+ node = candidates->elems[node_idx];
+ type = dfa->nodes[node].type;
+ /* Avoid infinite loop for the REs like "()\1+". */
+ if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+ continue;
+ if (type != OP_BACK_REF)
+ continue;
+
+ entry = mctx->bkref_ents + first_idx;
+ enabled_idx = first_idx;
+ do
+ {
+ int subexp_len, to_idx, dst_node;
+ re_dfastate_t *cur_state;
+
+ if (entry->node != node)
+ continue;
+ subexp_len = entry->subexp_to - entry->subexp_from;
+ to_idx = str_idx + subexp_len;
+ dst_node = (subexp_len ? dfa->nexts[node]
+ : dfa->edests[node].elems[0]);
+
+ if (to_idx > sctx->last_str_idx
+ || sctx->sifted_states[to_idx] == NULL
+ || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node)
+ || check_dst_limits (mctx, &sctx->limits, node,
+ str_idx, dst_node, to_idx))
+ continue;
+
+ if (local_sctx.sifted_states == NULL)
+ {
+ local_sctx = *sctx;
+ err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ local_sctx.last_node = node;
+ local_sctx.last_str_idx = str_idx;
+ err = re_node_set_insert (&local_sctx.limits, enabled_idx);
+ if (BE (err < 0, 0))
+ {
+ err = REG_ESPACE;
+ goto free_return;
+ }
+ cur_state = local_sctx.sifted_states[str_idx];
+ err = sift_states_backward (mctx, &local_sctx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ if (sctx->limited_states != NULL)
+ {
+ err = merge_state_array (dfa, sctx->limited_states,
+ local_sctx.sifted_states,
+ str_idx + 1);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ local_sctx.sifted_states[str_idx] = cur_state;
+ re_node_set_remove (&local_sctx.limits, enabled_idx);
+
+ /* mctx->bkref_ents may have changed, reload the pointer. */
+ entry = mctx->bkref_ents + enabled_idx;
+ }
+ while (enabled_idx++, entry++->more);
+ }
+ err = REG_NOERROR;
+ free_return:
+ if (local_sctx.sifted_states != NULL)
+ {
+ re_node_set_free (&local_sctx.limits);
+ }
+
+ return err;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+sift_states_iter_mb (mctx, sctx, node_idx, str_idx, max_str_idx)
+ const re_match_context_t *mctx;
+ re_sift_context_t *sctx;
+ int node_idx, str_idx, max_str_idx;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int naccepted;
+ /* Check the node can accept `multi byte'. */
+ naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx);
+ if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+ !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+ dfa->nexts[node_idx]))
+ /* The node can't accept the `multi byte', or the
+ destination was already thrown away, then the node
+ could't accept the current input `multi byte'. */
+ naccepted = 0;
+ /* Otherwise, it is sure that the node could accept
+ `naccepted' bytes input. */
+ return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+
+/* Functions for state transition. */
+
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte, and update STATE_LOG if necessary.
+ If STATE can accept a multibyte char/collating element/back reference
+ update the destination of STATE_LOG. */
+
+static re_dfastate_t *
+transit_state (err, mctx, state)
+ reg_errcode_t *err;
+ re_match_context_t *mctx;
+ re_dfastate_t *state;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ re_dfastate_t **trtable;
+ unsigned char ch;
+
+#ifdef RE_ENABLE_I18N
+ /* If the current state can accept multibyte. */
+ if (BE (state->accept_mb, 0))
+ {
+ *err = transit_state_mb (mctx, state);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+#endif /* RE_ENABLE_I18N */
+
+ /* Then decide the next state with the single byte. */
+ if (1)
+ {
+ /* Use transition table */
+ ch = re_string_fetch_byte (&mctx->input);
+ trtable = state->trtable;
+ if (trtable == NULL)
+ {
+ trtable = build_trtable (dfa, state);
+ if (trtable == NULL)
+ {
+ *err = REG_ESPACE;
+ return NULL;
+ }
+ }
+ if (BE (state->word_trtable, 0))
+ {
+ unsigned int context;
+ context
+ = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input) - 1,
+ mctx->eflags);
+ if (IS_WORD_CONTEXT (context))
+ return trtable[ch + SBC_MAX];
+ else
+ return trtable[ch];
+ }
+ else
+ return trtable[ch];
+ }
+#if 0
+ else
+ /* don't use transition table */
+ return transit_state_sb (err, mctx, state);
+#endif
+}
+
+/* Update the state_log if we need */
+re_dfastate_t *
+merge_state_with_log (err, mctx, next_state)
+ reg_errcode_t *err;
+ re_match_context_t *mctx;
+ re_dfastate_t *next_state;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int cur_idx = re_string_cur_idx (&mctx->input);
+
+ if (cur_idx > mctx->state_log_top)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ mctx->state_log_top = cur_idx;
+ }
+ else if (mctx->state_log[cur_idx] == 0)
+ {
+ mctx->state_log[cur_idx] = next_state;
+ }
+ else
+ {
+ re_dfastate_t *pstate;
+ unsigned int context;
+ re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+ /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+ the destination of a multibyte char/collating element/
+ back reference. Then the next state is the union set of
+ these destinations and the results of the transition table. */
+ pstate = mctx->state_log[cur_idx];
+ log_nodes = pstate->entrance_nodes;
+ if (next_state != NULL)
+ {
+ table_nodes = next_state->entrance_nodes;
+ *err = re_node_set_init_union (&next_nodes, table_nodes,
+ log_nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ }
+ else
+ next_nodes = *log_nodes;
+ /* Note: We already add the nodes of the initial state,
+ then we don't need to add them here. */
+
+ context = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input) - 1,
+ mctx->eflags);
+ next_state = mctx->state_log[cur_idx]
+ = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ if (table_nodes != NULL)
+ re_node_set_free (&next_nodes);
+ }
+
+ if (BE (dfa->nbackref, 0) && next_state != NULL)
+ {
+ /* Check OP_OPEN_SUBEXP in the current state in case that we use them
+ later. We must check them here, since the back references in the
+ next state might use them. */
+ *err = check_subexp_matching_top (mctx, &next_state->nodes,
+ cur_idx);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+
+ /* If the next state has back references. */
+ if (next_state->has_backref)
+ {
+ *err = transit_state_bkref (mctx, &next_state->nodes);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ next_state = mctx->state_log[cur_idx];
+ }
+ }
+
+ return next_state;
+}
+
+/* Skip bytes in the input that correspond to part of a
+ multi-byte match, then look in the log for a state
+ from which to restart matching. */
+re_dfastate_t *
+find_recover_state (err, mctx)
+ reg_errcode_t *err;
+ re_match_context_t *mctx;
+{
+ re_dfastate_t *cur_state = NULL;
+ do
+ {
+ int max = mctx->state_log_top;
+ int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+ do
+ {
+ if (++cur_str_idx > max)
+ return NULL;
+ re_string_skip_bytes (&mctx->input, 1);
+ }
+ while (mctx->state_log[cur_str_idx] == NULL);
+
+ cur_state = merge_state_with_log (err, mctx, NULL);
+ }
+ while (*err == REG_NOERROR && cur_state == NULL);
+ return cur_state;
+}
+
+/* Helper functions for transit_state. */
+
+/* From the node set CUR_NODES, pick up the nodes whose types are
+ OP_OPEN_SUBEXP and which have corresponding back references in the regular
+ expression. And register them to use them later for evaluating the
+ correspoding back references. */
+
+static reg_errcode_t
+check_subexp_matching_top (mctx, cur_nodes, str_idx)
+ re_match_context_t *mctx;
+ re_node_set *cur_nodes;
+ int str_idx;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int node_idx;
+ reg_errcode_t err;
+
+ /* TODO: This isn't efficient.
+ Because there might be more than one nodes whose types are
+ OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+ nodes.
+ E.g. RE: (a){2} */
+ for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
+ {
+ int node = cur_nodes->elems[node_idx];
+ if (dfa->nodes[node].type == OP_OPEN_SUBEXP
+ && dfa->nodes[node].opr.idx < (8 * sizeof (dfa->used_bkref_map))
+ && dfa->used_bkref_map & (1 << dfa->nodes[node].opr.idx))
+ {
+ err = match_ctx_add_subtop (mctx, node, str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ return REG_NOERROR;
+}
+
+#if 0
+/* Return the next state to which the current state STATE will transit by
+ accepting the current input byte. */
+
+static re_dfastate_t *
+transit_state_sb (err, mctx, state)
+ reg_errcode_t *err;
+ re_match_context_t *mctx;
+ re_dfastate_t *state;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ re_node_set next_nodes;
+ re_dfastate_t *next_state;
+ int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input);
+ unsigned int context;
+
+ *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+ if (BE (*err != REG_NOERROR, 0))
+ return NULL;
+ for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+ {
+ int cur_node = state->nodes.elems[node_cnt];
+ if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx))
+ {
+ *err = re_node_set_merge (&next_nodes,
+ dfa->eclosures + dfa->nexts[cur_node]);
+ if (BE (*err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return NULL;
+ }
+ }
+ }
+ context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags);
+ next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+ /* We don't need to check errors here, since the return value of
+ this function is next_state and ERR is already set. */
+
+ re_node_set_free (&next_nodes);
+ re_string_skip_bytes (&mctx->input, 1);
+ return next_state;
+}
+#endif
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+transit_state_mb (mctx, pstate)
+ re_match_context_t *mctx;
+ re_dfastate_t *pstate;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int i;
+
+ for (i = 0; i < pstate->nodes.nelem; ++i)
+ {
+ re_node_set dest_nodes, *new_nodes;
+ int cur_node_idx = pstate->nodes.elems[i];
+ int naccepted = 0, dest_idx;
+ unsigned int context;
+ re_dfastate_t *dest_state;
+
+ if (dfa->nodes[cur_node_idx].constraint)
+ {
+ context = re_string_context_at (&mctx->input,
+ re_string_cur_idx (&mctx->input),
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+ context))
+ continue;
+ }
+
+ /* How many bytes the node can accept? */
+ if (ACCEPT_MB_NODE (dfa->nodes[cur_node_idx].type))
+ naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input,
+ re_string_cur_idx (&mctx->input));
+ if (naccepted == 0)
+ continue;
+
+ /* The node can accepts `naccepted' bytes. */
+ dest_idx = re_string_cur_idx (&mctx->input) + naccepted;
+ mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+ : mctx->max_mb_elem_len);
+ err = clean_state_log_if_needed (mctx, dest_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+#ifdef DEBUG
+ assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+ /* `cur_node_idx' may point the entity of the OP_CONTEXT_NODE,
+ then we use pstate->nodes.elems[i] instead. */
+ new_nodes = dfa->eclosures + dfa->nexts[pstate->nodes.elems[i]];
+
+ dest_state = mctx->state_log[dest_idx];
+ if (dest_state == NULL)
+ dest_nodes = *new_nodes;
+ else
+ {
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes, new_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ context = re_string_context_at (&mctx->input, dest_idx - 1, mctx->eflags);
+ mctx->state_log[dest_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ if (dest_state != NULL)
+ re_node_set_free (&dest_nodes);
+ if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+ return err;
+ }
+ return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+transit_state_bkref (mctx, nodes)
+ re_match_context_t *mctx;
+ const re_node_set *nodes;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int i;
+ int cur_str_idx = re_string_cur_idx (&mctx->input);
+
+ for (i = 0; i < nodes->nelem; ++i)
+ {
+ int dest_str_idx, prev_nelem, bkc_idx;
+ int node_idx = nodes->elems[i];
+ unsigned int context;
+ const re_token_t *node = dfa->nodes + node_idx;
+ re_node_set *new_dest_nodes;
+
+ /* Check whether `node' is a backreference or not. */
+ if (node->type != OP_BACK_REF)
+ continue;
+
+ if (node->constraint)
+ {
+ context = re_string_context_at (&mctx->input, cur_str_idx,
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ continue;
+ }
+
+ /* `node' is a backreference.
+ Check the substring which the substring matched. */
+ bkc_idx = mctx->nbkref_ents;
+ err = get_subexp (mctx, node_idx, cur_str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+
+ /* And add the epsilon closures (which is `new_dest_nodes') of
+ the backreference to appropriate state_log. */
+#ifdef DEBUG
+ assert (dfa->nexts[node_idx] != -1);
+#endif
+ for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+ {
+ int subexp_len;
+ re_dfastate_t *dest_state;
+ struct re_backref_cache_entry *bkref_ent;
+ bkref_ent = mctx->bkref_ents + bkc_idx;
+ if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+ continue;
+ subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+ new_dest_nodes = (subexp_len == 0
+ ? dfa->eclosures + dfa->edests[node_idx].elems[0]
+ : dfa->eclosures + dfa->nexts[node_idx]);
+ dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+ - bkref_ent->subexp_from);
+ context = re_string_context_at (&mctx->input, dest_str_idx - 1,
+ mctx->eflags);
+ dest_state = mctx->state_log[dest_str_idx];
+ prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+ : mctx->state_log[cur_str_idx]->nodes.nelem);
+ /* Add `new_dest_node' to state_log. */
+ if (dest_state == NULL)
+ {
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, new_dest_nodes,
+ context);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ else
+ {
+ re_node_set dest_nodes;
+ err = re_node_set_init_union (&dest_nodes,
+ dest_state->entrance_nodes,
+ new_dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&dest_nodes);
+ goto free_return;
+ }
+ mctx->state_log[dest_str_idx]
+ = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+ re_node_set_free (&dest_nodes);
+ if (BE (mctx->state_log[dest_str_idx] == NULL
+ && err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ /* We need to check recursively if the backreference can epsilon
+ transit. */
+ if (subexp_len == 0
+ && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+ {
+ err = check_subexp_matching_top (mctx, new_dest_nodes,
+ cur_str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ err = transit_state_bkref (mctx, new_dest_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ goto free_return;
+ }
+ }
+ }
+ err = REG_NOERROR;
+ free_return:
+ return err;
+}
+
+/* Enumerate all the candidates which the backreference BKREF_NODE can match
+ at BKREF_STR_IDX, and register them by match_ctx_add_entry().
+ Note that we might collect inappropriate candidates here.
+ However, the cost of checking them strictly here is too high, then we
+ delay these checking for prune_impossible_nodes(). */
+
+static reg_errcode_t
+get_subexp (mctx, bkref_node, bkref_str_idx)
+ re_match_context_t *mctx;
+ int bkref_node, bkref_str_idx;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int subexp_num, sub_top_idx;
+ const char *buf = (const char *) re_string_get_buffer (&mctx->input);
+ /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */
+ int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+ if (cache_idx != -1)
+ {
+ const struct re_backref_cache_entry *entry = mctx->bkref_ents + cache_idx;
+ do
+ if (entry->node == bkref_node)
+ return REG_NOERROR; /* We already checked it. */
+ while (entry++->more);
+ }
+
+ subexp_num = dfa->nodes[bkref_node].opr.idx;
+
+ /* For each sub expression */
+ for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
+ {
+ reg_errcode_t err;
+ re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
+ re_sub_match_last_t *sub_last;
+ int sub_last_idx, sl_str, bkref_str_off;
+
+ if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
+ continue; /* It isn't related. */
+
+ sl_str = sub_top->str_idx;
+ bkref_str_off = bkref_str_idx;
+ /* At first, check the last node of sub expressions we already
+ evaluated. */
+ for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
+ {
+ int sl_str_diff;
+ sub_last = sub_top->lasts[sub_last_idx];
+ sl_str_diff = sub_last->str_idx - sl_str;
+ /* The matched string by the sub expression match with the substring
+ at the back reference? */
+ if (sl_str_diff > 0)
+ {
+ if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0))
+ {
+ /* Not enough chars for a successful match. */
+ if (bkref_str_off + sl_str_diff > mctx->input.len)
+ break;
+
+ err = clean_state_log_if_needed (mctx,
+ bkref_str_off
+ + sl_str_diff);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+ }
+ if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0)
+ break; /* We don't need to search this sub expression any more. */
+ }
+ bkref_str_off += sl_str_diff;
+ sl_str += sl_str_diff;
+ err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+ bkref_str_idx);
+
+ /* Reload buf, since the preceding call might have reallocated
+ the buffer. */
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+
+ if (err == REG_NOMATCH)
+ continue;
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+
+ if (sub_last_idx < sub_top->nlasts)
+ continue;
+ if (sub_last_idx > 0)
+ ++sl_str;
+ /* Then, search for the other last nodes of the sub expression. */
+ for (; sl_str <= bkref_str_idx; ++sl_str)
+ {
+ int cls_node, sl_str_off;
+ const re_node_set *nodes;
+ sl_str_off = sl_str - sub_top->str_idx;
+ /* The matched string by the sub expression match with the substring
+ at the back reference? */
+ if (sl_str_off > 0)
+ {
+ if (BE (bkref_str_off >= mctx->input.valid_len, 0))
+ {
+ /* If we are at the end of the input, we cannot match. */
+ if (bkref_str_off >= mctx->input.len)
+ break;
+
+ err = extend_buffers (mctx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+
+ buf = (const char *) re_string_get_buffer (&mctx->input);
+ }
+ if (buf [bkref_str_off++] != buf[sl_str - 1])
+ break; /* We don't need to search this sub expression
+ any more. */
+ }
+ if (mctx->state_log[sl_str] == NULL)
+ continue;
+ /* Does this state have a ')' of the sub expression? */
+ nodes = &mctx->state_log[sl_str]->nodes;
+ cls_node = find_subexp_node (dfa, nodes, subexp_num, OP_CLOSE_SUBEXP);
+ if (cls_node == -1)
+ continue; /* No. */
+ if (sub_top->path == NULL)
+ {
+ sub_top->path = calloc (sizeof (state_array_t),
+ sl_str - sub_top->str_idx + 1);
+ if (sub_top->path == NULL)
+ return REG_ESPACE;
+ }
+ /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
+ in the current context? */
+ err = check_arrival (mctx, sub_top->path, sub_top->node,
+ sub_top->str_idx, cls_node, sl_str, OP_CLOSE_SUBEXP);
+ if (err == REG_NOMATCH)
+ continue;
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
+ if (BE (sub_last == NULL, 0))
+ return REG_ESPACE;
+ err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
+ bkref_str_idx);
+ if (err == REG_NOMATCH)
+ continue;
+ }
+ }
+ return REG_NOERROR;
+}
+
+/* Helper functions for get_subexp(). */
+
+/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
+ If it can arrive, register the sub expression expressed with SUB_TOP
+ and SUB_LAST. */
+
+static reg_errcode_t
+get_subexp_sub (mctx, sub_top, sub_last, bkref_node, bkref_str)
+ re_match_context_t *mctx;
+ const re_sub_match_top_t *sub_top;
+ re_sub_match_last_t *sub_last;
+ int bkref_node, bkref_str;
+{
+ reg_errcode_t err;
+ int to_idx;
+ /* Can the subexpression arrive the back reference? */
+ err = check_arrival (mctx, &sub_last->path, sub_last->node,
+ sub_last->str_idx, bkref_node, bkref_str, OP_OPEN_SUBEXP);
+ if (err != REG_NOERROR)
+ return err;
+ err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
+ sub_last->str_idx);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
+ return clean_state_log_if_needed (mctx, to_idx);
+}
+
+/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
+ Search '(' if FL_OPEN, or search ')' otherwise.
+ TODO: This function isn't efficient...
+ Because there might be more than one nodes whose types are
+ OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+ nodes.
+ E.g. RE: (a){2} */
+
+static int
+find_subexp_node (dfa, nodes, subexp_idx, type)
+ const re_dfa_t *dfa;
+ const re_node_set *nodes;
+ int subexp_idx, type;
+{
+ int cls_idx;
+ for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
+ {
+ int cls_node = nodes->elems[cls_idx];
+ const re_token_t *node = dfa->nodes + cls_node;
+ if (node->type == type
+ && node->opr.idx == subexp_idx)
+ return cls_node;
+ }
+ return -1;
+}
+
+/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
+ LAST_NODE at LAST_STR. We record the path onto PATH since it will be
+ heavily reused.
+ Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */
+
+static reg_errcode_t
+check_arrival (mctx, path, top_node, top_str, last_node, last_str,
+ type)
+ re_match_context_t *mctx;
+ state_array_t *path;
+ int top_node, top_str, last_node, last_str, type;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int subexp_num, backup_cur_idx, str_idx, null_cnt;
+ re_dfastate_t *cur_state = NULL;
+ re_node_set *cur_nodes, next_nodes;
+ re_dfastate_t **backup_state_log;
+ unsigned int context;
+
+ subexp_num = dfa->nodes[top_node].opr.idx;
+ /* Extend the buffer if we need. */
+ if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0))
+ {
+ re_dfastate_t **new_array;
+ int old_alloc = path->alloc;
+ path->alloc += last_str + mctx->max_mb_elem_len + 1;
+ new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
+ if (new_array == NULL)
+ {
+ path->alloc = old_alloc;
+ return REG_ESPACE;
+ }
+ path->array = new_array;
+ memset (new_array + old_alloc, '\0',
+ sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+ }
+
+ str_idx = path->next_idx == 0 ? top_str : path->next_idx;
+
+ /* Temporary modify MCTX. */
+ backup_state_log = mctx->state_log;
+ backup_cur_idx = mctx->input.cur_idx;
+ mctx->state_log = path->array;
+ mctx->input.cur_idx = str_idx;
+
+ /* Setup initial node set. */
+ context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+ if (str_idx == top_str)
+ {
+ err = re_node_set_init_1 (&next_nodes, top_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ else
+ {
+ cur_state = mctx->state_log[str_idx];
+ if (cur_state && cur_state->has_backref)
+ {
+ err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
+ if (BE ( err != REG_NOERROR, 0))
+ return err;
+ }
+ else
+ re_node_set_init_empty (&next_nodes);
+ }
+ if (str_idx == top_str || (cur_state && cur_state->has_backref))
+ {
+ if (next_nodes.nelem)
+ {
+ err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+ subexp_num, type);
+ if (BE ( err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+ if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ mctx->state_log[str_idx] = cur_state;
+ }
+
+ for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+ {
+ re_node_set_empty (&next_nodes);
+ if (mctx->state_log[str_idx + 1])
+ {
+ err = re_node_set_merge (&next_nodes,
+ &mctx->state_log[str_idx + 1]->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ if (cur_state)
+ {
+ err = check_arrival_add_next_nodes (mctx, str_idx,
+ &cur_state->non_eps_nodes, &next_nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ ++str_idx;
+ if (next_nodes.nelem)
+ {
+ err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+ subexp_num, type);
+ if (BE ( err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ }
+ context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
+ cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+ if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&next_nodes);
+ return err;
+ }
+ mctx->state_log[str_idx] = cur_state;
+ null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
+ }
+ re_node_set_free (&next_nodes);
+ cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
+ : &mctx->state_log[last_str]->nodes);
+ path->next_idx = str_idx;
+
+ /* Fix MCTX. */
+ mctx->state_log = backup_state_log;
+ mctx->input.cur_idx = backup_cur_idx;
+
+ /* Then check the current node set has the node LAST_NODE. */
+ if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node))
+ return REG_NOERROR;
+
+ return REG_NOMATCH;
+}
+
+/* Helper functions for check_arrival. */
+
+/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
+ to NEXT_NODES.
+ TODO: This function is similar to the functions transit_state*(),
+ however this function has many additional works.
+ Can't we unify them? */
+
+static reg_errcode_t
+check_arrival_add_next_nodes (mctx, str_idx, cur_nodes, next_nodes)
+ re_match_context_t *mctx;
+ int str_idx;
+ re_node_set *cur_nodes, *next_nodes;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ int result;
+ int cur_idx;
+ reg_errcode_t err;
+ re_node_set union_set;
+ re_node_set_init_empty (&union_set);
+ for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
+ {
+ int naccepted = 0;
+ int cur_node = cur_nodes->elems[cur_idx];
+#if defined DEBUG || defined RE_ENABLE_I18N
+ re_token_type_t type = dfa->nodes[cur_node].type;
+#endif
+#ifdef DEBUG
+ assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+ /* If the node may accept `multi byte'. */
+ if (ACCEPT_MB_NODE (type))
+ {
+ naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input,
+ str_idx);
+ if (naccepted > 1)
+ {
+ re_dfastate_t *dest_state;
+ int next_node = dfa->nexts[cur_node];
+ int next_idx = str_idx + naccepted;
+ dest_state = mctx->state_log[next_idx];
+ re_node_set_empty (&union_set);
+ if (dest_state)
+ {
+ err = re_node_set_merge (&union_set, &dest_state->nodes);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&union_set);
+ return err;
+ }
+ }
+ result = re_node_set_insert (&union_set, next_node);
+ if (BE (result < 0, 0))
+ {
+ re_node_set_free (&union_set);
+ return REG_ESPACE;
+ }
+ mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
+ &union_set);
+ if (BE (mctx->state_log[next_idx] == NULL
+ && err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&union_set);
+ return err;
+ }
+ }
+ }
+#endif /* RE_ENABLE_I18N */
+ if (naccepted
+ || check_node_accept (mctx, dfa->nodes + cur_node, str_idx))
+ {
+ result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+ if (BE (result < 0, 0))
+ {
+ re_node_set_free (&union_set);
+ return REG_ESPACE;
+ }
+ }
+ }
+ re_node_set_free (&union_set);
+ return REG_NOERROR;
+}
+
+/* For all the nodes in CUR_NODES, add the epsilon closures of them to
+ CUR_NODES, however exclude the nodes which are:
+ - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
+ - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
+*/
+
+static reg_errcode_t
+check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, type)
+ re_dfa_t *dfa;
+ re_node_set *cur_nodes;
+ int ex_subexp, type;
+{
+ reg_errcode_t err;
+ int idx, outside_node;
+ re_node_set new_nodes;
+#ifdef DEBUG
+ assert (cur_nodes->nelem);
+#endif
+ err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ /* Create a new node set NEW_NODES with the nodes which are epsilon
+ closures of the node in CUR_NODES. */
+
+ for (idx = 0; idx < cur_nodes->nelem; ++idx)
+ {
+ int cur_node = cur_nodes->elems[idx];
+ re_node_set *eclosure = dfa->eclosures + cur_node;
+ outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type);
+ if (outside_node == -1)
+ {
+ /* There are no problematic nodes, just merge them. */
+ err = re_node_set_merge (&new_nodes, eclosure);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&new_nodes);
+ return err;
+ }
+ }
+ else
+ {
+ /* There are problematic nodes, re-calculate incrementally. */
+ err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
+ ex_subexp, type);
+ if (BE (err != REG_NOERROR, 0))
+ {
+ re_node_set_free (&new_nodes);
+ return err;
+ }
+ }
+ }
+ re_node_set_free (cur_nodes);
+ *cur_nodes = new_nodes;
+ return REG_NOERROR;
+}
+
+/* Helper function for check_arrival_expand_ecl.
+ Check incrementally the epsilon closure of TARGET, and if it isn't
+ problematic append it to DST_NODES. */
+
+static reg_errcode_t
+check_arrival_expand_ecl_sub (dfa, dst_nodes, target, ex_subexp, type)
+ re_dfa_t *dfa;
+ int target, ex_subexp, type;
+ re_node_set *dst_nodes;
+{
+ int cur_node;
+ for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
+ {
+ int err;
+
+ if (dfa->nodes[cur_node].type == type
+ && dfa->nodes[cur_node].opr.idx == ex_subexp)
+ {
+ if (type == OP_CLOSE_SUBEXP)
+ {
+ err = re_node_set_insert (dst_nodes, cur_node);
+ if (BE (err == -1, 0))
+ return REG_ESPACE;
+ }
+ break;
+ }
+ err = re_node_set_insert (dst_nodes, cur_node);
+ if (BE (err == -1, 0))
+ return REG_ESPACE;
+ if (dfa->edests[cur_node].nelem == 0)
+ break;
+ if (dfa->edests[cur_node].nelem == 2)
+ {
+ err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
+ dfa->edests[cur_node].elems[1],
+ ex_subexp, type);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ cur_node = dfa->edests[cur_node].elems[0];
+ }
+ return REG_NOERROR;
+}
+
+
+/* For all the back references in the current state, calculate the
+ destination of the back references by the appropriate entry
+ in MCTX->BKREF_ENTS. */
+
+static reg_errcode_t
+expand_bkref_cache (mctx, cur_nodes, cur_str, subexp_num,
+ type)
+ re_match_context_t *mctx;
+ int cur_str, subexp_num, type;
+ re_node_set *cur_nodes;
+{
+ re_dfa_t *const dfa = mctx->dfa;
+ reg_errcode_t err;
+ int cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+ struct re_backref_cache_entry *ent;
+
+ if (cache_idx_start == -1)
+ return REG_NOERROR;
+
+ restart:
+ ent = mctx->bkref_ents + cache_idx_start;
+ do
+ {
+ int to_idx, next_node;
+
+ /* Is this entry ENT is appropriate? */
+ if (!re_node_set_contains (cur_nodes, ent->node))
+ continue; /* No. */
+
+ to_idx = cur_str + ent->subexp_to - ent->subexp_from;
+ /* Calculate the destination of the back reference, and append it
+ to MCTX->STATE_LOG. */
+ if (to_idx == cur_str)
+ {
+ /* The backreference did epsilon transit, we must re-check all the
+ node in the current state. */
+ re_node_set new_dests;
+ reg_errcode_t err2, err3;
+ next_node = dfa->edests[ent->node].elems[0];
+ if (re_node_set_contains (cur_nodes, next_node))
+ continue;
+ err = re_node_set_init_1 (&new_dests, next_node);
+ err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type);
+ err3 = re_node_set_merge (cur_nodes, &new_dests);
+ re_node_set_free (&new_dests);
+ if (BE (err != REG_NOERROR || err2 != REG_NOERROR
+ || err3 != REG_NOERROR, 0))
+ {
+ err = (err != REG_NOERROR ? err
+ : (err2 != REG_NOERROR ? err2 : err3));
+ return err;
+ }
+ /* TODO: It is still inefficient... */
+ goto restart;
+ }
+ else
+ {
+ re_node_set union_set;
+ next_node = dfa->nexts[ent->node];
+ if (mctx->state_log[to_idx])
+ {
+ int ret;
+ if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
+ next_node))
+ continue;
+ err = re_node_set_init_copy (&union_set,
+ &mctx->state_log[to_idx]->nodes);
+ ret = re_node_set_insert (&union_set, next_node);
+ if (BE (err != REG_NOERROR || ret < 0, 0))
+ {
+ re_node_set_free (&union_set);
+ err = err != REG_NOERROR ? err : REG_ESPACE;
+ return err;
+ }
+ }
+ else
+ {
+ err = re_node_set_init_1 (&union_set, next_node);
+ if (BE (err != REG_NOERROR, 0))
+ return err;
+ }
+ mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
+ re_node_set_free (&union_set);
+ if (BE (mctx->state_log[to_idx] == NULL
+ && err != REG_NOERROR, 0))
+ return err;
+ }
+ }
+ while (ent++->more);
+ return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+ Return the new table if succeeded, otherwise return NULL. */
+
+static re_dfastate_t **
+build_trtable (dfa, state)
+ re_dfa_t *dfa;
+ re_dfastate_t *state;
+{
+ reg_errcode_t err;
+ int i, j, ch;
+ unsigned int elem, mask;
+ int dests_node_malloced = 0, dest_states_malloced = 0;
+ int ndests; /* Number of the destination states from `state'. */
+ re_dfastate_t **trtable;
+ re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
+ re_node_set follows, *dests_node;
+ bitset *dests_ch;
+ bitset acceptable;
+
+ /* We build DFA states which corresponds to the destination nodes
+ from `state'. `dests_node[i]' represents the nodes which i-th
+ destination state contains, and `dests_ch[i]' represents the
+ characters which i-th destination state accepts. */
+#ifdef _LIBC
+ if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX))
+ dests_node = (re_node_set *)
+ alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX);
+ else
+#endif
+ {
+ dests_node = (re_node_set *)
+ malloc ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX);
+ if (BE (dests_node == NULL, 0))
+ return NULL;
+ dests_node_malloced = 1;
+ }
+ dests_ch = (bitset *) (dests_node + SBC_MAX);
+
+ /* Initialize transiton table. */
+ state->word_trtable = 0;
+
+ /* At first, group all nodes belonging to `state' into several
+ destinations. */
+ ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch);
+ if (BE (ndests <= 0, 0))
+ {
+ if (dests_node_malloced)
+ free (dests_node);
+ /* Return NULL in case of an error, trtable otherwise. */
+ if (ndests == 0)
+ {
+ state->trtable = (re_dfastate_t **)
+ calloc (sizeof (re_dfastate_t *), SBC_MAX);;
+ return state->trtable;
+ }
+ return NULL;
+ }
+
+ err = re_node_set_alloc (&follows, ndests + 1);
+ if (BE (err != REG_NOERROR, 0))
+ goto out_free;
+
+#ifdef _LIBC
+ if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX
+ + ndests * 3 * sizeof (re_dfastate_t *)))
+ dest_states = (re_dfastate_t **)
+ alloca (ndests * 3 * sizeof (re_dfastate_t *));
+ else
+#endif
+ {
+ dest_states = (re_dfastate_t **)
+ malloc (ndests * 3 * sizeof (re_dfastate_t *));
+ if (BE (dest_states == NULL, 0))
+ {
+out_free:
+ if (dest_states_malloced)
+ free (dest_states);
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+ if (dests_node_malloced)
+ free (dests_node);
+ return NULL;
+ }
+ dest_states_malloced = 1;
+ }
+ dest_states_word = dest_states + ndests;
+ dest_states_nl = dest_states_word + ndests;
+ bitset_empty (acceptable);
+
+ /* Then build the states for all destinations. */
+ for (i = 0; i < ndests; ++i)
+ {
+ int next_node;
+ re_node_set_empty (&follows);
+ /* Merge the follows of this destination states. */
+ for (j = 0; j < dests_node[i].nelem; ++j)
+ {
+ next_node = dfa->nexts[dests_node[i].elems[j]];
+ if (next_node != -1)
+ {
+ err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+ if (BE (err != REG_NOERROR, 0))
+ goto out_free;
+ }
+ }
+ dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+ if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+ /* If the new state has context constraint,
+ build appropriate states for these contexts. */
+ if (dest_states[i]->has_constraint)
+ {
+ dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_WORD);
+ if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+
+ if (dest_states[i] != dest_states_word[i]
+ && dfa->mb_cur_max > 1)
+ state->word_trtable = 1;
+
+ dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+ CONTEXT_NEWLINE);
+ if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+ goto out_free;
+ }
+ else
+ {
+ dest_states_word[i] = dest_states[i];
+ dest_states_nl[i] = dest_states[i];
+ }
+ bitset_merge (acceptable, dests_ch[i]);
+ }
+
+ if (!BE (state->word_trtable, 0))
+ {
+ /* We don't care about whether the following character is a word
+ character, or we are in a single-byte character set so we can
+ discern by looking at the character code: allocate a
+ 256-entry transition table. */
+ trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+ if (BE (trtable == NULL, 0))
+ goto out_free;
+
+ /* For all characters ch...: */
+ for (i = 0; i < BITSET_UINTS; ++i)
+ for (ch = i * UINT_BITS, elem = acceptable[i], mask = 1;
+ elem;
+ mask <<= 1, elem >>= 1, ++ch)
+ if (BE (elem & 1, 0))
+ {
+ /* There must be exactly one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+ ;
+
+ /* j-th destination accepts the word character ch. */
+ if (dfa->word_char[i] & mask)
+ trtable[ch] = dest_states_word[j];
+ else
+ trtable[ch] = dest_states[j];
+ }
+ }
+ else
+ {
+ /* We care about whether the following character is a word
+ character, and we are in a multi-byte character set: discern
+ by looking at the character code: build two 256-entry
+ transition tables, one starting at trtable[0] and one
+ starting at trtable[SBC_MAX]. */
+ trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *),
+ 2 * SBC_MAX);
+ if (BE (trtable == NULL, 0))
+ goto out_free;
+
+ /* For all characters ch...: */
+ for (i = 0; i < BITSET_UINTS; ++i)
+ for (ch = i * UINT_BITS, elem = acceptable[i], mask = 1;
+ elem;
+ mask <<= 1, elem >>= 1, ++ch)
+ if (BE (elem & 1, 0))
+ {
+ /* There must be exactly one destination which accepts
+ character ch. See group_nodes_into_DFAstates. */
+ for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+ ;
+
+ /* j-th destination accepts the word character ch. */
+ trtable[ch] = dest_states[j];
+ trtable[ch + SBC_MAX] = dest_states_word[j];
+ }
+ }
+
+ /* new line */
+ if (bitset_contain (acceptable, NEWLINE_CHAR))
+ {
+ /* The current state accepts newline character. */
+ for (j = 0; j < ndests; ++j)
+ if (bitset_contain (dests_ch[j], NEWLINE_CHAR))
+ {
+ /* k-th destination accepts newline character. */
+ trtable[NEWLINE_CHAR] = dest_states_nl[j];
+ if (state->word_trtable)
+ trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j];
+ /* There must be only one destination which accepts
+ newline. See group_nodes_into_DFAstates. */
+ break;
+ }
+ }
+
+ if (dest_states_malloced)
+ free (dest_states);
+
+ re_node_set_free (&follows);
+ for (i = 0; i < ndests; ++i)
+ re_node_set_free (dests_node + i);
+
+ if (dests_node_malloced)
+ free (dests_node);
+
+ state->trtable = trtable;
+ return trtable;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+ Then for all destinations, set the nodes belonging to the destination
+ to DESTS_NODE[i] and set the characters accepted by the destination
+ to DEST_CH[i]. This function return the number of destinations. */
+
+static int
+group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch)
+ re_dfa_t *dfa;
+ const re_dfastate_t *state;
+ re_node_set *dests_node;
+ bitset *dests_ch;
+{
+ reg_errcode_t err;
+ int result;
+ int i, j, k;
+ int ndests; /* Number of the destinations from `state'. */
+ bitset accepts; /* Characters a node can accept. */
+ const re_node_set *cur_nodes = &state->nodes;
+ bitset_empty (accepts);
+ ndests = 0;
+
+ /* For all the nodes belonging to `state', */
+ for (i = 0; i < cur_nodes->nelem; ++i)
+ {
+ re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+ re_token_type_t type = node->type;
+ unsigned int constraint = node->constraint;
+
+ /* Enumerate all single byte character this node can accept. */
+ if (type == CHARACTER)
+ bitset_set (accepts, node->opr.c);
+ else if (type == SIMPLE_BRACKET)
+ {
+ bitset_merge (accepts, node->opr.sbcset);
+ }
+ else if (type == OP_PERIOD)
+ {
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ bitset_merge (accepts, dfa->sb_char);
+ else
+#endif
+ bitset_set_all (accepts);
+ if (!(dfa->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (dfa->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+#ifdef RE_ENABLE_I18N
+ else if (type == OP_UTF8_PERIOD)
+ {
+ memset (accepts, 255, sizeof (unsigned int) * BITSET_UINTS / 2);
+ if (!(dfa->syntax & RE_DOT_NEWLINE))
+ bitset_clear (accepts, '\n');
+ if (dfa->syntax & RE_DOT_NOT_NULL)
+ bitset_clear (accepts, '\0');
+ }
+#endif
+ else
+ continue;
+
+ /* Check the `accepts' and sift the characters which are not
+ match it the context. */
+ if (constraint)
+ {
+ if (constraint & NEXT_NEWLINE_CONSTRAINT)
+ {
+ int accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+ bitset_empty (accepts);
+ if (accepts_newline)
+ bitset_set (accepts, NEWLINE_CHAR);
+ else
+ continue;
+ }
+ if (constraint & NEXT_ENDBUF_CONSTRAINT)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+
+ if (constraint & NEXT_WORD_CONSTRAINT)
+ {
+ unsigned int any_set = 0;
+ if (type == CHARACTER && !node->word_char)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ for (j = 0; j < BITSET_UINTS; ++j)
+ any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j]));
+ else
+#endif
+ for (j = 0; j < BITSET_UINTS; ++j)
+ any_set |= (accepts[j] &= dfa->word_char[j]);
+ if (!any_set)
+ continue;
+ }
+ if (constraint & NEXT_NOTWORD_CONSTRAINT)
+ {
+ unsigned int any_set = 0;
+ if (type == CHARACTER && node->word_char)
+ {
+ bitset_empty (accepts);
+ continue;
+ }
+#ifdef RE_ENABLE_I18N
+ if (dfa->mb_cur_max > 1)
+ for (j = 0; j < BITSET_UINTS; ++j)
+ any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j]));
+ else
+#endif
+ for (j = 0; j < BITSET_UINTS; ++j)
+ any_set |= (accepts[j] &= ~dfa->word_char[j]);
+ if (!any_set)
+ continue;
+ }
+ }
+
+ /* Then divide `accepts' into DFA states, or create a new
+ state. Above, we make sure that accepts is not empty. */
+ for (j = 0; j < ndests; ++j)
+ {
+ bitset intersec; /* Intersection sets, see below. */
+ bitset remains;
+ /* Flags, see below. */
+ int has_intersec, not_subset, not_consumed;
+
+ /* Optimization, skip if this state doesn't accept the character. */
+ if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+ continue;
+
+ /* Enumerate the intersection set of this state and `accepts'. */
+ has_intersec = 0;
+ for (k = 0; k < BITSET_UINTS; ++k)
+ has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+ /* And skip if the intersection set is empty. */
+ if (!has_intersec)
+ continue;
+
+ /* Then check if this state is a subset of `accepts'. */
+ not_subset = not_consumed = 0;
+ for (k = 0; k < BITSET_UINTS; ++k)
+ {
+ not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+ not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+ }
+
+ /* If this state isn't a subset of `accepts', create a
+ new group state, which has the `remains'. */
+ if (not_subset)
+ {
+ bitset_copy (dests_ch[ndests], remains);
+ bitset_copy (dests_ch[j], intersec);
+ err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+ if (BE (err != REG_NOERROR, 0))
+ goto error_return;
+ ++ndests;
+ }
+
+ /* Put the position in the current group. */
+ result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+ if (BE (result < 0, 0))
+ goto error_return;
+
+ /* If all characters are consumed, go to next node. */
+ if (!not_consumed)
+ break;
+ }
+ /* Some characters remain, create a new group. */
+ if (j == ndests)
+ {
+ bitset_copy (dests_ch[ndests], accepts);
+ err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+ if (BE (err != REG_NOERROR, 0))
+ goto error_return;
+ ++ndests;
+ bitset_empty (accepts);
+ }
+ }
+ return ndests;
+ error_return:
+ for (j = 0; j < ndests; ++j)
+ re_node_set_free (dests_node + j);
+ return -1;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+ Return the number of the bytes the node accepts.
+ STR_IDX is the current index of the input string.
+
+ This function handles the nodes which can accept one character, or
+ one collating element like '.', '[a-z]', opposite to the other nodes
+ can only accept one byte. */
+
+static int
+check_node_accept_bytes (dfa, node_idx, input, str_idx)
+ re_dfa_t *dfa;
+ int node_idx, str_idx;
+ const re_string_t *input;
+{
+ const re_token_t *node = dfa->nodes + node_idx;
+ int char_len, elem_len;
+ int i;
+
+ if (BE (node->type == OP_UTF8_PERIOD, 0))
+ {
+ unsigned char c = re_string_byte_at (input, str_idx), d;
+ if (BE (c < 0xc2, 1))
+ return 0;
+
+ if (str_idx + 2 > input->len)
+ return 0;
+
+ d = re_string_byte_at (input, str_idx + 1);
+ if (c < 0xe0)
+ return (d < 0x80 || d > 0xbf) ? 0 : 2;
+ else if (c < 0xf0)
+ {
+ char_len = 3;
+ if (c == 0xe0 && d < 0xa0)
+ return 0;
+ }
+ else if (c < 0xf8)
+ {
+ char_len = 4;
+ if (c == 0xf0 && d < 0x90)
+ return 0;
+ }
+ else if (c < 0xfc)
+ {
+ char_len = 5;
+ if (c == 0xf8 && d < 0x88)
+ return 0;
+ }
+ else if (c < 0xfe)
+ {
+ char_len = 6;
+ if (c == 0xfc && d < 0x84)
+ return 0;
+ }
+ else
+ return 0;
+
+ if (str_idx + char_len > input->len)
+ return 0;
+
+ for (i = 1; i < char_len; ++i)
+ {
+ d = re_string_byte_at (input, str_idx + i);
+ if (d < 0x80 || d > 0xbf)
+ return 0;
+ }
+ return char_len;
+ }
+
+ char_len = re_string_char_size_at (input, str_idx);
+ if (node->type == OP_PERIOD)
+ {
+ if (char_len <= 1)
+ return 0;
+ /* FIXME: I don't think this if is needed, as both '\n'
+ and '\0' are char_len == 1. */
+ /* '.' accepts any one character except the following two cases. */
+ if ((!(dfa->syntax & RE_DOT_NEWLINE) &&
+ re_string_byte_at (input, str_idx) == '\n') ||
+ ((dfa->syntax & RE_DOT_NOT_NULL) &&
+ re_string_byte_at (input, str_idx) == '\0'))
+ return 0;
+ return char_len;
+ }
+
+ elem_len = re_string_elem_size_at (input, str_idx);
+ if ((elem_len <= 1 && char_len <= 1) || char_len == 0)
+ return 0;
+
+ if (node->type == COMPLEX_BRACKET)
+ {
+ const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+ const unsigned char *pin = ((char *) re_string_get_buffer (input)
+ + str_idx);
+ int j;
+ uint32_t nrules;
+# endif /* _LIBC */
+ int match_len = 0;
+ wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+ ? re_string_wchar_at (input, str_idx) : 0);
+
+ /* match with multibyte character? */
+ for (i = 0; i < cset->nmbchars; ++i)
+ if (wc == cset->mbchars[i])
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ /* match with character_class? */
+ for (i = 0; i < cset->nchar_classes; ++i)
+ {
+ wctype_t wt = cset->char_classes[i];
+ if (__iswctype (wc, wt))
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+# ifdef _LIBC
+ nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules != 0)
+ {
+ unsigned int in_collseq = 0;
+ const int32_t *table, *indirect;
+ const unsigned char *weights, *extra;
+ const char *collseqwc;
+ int32_t idx;
+ /* This #include defines a local function! */
+# include <locale/weight.h>
+
+ /* match with collating_symbol? */
+ if (cset->ncoll_syms)
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ for (i = 0; i < cset->ncoll_syms; ++i)
+ {
+ const unsigned char *coll_sym = extra + cset->coll_syms[i];
+ /* Compare the length of input collating element and
+ the length of current collating element. */
+ if (*coll_sym != elem_len)
+ continue;
+ /* Compare each bytes. */
+ for (j = 0; j < *coll_sym; j++)
+ if (pin[j] != coll_sym[1 + j])
+ break;
+ if (j == *coll_sym)
+ {
+ /* Match if every bytes is equal. */
+ match_len = j;
+ goto check_node_accept_bytes_match;
+ }
+ }
+
+ if (cset->nranges)
+ {
+ if (elem_len <= char_len)
+ {
+ collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+ in_collseq = __collseq_table_lookup (collseqwc, wc);
+ }
+ else
+ in_collseq = find_collation_sequence_value (pin, elem_len);
+ }
+ /* match with range expression? */
+ for (i = 0; i < cset->nranges; ++i)
+ if (cset->range_starts[i] <= in_collseq
+ && in_collseq <= cset->range_ends[i])
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+
+ /* match with equivalence_class? */
+ if (cset->nequiv_classes)
+ {
+ const unsigned char *cp = pin;
+ table = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+ weights = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+ extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+ indirect = (const int32_t *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+ idx = findidx (&cp);
+ if (idx > 0)
+ for (i = 0; i < cset->nequiv_classes; ++i)
+ {
+ int32_t equiv_class_idx = cset->equiv_classes[i];
+ size_t weight_len = weights[idx];
+ if (weight_len == weights[equiv_class_idx])
+ {
+ int cnt = 0;
+ while (cnt <= weight_len
+ && (weights[equiv_class_idx + 1 + cnt]
+ == weights[idx + 1 + cnt]))
+ ++cnt;
+ if (cnt > weight_len)
+ {
+ match_len = elem_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ }
+ }
+ else
+# endif /* _LIBC */
+ {
+ /* match with range expression? */
+#if __GNUC__ >= 2
+ wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+ wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+ cmp_buf[2] = wc;
+#endif
+ for (i = 0; i < cset->nranges; ++i)
+ {
+ cmp_buf[0] = cset->range_starts[i];
+ cmp_buf[4] = cset->range_ends[i];
+ if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+ && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+ {
+ match_len = char_len;
+ goto check_node_accept_bytes_match;
+ }
+ }
+ }
+ check_node_accept_bytes_match:
+ if (!cset->non_match)
+ return match_len;
+ else
+ {
+ if (match_len > 0)
+ return 0;
+ else
+ return (elem_len > char_len) ? elem_len : char_len;
+ }
+ }
+ return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+find_collation_sequence_value (mbs, mbs_len)
+ const unsigned char *mbs;
+ size_t mbs_len;
+{
+ uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+ if (nrules == 0)
+ {
+ if (mbs_len == 1)
+ {
+ /* No valid character. Match it as a single byte character. */
+ const unsigned char *collseq = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+ return collseq[mbs[0]];
+ }
+ return UINT_MAX;
+ }
+ else
+ {
+ int32_t idx;
+ const unsigned char *extra = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+ int32_t extrasize = (const unsigned char *)
+ _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra;
+
+ for (idx = 0; idx < extrasize;)
+ {
+ int mbs_cnt, found = 0;
+ int32_t elem_mbs_len;
+ /* Skip the name of collating element name. */
+ idx = idx + extra[idx] + 1;
+ elem_mbs_len = extra[idx++];
+ if (mbs_len == elem_mbs_len)
+ {
+ for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+ if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+ break;
+ if (mbs_cnt == elem_mbs_len)
+ /* Found the entry. */
+ found = 1;
+ }
+ /* Skip the byte sequence of the collating element. */
+ idx += elem_mbs_len;
+ /* Adjust for the alignment. */
+ idx = (idx + 3) & ~3;
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ /* Skip the wide char sequence of the collating element. */
+ idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+ /* If we found the entry, return the sequence value. */
+ if (found)
+ return *(uint32_t *) (extra + idx);
+ /* Skip the collation sequence value. */
+ idx += sizeof (uint32_t);
+ }
+ return UINT_MAX;
+ }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+ byte of the INPUT. */
+
+static int
+check_node_accept (mctx, node, idx)
+ const re_match_context_t *mctx;
+ const re_token_t *node;
+ int idx;
+{
+ unsigned char ch;
+ ch = re_string_byte_at (&mctx->input, idx);
+ switch (node->type)
+ {
+ case CHARACTER:
+ if (node->opr.c != ch)
+ return 0;
+ break;
+
+ case SIMPLE_BRACKET:
+ if (!bitset_contain (node->opr.sbcset, ch))
+ return 0;
+ break;
+
+#ifdef RE_ENABLE_I18N
+ case OP_UTF8_PERIOD:
+ if (ch >= 0x80)
+ return 0;
+ /* FALLTHROUGH */
+#endif
+ case OP_PERIOD:
+ if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE))
+ || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL)))
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (node->constraint)
+ {
+ /* The node has constraints. Check whether the current context
+ satisfies the constraints. */
+ unsigned int context = re_string_context_at (&mctx->input, idx,
+ mctx->eflags);
+ if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Extend the buffers, if the buffers have run out. */
+
+static reg_errcode_t
+extend_buffers (mctx)
+ re_match_context_t *mctx;
+{
+ reg_errcode_t ret;
+ re_string_t *pstr = &mctx->input;
+
+ /* Double the lengthes of the buffers. */
+ ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+
+ if (mctx->state_log != NULL)
+ {
+ /* And double the length of state_log. */
+ /* XXX We have no indication of the size of this buffer. If this
+ allocation fail we have no indication that the state_log array
+ does not have the right size. */
+ re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+ pstr->bufs_len + 1);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ mctx->state_log = new_array;
+ }
+
+ /* Then reconstruct the buffers. */
+ if (pstr->icase)
+ {
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ {
+ ret = build_wcs_upper_buffer (pstr);
+ if (BE (ret != REG_NOERROR, 0))
+ return ret;
+ }
+ else
+#endif /* RE_ENABLE_I18N */
+ build_upper_buffer (pstr);
+ }
+ else
+ {
+#ifdef RE_ENABLE_I18N
+ if (pstr->mb_cur_max > 1)
+ build_wcs_buffer (pstr);
+ else
+#endif /* RE_ENABLE_I18N */
+ {
+ if (pstr->trans != NULL)
+ re_string_translate_buffer (pstr);
+ }
+ }
+ return REG_NOERROR;
+}
+
+
+/* Functions for matching context. */
+
+/* Initialize MCTX. */
+
+static reg_errcode_t
+match_ctx_init (mctx, eflags, n)
+ re_match_context_t *mctx;
+ int eflags, n;
+{
+ mctx->eflags = eflags;
+ mctx->match_last = -1;
+ if (n > 0)
+ {
+ mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+ mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+ if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
+ return REG_ESPACE;
+ }
+ /* Already zero-ed by the caller.
+ else
+ mctx->bkref_ents = NULL;
+ mctx->nbkref_ents = 0;
+ mctx->nsub_tops = 0; */
+ mctx->abkref_ents = n;
+ mctx->max_mb_elem_len = 1;
+ mctx->asub_tops = n;
+ return REG_NOERROR;
+}
+
+/* Clean the entries which depend on the current input in MCTX.
+ This function must be invoked when the matcher changes the start index
+ of the input, or changes the input string. */
+
+static void
+match_ctx_clean (mctx)
+ re_match_context_t *mctx;
+{
+ int st_idx;
+ for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
+ {
+ int sl_idx;
+ re_sub_match_top_t *top = mctx->sub_tops[st_idx];
+ for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
+ {
+ re_sub_match_last_t *last = top->lasts[sl_idx];
+ re_free (last->path.array);
+ re_free (last);
+ }
+ re_free (top->lasts);
+ if (top->path)
+ {
+ re_free (top->path->array);
+ re_free (top->path);
+ }
+ free (top);
+ }
+
+ mctx->nsub_tops = 0;
+ mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX. */
+
+static void
+match_ctx_free (mctx)
+ re_match_context_t *mctx;
+{
+ /* First, free all the memory associated with MCTX->SUB_TOPS. */
+ match_ctx_clean (mctx);
+ re_free (mctx->sub_tops);
+ re_free (mctx->bkref_ents);
+}
+
+/* Add a new backreference entry to MCTX.
+ Note that we assume that caller never call this function with duplicate
+ entry, and call with STR_IDX which isn't smaller than any existing entry.
+*/
+
+static reg_errcode_t
+match_ctx_add_entry (mctx, node, str_idx, from, to)
+ re_match_context_t *mctx;
+ int node, str_idx, from, to;
+{
+ if (mctx->nbkref_ents >= mctx->abkref_ents)
+ {
+ struct re_backref_cache_entry* new_entry;
+ new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+ mctx->abkref_ents * 2);
+ if (BE (new_entry == NULL, 0))
+ {
+ re_free (mctx->bkref_ents);
+ return REG_ESPACE;
+ }
+ mctx->bkref_ents = new_entry;
+ memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+ sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+ mctx->abkref_ents *= 2;
+ }
+ if (mctx->nbkref_ents > 0
+ && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
+ mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1;
+
+ mctx->bkref_ents[mctx->nbkref_ents].node = node;
+ mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+ mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+
+ /* This is a cache that saves negative results of check_dst_limits_calc_pos.
+ If bit N is clear, means that this entry won't epsilon-transition to
+ an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If
+ it is set, check_dst_limits_calc_pos_1 will recurse and try to find one
+ such node.
+
+ A backreference does not epsilon-transition unless it is empty, so set
+ to all zeros if FROM != TO. */
+ mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map
+ = (from == to ? ~0 : 0);
+
+ mctx->bkref_ents[mctx->nbkref_ents++].more = 0;
+ if (mctx->max_mb_elem_len < to - from)
+ mctx->max_mb_elem_len = to - from;
+ return REG_NOERROR;
+}
+
+/* Search for the first entry which has the same str_idx, or -1 if none is
+ found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */
+
+static int
+search_cur_bkref_entry (mctx, str_idx)
+ re_match_context_t *mctx;
+ int str_idx;
+{
+ int left, right, mid, last;
+ last = right = mctx->nbkref_ents;
+ for (left = 0; left < right;)
+ {
+ mid = (left + right) / 2;
+ if (mctx->bkref_ents[mid].str_idx < str_idx)
+ left = mid + 1;
+ else
+ right = mid;
+ }
+ if (left < last && mctx->bkref_ents[left].str_idx == str_idx)
+ return left;
+ else
+ return -1;
+}
+
+/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
+ at STR_IDX. */
+
+static reg_errcode_t
+match_ctx_add_subtop (mctx, node, str_idx)
+ re_match_context_t *mctx;
+ int node, str_idx;
+{
+#ifdef DEBUG
+ assert (mctx->sub_tops != NULL);
+ assert (mctx->asub_tops > 0);
+#endif
+ if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
+ {
+ int new_asub_tops = mctx->asub_tops * 2;
+ re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
+ re_sub_match_top_t *,
+ new_asub_tops);
+ if (BE (new_array == NULL, 0))
+ return REG_ESPACE;
+ mctx->sub_tops = new_array;
+ mctx->asub_tops = new_asub_tops;
+ }
+ mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
+ if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0))
+ return REG_ESPACE;
+ mctx->sub_tops[mctx->nsub_tops]->node = node;
+ mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
+ return REG_NOERROR;
+}
+
+/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
+ at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */
+
+static re_sub_match_last_t *
+match_ctx_add_sublast (subtop, node, str_idx)
+ re_sub_match_top_t *subtop;
+ int node, str_idx;
+{
+ re_sub_match_last_t *new_entry;
+ if (BE (subtop->nlasts == subtop->alasts, 0))
+ {
+ int new_alasts = 2 * subtop->alasts + 1;
+ re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
+ re_sub_match_last_t *,
+ new_alasts);
+ if (BE (new_array == NULL, 0))
+ return NULL;
+ subtop->lasts = new_array;
+ subtop->alasts = new_alasts;
+ }
+ new_entry = calloc (1, sizeof (re_sub_match_last_t));
+ if (BE (new_entry != NULL, 1))
+ {
+ subtop->lasts[subtop->nlasts] = new_entry;
+ new_entry->node = node;
+ new_entry->str_idx = str_idx;
+ ++subtop->nlasts;
+ }
+ return new_entry;
+}
+
+static void
+sift_ctx_init (sctx, sifted_sts, limited_sts, last_node, last_str_idx)
+ re_sift_context_t *sctx;
+ re_dfastate_t **sifted_sts, **limited_sts;
+ int last_node, last_str_idx;
+{
+ sctx->sifted_states = sifted_sts;
+ sctx->limited_states = limited_sts;
+ sctx->last_node = last_node;
+ sctx->last_str_idx = last_str_idx;
+ re_node_set_init_empty (&sctx->limits);
+}
diff --git a/src/res/Makeinfo b/src/res/Makeinfo
new file mode 100644
index 0000000..61215fc
--- /dev/null
+++ b/src/res/Makeinfo
@@ -0,0 +1,6 @@
+case "$HOST_SYSTEM" in
+ MINGW32*)
+ uqm_RCFILES="UrQuanMasters.rc"
+ ;;
+esac
+
diff --git a/src/res/UrQuanMasters.rc b/src/res/UrQuanMasters.rc
new file mode 100644
index 0000000..aaf61a1
--- /dev/null
+++ b/src/res/UrQuanMasters.rc
@@ -0,0 +1,76 @@
+/////////////////////////////////////////////////////////////////////////////
+// UrQuanMasters.rc
+//
+// Resource script for Win32 builds
+//
+
+#include "../uqmversion.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#ifdef _WIN32
+LANGUAGE 0x09, 0x01 // LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+1 VERSIONINFO
+ FILEVERSION UQM_MAJOR_VERSION,UQM_MINOR_VERSION,UQM_PATCH_VERSION,0
+ PRODUCTVERSION UQM_MAJOR_VERSION,UQM_MINOR_VERSION,UQM_PATCH_VERSION,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "See http://sc2.sourceforge.net\0"
+ VALUE "CompanyName", "sc2.sourceforge.net\0"
+ VALUE "FileDescription", "The Ur-Quan Masters main executable\0"
+ VALUE "FileVersion", UQM_STRING_VERSION "\0"
+ VALUE "InternalName", "uqm\0"
+ VALUE "LegalCopyright", "(C) 1992-1993, 2002-2011 by respective authors\0"
+#ifdef _DEBUG
+ VALUE "OriginalFilename", "uqmdebug.exe\0"
+#else
+ VALUE "OriginalFilename", "uqm.exe\0"
+#endif
+ VALUE "ProductName", "The Ur-Quan Masters\0"
+ VALUE "ProductVersion", UQM_STRING_VERSION "\0"
+ VALUE "SpecialBuild", "Alpha\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+SDL_app ICON PRELOAD DISCARDABLE "ur-quan-icon-alpha.ico"
+102 ICON DISCARDABLE "ur-quan-icon-std.ico"
+103 ICON DISCARDABLE "ur-quan1.ico"
+104 ICON DISCARDABLE "sis1.ico"
+105 ICON DISCARDABLE "ur-quan2.ico"
+106 ICON DISCARDABLE "kohr-ah1.ico"
+107 ICON DISCARDABLE "starcon2.ico"
diff --git a/src/res/darwin/Info.plist b/src/res/darwin/Info.plist
new file mode 100644
index 0000000..775285e
--- /dev/null
+++ b/src/res/darwin/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>The Ur-Quan Masters</string>
+ <key>CFBundleIconFile</key>
+ <string>The Ur-Quan Masters.icns</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@@VERSION@@</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>@@VERSION@@</string>
+</dict>
+</plist>
diff --git a/src/res/darwin/PkgInfo b/src/res/darwin/PkgInfo
new file mode 100644
index 0000000..bd04210
--- /dev/null
+++ b/src/res/darwin/PkgInfo
@@ -0,0 +1 @@
+APPL???? \ No newline at end of file
diff --git a/src/res/darwin/The Ur-Quan Masters.icns b/src/res/darwin/The Ur-Quan Masters.icns
new file mode 100644
index 0000000..5948823
--- /dev/null
+++ b/src/res/darwin/The Ur-Quan Masters.icns
Binary files differ
diff --git a/src/res/darwin/uqm.r b/src/res/darwin/uqm.r
new file mode 100644
index 0000000..35bb001
--- /dev/null
+++ b/src/res/darwin/uqm.r
@@ -0,0 +1,3 @@
+data 'MBAR' (128) {
+ $"0001 0080" /* ...€ */
+};
diff --git a/src/res/kohr-ah1.ico b/src/res/kohr-ah1.ico
new file mode 100644
index 0000000..d4aa7e7
--- /dev/null
+++ b/src/res/kohr-ah1.ico
Binary files differ
diff --git a/src/res/sis1.ico b/src/res/sis1.ico
new file mode 100644
index 0000000..84f9224
--- /dev/null
+++ b/src/res/sis1.ico
Binary files differ
diff --git a/src/res/starcon2.ico b/src/res/starcon2.ico
new file mode 100644
index 0000000..8b28c2c
--- /dev/null
+++ b/src/res/starcon2.ico
Binary files differ
diff --git a/src/res/ur-quan-icon-24-hover-alpha.ico b/src/res/ur-quan-icon-24-hover-alpha.ico
new file mode 100644
index 0000000..fb8b472
--- /dev/null
+++ b/src/res/ur-quan-icon-24-hover-alpha.ico
Binary files differ
diff --git a/src/res/ur-quan-icon-24-hover.ico b/src/res/ur-quan-icon-24-hover.ico
new file mode 100644
index 0000000..5c46054
--- /dev/null
+++ b/src/res/ur-quan-icon-24-hover.ico
Binary files differ
diff --git a/src/res/ur-quan-icon-alpha.ico b/src/res/ur-quan-icon-alpha.ico
new file mode 100644
index 0000000..4242535
--- /dev/null
+++ b/src/res/ur-quan-icon-alpha.ico
Binary files differ
diff --git a/src/res/ur-quan-icon-std.ico b/src/res/ur-quan-icon-std.ico
new file mode 100644
index 0000000..d084908
--- /dev/null
+++ b/src/res/ur-quan-icon-std.ico
Binary files differ
diff --git a/src/res/ur-quan1.ico b/src/res/ur-quan1.ico
new file mode 100644
index 0000000..1c40bbb
--- /dev/null
+++ b/src/res/ur-quan1.ico
Binary files differ
diff --git a/src/res/ur-quan2.ico b/src/res/ur-quan2.ico
new file mode 100644
index 0000000..d1ae7ec
--- /dev/null
+++ b/src/res/ur-quan2.ico
Binary files differ
diff --git a/src/symbian/bld.inf b/src/symbian/bld.inf
new file mode 100644
index 0000000..f9f1ad4
--- /dev/null
+++ b/src/symbian/bld.inf
@@ -0,0 +1,9 @@
+PRJ_PLATFORMS
+
+
+PRJ_EXPORTS
+
+
+PRJ_MMPFILES
+gnumakefile icons_scalable_dc.mk
+uqm.mmp
diff --git a/src/symbian/config.h b/src/symbian/config.h
new file mode 100644
index 0000000..c97b49e
--- /dev/null
+++ b/src/symbian/config.h
@@ -0,0 +1,57 @@
+/* This file contains some compile-time configuration options for Symbian
+ */
+
+#ifndef SYMBIAN_CONFIG_H_
+#define SYMBIAN_CONFIG_H_
+
+/* Directory where the UQM game data is located */
+#define CONTENTDIR "content"
+
+/* Directory where game data will be stored */
+#define USERDIR "userdata"
+
+/* Directory where config files will be stored */
+#define CONFIGDIR USERDIR
+
+/* Directory where supermelee teams will be stored */
+#define MELEEDIR "userdata\\teams\\"
+
+/* Directory where save games will be stored */
+#define SAVEDIR "userdata\\save\\"
+
+/* Define if words are stored with the most significant byte first */
+#undef WORDS_BIGENDIAN
+
+/* Defined if your system has readdir_r of its own */
+#undef HAVE_READDIR_R
+
+/* Defined if your system has setenv of its own */
+#define HAVE_SETENV
+
+/* Defined if your system has strupr of its own */
+#undef HAVE_STRUPR
+
+/* Defined if your system has strcasecmp of its own */
+#define HAVE_STRCASECMP_UQM
+ // Not using "HAVE_STRCASECMP" as that conflicts with SDL.
+
+/* Defined if your system has stricmp of its own */
+#undef HAVE_STRICMP
+
+/* Defined if your system has getopt_long */
+#undef HAVE_GETOPT_LONG
+
+/* Defined if your system has iswgraph of its own*/
+#define HAVE_ISWGRAPH
+
+/* Defined if your system has wchar_t of its own */
+#define HAVE_WCHAR_T
+
+/* Defined if your system has wint_t of its own */
+#define HAVE_WINT_T
+
+#define HAVE__BOOL
+
+#define PATH_MAX _POSIX_PATH_MAX
+
+#endif /* SYMBIAN_CONFIG_H_ */
diff --git a/src/symbian/icons_scalable_dc.mk b/src/symbian/icons_scalable_dc.mk
new file mode 100644
index 0000000..99ac591
--- /dev/null
+++ b/src/symbian/icons_scalable_dc.mk
@@ -0,0 +1,37 @@
+ifeq (WINS,$(findstring WINS, $(PLATFORM)))
+ZDIR=$(EPOCROOT)epoc32\release\$(PLATFORM)\$(CFG)\Z
+else
+ZDIR=$(EPOCROOT)epoc32\data\z
+endif
+
+TARGETDIR=$(ZDIR)\resource\apps
+ICONTARGETFILENAME=$(TARGETDIR)\uqm_icon.mif
+
+ICONDIR=
+
+do_nothing :
+ @rem do_nothing
+
+MAKMAKE : do_nothing
+
+BLD : do_nothing
+
+CLEAN : do_nothing
+
+LIB : do_nothing
+
+CLEANLIB : do_nothing
+
+RESOURCE :
+ mifconv $(ICONTARGETFILENAME) \
+ /c32 uqm.svg
+
+FREEZE : do_nothing
+
+SAVESPACE : do_nothing
+
+RELEASABLES :
+ @echo $(ICONTARGETFILENAME)
+
+FINAL : do_nothing
+
diff --git a/src/symbian/uqm-armv5.pkg b/src/symbian/uqm-armv5.pkg
new file mode 100644
index 0000000..3549fd7
--- /dev/null
+++ b/src/symbian/uqm-armv5.pkg
@@ -0,0 +1,26 @@
+;Language - standard language definitions
+&EN
+
+; standard SIS file header
+#{"Ur-Quan Masters"},(0xA000A0C3),1,0,0
+
+;Localised Vendor name
+%{"Interstellar Frungy League"}
+
+;Unique Vendor name
+:"Interstellar Frungy League"
+
+;Supports Series 60 v 3.0
+[0x101F7961], 0, 0, 0, {"Series60ProductID"}
+
+*"uqm.key", "uqm.cer"
+
+;Files to install
+;<source> <destination>
+"\Epoc32\release\Armv5\urel\uqm.exe" - "!:\sys\bin\uqm.exe"
+"\Epoc32\data\z\resource\apps\uqm.rsc" - "!:\resource\apps\uqm.rsc"
+"\Epoc32\data\z\resource\apps\uqm_icon.mif" - "!:\resource\apps\uqm_icon.mif"
+"\Epoc32\data\z\private\10003a3f\import\apps\uqm_reg.rsc" - "!:\private\10003a3f\import\apps\uqm_reg.rsc"
+"uqm.cfg" - "!:\private\A000A0C3\userdata\uqm.cfg"
+"..\..\content\version" - "!:\private\A000A0C3\content\version"
+"..\..\content.uqm" - "!:\private\A000A0C3\content\packages\content.uqm"
diff --git a/src/symbian/uqm-gcce.pkg b/src/symbian/uqm-gcce.pkg
new file mode 100644
index 0000000..afc6e57
--- /dev/null
+++ b/src/symbian/uqm-gcce.pkg
@@ -0,0 +1,26 @@
+;Language - standard language definitions
+&EN
+
+; standard SIS file header
+#{"Ur-Quan Masters"},(0xA000A0C3),1,0,0
+
+;Localised Vendor name
+%{"Interstellar Frungy League"}
+
+;Unique Vendor name
+:"Interstellar Frungy League"
+
+;Supports Series 60 v 3.0
+[0x101F7961], 0, 0, 0, {"Series60ProductID"}
+
+*"uqm.key", "uqm.cer"
+
+;Files to install
+;<source> <destination>
+"\Epoc32\release\gcce\urel\uqm.exe" - "!:\sys\bin\uqm.exe"
+"\Epoc32\data\z\resource\apps\uqm.rsc" - "!:\resource\apps\uqm.rsc"
+"\Epoc32\data\z\resource\apps\uqm_icon.mif" - "!:\resource\apps\uqm_icon.mif"
+"\Epoc32\data\z\private\10003a3f\import\apps\uqm_reg.rsc" - "!:\private\10003a3f\import\apps\uqm_reg.rsc"
+"uqm.cfg" - "!:\private\A000A0C3\userdata\uqm.cfg"
+"..\..\content\version" - "!:\private\A000A0C3\content\version"
+"..\..\content.uqm" - "!:\private\A000A0C3\content\packages\content.uqm"
diff --git a/src/symbian/uqm.cfg b/src/symbian/uqm.cfg
new file mode 100644
index 0000000..4dbde82
--- /dev/null
+++ b/src/symbian/uqm.cfg
@@ -0,0 +1,26 @@
+alwaysgl = BOOLEAN:false
+sfxvol = INT32:20
+reswidth = INT32:320
+usegl = BOOLEAN:false
+3domusic = BOOLEAN:true
+textmenu = BOOLEAN:true
+musicvol = INT32:20
+textgradients = BOOLEAN:true
+subtitles = BOOLEAN:true
+iconicscan = BOOLEAN:false
+resheight = INT32:240
+scaler = STRING:no
+3domovies = BOOLEAN:false
+speechvol = INT32:20
+audioquality = STRING:low
+positionalsfx = BOOLEAN:false
+player1control = INT32:0
+showfps = BOOLEAN:false
+pulseshield = BOOLEAN:false
+smoothmelee = BOOLEAN:false
+fullscreen = BOOLEAN:false
+smoothscroll = BOOLEAN:false
+audiodriver = STRING:mixsdl
+player2control = INT32:3
+scanlines = BOOLEAN:false
+remixmusic = BOOLEAN:false
diff --git a/src/symbian/uqm.mmp b/src/symbian/uqm.mmp
new file mode 100644
index 0000000..5c7a85c
--- /dev/null
+++ b/src/symbian/uqm.mmp
@@ -0,0 +1,45 @@
+TARGET uqm.exe
+TARGETTYPE exe
+UID 0 0xA000A0C3
+EPOCHEAPSIZE 1000000 50000000
+EPOCSTACKSIZE 81920
+
+SYSTEMINCLUDE \epoc32\include \epoc32\include\stdapis
+
+SOURCEPATH .
+SOURCE uqmapp.cpp
+
+START RESOURCE uqm.rss
+HEADER
+TARGETPATH resource\apps
+LANG SC
+END // RESOURCE
+
+START RESOURCE uqm_reg.rss
+#ifdef WINSCW
+TARGETPATH \private\10003a3f\apps
+#else
+TARGETPATH \private\10003a3f\import\apps
+#endif
+END
+
+OPTION_REPLACE ARMCC --cpu ARM926EJ-S -O3 -Otime
+ALWAYS_BUILD_AS_ARM
+
+LIBRARY avkon.lib
+LIBRARY apparc.lib
+LIBRARY cone.lib
+LIBRARY eikcore.lib
+LIBRARY ws32.lib
+LIBRARY bafl.lib
+LIBRARY euser.lib
+LIBRARY efsrv.lib
+LIBRARY sdl.lib
+LIBRARY libc.lib
+LIBRARY libm.lib
+LIBRARY libz.lib
+LIBRARY libpthread.lib
+
+STATICLIBRARY uqm.lib
+STATICLIBRARY SDL_image.lib
+STATICLIBRARY tremor.lib
diff --git a/src/symbian/uqm.rss b/src/symbian/uqm.rss
new file mode 100644
index 0000000..8f9ea8c
--- /dev/null
+++ b/src/symbian/uqm.rss
@@ -0,0 +1,26 @@
+NAME UQM
+
+#include <eikon.rh>
+#include <avkon.rh>
+#include <avkon.rsg>
+#include <avkon.hrh>
+#include <appinfo.rh>
+
+RESOURCE RSS_SIGNATURE { }
+
+RESOURCE TBUF { buf="UQM"; }
+
+RESOURCE EIK_APP_INFO
+ {
+ }
+
+RESOURCE LOCALISABLE_APP_INFO r_uqm_localisable_app_info
+ {
+ short_caption = "UQM";
+ caption_and_icon = CAPTION_AND_ICON_INFO
+ {
+ caption = "UrQuanMasters";
+ number_of_icons = 1;
+ icon_file = "\\resource\\apps\\uqm_icon.mif";
+ };
+ }
diff --git a/src/symbian/uqm.svg b/src/symbian/uqm.svg
new file mode 100644
index 0000000..b95264b
--- /dev/null
+++ b/src/symbian/uqm.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="48px" height="48px" viewBox="0 0 48 48" enable-background="new 0 0 48 48" xml:space="preserve">
+<g>
+ <g>
+ <path fill="#5B5F54" d="M15,5c1.44,0.56,2.44,1.56,3,3c-0.81,0.19-0.997,1.003-2,1c-0.333,0-0.667,0-1,0
+ c0.184-0.851-0.39-0.943-1-1C13.718,6.385,14.945,6.278,15,5z"/>
+ <path fill="#5B5F54" d="M13,15c-0.586,3.511,4.727,3.289,2,6c-2.323,2.416-2.677-1.562-5-2c0-0.667,0-1.333,0-2
+ C10.437,15.77,11.28,14.946,13,15z"/>
+ <path fill="#B43905" d="M5,29c0.333,0,0.667,0,1,0c0.19,0.811,1.003,0.997,1,2c1.563,3.771,4.23,6.438,8,8
+ c0.89,0.443,1.789,0.878,2,2c-4.236,0.236-5.083-2.917-8-4c0.184-0.852-0.39-0.943-1-1C7.549,33.118,4.461,32.872,5,29z"/>
+ <path fill="#03D002" d="M9,31c5.35-2.9,9.348,4.807,9,8c-4.555-0.779-5.266-5.4-9-7C9,31.667,9,31.333,9,31z"/>
+ <path fill="#5B5F54" d="M27,30c0.902,1.764,2.236,3.098,4,4c-0.238,1.428-1.646,1.688-2,3c-0.333,0-0.667,0-1,0
+ c-0.902-1.764-2.236-3.098-4-4c0.239-1.428,1.646-1.688,2-3C26.333,30,26.667,30,27,30z"/>
+ <path opacity="0" fill="#EAECE6" d="M4,14c3.582-4.418,7.582-8.418,12-12C12.418,6.418,8.418,10.418,4,14z"/>
+ <path opacity="0" fill="#FFFFFF" d="M48,0c0,16,0,32,0,48c-16,0-32,0-48,0C0,32,0,16,0,0C16,0,32,0,48,0z M4,14
+ c-0.634,3.699-2.713,5.953-2,11c4.654,0.943,6.093-6.516,10-3c-1.412,2.588-3.231,4.77-8,4c-0.664,5.33,0.582,8.751,4,10
+ c0.61,0.057,1.184,0.148,1,1c0,0.333,0,0.667,0,1c2.853,1.813,4.086,5.247,10,4c0.792-3.339,2.546-9.477,7-6
+ c-0.854,3.275-7.298,4.725-5,8c13.313,1.313,15.825-8.175,23-13c0.253-3.586-3.003-3.664-3-7C52.211,11.655,34.152-2.889,21,6
+ c-1.286-1.714-2.068-3.932-5-4C11.582,5.582,7.582,9.582,4,14z"/>
+ <path fill="#21351B" d="M4,14c4.418-3.582,8.418-7.582,12-12c2.932,0.068,3.714,2.286,5,4c13.152-8.889,31.211,5.655,20,18
+ c-0.003,3.336,3.253,3.414,3,7c-7.175,4.825-9.688,14.313-23,13c-2.298-3.275,4.146-4.725,5-8c-4.454-3.477-6.208,2.661-7,6
+ c-5.914,1.247-7.147-2.188-10-4c0-0.333,0-0.667,0-1c2.917,1.083,3.764,4.236,8,4c0.333,0,0.667,0,1,0
+ c2.208-0.125,0.425-4.242,1-6c0.89,0.615,1.789,1.208,2,0c1.313-0.354,1.573-1.762,3-2c1.764,0.902,3.098,2.236,4,4
+ c-0.797,1.072-7.094,4.348-2,5c6.992-0.341,10.803-3.863,13-9c-0.354-1.313-1.762-1.572-2-3c-3.512-0.178-3.289,3.378-6,4
+ c-1.764-0.902-3.098-2.236-4-4c1.241-2.092,2.908-3.759,5-5c-1.436,2.951-5.811,4.049-2,7c2.748-1.131,12.85-8.146,3-7
+ c1.807-3.096,3.063-0.534,7-1c3.793-2.643,2.902-15.629-2-16c-0.529-0.804-1.043-1.624-1-3c-4.564,0.435-11.012-1.011-14,1
+ c3.873,11.531-9.844,12.351-12,21c-2,0-4,0-6,0c0,0.667,0,1.333,0,2c-0.539,3.872,2.549,4.118,3,7c-3.418-1.249-4.664-4.67-4-10
+ c4.769,0.77,6.588-1.412,8-4c-3.907-3.516-5.346,3.943-10,3C1.287,19.953,3.366,17.699,4,14z M18,8c1.278,0.055,1.385,1.282,3,1
+ c1.074-3.808-3.845-6.732-6-4c-0.055,1.278-1.282,1.385-1,3C8.369,11.035,3.757,15.09,3,23c2.951,2.451,4.049-3.356,7-4
+ c2.323,0.438,2.677,4.416,5,2c2.727-2.711-2.586-2.489-2-6c2.039-0.294,1.961-2.706,4-3c0.043-1.376-0.471-2.196-1-3
+ C17.003,9.003,17.19,8.19,18,8z M21,9c-1.546,2.867-8.463,4.297-5,9C19.536,17.498,24.081,12.798,21,9z M37,28
+ c3.384-0.384,1.572,4.428,6,3C42.991,27.918,38.283,23.799,37,28z"/>
+ <path fill="#65C11B" d="M34,6c1.725,3.276,5.275,4.724,7,8c-2.754-1.913-5.088-4.246-7-7c-1.758,0.575-5.875-1.208-6,1
+ C22.684,5.654,30.9,5.834,34,6z"/>
+ <path fill="#65C11B" d="M31,8c4.893,2.441,6.87,7.797,10,12c-4.051,1.688-3.527-9.783-9-10C31.811,9.19,30.997,9.003,31,8z"/>
+ <path fill="#03D002" d="M16,9c0.529,0.804,1.043,1.624,1,3c-2.039,0.294-1.961,2.706-4,3c-1.72-0.054-2.563,0.77-3,2
+ c-2.168,1.499-2.329,5.004-6,5c1.383-6.617,7.15-8.85,11-13C15.333,9,15.667,9,16,9z"/>
+ <path fill="#038C01" d="M15,9c-3.85,4.15-9.617,6.383-11,13c3.671,0.004,3.832-3.501,6-5c0,0.667,0,1.333,0,2
+ c-2.951,0.644-4.049,6.451-7,4c0.757-7.91,5.369-11.965,11-15C14.61,8.057,15.184,8.149,15,9z"/>
+ <path opacity="0" fill="#FFFFFF" d="M16,18c-3.463-4.703,3.454-6.133,5-9C24.081,12.798,19.536,17.498,16,18z"/>
+ <path fill="#03D002" d="M38,8c4.902,0.371,5.793,13.357,2,16c-3.938,0.466-5.193-2.096-7,1c-0.333,0-0.667,0-1,0
+ c-2.092,1.241-3.759,2.908-5,5c-0.333,0-0.667,0-1,0c-0.354,1.313-1.761,1.572-2,3c-1.427,0.238-1.687,1.646-3,2
+ c-0.333,0-0.667,0-1,0c-2.573-1.762-4.318-4.35-7-6c1.167-1.834,2.614-3.387,4-5c3.401,1.891-1.712,3.506-1,6
+ c3.094-1.236,5.508-3.184,8,0c-1.317-2.684,0.114-8.114-5-7c2.849-4.484,8.209-6.458,9-13c1.333,0,2.667,0,4,0
+ c5.473,0.217,4.949,11.688,9,10c0-2,0-4,0-6c-1.725-3.276-5.275-4.724-7-8C35.764,5.714,37.098,10.709,38,8z"/>
+ <path fill="#038C01" d="M17,24c-1.386,1.613-2.833,3.166-4,5c2.682,1.65,4.427,4.238,7,6c0.333,0,0.667,0,1,0
+ c-0.211,1.208-1.11,0.615-2,0c-0.575,1.758,1.208,5.875-1,6c0-0.667,0-1.333,0-2c0.348-3.193-3.65-10.9-9-8
+ c-1.313-0.354-1.573-1.762-3-2c-0.333,0-0.667,0-1,0c0-0.667,0-1.333,0-2c2,0,4,0,6,0c2.156-8.649,15.873-9.469,12-21
+ c2.988-2.011,9.436-0.565,14-1c-0.043,1.376,0.471,2.196,1,3c-0.902,2.709-2.236-2.286-4-2c-3.1-0.166-11.316-0.346-6,2
+ c1,0,2,0,3,0c-0.003,1.003,0.811,1.19,1,2c-1.333,0-2.667,0-4,0c-0.791,6.542-6.151,8.516-9,13c-1.313-0.354-1.573-1.761-3-2
+ C15.718,22.615,16.945,22.722,17,24z"/>
+ <path fill="#E6C626" d="M19,23c5.114-1.114,3.683,4.316,5,7c-2.492-3.184-4.906-1.236-8,0c-0.712-2.494,4.401-4.109,1-6
+ c-0.055-1.278-1.282-1.385-1-3C17.427,21.239,17.687,22.646,19,23z"/>
+ <path opacity="0" fill="#FFFFFF" d="M33,25c9.85-1.146-0.252,5.869-3,7c-3.811-2.951,0.564-4.049,2-7C32.333,25,32.667,25,33,25z"
+ />
+ <path fill="#B43905" d="M43,31c-4.428,1.428-2.616-3.384-6-3C38.283,23.799,42.991,27.918,43,31z"/>
+ <path fill="#E6C626" d="M7,31c3.086,2.247,5.753,4.913,8,8C11.23,37.438,8.563,34.771,7,31z"/>
+ <path fill="#65C11B" d="M15,39c-2.247-3.087-4.914-5.753-8-8c0.003-1.003-0.81-1.189-1-2c1.427,0.238,1.687,1.646,3,2
+ c0,0.333,0,0.667,0,1c3.734,1.6,4.445,6.221,9,7c0,0.667,0,1.333,0,2c-0.333,0-0.667,0-1,0C16.789,39.878,15.89,39.443,15,39z"/>
+ <path fill="#038C01" d="M31,34c2.711-0.622,2.488-4.178,6-4c0.238,1.428,1.646,1.688,2,3c-4.995-1.393-9.604,5.531-13,9
+ c-5.094-0.652,1.203-3.928,2-5c0.333,0,0.667,0,1,0C29.354,35.688,30.762,35.428,31,34z"/>
+ <path fill="#03D002" d="M39,33c-2.197,5.137-6.008,8.659-13,9C29.396,38.531,34.005,31.607,39,33z"/>
+ <path fill="#B43905" d="M15,5c2.155-2.732,7.074,0.192,6,4c-1.615,0.282-1.722-0.945-3-1C17.44,6.56,16.44,5.56,15,5z"/>
+ <path fill="#E6C626" d="M41,14c0,2,0,4,0,6c-3.13-4.203-5.107-9.559-10-12c-1,0-2,0-3,0c0.125-2.208,4.242-0.425,6-1
+ C35.912,9.754,38.246,12.087,41,14z"/>
+ </g>
+</g>
+</svg>
diff --git a/src/symbian/uqm_reg.rss b/src/symbian/uqm_reg.rss
new file mode 100644
index 0000000..a57e670
--- /dev/null
+++ b/src/symbian/uqm_reg.rss
@@ -0,0 +1,12 @@
+#include <appinfo.rh>
+#include <uqm.rsg>
+
+UID2 KUidAppRegistrationResourceFile
+UID3 0xA000A0C3
+
+RESOURCE APP_REGISTRATION_INFO
+ {
+ app_file = "uqm";
+ localisable_resource_file = "\\resource\\apps\\uqm";
+ localisable_resource_id = R_UQM_LOCALISABLE_APP_INFO;
+ }
diff --git a/src/symbian/uqmapp.cpp b/src/symbian/uqmapp.cpp
new file mode 100644
index 0000000..86ac5dd
--- /dev/null
+++ b/src/symbian/uqmapp.cpp
@@ -0,0 +1,308 @@
+#include <coecntrl.h>
+#include <aknappui.h>
+#include <aknapp.h>
+#include <akndoc.h>
+#include <sdlepocapi.h>
+#include <aknnotewrappers.h>
+#include <eikstart.h>
+#include <badesca.h>
+#include <bautils.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <SDL/SDL_keysym.h>
+
+const TUid KUidSdlApp={ 0xA000A0C3 };
+
+class CSDL;
+
+class CSDLObserver : public CBase, public MSDLObserver
+{
+public:
+ CSDLObserver(CSDL* aSdl);
+
+ TInt SdlEvent(TInt aEvent, TInt aParam);
+ TInt SdlThreadEvent(TInt aEvent, TInt aParam);
+
+private:
+ CSDL* iSdl;
+};
+
+class MExitWait
+ {
+ public:
+ virtual void DoExit(TInt aErr) = 0;
+ };
+
+class CExitWait : public CActive
+ {
+ public:
+ CExitWait(MExitWait& aWait);
+ void Start();
+ ~CExitWait();
+ private:
+ void RunL();
+ void DoCancel();
+ private:
+ MExitWait& iWait;
+ TRequestStatus* iStatusPtr;
+ };
+
+class CSDLWin : public CCoeControl
+ {
+ public:
+ void ConstructL(const TRect& aRect);
+ RWindow& GetWindow() const;
+ void SetNoDraw();
+ private:
+ void Draw(const TRect& aRect) const;
+ };
+
+class CSdlApplication : public CAknApplication
+ {
+private:
+ // from CApaApplication
+ CApaDocument* CreateDocumentL();
+ TUid AppDllUid() const;
+ };
+
+
+class CSdlAppDocument : public CAknDocument
+ {
+public:
+ CSdlAppDocument(CEikApplication& aApp): CAknDocument(aApp) { }
+private:
+ CEikAppUi* CreateAppUiL();
+ };
+
+
+class CSdlAppUi : public CAknAppUi, public MExitWait
+ {
+public:
+ void ConstructL();
+ ~CSdlAppUi();
+private:
+ void HandleCommandL(TInt aCommand);
+ void DoExit(TInt aErr);
+ void HandleWsEventL(const TWsEvent& aEvent, CCoeControl* aDestination);
+ void HandleResourceChangeL(TInt aType);
+ void AddCmdLineParamsL(CDesC8Array& aArgs);
+private:
+ CExitWait* iWait;
+ CSDLWin* iSDLWin;
+ CSDL* iSdl;
+ CSDLObserver* iSdlObserver;
+ TBool iExit;
+ };
+
+
+CExitWait::CExitWait(MExitWait& aWait) : CActive(CActive::EPriorityStandard), iWait(aWait)
+ {
+ CActiveScheduler::Add(this);
+ }
+
+CExitWait::~CExitWait()
+ {
+ Cancel();
+ }
+
+void CExitWait::RunL()
+ {
+ if(iStatusPtr != NULL )
+ iWait.DoExit(iStatus.Int());
+ }
+
+void CExitWait::DoCancel()
+ {
+ if(iStatusPtr != NULL )
+ User::RequestComplete(iStatusPtr , KErrCancel);
+ }
+
+void CExitWait::Start()
+ {
+ SetActive();
+ iStatusPtr = &iStatus;
+ }
+
+void CSDLWin:: ConstructL(const TRect& aRect)
+ {
+ CreateWindowL();
+ SetRect(aRect);
+ ActivateL();
+ }
+
+
+RWindow& CSDLWin::GetWindow() const
+ {
+ return Window();
+ }
+
+
+void CSDLWin::Draw(const TRect& /*aRect*/) const
+ {
+ CWindowGc& gc = SystemGc();
+ gc.SetPenStyle(CGraphicsContext::ESolidPen);
+ gc.SetPenColor(KRgbBlack);
+ gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
+ gc.SetBrushColor(0x000000);
+ gc.DrawRect(Rect());
+ }
+
+void CSdlAppUi::ConstructL()
+ {
+ BaseConstructL(ENoScreenFurniture | EAppOrientationLandscape);
+
+ iSDLWin = new (ELeave) CSDLWin;
+ iSDLWin->ConstructL(ApplicationRect());
+
+ iSdl = CSDL::NewL(CSDL::EEnableFocusStop);
+ iSdlObserver = new (ELeave) CSDLObserver(iSdl);
+
+ iSdl->SetContainerWindowL(
+ iSDLWin->GetWindow(),
+ iEikonEnv->WsSession(),
+ *iEikonEnv->ScreenDevice());
+ iSdl->SetObserver(iSdlObserver);
+ iSdl->DisableKeyBlocking(*this);
+
+ iWait = new (ELeave) CExitWait(*this);
+ CDesC8ArrayFlat* args = new (ELeave)CDesC8ArrayFlat(10);
+ AddCmdLineParamsL(*args);
+
+ iSdl->CallMainL(iWait->iStatus, *args, CSDL::ENoFlags, 81920);
+ delete args;
+
+ iWait->Start();
+ }
+
+void CSdlAppUi::HandleCommandL(TInt aCommand)
+ {
+ switch(aCommand)
+ {
+ case EAknCmdExit:
+ case EAknSoftkeyExit:
+ case EEikCmdExit:
+ exit(0);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+void CSdlAppUi::DoExit(TInt aErr)
+ {
+ delete iSdl;
+ iSdl = NULL;
+
+ if(iExit)
+ Exit();
+ }
+
+void CSdlAppUi::HandleWsEventL(const TWsEvent& aEvent, CCoeControl* aDestination)
+ {
+ if(iSdl != NULL)
+ iSdl->AppendWsEvent(aEvent);
+ CAknAppUi::HandleWsEventL(aEvent, aDestination);
+ }
+
+void CSdlAppUi::HandleResourceChangeL(TInt aType)
+ {
+ CAknAppUi::HandleResourceChangeL(aType);
+
+ if(aType == KEikDynamicLayoutVariantSwitch)
+ {
+ iSDLWin->SetRect(ApplicationRect());
+ if (iSdl)
+ {
+ iSdl->SetContainerWindowL(
+ iSDLWin->GetWindow(),
+ iEikonEnv->WsSession(),
+ *iEikonEnv->ScreenDevice());
+ }
+ }
+ }
+
+void CSdlAppUi::AddCmdLineParamsL(CDesC8Array& aArgs)
+ {
+ _LIT8(KAddonParam, "--addondir=?:\\uqm-addons");
+ _LIT16(KTestFolder, "?:\\uqm-addons");
+ RFs fs;
+
+ fs.Connect();
+ for (TInt8 c = 'e'; c < 'z'; ++c)
+ {
+ TBuf16<32> buf(KTestFolder);
+ buf[0] = c;
+ if (BaflUtils::FolderExists(fs, buf))
+ {
+ TBuf8<32> arg(KAddonParam);
+ arg[11] = c;
+ aArgs.AppendL(arg);
+ break;
+ }
+ }
+ fs.Close();
+ }
+
+CSdlAppUi::~CSdlAppUi()
+ {
+ if(iWait != NULL)
+ iWait->Cancel();
+ delete iSdl;
+ delete iWait;
+ delete iSDLWin;
+ delete iSdlObserver;
+ }
+
+CEikAppUi* CSdlAppDocument::CreateAppUiL()
+ {
+ return new(ELeave) CSdlAppUi();
+ }
+
+TUid CSdlApplication::AppDllUid() const
+ {
+ return KUidSdlApp;
+ }
+
+
+CApaDocument* CSdlApplication::CreateDocumentL()
+ {
+ CSdlAppDocument* document = new (ELeave) CSdlAppDocument(*this);
+ return document;
+ }
+
+LOCAL_C CApaApplication* NewApplication()
+ {
+ return new CSdlApplication;
+ }
+
+GLDEF_C TInt E32Main()
+ {
+ return EikStart::RunApplication(NewApplication);
+ }
+
+CSDLObserver::CSDLObserver(CSDL* aSdl) : iSdl(aSdl)
+{
+}
+
+TInt CSDLObserver::SdlEvent(TInt aEvent, TInt aParam)
+{
+ if (aEvent == EEventKeyMapInit)
+ {
+ // starmap zoom
+ iSdl->SetSDLCode('3', SDLK_KP_PLUS);
+ iSdl->SetSDLCode('2', SDLK_KP_MINUS);
+
+ iSdl->SetSDLCode('A', SDLK_KP_PLUS);
+ iSdl->SetSDLCode('Z', SDLK_KP_MINUS);
+
+ iSdl->SetSDLCode('a', SDLK_KP_PLUS);
+ iSdl->SetSDLCode('z', SDLK_KP_MINUS);
+ }
+ return 0;
+}
+
+TInt CSDLObserver::SdlThreadEvent(TInt aEvent, TInt aParam)
+{
+ return 0;
+}
diff --git a/src/types.h b/src/types.h
new file mode 100644
index 0000000..db58630
--- /dev/null
+++ b/src/types.h
@@ -0,0 +1,188 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Data types used in the UQM port
+ * Draft 3
+ * Compiled from various sources
+ */
+
+#ifndef TYPES_H_
+#define TYPES_H_
+
+#include "config.h"
+
+#ifdef _MSC_VER
+# if (_MSC_VER >= 1300)
+# include <stddef.h>
+# else
+ typedef int intptr_t;
+ typedef unsigned int uintptr_t;
+# endif
+# ifdef _WIN64
+# define PRIxPTR "lx"
+# else
+# define PRIxPTR "x"
+# endif
+#else
+# include <inttypes.h>
+# ifndef PRIxPTR
+ /* SunOS (at least version 5.9) does not have PRIxPTR in inttypes.h */
+# if defined(__arch64__) || defined(__alpha) || defined(__x86_64) \
+ || defined(_M_IA64) || defined(_M_AMD64)
+ /* 64-bit platforms */
+# define PRIxPTR "lx"
+# else
+# define PRIxPTR "x"
+# endif
+# endif /* defined(PRIxPTR) */
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#if defined(__arch64__) || defined(__alpha) || defined(__x86_64) \
+ || defined(_M_IA64) || defined(_M_AMD64)
+ /* 64-bit platforms */
+# define UQM_INT16 short
+# define UQM_INT32 int
+# define UQM_INT64 long
+
+#elif defined(__MACOS__)
+
+# define UQM_INT16 short
+# define UQM_INT32 long
+
+/* Add your OS support here */
+
+#else
+/* all other sane 32bit: WIN32, __MACOSX__, __BEOS__, __EMX__ */
+
+# define UQM_INT16 short
+# define UQM_INT32 int
+
+#endif
+
+/* Figure out how to support 64-bit datatypes */
+#if !defined(UQM_INT64) && !defined(__STRICT_ANSI__)
+# if defined(__GNUC__) || defined(__MWERKS__) || defined(__SUNPRO_C) || defined(__ARMCC__)
+# define UQM_INT64 long long
+# elif defined(_MSC_VER) || defined(__BORLANDC__)
+# define UQM_INT64 __int64
+# endif
+#endif /* !__STRICT_ANSI__ */
+
+// ISO C99 compatible boolean types. The ISO C99 standard defines:
+// - An object declared as type _Bool, large enough to store the values 0
+// and 1, the rank of which is less than the rank of all other standard
+// integer types.
+// - A macro "bool", which expands to "_Bool".
+// - A macro "true", which expands to the integer constant 1, suitable for
+// use in #if preprocessing directives.
+// - A macro "false", which expands to the integer constant 0, suitable for
+// use in #if preprocessing directives.
+// - A macro "__bool_true_false_are_defined", which expands to the integer
+// constant 1, suitable for use in #if preprocessing directives.
+#ifndef __bool_true_false_are_defined
+#undef bool
+#undef false
+#undef true
+#ifndef HAVE__BOOL
+typedef unsigned char _Bool;
+#endif /* HAVE_BOOL */
+#define bool _Bool
+#define true 1
+#define false 0
+#define __bool_true_false_are_defined
+#endif /* __bool_true_false_are_defined */
+
+/* If it isn't char, what is it ?!*/
+typedef unsigned char uint8;
+typedef signed char sint8;
+
+typedef unsigned UQM_INT16 uint16;
+typedef signed UQM_INT16 sint16;
+
+typedef unsigned UQM_INT32 uint32;
+typedef signed UQM_INT32 sint32;
+
+#if defined(UQM_INT64)
+
+typedef unsigned UQM_INT64 uint64;
+typedef signed UQM_INT64 sint64;
+
+#undef UQM_INT64
+
+#else /* dont have UQM_INT64 */
+
+typedef struct
+{
+ uint32 hi;
+ uint32 lo;
+} uint64;
+typedef struct
+{
+ sint32 hi;
+ uint32 lo;
+} sint64;
+
+#endif /* UQM_INT64 */
+
+#undef UQM_INT16
+#undef UQM_INT32
+
+/* Make sure the types really have the right sizes
+ * Adapted from SDL
+ * This will generate "negative subscript or subscript is too large"
+ * error during compile, if the actual size of a type is wrong
+ */
+#define UQM_COMPILE_TIME_ASSERT(name, x) \
+ typedef int UQM_dummy_##name [(x) * 2 - 1]
+
+UQM_COMPILE_TIME_ASSERT(uint8, sizeof(uint8) == 1);
+UQM_COMPILE_TIME_ASSERT(uint16, sizeof(uint16) == 2);
+UQM_COMPILE_TIME_ASSERT(uint32, sizeof(uint32) == 4);
+UQM_COMPILE_TIME_ASSERT(uint64, sizeof(uint64) == 8);
+
+#undef UQM_COMPILE_TIME_ASSERT
+
+#undef SINT8_MIN
+#undef SINT8_MAX
+#undef SINT16_MIN
+#undef SINT16_MAX
+#undef SINT32_MIN
+#undef SINT32_MAX
+
+#undef UINT8_MAX
+#undef UINT16_MAX
+#undef UINT32_MAX
+
+#define SINT8_MIN (-128)
+#define SINT8_MAX 127
+#define SINT16_MIN (-32767-1)
+#define SINT16_MAX 32767
+#define SINT32_MIN (-2147483647-1)
+#define SINT32_MAX 2147483647
+
+#define UINT8_MAX 0xff /* 255U */
+#define UINT16_MAX 0xffff /* 65535U */
+#define UINT32_MAX 0xffffffff /* 4294967295U */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* TYPES_H_ */
diff --git a/src/uqm.c b/src/uqm.c
new file mode 100644
index 0000000..43c25d8
--- /dev/null
+++ b/src/uqm.c
@@ -0,0 +1,1285 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_GETOPT_LONG
+# include <getopt.h>
+#else
+# include "getopt/getopt.h"
+#endif
+
+#include <stdarg.h>
+#include <errno.h>
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/cmap.h"
+#include "libs/sound/sound.h"
+#include "libs/input/input_common.h"
+#include "libs/inplib.h"
+#include "libs/tasklib.h"
+#include "uqm/controls.h"
+#include "uqm/battle.h"
+ // For BATTLE_FRAME_RATE
+#include "libs/file.h"
+#include "types.h"
+#include "port.h"
+#include "libs/memlib.h"
+#include "libs/platform.h"
+#include "libs/log.h"
+#include "options.h"
+#include "uqmversion.h"
+#include "uqm/comm.h"
+#ifdef NETPLAY
+# include "libs/callback.h"
+# include "libs/alarm.h"
+# include "libs/net.h"
+# include "uqm/supermelee/netplay/netoptions.h"
+# include "uqm/supermelee/netplay/netplay.h"
+#endif
+#include "uqm/setup.h"
+#include "uqm/starcon.h"
+
+
+#if defined (GFXMODULE_SDL)
+# include SDL_INCLUDE(SDL.h)
+ // Including this is actually necessary on OSX.
+#endif
+
+struct bool_option
+{
+ bool value;
+ bool set;
+};
+
+struct int_option
+{
+ int value;
+ bool set;
+};
+
+struct float_option
+{
+ float value;
+ bool set;
+};
+
+struct options_struct
+{
+#define DECL_CONFIG_OPTION(type, name) \
+ struct type##_option name
+
+#define DECL_CONFIG_OPTION2(type, name, val1, val2) \
+ struct { type val1; type val2; bool set; } name
+
+ // Commandline-only options
+ const char *logFile;
+ enum {
+ runMode_normal,
+ runMode_usage,
+ runMode_version,
+ } runMode;
+
+ const char *configDir;
+ const char *contentDir;
+ const char *addonDir;
+ const char **addons;
+ int numAddons;
+
+ const char *graphicsBackend;
+
+ // Commandline and user config options
+ DECL_CONFIG_OPTION(bool, opengl);
+ DECL_CONFIG_OPTION2(int, resolution, width, height);
+ DECL_CONFIG_OPTION(bool, fullscreen);
+ DECL_CONFIG_OPTION(bool, scanlines);
+ DECL_CONFIG_OPTION(int, scaler);
+ DECL_CONFIG_OPTION(bool, showFps);
+ DECL_CONFIG_OPTION(bool, keepAspectRatio);
+ DECL_CONFIG_OPTION(float, gamma);
+ DECL_CONFIG_OPTION(int, soundDriver);
+ DECL_CONFIG_OPTION(int, soundQuality);
+ DECL_CONFIG_OPTION(bool, use3doMusic);
+ DECL_CONFIG_OPTION(bool, useRemixMusic);
+ DECL_CONFIG_OPTION(bool, useSpeech);
+ DECL_CONFIG_OPTION(int, whichCoarseScan);
+ DECL_CONFIG_OPTION(int, whichMenu);
+ DECL_CONFIG_OPTION(int, whichFonts);
+ DECL_CONFIG_OPTION(int, whichIntro);
+ DECL_CONFIG_OPTION(int, whichShield);
+ DECL_CONFIG_OPTION(int, smoothScroll);
+ DECL_CONFIG_OPTION(int, meleeScale);
+ DECL_CONFIG_OPTION(bool, subtitles);
+ DECL_CONFIG_OPTION(bool, stereoSFX);
+ DECL_CONFIG_OPTION(float, musicVolumeScale);
+ DECL_CONFIG_OPTION(float, sfxVolumeScale);
+ DECL_CONFIG_OPTION(float, speechVolumeScale);
+ DECL_CONFIG_OPTION(bool, safeMode);
+
+#define INIT_CONFIG_OPTION(name, val) \
+ { val, false }
+
+#define INIT_CONFIG_OPTION2(name, val1, val2) \
+ { val1, val2, false }
+};
+
+struct option_list_value
+{
+ const char *str;
+ int value;
+};
+
+static const struct option_list_value scalerList[] =
+{
+ {"bilinear", TFB_GFXFLAGS_SCALE_BILINEAR},
+ {"biadapt", TFB_GFXFLAGS_SCALE_BIADAPT},
+ {"biadv", TFB_GFXFLAGS_SCALE_BIADAPTADV},
+ {"triscan", TFB_GFXFLAGS_SCALE_TRISCAN},
+ {"hq", TFB_GFXFLAGS_SCALE_HQXX},
+ {"none", 0},
+ {"no", 0}, /* uqm.cfg value */
+ {NULL, 0}
+};
+
+static const struct option_list_value meleeScaleList[] =
+{
+ {"smooth", TFB_SCALE_TRILINEAR},
+ {"3do", TFB_SCALE_TRILINEAR},
+ {"step", TFB_SCALE_STEP},
+ {"pc", TFB_SCALE_STEP},
+ {"bilinear", TFB_SCALE_BILINEAR},
+ {NULL, 0}
+};
+
+static const struct option_list_value audioDriverList[] =
+{
+ {"openal", audio_DRIVER_OPENAL},
+ {"mixsdl", audio_DRIVER_MIXSDL},
+ {"none", audio_DRIVER_NOSOUND},
+ {"nosound", audio_DRIVER_NOSOUND},
+ {NULL, 0}
+};
+
+static const struct option_list_value audioQualityList[] =
+{
+ {"low", audio_QUALITY_LOW},
+ {"medium", audio_QUALITY_MEDIUM},
+ {"high", audio_QUALITY_HIGH},
+ {NULL, 0}
+};
+
+static const struct option_list_value choiceList[] =
+{
+ {"pc", OPT_PC},
+ {"3do", OPT_3DO},
+ {NULL, 0}
+};
+
+static const struct option_list_value accelList[] =
+{
+ {"mmx", PLATFORM_MMX},
+ {"sse", PLATFORM_SSE},
+ {"3dnow", PLATFORM_3DNOW},
+ {"none", PLATFORM_C},
+ {"detect", PLATFORM_NULL},
+ {NULL, 0}
+};
+
+// Looks up the given string value in the given list and passes
+// the associated int value back. returns true if value was found.
+// The list is terminated by a NULL 'str' value.
+static bool lookupOptionValue (const struct option_list_value *list,
+ const char *strval, int *ret);
+
+// Error message buffer used for when we cannot use logging facility yet
+static char errBuffer[512];
+
+static void saveError (const char *fmt, ...)
+ PRINTF_FUNCTION(1, 2);
+
+static int parseOptions (int argc, char *argv[],
+ struct options_struct *options);
+static void getUserConfigOptions (struct options_struct *options);
+static void usage (FILE *out, const struct options_struct *defaultOptions);
+static int parseIntOption (const char *str, int *result,
+ const char *optName);
+static int parseFloatOption (const char *str, float *f,
+ const char *optName);
+static void parseIntVolume (int intVol, float *vol);
+static int InvalidArgument (const char *supplied, const char *opt_name);
+static const char *choiceOptString (const struct int_option *option);
+static const char *boolOptString (const struct bool_option *option);
+static const char *boolNotOptString (const struct bool_option *option);
+
+int
+main (int argc, char *argv[])
+{
+ struct options_struct options = {
+ /* .logFile = */ NULL,
+ /* .runMode = */ runMode_normal,
+ /* .configDir = */ NULL,
+ /* .contentDir = */ NULL,
+ /* .addonDir = */ NULL,
+ /* .addons = */ NULL,
+ /* .numAddons = */ 0,
+ /* .graphicsBackend = */ NULL,
+
+ INIT_CONFIG_OPTION( opengl, false ),
+ INIT_CONFIG_OPTION2( resolution, 640, 480 ),
+ INIT_CONFIG_OPTION( fullscreen, false ),
+ INIT_CONFIG_OPTION( scanlines, false ),
+ INIT_CONFIG_OPTION( scaler, 0 ),
+ INIT_CONFIG_OPTION( showFps, false ),
+ INIT_CONFIG_OPTION( keepAspectRatio, false ),
+ INIT_CONFIG_OPTION( gamma, 1.0f ),
+ INIT_CONFIG_OPTION( soundDriver, audio_DRIVER_MIXSDL ),
+ INIT_CONFIG_OPTION( soundQuality, audio_QUALITY_MEDIUM ),
+ INIT_CONFIG_OPTION( use3doMusic, true ),
+ INIT_CONFIG_OPTION( useRemixMusic, false ),
+ INIT_CONFIG_OPTION( useSpeech, true ),
+ INIT_CONFIG_OPTION( whichCoarseScan, OPT_PC ),
+ INIT_CONFIG_OPTION( whichMenu, OPT_PC ),
+ INIT_CONFIG_OPTION( whichFonts, OPT_PC ),
+ INIT_CONFIG_OPTION( whichIntro, OPT_PC ),
+ INIT_CONFIG_OPTION( whichShield, OPT_PC ),
+ INIT_CONFIG_OPTION( smoothScroll, OPT_PC ),
+ INIT_CONFIG_OPTION( meleeScale, TFB_SCALE_TRILINEAR ),
+ INIT_CONFIG_OPTION( subtitles, true ),
+ INIT_CONFIG_OPTION( stereoSFX, false ),
+ INIT_CONFIG_OPTION( musicVolumeScale, 1.0f ),
+ INIT_CONFIG_OPTION( sfxVolumeScale, 1.0f ),
+ INIT_CONFIG_OPTION( speechVolumeScale, 1.0f ),
+ INIT_CONFIG_OPTION( safeMode, false ),
+ };
+ struct options_struct defaults = options;
+ int optionsResult;
+ int gfxDriver;
+ int gfxFlags;
+ int i;
+
+ // NOTE: we cannot use the logging facility yet because we may have to
+ // log to a file, and we'll only get the log file name after parsing
+ // the options.
+ optionsResult = parseOptions (argc, argv, &options);
+
+ log_init (15);
+
+ if (options.logFile != NULL)
+ {
+ int i;
+ if (!freopen (options.logFile, "w", stderr))
+ {
+ printf ("Error %d calling freopen() on stderr\n", errno);
+ return EXIT_FAILURE;
+ }
+#ifdef UNBUFFERED_LOGFILE
+ setbuf (stderr, NULL);
+#endif
+ for (i = 0; i < argc; ++i)
+ log_add (log_User, "argv[%d] = [%s]", i, argv[i]);
+ }
+
+ if (options.runMode == runMode_version)
+ {
+ printf ("%d.%d.%d%s\n", UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ log_showBox (false, false);
+ return EXIT_SUCCESS;
+ }
+
+ log_add (log_User, "The Ur-Quan Masters v%d.%d.%d%s (compiled %s %s)\n"
+ "This software comes with ABSOLUTELY NO WARRANTY;\n"
+ "for details see the included 'COPYING' file.\n",
+ UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION,
+ __DATE__, __TIME__);
+#ifdef NETPLAY
+ log_add (log_User, "Netplay protocol version %d.%d. Netplay opponent "
+ "must have UQM %d.%d.%d or later.",
+ NETPLAY_PROTOCOL_VERSION_MAJOR, NETPLAY_PROTOCOL_VERSION_MINOR,
+ NETPLAY_MIN_UQM_VERSION_MAJOR, NETPLAY_MIN_UQM_VERSION_MINOR,
+ NETPLAY_MIN_UQM_VERSION_PATCH);
+#endif
+
+ if (errBuffer[0] != '\0')
+ { // Have some saved error to log
+ log_add (log_Error, "%s", errBuffer);
+ errBuffer[0] = '\0';
+ }
+
+ if (options.runMode == runMode_usage)
+ {
+ usage (stdout, &defaults);
+ log_showBox (true, false);
+ return EXIT_SUCCESS;
+ }
+
+ if (optionsResult != EXIT_SUCCESS)
+ { // Options parsing failed. Oh, well.
+ log_add (log_Fatal, "Run with -h to see the allowed arguments.");
+ return optionsResult;
+ }
+
+ TFB_PreInit ();
+ mem_init ();
+ InitThreadSystem ();
+ log_initThreads ();
+ initIO ();
+ prepareConfigDir (options.configDir);
+
+ PlayerControls[0] = CONTROL_TEMPLATE_KB_1;
+ PlayerControls[1] = CONTROL_TEMPLATE_JOY_1;
+
+ // Fill in the options struct based on uqm.cfg
+ if (!options.safeMode.value)
+ {
+ LoadResourceIndex (configDir, "uqm.cfg", "config.");
+ getUserConfigOptions (&options);
+ }
+
+ { /* remove old control template names */
+ int i;
+
+ for (i = 0; i < 6; ++i)
+ {
+ char cfgkey[64];
+
+ snprintf(cfgkey, sizeof(cfgkey), "config.keys.%d.name", i + 1);
+ cfgkey[sizeof(cfgkey) - 1] = '\0';
+
+ res_Remove (cfgkey);
+ }
+ }
+
+ /* TODO: Once threading is gone, these become local variables
+ again. In the meantime, they must be global so that
+ initAudio (in StarCon2Main) can see them. initAudio needed
+ to be moved there because calling AssignTask in the main
+ thread doesn't work */
+ snddriver = options.soundDriver.value;
+ soundflags = options.soundQuality.value;
+
+ // Fill in global variables:
+ opt3doMusic = options.use3doMusic.value;
+ optRemixMusic = options.useRemixMusic.value;
+ optSpeech = options.useSpeech.value;
+ optWhichCoarseScan = options.whichCoarseScan.value;
+ optWhichMenu = options.whichMenu.value;
+ optWhichFonts = options.whichFonts.value;
+ optWhichIntro = options.whichIntro.value;
+ optWhichShield = options.whichShield.value;
+ optSmoothScroll = options.smoothScroll.value;
+ optMeleeScale = options.meleeScale.value;
+ optKeepAspectRatio = options.keepAspectRatio.value;
+ optSubtitles = options.subtitles.value;
+ optStereoSFX = options.stereoSFX.value;
+ musicVolumeScale = options.musicVolumeScale.value;
+ sfxVolumeScale = options.sfxVolumeScale.value;
+ speechVolumeScale = options.speechVolumeScale.value;
+ optAddons = options.addons;
+
+ prepareContentDir (options.contentDir, options.addonDir, argv[0]);
+ prepareMeleeDir ();
+ prepareSaveDir ();
+ prepareShadowAddons (options.addons);
+#if 0
+ initTempDir ();
+#endif
+
+ InitTimeSystem ();
+ InitTaskSystem ();
+
+ Alarm_init ();
+ Callback_init ();
+
+#ifdef NETPLAY
+ Network_init ();
+ NetManager_init ();
+#endif
+
+ gfxDriver = options.opengl.value ?
+ TFB_GFXDRIVER_SDL_OPENGL : TFB_GFXDRIVER_SDL_PURE;
+ gfxFlags = options.scaler.value;
+ if (options.fullscreen.value)
+ gfxFlags |= TFB_GFXFLAGS_FULLSCREEN;
+ if (options.scanlines.value)
+ gfxFlags |= TFB_GFXFLAGS_SCANLINES;
+ if (options.showFps.value)
+ gfxFlags |= TFB_GFXFLAGS_SHOWFPS;
+ TFB_InitGraphics (gfxDriver, gfxFlags, options.graphicsBackend,
+ options.resolution.width, options.resolution.height);
+ if (options.gamma.set && setGammaCorrection (options.gamma.value))
+ optGamma = options.gamma.value;
+ else
+ optGamma = 1.0f; // failed or default
+
+ InitColorMaps ();
+ init_communication ();
+ /* TODO: Once threading is gone, restore initAudio here.
+ initAudio calls AssignTask, which currently blocks on
+ ProcessThreadLifecycles... */
+ // initAudio (snddriver, soundflags);
+ // Make sure that the compiler treats multidim arrays the way we expect
+ assert (sizeof (int [NUM_TEMPLATES * NUM_KEYS]) ==
+ sizeof (int [NUM_TEMPLATES][NUM_KEYS]));
+ TFB_SetInputVectors (ImmediateInputState.menu, NUM_MENU_KEYS,
+ (volatile int *)ImmediateInputState.key, NUM_TEMPLATES, NUM_KEYS);
+ TFB_InitInput (TFB_INPUTDRIVER_SDL, 0);
+
+ StartThread (Starcon2Main, NULL, 1024, "Starcon2Main");
+
+ for (i = 0; i < 2000 && !MainExited; )
+ {
+ if (QuitPosted)
+ { /* Try to stop the main thread, but limited number of times */
+ SignalStopMainThread ();
+ ++i;
+ }
+ else if (!GameActive)
+ { // Throttle down the main loop when game is inactive
+ HibernateThread (ONE_SECOND / 4);
+ }
+
+ TFB_ProcessEvents ();
+ ProcessUtilityKeys ();
+ ProcessThreadLifecycles ();
+ TFB_FlushGraphics ();
+ }
+
+ /* Currently, we use atexit() callbacks everywhere, so we
+ * cannot simply call unInitAudio() and the like, because other
+ * tasks might still be using it */
+ if (MainExited)
+ {
+ TFB_UninitInput ();
+ unInitAudio ();
+ uninit_communication ();
+
+ TFB_PurgeDanglingGraphics ();
+ // Purge above refers to colormaps which have to be still up
+ UninitColorMaps ();
+ TFB_UninitGraphics ();
+
+#ifdef NETPLAY
+ NetManager_uninit ();
+ Network_uninit ();
+#endif
+
+ Callback_uninit ();
+ Alarm_uninit ();
+
+ CleanupTaskSystem ();
+ UnInitTimeSystem ();
+#if 0
+ unInitTempDir ();
+#endif
+ unprepareAllDirs ();
+ uninitIO ();
+ UnInitThreadSystem ();
+ mem_uninit ();
+ }
+
+ HFree (options.addons);
+
+ return EXIT_SUCCESS;
+}
+
+static void
+saveErrorV (const char *fmt, va_list list)
+{
+ int len = strlen (errBuffer);
+ int left = sizeof (errBuffer) - len;
+ if (len > 0 && left > 0)
+ { // Already something there
+ errBuffer[len] = '\n';
+ ++len;
+ --left;
+ }
+ vsnprintf (errBuffer + len, left, fmt, list);
+ errBuffer[sizeof (errBuffer) - 1] = '\0';
+}
+
+static void
+saveError (const char *fmt, ...)
+{
+ va_list list;
+
+ va_start (list, fmt);
+ saveErrorV (fmt, list);
+ va_end (list);
+}
+
+
+static bool
+lookupOptionValue (const struct option_list_value *list,
+ const char *strval, int *ret)
+{
+ if (!list)
+ return false;
+
+ // The list is terminated by a NULL 'str' value.
+ while (list->str && strcmp (strval, list->str) != 0)
+ ++list;
+ if (!list->str)
+ return false;
+
+ *ret = list->value;
+ return true;
+}
+
+static void
+getBoolConfigValue (struct bool_option *option, const char *config_val)
+{
+ if (option->set || !res_IsBoolean (config_val))
+ return;
+
+ option->value = res_GetBoolean (config_val);
+ option->set = true;
+}
+
+static void
+getBoolConfigValueXlat (struct int_option *option, const char *config_val,
+ int true_val, int false_val)
+{
+ if (option->set || !res_IsBoolean (config_val))
+ return;
+
+ option->value = res_GetBoolean (config_val) ? true_val : false_val;
+ option->set = true;
+}
+
+static void
+getVolumeConfigValue (struct float_option *option, const char *config_val)
+{
+ if (option->set || !res_IsInteger (config_val))
+ return;
+
+ parseIntVolume (res_GetInteger (config_val), &option->value);
+ option->set = true;
+}
+
+static void
+getGammaConfigValue (struct float_option *option, const char *config_val)
+{
+ int val;
+
+ if (option->set || !res_IsInteger (config_val))
+ return;
+
+ val = res_GetInteger (config_val);
+ // gamma config option is a fixed-point number
+ // ignore ridiculously out-of-range values
+ if (val < (int)(0.03 * GAMMA_SCALE) || val > (int)(9.9 * GAMMA_SCALE))
+ return;
+ option->value = val / (float)GAMMA_SCALE;
+ // avoid setting gamma when not necessary
+ if (option->value != 1.0f)
+ option->set = true;
+}
+
+static bool
+getListConfigValue (struct int_option *option, const char *config_val,
+ const struct option_list_value *list)
+{
+ const char *strval;
+ bool found;
+
+ if (option->set || !res_IsString (config_val) || !list)
+ return false;
+
+ strval = res_GetString (config_val);
+ found = lookupOptionValue (list, strval, &option->value);
+ option->set = found;
+
+ return found;
+}
+
+static void
+getUserConfigOptions (struct options_struct *options)
+{
+ // Most of the user config options are only applied if they
+ // have not already been set (i.e. on the commandline)
+
+ if (res_IsInteger ("config.reswidth") && res_IsInteger ("config.resheight")
+ && !options->resolution.set)
+ {
+ options->resolution.width = res_GetInteger ("config.reswidth");
+ options->resolution.height = res_GetInteger ("config.resheight");
+ options->resolution.set = true;
+ }
+
+ if (res_IsBoolean ("config.alwaysgl") && !options->opengl.set)
+ { // config.alwaysgl is processed differently than others
+ // Only set when it's 'true'
+ if (res_GetBoolean ("config.alwaysgl"))
+ {
+ options->opengl.value = true;
+ options->opengl.set = true;
+ }
+ }
+ getBoolConfigValue (&options->opengl, "config.usegl");
+
+ getListConfigValue (&options->scaler, "config.scaler", scalerList);
+
+ getBoolConfigValue (&options->fullscreen, "config.fullscreen");
+ getBoolConfigValue (&options->scanlines, "config.scanlines");
+ getBoolConfigValue (&options->showFps, "config.showfps");
+ getBoolConfigValue (&options->keepAspectRatio, "config.keepaspectratio");
+ getGammaConfigValue (&options->gamma, "config.gamma");
+
+ getBoolConfigValue (&options->subtitles, "config.subtitles");
+
+ getBoolConfigValueXlat (&options->whichMenu, "config.textmenu",
+ OPT_PC, OPT_3DO);
+ getBoolConfigValueXlat (&options->whichFonts, "config.textgradients",
+ OPT_PC, OPT_3DO);
+ getBoolConfigValueXlat (&options->whichCoarseScan, "config.iconicscan",
+ OPT_3DO, OPT_PC);
+ getBoolConfigValueXlat (&options->smoothScroll, "config.smoothscroll",
+ OPT_3DO, OPT_PC);
+ getBoolConfigValueXlat (&options->whichShield, "config.pulseshield",
+ OPT_3DO, OPT_PC);
+ getBoolConfigValueXlat (&options->whichIntro, "config.3domovies",
+ OPT_3DO, OPT_PC);
+
+ getBoolConfigValue (&options->use3doMusic, "config.3domusic");
+ getBoolConfigValue (&options->useRemixMusic, "config.remixmusic");
+ getBoolConfigValue (&options->useSpeech, "config.speech");
+
+ getBoolConfigValueXlat (&options->meleeScale, "config.smoothmelee",
+ TFB_SCALE_TRILINEAR, TFB_SCALE_STEP);
+
+ getListConfigValue (&options->soundDriver, "config.audiodriver",
+ audioDriverList);
+ getListConfigValue (&options->soundQuality, "config.audioquality",
+ audioQualityList);
+ getBoolConfigValue (&options->stereoSFX, "config.positionalsfx");
+ getVolumeConfigValue (&options->musicVolumeScale, "config.musicvol");
+ getVolumeConfigValue (&options->sfxVolumeScale, "config.sfxvol");
+ getVolumeConfigValue (&options->speechVolumeScale, "config.speechvol");
+
+ if (res_IsInteger ("config.player1control"))
+ {
+ PlayerControls[0] = res_GetInteger ("config.player1control");
+ /* This is an unsigned, so no < 0 check is necessary */
+ if (PlayerControls[0] >= NUM_TEMPLATES)
+ {
+ log_add (log_Error, "Illegal control template '%d' for Player "
+ "One.", PlayerControls[0]);
+ PlayerControls[0] = CONTROL_TEMPLATE_KB_1;
+ }
+ }
+
+ if (res_IsInteger ("config.player2control"))
+ {
+ /* This is an unsigned, so no < 0 check is necessary */
+ PlayerControls[1] = res_GetInteger ("config.player2control");
+ if (PlayerControls[1] >= NUM_TEMPLATES)
+ {
+ log_add (log_Error, "Illegal control template '%d' for Player "
+ "Two.", PlayerControls[1]);
+ PlayerControls[1] = CONTROL_TEMPLATE_JOY_1;
+ }
+ }
+}
+
+enum
+{
+ CSCAN_OPT = 1000,
+ MENU_OPT,
+ FONT_OPT,
+ SHIELD_OPT,
+ SCROLL_OPT,
+ SOUND_OPT,
+ STEREOSFX_OPT,
+ ADDON_OPT,
+ ADDONDIR_OPT,
+ ACCEL_OPT,
+ SAFEMODE_OPT,
+ RENDERER_OPT,
+#ifdef NETPLAY
+ NETHOST1_OPT,
+ NETPORT1_OPT,
+ NETHOST2_OPT,
+ NETPORT2_OPT,
+ NETDELAY_OPT,
+#endif
+};
+
+static const char *optString = "+r:foc:b:spC:n:?hM:S:T:q:ug:l:i:vwxk";
+static struct option longOptions[] =
+{
+ {"res", 1, NULL, 'r'},
+ {"fullscreen", 0, NULL, 'f'},
+ {"opengl", 0, NULL, 'o'},
+ {"scale", 1, NULL, 'c'},
+ {"meleezoom", 1, NULL, 'b'},
+ {"scanlines", 0, NULL, 's'},
+ {"fps", 0, NULL, 'p'},
+ {"configdir", 1, NULL, 'C'},
+ {"contentdir", 1, NULL, 'n'},
+ {"help", 0, NULL, 'h'},
+ {"musicvol", 1, NULL, 'M'},
+ {"sfxvol", 1, NULL, 'S'},
+ {"speechvol", 1, NULL, 'T'},
+ {"audioquality", 1, NULL, 'q'},
+ {"nosubtitles", 0, NULL, 'u'},
+ {"gamma", 1, NULL, 'g'},
+ {"logfile", 1, NULL, 'l'},
+ {"intro", 1, NULL, 'i'},
+ {"version", 0, NULL, 'v'},
+ {"windowed", 0, NULL, 'w'},
+ {"nogl", 0, NULL, 'x'},
+ {"keepaspectratio", 0, NULL, 'k'},
+
+ // options with no short equivalent:
+ {"cscan", 1, NULL, CSCAN_OPT},
+ {"menu", 1, NULL, MENU_OPT},
+ {"font", 1, NULL, FONT_OPT},
+ {"shield", 1, NULL, SHIELD_OPT},
+ {"scroll", 1, NULL, SCROLL_OPT},
+ {"sound", 1, NULL, SOUND_OPT},
+ {"stereosfx", 0, NULL, STEREOSFX_OPT},
+ {"addon", 1, NULL, ADDON_OPT},
+ {"addondir", 1, NULL, ADDONDIR_OPT},
+ {"accel", 1, NULL, ACCEL_OPT},
+ {"safe", 0, NULL, SAFEMODE_OPT},
+ {"renderer", 1, NULL, RENDERER_OPT},
+#ifdef NETPLAY
+ {"nethost1", 1, NULL, NETHOST1_OPT},
+ {"netport1", 1, NULL, NETPORT1_OPT},
+ {"nethost2", 1, NULL, NETHOST2_OPT},
+ {"netport2", 1, NULL, NETPORT2_OPT},
+ {"netdelay", 1, NULL, NETDELAY_OPT},
+#endif
+ {0, 0, 0, 0}
+};
+
+static inline void
+setBoolOption (struct bool_option *option, bool value)
+{
+ option->value = value;
+ option->set = true;
+}
+
+static bool
+setFloatOption (struct float_option *option, const char *strval,
+ const char *optName)
+{
+ if (parseFloatOption (strval, &option->value, optName) != 0)
+ return false;
+ option->set = true;
+ return true;
+}
+
+// returns true is value was found and set successfully
+static bool
+setListOption (struct int_option *option, const char *strval,
+ const struct option_list_value *list)
+{
+ bool found;
+
+ if (!list)
+ return false; // not found
+
+ found = lookupOptionValue (list, strval, &option->value);
+ option->set = found;
+
+ return found;
+}
+
+static inline bool
+setChoiceOption (struct int_option *option, const char *strval)
+{
+ return setListOption (option, strval, choiceList);
+}
+
+static bool
+setVolumeOption (struct float_option *option, const char *strval,
+ const char *optName)
+{
+ int intVol;
+
+ if (parseIntOption (strval, &intVol, optName) != 0)
+ return false;
+ parseIntVolume (intVol, &option->value);
+ option->set = true;
+ return true;
+}
+
+static int
+parseOptions (int argc, char *argv[], struct options_struct *options)
+{
+ int optionIndex;
+ bool badArg = false;
+
+ opterr = 0;
+
+ options->addons = HMalloc (1 * sizeof (const char *));
+ options->addons[0] = NULL;
+ options->numAddons = 0;
+
+ if (argc == 0)
+ {
+ saveError ("Error: Bad command line.");
+ return EXIT_FAILURE;
+ }
+
+#ifdef __APPLE__
+ // If we are launched by double-clicking an application bundle, Finder
+ // sticks a "-psn_<some_number>" argument into the list, which makes
+ // getopt extremely unhappy. Check for this case and wipe out the
+ // entire command line if it looks like it happened.
+ if ((argc >= 2) && (strncmp (argv[1], "-psn_", 5) == 0))
+ {
+ return EXIT_SUCCESS;
+ }
+#endif
+
+ while (!badArg)
+ {
+ int c;
+ optionIndex = -1;
+ c = getopt_long (argc, argv, optString, longOptions, &optionIndex);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '?':
+ if (optopt != '?')
+ {
+ saveError ("\nInvalid option or its argument");
+ badArg = true;
+ break;
+ }
+ // fall through
+ case 'h':
+ options->runMode = runMode_usage;
+ return EXIT_SUCCESS;
+ case 'v':
+ options->runMode = runMode_version;
+ return EXIT_SUCCESS;
+ case 'r':
+ {
+ int width, height;
+ if (sscanf (optarg, "%dx%d", &width, &height) != 2)
+ {
+ saveError ("Error: invalid argument specified "
+ "as resolution.");
+ badArg = true;
+ break;
+ }
+ options->resolution.width = width;
+ options->resolution.height = height;
+ options->resolution.set = true;
+ break;
+ }
+ case 'f':
+ setBoolOption (&options->fullscreen, true);
+ break;
+ case 'w':
+ setBoolOption (&options->fullscreen, false);
+ break;
+ case 'o':
+ setBoolOption (&options->opengl, true);
+ break;
+ case 'x':
+ setBoolOption (&options->opengl, false);
+ break;
+ case 'k':
+ setBoolOption (&options->keepAspectRatio, true);
+ break;
+ case 'c':
+ if (!setListOption (&options->scaler, optarg, scalerList))
+ {
+ InvalidArgument (optarg, "--scale or -c");
+ badArg = true;
+ }
+ break;
+ case 'b':
+ if (!setListOption (&options->meleeScale, optarg,
+ meleeScaleList))
+ {
+ InvalidArgument (optarg, "--meleezoom or -b");
+ badArg = true;
+ }
+ break;
+ case 's':
+ setBoolOption (&options->scanlines, true);
+ break;
+ case 'p':
+ setBoolOption (&options->showFps, true);
+ break;
+ case 'n':
+ options->contentDir = optarg;
+ break;
+ case 'M':
+ if (!setVolumeOption (&options->musicVolumeScale, optarg,
+ "music volume"))
+ {
+ badArg = true;
+ }
+ break;
+ case 'S':
+ if (!setVolumeOption (&options->sfxVolumeScale, optarg,
+ "sfx volume"))
+ {
+ badArg = true;
+ }
+ break;
+ case 'T':
+ if (!setVolumeOption (&options->speechVolumeScale, optarg,
+ "speech volume"))
+ {
+ badArg = true;
+ }
+ break;
+ case 'q':
+ if (!setListOption (&options->soundQuality, optarg,
+ audioQualityList))
+ {
+ InvalidArgument (optarg, "--audioquality or -q");
+ badArg = true;
+ }
+ break;
+ case 'u':
+ setBoolOption (&options->subtitles, false);
+ break;
+ case 'g':
+ if (!setFloatOption (&options->gamma, optarg,
+ "gamma correction"))
+ {
+ badArg = true;
+ }
+ break;
+ case 'l':
+ options->logFile = optarg;
+ break;
+ case 'C':
+ options->configDir = optarg;
+ break;
+ case 'i':
+ if (!setChoiceOption (&options->whichIntro, optarg))
+ {
+ InvalidArgument (optarg, "--intro or -i");
+ badArg = true;
+ }
+ break;
+ case CSCAN_OPT:
+ if (!setChoiceOption (&options->whichCoarseScan, optarg))
+ {
+ InvalidArgument (optarg, "--cscan");
+ badArg = true;
+ }
+ break;
+ case MENU_OPT:
+ if (!setChoiceOption (&options->whichMenu, optarg))
+ {
+ InvalidArgument (optarg, "--menu");
+ badArg = true;
+ }
+ break;
+ case FONT_OPT:
+ if (!setChoiceOption (&options->whichFonts, optarg))
+ {
+ InvalidArgument (optarg, "--font");
+ badArg = true;
+ }
+ break;
+ case SHIELD_OPT:
+ if (!setChoiceOption (&options->whichShield, optarg))
+ {
+ InvalidArgument (optarg, "--shield");
+ badArg = true;
+ }
+ break;
+ case SCROLL_OPT:
+ if (!setChoiceOption (&options->smoothScroll, optarg))
+ {
+ InvalidArgument (optarg, "--scroll");
+ badArg = true;
+ }
+ break;
+ case SOUND_OPT:
+ if (!setListOption (&options->soundDriver, optarg,
+ audioDriverList))
+ {
+ InvalidArgument (optarg, "--sound");
+ badArg = true;
+ }
+ break;
+ case STEREOSFX_OPT:
+ setBoolOption (&options->stereoSFX, true);
+ break;
+ case ADDON_OPT:
+ options->numAddons++;
+ options->addons = HRealloc ((void *) options->addons,
+ (options->numAddons + 1) * sizeof (const char *));
+ options->addons[options->numAddons - 1] = optarg;
+ options->addons[options->numAddons] = NULL;
+ break;
+ case ADDONDIR_OPT:
+ options->addonDir = optarg;
+ break;
+ case ACCEL_OPT:
+ {
+ int value;
+ if (lookupOptionValue (accelList, optarg, &value))
+ {
+ force_platform = value;
+ }
+ else
+ {
+ InvalidArgument (optarg, "--accel");
+ badArg = true;
+ }
+ break;
+ }
+ case SAFEMODE_OPT:
+ setBoolOption (&options->safeMode, true);
+ break;
+ case RENDERER_OPT:
+ options->graphicsBackend = optarg;
+ break;
+#ifdef NETPLAY
+ case NETHOST1_OPT:
+ netplayOptions.peer[0].isServer = false;
+ netplayOptions.peer[0].host = optarg;
+ break;
+ case NETPORT1_OPT:
+ netplayOptions.peer[0].port = optarg;
+ break;
+ case NETHOST2_OPT:
+ netplayOptions.peer[1].isServer = false;
+ netplayOptions.peer[1].host = optarg;
+ break;
+ case NETPORT2_OPT:
+ netplayOptions.peer[1].port = optarg;
+ break;
+ case NETDELAY_OPT:
+ {
+ int temp;
+ if (parseIntOption (optarg, &temp, "network input delay")
+ == -1)
+ {
+ badArg = true;
+ break;
+ }
+ netplayOptions.inputDelay = temp;
+
+ if (netplayOptions.inputDelay > BATTLE_FRAME_RATE)
+ {
+ saveError ("Network input delay is absurdly large.");
+ badArg = true;
+ }
+ break;
+ }
+#endif
+ default:
+ saveError ("Error: Unknown option '%s'",
+ optionIndex < 0 ? "<unknown>" :
+ longOptions[optionIndex].name);
+ badArg = true;
+ break;
+ }
+ }
+
+ if (!badArg && optind != argc)
+ {
+ saveError ("\nError: Extra arguments found on the command line.");
+ badArg = true;
+ }
+
+ return badArg ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+static void
+parseIntVolume (int intVol, float *vol)
+{
+ if (intVol < 0)
+ {
+ *vol = 0.0f;
+ return;
+ }
+
+ if (intVol > 100)
+ {
+ *vol = 1.0f;
+ return;
+ }
+
+ *vol = intVol / 100.0f;
+ return;
+}
+
+static int
+parseIntOption (const char *str, int *result, const char *optName)
+{
+ char *endPtr;
+ int temp;
+
+ if (str[0] == '\0')
+ {
+ saveError ("Error: Invalid value for '%s'.", optName);
+ return -1;
+ }
+ temp = (int) strtol (str, &endPtr, 10);
+ if (*endPtr != '\0')
+ {
+ saveError ("Error: Junk characters in argument '%s'.", optName);
+ return -1;
+ }
+
+ *result = temp;
+ return 0;
+}
+
+static int
+parseFloatOption (const char *str, float *f, const char *optName)
+{
+ char *endPtr;
+ float temp;
+
+ if (str[0] == '\0')
+ {
+ saveError ("Error: Invalid value for '%s'.", optName);
+ return -1;
+ }
+ temp = (float) strtod (str, &endPtr);
+ if (*endPtr != '\0')
+ {
+ saveError ("Error: Junk characters in argument '%s'.", optName);
+ return -1;
+ }
+
+ *f = temp;
+ return 0;
+}
+
+static void
+usage (FILE *out, const struct options_struct *defaults)
+{
+ FILE *old = log_setOutput (out);
+ log_captureLines (LOG_CAPTURE_ALL);
+
+ log_add (log_User, "Options:");
+ log_add (log_User, " -r, --res=WIDTHxHEIGHT (default 640x480, bigger "
+ "works only with --opengl)");
+ log_add (log_User, " -f, --fullscreen (default %s)",
+ boolOptString (&defaults->fullscreen));
+ log_add (log_User, " -w, --windowed (default %s)",
+ boolNotOptString (&defaults->fullscreen));
+ log_add (log_User, " -o, --opengl (default %s)",
+ boolOptString (&defaults->opengl));
+ log_add (log_User, " -x, --nogl (default %s)",
+ boolNotOptString (&defaults->opengl));
+ log_add (log_User, " -k, --keepaspectratio (default %s)",
+ boolOptString (&defaults->keepAspectRatio));
+ log_add (log_User, " -c, --scale=MODE (bilinear, biadapt, biadv, "
+ "triscan, hq or none (default) )");
+ log_add (log_User, " -b, --meleezoom=MODE (step, aka pc, or smooth, "
+ "aka 3do; default is 3do)");
+ log_add (log_User, " -s, --scanlines (default %s)",
+ boolOptString (&defaults->scanlines));
+ log_add (log_User, " -p, --fps (default %s)",
+ boolOptString (&defaults->showFps));
+ log_add (log_User, " -g, --gamma=CORRECTIONVALUE (default 1.0, which "
+ "causes no change)");
+ log_add (log_User, " -C, --configdir=CONFIGDIR");
+ log_add (log_User, " -n, --contentdir=CONTENTDIR");
+ log_add (log_User, " -M, --musicvol=VOLUME (0-100, default 100)");
+ log_add (log_User, " -S, --sfxvol=VOLUME (0-100, default 100)");
+ log_add (log_User, " -T, --speechvol=VOLUME (0-100, default 100)");
+ log_add (log_User, " -q, --audioquality=QUALITY (high, medium or low, "
+ "default medium)");
+ log_add (log_User, " -u, --nosubtitles");
+ log_add (log_User, " -l, --logfile=FILE (sends console output to "
+ "logfile FILE)");
+ log_add (log_User, " --addon ADDON (using a specific addon; "
+ "may be specified multiple times)");
+ log_add (log_User, " --addondir=ADDONDIR (directory where addons "
+ "reside)");
+ log_add (log_User, " --renderer=name (Select named rendering engine "
+ "if possible)");
+ log_add (log_User, " --sound=DRIVER (openal, mixsdl, none; default "
+ "mixsdl)");
+ log_add (log_User, " --stereosfx (enables positional sound effects, "
+ "currently only for openal)");
+ log_add (log_User, " --safe (start in safe mode)");
+#ifdef NETPLAY
+ log_add (log_User, " --nethostN=HOSTNAME (server to connect to for "
+ "player N (1=bottom, 2=top)");
+ log_add (log_User, " --netportN=PORT (port to connect to/listen on for "
+ "player N (1=bottom, 2=top)");
+ log_add (log_User, " --netdelay=FRAMES (number of frames to "
+ "buffer/delay network input for");
+#endif
+ log_add (log_User, "The following options can take either '3do' or 'pc' "
+ "as an option:");
+ log_add (log_User, " -i, --intro : Intro/ending version (default %s)",
+ choiceOptString (&defaults->whichIntro));
+ log_add (log_User, " --cscan : coarse-scan display, pc=text, "
+ "3do=hieroglyphs (default %s)",
+ choiceOptString (&defaults->whichCoarseScan));
+ log_add (log_User, " --menu : menu type, pc=text, 3do=graphical "
+ "(default %s)", choiceOptString (&defaults->whichMenu));
+ log_add (log_User, " --font : font types and colors (default %s)",
+ choiceOptString (&defaults->whichFonts));
+ log_add (log_User, " --shield : slave shield type; pc=static, "
+ "3do=throbbing (default %s)",
+ choiceOptString (&defaults->whichShield));
+ log_add (log_User, " --scroll : ff/frev during comm. pc=per-page, "
+ "3do=smooth (default %s)",
+ choiceOptString (&defaults->smoothScroll));
+ log_setOutput (old);
+}
+
+static int
+InvalidArgument (const char *supplied, const char *opt_name)
+{
+ saveError ("Invalid argument '%s' to option %s.", supplied, opt_name);
+ return EXIT_FAILURE;
+}
+
+static const char *
+choiceOptString (const struct int_option *option)
+{
+ switch (option->value)
+ {
+ case OPT_3DO:
+ return "3do";
+ case OPT_PC:
+ return "pc";
+ default: /* 0 */
+ return "none";
+ }
+}
+
+static const char *
+boolOptString (const struct bool_option *option)
+{
+ return option->value ? "on" : "off";
+}
+
+static const char *
+boolNotOptString (const struct bool_option *option)
+{
+ return option->value ? "off" : "on";
+}
diff --git a/src/uqm/Makeinfo b/src/uqm/Makeinfo
new file mode 100644
index 0000000..4b224fa
--- /dev/null
+++ b/src/uqm/Makeinfo
@@ -0,0 +1,24 @@
+uqm_SUBDIRS="comm planets ships supermelee"
+uqm_CFILES="battle.c battlecontrols.c border.c build.c cleanup.c clock.c
+ cnctdlg.c collide.c comm.c commanim.c commglue.c confirm.c credits.c
+ cyborg.c demo.c displist.c dummy.c encount.c flash.c fmv.c galaxy.c
+ gameev.c gameinp.c gameopt.c gendef.c getchar.c globdata.c gravity.c
+ cons_res.c grpinfo.c hyper.c init.c intel.c intro.c ipdisp.c load.c
+ load_legacy.c
+ loadship.c master.c menu.c misc.c oscill.c outfit.c pickship.c
+ plandata.c process.c restart.c save.c settings.c setup.c setupmenu.c
+ ship.c shipstat.c shipyard.c sis.c sounds.c starbase.c starcon.c
+ starmap.c state.c status.c tactrans.c trans.c uqmdebug.c util.c
+ velocity.c weapon.c"
+uqm_HFILES="battlecontrols.h battle.h build.h clock.h cnctdlg.h coderes.h
+ collide.h colors.h commanim.h commglue.h comm.h cons_res.h controls.h
+ corecode.h credits.h demo.h displist.h dummy.h element.h encount.h
+ flash.h fmv.h gameev.h gameopt.h gamestr.h gendef.h globdata.h
+ grpinfo.h hyper.h ifontres.h igfxres.h ikey_con.h imusicre.h init.h
+ intel.h ipdisp.h isndres.h istrtab.h master.h menustat.h
+ nameref.h oscill.h pickship.h process.h races.h resinst.h respkg.h
+ restart.h save.h settings.h setup.h setupmenu.h shipcont.h ship.h
+ sis.h sounds.h starbase.h starcon.h state.h status.h tactrans.h
+ starmap.h
+ units.h uqmdebug.h util.h velocity.h weapon.h"
+
diff --git a/src/uqm/battle.c b/src/uqm/battle.c
new file mode 100644
index 0000000..f33e66d
--- /dev/null
+++ b/src/uqm/battle.c
@@ -0,0 +1,512 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "battle.h"
+
+#include "battlecontrols.h"
+#include "controls.h"
+#include "init.h"
+#include "element.h"
+#include "ship.h"
+#include "process.h"
+#include "tactrans.h"
+ // for flee_preprocess()
+#include "intel.h"
+#ifdef NETPLAY
+# include "supermelee/netplay/netmelee.h"
+# ifdef NETPLAY_CHECKSUM
+# include "supermelee/netplay/checksum.h"
+# endif
+# include "supermelee/netplay/notifyall.h"
+#endif
+#include "supermelee/pickmele.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "setup.h"
+#include "settings.h"
+#include "sounds.h"
+#include "libs/async.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/log.h"
+#include "libs/mathlib.h"
+
+
+BYTE battle_counter[NUM_SIDES];
+ // The number of ships still available for battle to each side.
+ // A ship that has warped out is no longer available.
+BOOLEAN instantVictory;
+size_t battleInputOrder[NUM_SIDES];
+ // Indices of the sides in the order their input is processed.
+ // Network sides are last so that the sides will never be waiting
+ // on eachother, and games with a 0 frame delay are theoretically
+ // possible.
+#ifdef NETPLAY
+BattleFrameCounter battleFrameCount;
+ // Used for synchronisation purposes during netplay.
+#endif
+
+static BOOLEAN
+RunAwayAllowed (void)
+{
+ return (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER
+ || LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ && GET_GAME_STATE (STARBASE_AVAILABLE)
+ && !GET_GAME_STATE (BOMB_CARRIER);
+}
+
+static void
+DoRunAway (STARSHIP *StarShipPtr)
+{
+ ELEMENT *ElementPtr;
+
+ LockElement (StarShipPtr->hShip, &ElementPtr);
+ if (GetPrimType (&DisplayArray[ElementPtr->PrimIndex]) == STAMP_PRIM
+ && ElementPtr->life_span == NORMAL_LIFE
+ && !(ElementPtr->state_flags & FINITE_LIFE)
+ && ElementPtr->mass_points != MAX_SHIP_MASS * 10
+ && !(ElementPtr->state_flags & APPEARING))
+ {
+ battle_counter[0]--;
+
+ ElementPtr->turn_wait = 3;
+ ElementPtr->thrust_wait = 4;
+ ElementPtr->colorCycleIndex = 0;
+ ElementPtr->preprocess_func = flee_preprocess;
+ ElementPtr->mass_points = MAX_SHIP_MASS * 10;
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+
+ SetPrimColor (&DisplayArray[ElementPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x0B, 0x00, 0x00), 0x2E));
+ // XXX: I think this is supposed to be the same as the
+ // first entry of the color cycle table in flee_preeprocess,
+ // but it is slightly different (0x0A as red value). - SvdB.
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMPFILL_PRIM);
+
+ StarShipPtr->ship_input_state = 0;
+ }
+ UnlockElement (StarShipPtr->hShip);
+}
+
+static void
+setupBattleInputOrder(void)
+{
+ size_t i;
+
+#ifndef NETPLAY
+ for (i = 0; i < NUM_SIDES; i++)
+ battleInputOrder[i] = i;
+#else
+ int j;
+
+ i = 0;
+ // First put the locally controlled players in the array.
+ for (j = 0; j < NUM_SIDES; j++) {
+ if (!(PlayerControl[j] & NETWORK_CONTROL)) {
+ battleInputOrder[i] = j;
+ i++;
+ }
+ }
+
+ // Next put the network controlled players in the array.
+ for (j = 0; j < NUM_SIDES; j++) {
+ if (PlayerControl[j] & NETWORK_CONTROL) {
+ battleInputOrder[i] = j;
+ i++;
+ }
+ }
+#endif
+}
+
+BATTLE_INPUT_STATE
+frameInputHuman (HumanInputContext *context, STARSHIP *StarShipPtr)
+{
+ (void) StarShipPtr;
+ return CurrentInputToBattleInput (context->playerNr);
+}
+
+static void
+ProcessInput (void)
+{
+ BOOLEAN CanRunAway;
+ size_t sideI;
+
+#ifdef NETPLAY
+ netInput ();
+#endif
+
+ CanRunAway = RunAwayAllowed ();
+
+ for (sideI = 0; sideI < NUM_SIDES; sideI++)
+ {
+ HSTARSHIP hBattleShip, hNextShip;
+ size_t cur_player = battleInputOrder[sideI];
+
+ for (hBattleShip = GetHeadLink (&race_q[cur_player]);
+ hBattleShip != 0; hBattleShip = hNextShip)
+ {
+ BATTLE_INPUT_STATE InputState;
+ STARSHIP *StarShipPtr;
+
+ StarShipPtr = LockStarShip (&race_q[cur_player], hBattleShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+
+ if (StarShipPtr->hShip)
+ {
+ // TODO: review and see if we have to do this every frame, or
+ // if we can do this once somewhere
+ StarShipPtr->control = PlayerControl[cur_player];
+
+ InputState = PlayerInput[cur_player]->handlers->frameInput (
+ PlayerInput[cur_player], StarShipPtr);
+
+#if CREATE_JOURNAL
+ JournalInput (InputState);
+#endif /* CREATE_JOURNAL */
+#ifdef NETPLAY
+ if (!(PlayerControl[cur_player] & NETWORK_CONTROL))
+ {
+ BattleInputBuffer *bib = getBattleInputBuffer(cur_player);
+ Netplay_NotifyAll_battleInput (InputState);
+ flushPacketQueues ();
+
+ BattleInputBuffer_push (bib, InputState);
+ // Add this input to the end of the buffer.
+ BattleInputBuffer_pop (bib, &InputState);
+ // Get the input from the front of the buffer.
+ }
+#endif
+
+ StarShipPtr->ship_input_state = 0;
+ if (StarShipPtr->RaceDescPtr->ship_info.crew_level)
+ {
+ if (InputState & BATTLE_LEFT)
+ StarShipPtr->ship_input_state |= LEFT;
+ else if (InputState & BATTLE_RIGHT)
+ StarShipPtr->ship_input_state |= RIGHT;
+ if (InputState & BATTLE_THRUST)
+ StarShipPtr->ship_input_state |= THRUST;
+ if (InputState & BATTLE_WEAPON)
+ StarShipPtr->ship_input_state |= WEAPON;
+ if (InputState & BATTLE_SPECIAL)
+ StarShipPtr->ship_input_state |= SPECIAL;
+
+ if (CanRunAway && cur_player == 0 &&
+ (InputState & BATTLE_ESCAPE))
+ DoRunAway (StarShipPtr);
+ }
+ }
+
+ UnlockStarShip (&race_q[cur_player], hBattleShip);
+ }
+ }
+
+#ifdef NETPLAY
+ flushPacketQueues ();
+#endif
+
+ if (GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT))
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+}
+
+#if DEMO_MODE || CREATE_JOURNAL
+DWORD BattleSeed;
+#endif /* DEMO_MODE */
+
+static MUSIC_REF BattleRef;
+
+void
+BattleSong (BOOLEAN DoPlay)
+{
+ if (BattleRef == 0)
+ {
+ if (inHyperSpace ())
+ BattleRef = LoadMusic (HYPERSPACE_MUSIC);
+ else if (inQuasiSpace ())
+ BattleRef = LoadMusic (QUASISPACE_MUSIC);
+ else
+ BattleRef = LoadMusic (BATTLE_MUSIC);
+ }
+
+ if (DoPlay)
+ PlayMusic (BattleRef, TRUE, 1);
+}
+
+void
+FreeBattleSong (void)
+{
+ DestroyMusic (BattleRef);
+ BattleRef = 0;
+}
+
+static BOOLEAN
+DoBattle (BATTLE_STATE *bs)
+{
+ extern UWORD nth_frame;
+ RECT r;
+ BYTE battle_speed;
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+
+#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM)
+ if (getNumNetConnections() > 0 &&
+ battleFrameCount % NETPLAY_CHECKSUM_INTERVAL == 0)
+ {
+ crc_State state;
+ Checksum checksum;
+
+ crc_init(&state);
+ crc_processState (&state);
+ checksum = (Checksum) crc_finish (&state);
+
+ Netplay_NotifyAll_checksum ((uint32) battleFrameCount,
+ (uint32) checksum);
+ flushPacketQueues ();
+ addLocalChecksum (battleFrameCount, checksum);
+ }
+#endif
+ ProcessInput ();
+ // Also calls NetInput()
+#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM)
+ if (getNumNetConnections() > 0)
+ {
+ size_t delay = getBattleInputDelay();
+
+ if (battleFrameCount >= delay
+ && (battleFrameCount - delay) % NETPLAY_CHECKSUM_INTERVAL == 0)
+ {
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (!verifyChecksums (battleFrameCount - delay)) {
+ GLOBAL(CurrentActivity) |= CHECK_ABORT;
+ resetConnections (ResetReason_syncLoss);
+ }
+ }
+ }
+ }
+#endif
+
+ if (bs->first_time)
+ {
+ r.corner.x = SIS_ORG_X;
+ r.corner.y = SIS_ORG_Y;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = SIS_SCREEN_HEIGHT;
+ SetTransitionSource (&r);
+ }
+ BatchGraphics ();
+
+ // Call the callback function, if set
+ if (bs->frame_cb)
+ bs->frame_cb ();
+
+ RedrawQueue (TRUE);
+
+ if (bs->first_time)
+ {
+ bs->first_time = FALSE;
+ ScreenTransition (3, &r);
+ }
+ UnbatchGraphics ();
+ if ((!(GLOBAL (CurrentActivity) & IN_BATTLE)) ||
+ (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ return FALSE;
+ }
+
+ battle_speed = HIBYTE (nth_frame);
+ if (battle_speed == (BYTE)~0)
+ { // maximum speed, nothing rendered at all
+ Async_process ();
+ TaskSwitch ();
+ }
+ else
+ {
+ SleepThreadUntil (bs->NextTime
+ + BATTLE_FRAME_RATE / (battle_speed + 1));
+ bs->NextTime = GetTimeCounter ();
+ }
+
+ if ((GLOBAL (CurrentActivity) & IN_BATTLE) == 0)
+ return FALSE;
+
+#ifdef NETPLAY
+ battleFrameCount++;
+#endif
+ return TRUE;
+}
+
+#ifdef NETPLAY
+COUNT
+GetPlayerOrder (COUNT i)
+{
+ // Iff 'myTurn' is set on a connection, the local party will be
+ // processed first.
+ // If neither is network controlled, the top player (1) is handled
+ // first.
+ if (((PlayerControl[0] & NETWORK_CONTROL) &&
+ !NetConnection_getDiscriminant (netConnections[0])) ||
+ ((PlayerControl[1] & NETWORK_CONTROL) &&
+ NetConnection_getDiscriminant (netConnections[1])))
+ return i;
+ else
+ return 1 - i;
+}
+#endif
+
+// Let each player pick his ship.
+static BOOLEAN
+selectAllShips (SIZE num_ships)
+{
+ if (num_ships == 1) {
+ // HyperSpace in full game.
+ return GetNextStarShip (NULL, 0);
+ }
+
+#ifdef NETPLAY
+ if ((PlayerControl[0] & NETWORK_CONTROL) &&
+ (PlayerControl[1] & NETWORK_CONTROL))
+ {
+ log_add (log_Error, "Only one side at a time can be network "
+ "controlled.\n");
+ return FALSE;
+ }
+#endif
+
+ return GetInitialStarShips ();
+}
+
+BOOLEAN
+Battle (BattleFrameCallback *callback)
+{
+ SIZE num_ships;
+
+
+#if !(DEMO_MODE || CREATE_JOURNAL)
+ if (LOBYTE (GLOBAL (CurrentActivity)) != SUPER_MELEE) {
+ // In Supermelee, the RNG is already initialised.
+ TFB_SeedRandom (GetTimeCounter ());
+ }
+#else /* DEMO_MODE */
+ if (BattleSeed == 0)
+ BattleSeed = TFB_Random ();
+ TFB_SeedRandom (BattleSeed);
+ BattleSeed = TFB_Random (); /* get next battle seed */
+#endif /* DEMO_MODE */
+
+ BattleSong (FALSE);
+
+ num_ships = InitShips ();
+
+ if (instantVictory)
+ {
+ num_ships = 0;
+ battle_counter[0] = 1;
+ battle_counter[1] = 0;
+ instantVictory = FALSE;
+ }
+
+ if (num_ships)
+ {
+ BATTLE_STATE bs;
+
+ GLOBAL (CurrentActivity) |= IN_BATTLE;
+ battle_counter[0] = CountLinks (&race_q[0]);
+ battle_counter[1] = CountLinks (&race_q[1]);
+
+ if (optMeleeScale != TFB_SCALE_STEP)
+ SetGraphicScaleMode (optMeleeScale);
+
+ setupBattleInputOrder ();
+#ifdef NETPLAY
+ initBattleInputBuffers ();
+#ifdef NETPLAY_CHECKSUM
+ initChecksumBuffers ();
+#endif /* NETPLAY_CHECKSUM */
+ battleFrameCount = 0;
+ ResetWinnerStarShip ();
+ setBattleStateConnections (&bs);
+#endif /* NETPLAY */
+
+ if (!selectAllShips (num_ships)) {
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ goto AbortBattle;
+ }
+
+ BattleSong (TRUE);
+ bs.NextTime = 0;
+#ifdef NETPLAY
+ initBattleStateDataConnections ();
+ {
+ bool allOk = negotiateReadyConnections (true, NetState_inBattle);
+ if (!allOk) {
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ goto AbortBattle;
+ }
+ }
+#endif /* NETPLAY */
+ bs.InputFunc = DoBattle;
+ bs.frame_cb = callback;
+ bs.first_time = inHQSpace ();
+
+ DoInput (&bs, FALSE);
+
+AbortBattle:
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ {
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ // Do not return to the main menu when a game is aborted,
+ // (just to the supermelee menu).
+#ifdef NETPLAY
+ waitResetConnections(NetState_inSetup);
+ // A connection may already be in inSetup (set from
+ // GetMeleeStarship). This is not a problem, although
+ // it will generate a warning in debug mode.
+#endif
+
+ GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
+ }
+ else
+ {
+ // Show the result of the battle.
+ MeleeGameOver ();
+ }
+ }
+
+#ifdef NETPLAY
+ uninitBattleInputBuffers();
+#ifdef NETPLAY_CHECKSUM
+ uninitChecksumBuffers ();
+#endif /* NETPLAY_CHECKSUM */
+ setBattleStateConnections (NULL);
+#endif /* NETPLAY */
+
+ StopDitty ();
+ StopMusic ();
+ StopSound ();
+ }
+
+ UninitShips ();
+ FreeBattleSong ();
+
+
+ return (BOOLEAN) (num_ships < 0);
+}
+
diff --git a/src/uqm/battle.h b/src/uqm/battle.h
new file mode 100644
index 0000000..2b1d912
--- /dev/null
+++ b/src/uqm/battle.h
@@ -0,0 +1,66 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef UQM_BATTLE_H_
+#define UQM_BATTLE_H_
+
+#include "options.h"
+#include "libs/compiler.h"
+
+#if defined (NETPLAY)
+typedef DWORD BattleFrameCounter;
+#endif
+
+#include "init.h"
+ // For NUM_SIDES
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// The callback function is called on every battle frame
+// just before the display queue is drawn
+typedef void (BattleFrameCallback) (void);
+
+typedef struct battlestate_struct {
+ BOOLEAN (*InputFunc) (struct battlestate_struct *pInputState);
+ BOOLEAN first_time;
+ DWORD NextTime;
+ BattleFrameCallback *frame_cb;
+} BATTLE_STATE;
+
+extern BYTE battle_counter[NUM_SIDES];
+extern BOOLEAN instantVictory;
+#if defined (NETPLAY)
+extern BattleFrameCounter battleFrameCount;
+#endif
+#ifdef NETPLAY
+COUNT GetPlayerOrder (COUNT i);
+#else
+# define GetPlayerOrder(i) (i)
+#endif
+
+BOOLEAN Battle (BattleFrameCallback *);
+
+#define BATTLE_FRAME_RATE (ONE_SECOND / 24)
+
+extern void BattleSong (BOOLEAN DoPlay);
+extern void FreeBattleSong (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_BATTLE_H_ */
diff --git a/src/uqm/battlecontrols.c b/src/uqm/battlecontrols.c
new file mode 100644
index 0000000..0f241e6
--- /dev/null
+++ b/src/uqm/battlecontrols.c
@@ -0,0 +1,100 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "battlecontrols.h"
+
+#include "intel.h"
+ // For computer_intelligence()
+#include "tactrans.h"
+ // For battleEndReady*
+#include "init.h"
+ // For NUM_PLAYERS
+#include "libs/memlib.h"
+ // For HMalloc(), HFree()
+#ifdef NETPLAY
+# include "supermelee/netplay/netmelee.h"
+#endif /* NETPLAY */
+
+InputContext *PlayerInput[NUM_PLAYERS];
+
+BattleInputHandlers ComputerInputHandlers = {
+ /* .frameInput = */ (BattleFrameInputFunction) computer_intelligence,
+ /* .selectShip = */ (SelectShipFunction) selectShipComputer,
+ /* .battleEndReady = */ (BattleEndReadyFunction) battleEndReadyComputer,
+ /* .deleteContext = */ InputContext_delete,
+};
+
+BattleInputHandlers HumanInputHandlers = {
+ /* .frameInput = */ (BattleFrameInputFunction) frameInputHuman,
+ /* .selectShip = */ (SelectShipFunction) selectShipHuman,
+ /* .battleEndReady = */ (BattleEndReadyFunction) battleEndReadyHuman,
+ /* .deleteContext = */ InputContext_delete,
+};
+
+#ifdef NETPLAY
+BattleInputHandlers NetworkInputHandlers = {
+ /* .frameInput = */ (BattleFrameInputFunction) networkBattleInput,
+ /* .selectShip = */ (SelectShipFunction) selectShipNetwork,
+ /* .battleEndReady = */ (BattleEndReadyFunction) battleEndReadyNetwork,
+ /* .deleteContext = */ InputContext_delete,
+};
+#endif
+
+
+void
+InputContext_init (InputContext *context, BattleInputHandlers *handlers,
+ COUNT playerNr)
+{
+ context->handlers = handlers;
+ context->playerNr = playerNr;
+}
+
+void
+InputContext_delete (InputContext *context)
+{
+ HFree (context);
+}
+
+ComputerInputContext *
+ComputerInputContext_new (COUNT playerNr)
+{
+ ComputerInputContext *result = HMalloc (sizeof (ComputerInputContext));
+ InputContext_init ((InputContext *) result,
+ &ComputerInputHandlers, playerNr);
+ return result;
+}
+
+HumanInputContext *
+HumanInputContext_new (COUNT playerNr)
+{
+ HumanInputContext *result = HMalloc (sizeof (HumanInputContext));
+ InputContext_init ((InputContext *) result,
+ &HumanInputHandlers, playerNr);
+ return result;
+}
+
+#ifdef NETPLAY
+NetworkInputContext *
+NetworkInputContext_new (COUNT playerNr)
+{
+ NetworkInputContext *result = HMalloc (sizeof (NetworkInputContext));
+ InputContext_init ((InputContext *) result,
+ &NetworkInputHandlers, playerNr);
+ return result;
+}
+#endif
+
+
diff --git a/src/uqm/battlecontrols.h b/src/uqm/battlecontrols.h
new file mode 100644
index 0000000..4d424b7
--- /dev/null
+++ b/src/uqm/battlecontrols.h
@@ -0,0 +1,99 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_BATTLECONTROLS_H_
+#define UQM_BATTLECONTROLS_H_
+
+typedef struct BattleInputHandlers BattleInputHandlers;
+typedef struct InputContext InputContext;
+typedef struct ComputerInputContext ComputerInputContext;
+typedef struct HumanInputContext HumanInputContext;
+#ifdef NETPLAY
+typedef struct NetworkInputContext NetworkInputContext;
+#endif /* NETPLAY */
+
+#include "controls.h"
+#include "supermelee/pickmele.h"
+#include "races.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef BATTLE_INPUT_STATE (*BattleFrameInputFunction) (
+ InputContext *context, STARSHIP *StarShipPtr);
+typedef BOOLEAN (*SelectShipFunction) (InputContext *context,
+ GETMELEE_STATE *gms);
+typedef bool (*BattleEndReadyFunction) (InputContext *context);
+typedef void (*DeleteInputContextFunction) (InputContext *context);
+
+
+struct BattleInputHandlers {
+ BattleFrameInputFunction frameInput;
+ SelectShipFunction selectShip;
+ BattleEndReadyFunction battleEndReady;
+ DeleteInputContextFunction deleteContext;
+};
+
+#define INPUT_CONTEXT_COMMON \
+ BattleInputHandlers *handlers; \
+ COUNT playerNr;
+
+// Base "class" for all ...InputContext structures
+struct InputContext {
+ INPUT_CONTEXT_COMMON
+};
+
+struct ComputerInputContext {
+ INPUT_CONTEXT_COMMON
+ // TODO: Put RNG Context used for the AI here.
+};
+
+struct HumanInputContext {
+ INPUT_CONTEXT_COMMON
+};
+
+#ifdef NETPLAY
+struct NetworkInputContext {
+ INPUT_CONTEXT_COMMON
+ // TODO: put NetworkConnection for this player here.
+};
+#endif /* NETPLAY */
+
+ComputerInputContext *ComputerInputContext_new (COUNT playerNr);
+HumanInputContext *HumanInputContext_new (COUNT playerNr);
+#ifdef NETPLAY
+NetworkInputContext *NetworkInputContext_new (COUNT playerNr);
+#endif /* NETPLAY */
+
+extern InputContext *PlayerInput[];
+
+
+BATTLE_INPUT_STATE frameInputHuman (HumanInputContext *context,
+ STARSHIP *StarShipPtr);
+void InputContext_init(InputContext *context, BattleInputHandlers *handlers,
+ COUNT playerNr);
+void InputContext_delete (InputContext *context);
+ // Do not call directly, only from the FreeInputContextFunction.
+ // Call InputContext->handlers->freeContext() to release an
+ // InputContext.
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_BATTLECONTROLS_H_ */
diff --git a/src/uqm/border.c b/src/uqm/border.c
new file mode 100644
index 0000000..193d19a
--- /dev/null
+++ b/src/uqm/border.c
@@ -0,0 +1,200 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "libs/gfxlib.h"
+#include "libs/threadlib.h"
+#include "colors.h"
+#include "setup.h"
+#include "sis.h"
+#include "units.h"
+#include "util.h"
+
+
+void
+InitSISContexts (void)
+{
+ RECT r;
+
+ SetContext (StatusContext);
+
+ SetContext (SpaceContext);
+ SetContextFGFrame (Screen);
+
+ r.corner.x = SIS_ORG_X;
+ r.corner.y = SIS_ORG_Y;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = SIS_SCREEN_HEIGHT;
+ SetContextClipRect (&r);
+}
+
+void
+DrawSISFrame (void)
+{
+ RECT r;
+
+ SetContext (ScreenContext);
+
+ BatchGraphics ();
+ {
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = SIS_ORG_X + SIS_SCREEN_WIDTH + 1;
+ r.extent.height = SIS_ORG_Y - 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = SIS_ORG_X - 1;
+ r.extent.height = SIS_ORG_Y + SIS_SCREEN_HEIGHT + 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = 0;
+ r.corner.y = r.extent.height;
+ r.extent.width = SIS_ORG_X + SIS_SCREEN_WIDTH + 1;
+ r.extent.height = SCREEN_HEIGHT - SIS_ORG_Y + SIS_SCREEN_HEIGHT;
+ DrawFilledRectangle (&r);
+ r.corner.x = SIS_ORG_X + SIS_SCREEN_WIDTH + 1;
+ r.corner.y = 0;
+ r.extent.width = SCREEN_WIDTH - r.corner.x;
+ r.extent.height = SCREEN_HEIGHT;
+ DrawFilledRectangle (&r);
+
+ r.corner.x = SIS_ORG_X - 1;
+ r.corner.y = SIS_ORG_Y - 1;
+ r.extent.width = SIS_SCREEN_WIDTH + 2;
+ r.extent.height = SIS_SCREEN_HEIGHT + 2;
+ DrawStarConBox (&r, 1,
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19),
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F),
+ TRUE, BLACK_COLOR);
+
+ r.corner.y = 0;
+ r.extent.height = SIS_ORG_Y;
+
+ r.corner.x = SIS_ORG_X;
+ r.extent.width = SIS_MESSAGE_BOX_WIDTH;
+ DrawStarConBox (&r, 1,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x0E), 0x54),
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x1C), 0x4E),
+ TRUE, BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01));
+
+ r.extent.width = SIS_TITLE_BOX_WIDTH;
+ r.corner.x = SIS_ORG_X + SIS_SCREEN_WIDTH - SIS_TITLE_BOX_WIDTH;
+ DrawStarConBox (&r, 1,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x0E), 0x54),
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x1C), 0x4E),
+ TRUE, BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01));
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.x = SAFE_X + SPACE_WIDTH - 1;
+ r.corner.y = 0;
+ r.extent.width = 1;
+ r.extent.height = SCREEN_HEIGHT;
+ DrawFilledRectangle (&r);
+ r.corner.x = SAFE_X + SPACE_WIDTH;
+ r.corner.y = SAFE_Y + 139;
+ DrawPoint (&r.corner);
+ r.corner.x = SCREEN_WIDTH - 1;
+ DrawPoint (&r.corner);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19));
+ r.corner.y = 1;
+ r.extent.width = 1;
+ r.extent.height = SAFE_Y + SIS_TITLE_HEIGHT;
+ r.corner.x = SIS_ORG_X - 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = SIS_ORG_X + SIS_SCREEN_WIDTH - SIS_TITLE_BOX_WIDTH - 1;
+ DrawFilledRectangle (&r);
+
+ r.corner.x = 0;
+ r.corner.y = SCREEN_HEIGHT - 1;
+ r.extent.width = SAFE_X + SPACE_WIDTH - 1;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = SAFE_X + SPACE_WIDTH - 2;
+ r.corner.y = 0;
+ r.extent.width = 1;
+ r.extent.height = SCREEN_HEIGHT - 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = SCREEN_WIDTH - 1;
+ r.corner.y = 0;
+ r.extent.width = 1;
+ r.extent.height = SAFE_Y + 139;
+ DrawFilledRectangle (&r);
+ r.corner.x = SAFE_X + SPACE_WIDTH;
+ r.corner.y = SCREEN_HEIGHT - 1;
+ r.extent.width = SCREEN_WIDTH - r.corner.x;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = SCREEN_WIDTH - 1;
+ r.corner.y = SAFE_Y + 140;
+ r.extent.width = 1;
+ r.extent.height = (SCREEN_HEIGHT - 1) - r.corner.y;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F));
+ r.corner.y = 1;
+ r.extent.width = 1;
+ r.extent.height = SAFE_Y + SIS_MESSAGE_HEIGHT;
+ r.corner.x = SIS_ORG_X + SIS_MESSAGE_BOX_WIDTH;
+ DrawFilledRectangle (&r);
+ r.corner.x = SIS_ORG_X + SIS_SCREEN_WIDTH;
+ ++r.extent.height;
+ DrawFilledRectangle (&r);
+ r.corner.y = 0;
+ r.extent.width = (SAFE_X + SPACE_WIDTH - 2) - r.corner.x;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = 0;
+ r.extent.width = SIS_ORG_X - r.corner.x;
+ DrawFilledRectangle (&r);
+ r.corner.x = SIS_ORG_X + SIS_MESSAGE_BOX_WIDTH;
+ r.extent.width = SIS_SPACER_BOX_WIDTH;
+ DrawFilledRectangle (&r);
+
+ r.corner.x = 0;
+ r.corner.y = 1;
+ r.extent.width = 1;
+ r.extent.height = (SCREEN_HEIGHT - 1) - r.corner.y;
+ DrawFilledRectangle (&r);
+ r.corner.x = SAFE_X + SPACE_WIDTH;
+ r.corner.y = 0;
+ r.extent.width = 1;
+ r.extent.height = SAFE_Y + 139;
+ DrawFilledRectangle (&r);
+ r.corner.x = SAFE_X + SPACE_WIDTH + 1;
+ r.corner.y = SAFE_Y + 139;
+ r.extent.width = STATUS_WIDTH - 2;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = SAFE_X + SPACE_WIDTH;
+ r.corner.y = SAFE_Y + 140;
+ r.extent.width = 1;
+ r.extent.height = SCREEN_HEIGHT - r.corner.y;
+ DrawFilledRectangle (&r);
+ }
+
+ InitSISContexts ();
+ ClearSISRect (DRAW_SIS_DISPLAY);
+
+ UnbatchGraphics ();
+}
+
diff --git a/src/uqm/build.c b/src/uqm/build.c
new file mode 100644
index 0000000..070453a
--- /dev/null
+++ b/src/uqm/build.c
@@ -0,0 +1,547 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "build.h"
+
+#include "races.h"
+#include "master.h"
+#include "sis.h"
+#include "setup.h"
+#include "libs/compiler.h"
+#include "libs/mathlib.h"
+
+
+// Allocate a new STARSHIP or SHIP_FRAGMENT and put it in the queue
+HLINK
+Build (QUEUE *pQueue, SPECIES_ID SpeciesID)
+{
+ HLINK hNewShip;
+ SHIP_BASE *ShipPtr;
+
+ assert (GetLinkSize (pQueue) == sizeof (STARSHIP) ||
+ GetLinkSize (pQueue) == sizeof (SHIP_FRAGMENT));
+
+ hNewShip = AllocLink (pQueue);
+ if (!hNewShip)
+ return 0;
+
+ ShipPtr = (SHIP_BASE *) LockLink (pQueue, hNewShip);
+ memset (ShipPtr, 0, GetLinkSize (pQueue));
+ ShipPtr->SpeciesID = SpeciesID;
+
+ UnlockLink (pQueue, hNewShip);
+ PutQueue (pQueue, hNewShip);
+
+ return hNewShip;
+}
+
+HLINK
+GetStarShipFromIndex (QUEUE *pShipQ, COUNT Index)
+{
+ HLINK hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (pShipQ);
+ Index > 0 && hStarShip; hStarShip = hNextShip, --Index)
+ {
+ LINK *StarShipPtr;
+
+ StarShipPtr = LockLink (pShipQ, hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockLink (pShipQ, hStarShip);
+ }
+
+ return (hStarShip);
+}
+
+HSHIPFRAG
+GetEscortByStarShipIndex (COUNT index)
+{
+ HSHIPFRAG hStarShip;
+ HSHIPFRAG hNextShip;
+ SHIP_FRAGMENT *StarShipPtr;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (StarShipPtr->index == index)
+ {
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ break;
+ }
+
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ return hStarShip;
+}
+
+/*
+ * Give the player 'count' ships of the specified race,
+ * limited by the number of free slots.
+ * Returns the number of ships added.
+ */
+COUNT
+AddEscortShips (COUNT race, SIZE count)
+{
+ HFLEETINFO hFleet;
+ BYTE which_window;
+ COUNT i;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
+ if (!hFleet)
+ return 0;
+
+ assert (count > 0);
+
+ which_window = 0;
+ for (i = 0; i < (COUNT) count; i++)
+ {
+ HSHIPFRAG hStarShip;
+ HSHIPFRAG hOldShip;
+ SHIP_FRAGMENT *StarShipPtr;
+
+ hStarShip = CloneShipFragment (race, &GLOBAL (built_ship_q), 0);
+ if (!hStarShip)
+ break;
+
+ RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
+
+ /* Find first available escort window */
+ while ((hOldShip = GetStarShipFromIndex (
+ &GLOBAL (built_ship_q), which_window++)))
+ {
+ BYTE win_loc;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hOldShip);
+ win_loc = StarShipPtr->index;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hOldShip);
+ if (which_window <= win_loc)
+ break;
+ }
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ StarShipPtr->index = which_window - 1;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ InsertQueue (&GLOBAL (built_ship_q), hStarShip, hOldShip);
+ }
+
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+ return i;
+}
+
+/*
+ * Returns the total value of all the ships escorting the SIS.
+ */
+COUNT
+CalculateEscortsWorth (void)
+{
+ COUNT ShipCost[] =
+ {
+ RACE_SHIP_COST
+ };
+ COUNT total = 0;
+ HSHIPFRAG hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+ total += ShipCost[StarShipPtr->race_id];
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+ return total;
+}
+
+#if 0
+/*
+ * Returns the size of the fleet of the specified race when the starmap was
+ * last checked. If the race has no SoI, 0 is returned.
+ */
+COUNT
+GetRaceKnownSize (COUNT race)
+{
+ HFLEETINFO hFleet;
+ FLEET_INFO *FleetPtr;
+ COUNT result;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
+ if (!hFleet)
+ return 0;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+
+ result = FleetPtr->known_strength;
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ return result;
+}
+#endif
+
+/*
+ * Start or end an alliance with the specified race.
+ * Being in an alliance with a race makes their ships available for building
+ * in the shipyard.
+ * flag == TRUE: start an alliance
+ * flag == TRUE: end an alliance
+ */
+COUNT
+SetRaceAllied (COUNT race, BOOLEAN flag) {
+ HFLEETINFO hFleet;
+ FLEET_INFO *FleetPtr;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
+ if (!hFleet)
+ return 0;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+
+ if (FleetPtr->allied_state == DEAD_GUY)
+ {
+ /* Strange request, silently ignore it */
+ }
+ else
+ {
+ FleetPtr->allied_state = (flag ? GOOD_GUY : BAD_GUY);
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ return 1;
+}
+
+/*
+ * Make the sphere of influence for the specified race shown on the starmap
+ * in the future.
+ * The value returned is 'race', unless the type of ship is only available
+ * in SuperMelee, in which case 0 is returned.
+ */
+COUNT
+StartSphereTracking (COUNT race)
+{
+ HFLEETINFO hFleet;
+ FLEET_INFO *FleetPtr;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
+ if (!hFleet)
+ return 0;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+
+ if (FleetPtr->actual_strength == 0)
+ {
+ if (FleetPtr->allied_state == DEAD_GUY)
+ {
+ // Race is extinct.
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ return 0;
+ }
+ }
+ else if (FleetPtr->known_strength == 0
+ && FleetPtr->actual_strength != INFINITE_RADIUS)
+ {
+ FleetPtr->known_strength = 1;
+ FleetPtr->known_loc = FleetPtr->loc;
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ return race;
+}
+
+/*
+ * Returns the number of ships of the specified race among the
+ * escort ships.
+ */
+COUNT
+CountEscortShips (COUNT race)
+{
+ HFLEETINFO hFleet;
+ HSHIPFRAG hStarShip, hNextShip;
+ COUNT result = 0;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
+ if (!hFleet)
+ return 0;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)); hStarShip;
+ hStarShip = hNextShip)
+ {
+ BYTE ship_type;
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+ ship_type = StarShipPtr->race_id;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (ship_type == race)
+ result++;
+ }
+ return result;
+}
+
+/*
+ * Returns true if and only if a ship of the specified race is among the
+ * escort ships.
+ */
+BOOLEAN
+HaveEscortShip (COUNT race)
+{
+ return (CountEscortShips (race) > 0);
+}
+
+/*
+ * Test if the SIS can have an escort of the specified race.
+ * Returns 0 if 'race' is not available.
+ * Otherwise, returns the number of ships that can be added.
+ */
+COUNT
+EscortFeasibilityStudy (COUNT race)
+{
+ HFLEETINFO hFleet;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
+ if (!hFleet)
+ return 0;
+
+ return (MAX_BUILT_SHIPS - CountLinks (&GLOBAL (built_ship_q)));
+}
+
+/*
+ * Test the alliance status of the specified race.
+ * Either DEAD_GUY (extinct), GOOD_GUY (allied), or BAD_GUY (not allied) is
+ * returned.
+ */
+COUNT
+CheckAlliance (COUNT race)
+{
+ HFLEETINFO hFleet;
+ UWORD flags;
+ FLEET_INFO *FleetPtr;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race);
+ if (!hFleet)
+ return 0;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ flags = FleetPtr->allied_state;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+
+ return flags;
+}
+
+/*
+ * Remove a number of escort ships of the specified race (if present).
+ * Returns the number of escort ships removed.
+ */
+COUNT
+RemoveSomeEscortShips (COUNT race, COUNT count)
+{
+ HSHIPFRAG hStarShip;
+ HSHIPFRAG hNextShip;
+
+ if (count == 0)
+ return 0;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)); hStarShip;
+ hStarShip = hNextShip)
+ {
+ BOOLEAN RemoveShip;
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+ RemoveShip = (StarShipPtr->race_id == race);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (RemoveShip)
+ {
+ RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
+ FreeShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ count--;
+ if (count == 0)
+ break;
+ }
+ }
+
+ if (count > 0)
+ {
+ // Update the display.
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+ }
+
+ return count;
+}
+
+/*
+ * Remove all escort ships of the specified race.
+ */
+void
+RemoveEscortShips (COUNT race)
+{
+ RemoveSomeEscortShips (race, (COUNT) -1);
+}
+
+COUNT
+GetIndexFromStarShip (QUEUE *pShipQ, HLINK hStarShip)
+{
+ COUNT Index;
+
+ Index = 0;
+ while (hStarShip != GetHeadLink (pShipQ))
+ {
+ HLINK hNextShip;
+ LINK *StarShipPtr;
+
+ StarShipPtr = LockLink (pShipQ, hStarShip);
+ hNextShip = _GetPredLink (StarShipPtr);
+ UnlockLink (pShipQ, hStarShip);
+
+ hStarShip = hNextShip;
+ ++Index;
+ }
+
+ return Index;
+}
+
+BYTE
+NameCaptain (QUEUE *pQueue, SPECIES_ID SpeciesID)
+{
+ BYTE name_index;
+ HLINK hStarShip;
+
+ assert (GetLinkSize (pQueue) == sizeof (STARSHIP) ||
+ GetLinkSize (pQueue) == sizeof (SHIP_FRAGMENT));
+
+ do
+ {
+ HLINK hNextShip;
+
+ name_index = PickCaptainName ();
+ for (hStarShip = GetHeadLink (pQueue); hStarShip;
+ hStarShip = hNextShip)
+ {
+ SHIP_BASE *ShipPtr;
+ BYTE test_name_index = -1;
+
+ ShipPtr = (SHIP_BASE *) LockLink (pQueue, hStarShip);
+ hNextShip = _GetSuccLink (ShipPtr);
+ if (ShipPtr->SpeciesID == SpeciesID)
+ test_name_index = ShipPtr->captains_name_index;
+ UnlockLink (pQueue, hStarShip);
+
+ if (name_index == test_name_index)
+ break;
+ }
+ } while (hStarShip /* name matched another ship */);
+
+ return name_index;
+}
+
+// crew_level can be set to INFINITE_FLEET for a ship which is to
+// represent an infinite number of ships.
+HSHIPFRAG
+CloneShipFragment (COUNT shipIndex, QUEUE *pDstQueue, COUNT crew_level)
+{
+ HFLEETINFO hFleet;
+ HSHIPFRAG hBuiltShip;
+ FLEET_INFO *TemplatePtr;
+ BYTE captains_name_index;
+
+ assert (GetLinkSize (pDstQueue) == sizeof (SHIP_FRAGMENT));
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), shipIndex);
+ if (!hFleet)
+ return 0;
+
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ if (shipIndex == SAMATRA_SHIP)
+ captains_name_index = 0;
+ else
+ captains_name_index = NameCaptain (pDstQueue,
+ TemplatePtr->SpeciesID);
+ hBuiltShip = Build (pDstQueue, TemplatePtr->SpeciesID);
+ if (hBuiltShip)
+ {
+ SHIP_FRAGMENT *ShipFragPtr;
+
+ ShipFragPtr = LockShipFrag (pDstQueue, hBuiltShip);
+ ShipFragPtr->captains_name_index = captains_name_index;
+ ShipFragPtr->race_strings = TemplatePtr->race_strings;
+ ShipFragPtr->icons = TemplatePtr->icons;
+ ShipFragPtr->melee_icon = TemplatePtr->melee_icon;
+ if (crew_level)
+ ShipFragPtr->crew_level = crew_level;
+ else
+ ShipFragPtr->crew_level = TemplatePtr->crew_level;
+ ShipFragPtr->max_crew = TemplatePtr->max_crew;
+ ShipFragPtr->energy_level = 0;
+ ShipFragPtr->max_energy = TemplatePtr->max_energy;
+ ShipFragPtr->race_id = (BYTE)shipIndex;
+ ShipFragPtr->index = 0;
+ UnlockShipFrag (pDstQueue, hBuiltShip);
+ }
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+
+ return hBuiltShip;
+}
+
+/* Set the crew and captain's name on the first fully-crewed escort
+ * ship of race 'which_ship' */
+int
+SetEscortCrewComplement (COUNT which_ship, COUNT crew_level, BYTE captain)
+{
+ HFLEETINFO hFleet;
+ FLEET_INFO *TemplatePtr;
+ HSHIPFRAG hStarShip, hNextShip;
+ SHIP_FRAGMENT *StarShipPtr = 0;
+ int Index;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), which_ship);
+ if (!hFleet)
+ return -1;
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+
+ /* Find first ship of which_ship race */
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)), Index = 0;
+ hStarShip; hStarShip = hNextShip, ++Index)
+ {
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+ if (which_ship == StarShipPtr->race_id &&
+ StarShipPtr->crew_level == TemplatePtr->crew_level)
+ break; /* found one */
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+ if (hStarShip)
+ {
+ StarShipPtr->crew_level = crew_level;
+ StarShipPtr->captains_name_index = captain;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+ else
+ Index = -1;
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ return Index;
+}
diff --git a/src/uqm/build.h b/src/uqm/build.h
new file mode 100644
index 0000000..4bf21cc
--- /dev/null
+++ b/src/uqm/build.h
@@ -0,0 +1,69 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_BUILD_H_
+#define UQM_BUILD_H_
+
+#include "races.h"
+#include "displist.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define NAME_OFFSET 5
+#define NUM_CAPTAINS_NAMES 16
+
+#define PickCaptainName() (((COUNT)TFB_Random () \
+ & (NUM_CAPTAINS_NAMES - 1)) \
+ + NAME_OFFSET)
+
+extern HLINK Build (QUEUE *pQueue, SPECIES_ID SpeciesID);
+extern HSHIPFRAG CloneShipFragment (COUNT shipIndex, QUEUE *pDstQueue,
+ COUNT crew_level);
+extern HLINK GetStarShipFromIndex (QUEUE *pShipQ, COUNT Index);
+extern HSHIPFRAG GetEscortByStarShipIndex (COUNT index);
+extern BYTE NameCaptain (QUEUE *pQueue, SPECIES_ID SpeciesID);
+
+extern COUNT ActivateStarShip (COUNT which_ship, SIZE state);
+extern COUNT GetIndexFromStarShip (QUEUE *pShipQ, HLINK hStarShip);
+extern int SetEscortCrewComplement (COUNT which_ship, COUNT crew_level,
+ BYTE captain);
+
+extern COUNT AddEscortShips (COUNT race, SIZE count);
+extern COUNT CalculateEscortsWorth (void);
+//extern COUNT GetRaceKnownSize (COUNT race);
+extern COUNT SetRaceAllied (COUNT race, BOOLEAN flag);
+extern COUNT StartSphereTracking (COUNT race);
+extern COUNT CountEscortShips (COUNT race);
+extern BOOLEAN HaveEscortShip (COUNT race);
+extern COUNT EscortFeasibilityStudy (COUNT race);
+extern COUNT CheckAlliance (COUNT race);
+extern COUNT RemoveSomeEscortShips (COUNT race, COUNT count);
+extern void RemoveEscortShips (COUNT race);
+
+extern RACE_DESC *load_ship (SPECIES_ID SpeciesID, BOOLEAN LoadBattleData);
+extern void free_ship (RACE_DESC *RaceDescPtr, BOOLEAN FreeIconData,
+ BOOLEAN FreeBattleData);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_BUILD_H_ */
diff --git a/src/uqm/cleanup.c b/src/uqm/cleanup.c
new file mode 100644
index 0000000..a6e4377
--- /dev/null
+++ b/src/uqm/cleanup.c
@@ -0,0 +1,99 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "nameref.h"
+#include "libs/reslib.h"
+#include "gamestr.h"
+#include "init.h"
+#include "element.h"
+#include "hyper.h"
+#include "planets/lander.h"
+#include "starcon.h"
+#include "setup.h"
+#include "planets/solarsys.h"
+#include "sounds.h"
+#include "libs/sndlib.h"
+#include "libs/vidlib.h"
+
+
+void
+FreeKernel (void)
+{
+ UninitPlayerInput ();
+
+ UninitResourceSystem ();
+
+ DestroyDrawable (ReleaseDrawable (Screen));
+ Screen = 0;
+ DestroyContext (ScreenContext);
+ ScreenContext = 0;
+
+ UninitVideoPlayer ();
+ UninitSound ();
+}
+
+static void
+UninitContexts (void)
+{
+ UninitQueue (&disp_q);
+
+ DestroyContext (OffScreenContext);
+ DestroyContext (SpaceContext);
+ DestroyContext (StatusContext);
+}
+
+static void
+UninitKernel (void)
+{
+ UninitSpace ();
+
+ DestroySound (ReleaseSound (MenuSounds));
+ DestroyFont (MicroFont);
+ DestroyStringTable (ReleaseStringTable (GameStrings));
+ DestroyDrawable (ReleaseDrawable (StatusFrame));
+ DestroyDrawable (ReleaseDrawable (ActivityFrame));
+ DestroyFont (TinyFont);
+ DestroyFont (StarConFont);
+
+ UninitQueue (&race_q[0]);
+ UninitQueue (&race_q[1]);
+
+ ActivityFrame = 0;
+}
+
+void
+FreeGameData (void)
+{
+ FreeSC2Data ();
+ FreeLanderData ();
+ FreeIPData ();
+ FreeHyperData ();
+}
+
+void
+UninitGameKernel (void)
+{
+ if (ActivityFrame)
+ {
+ FreeGameData ();
+
+ UninitKernel ();
+ UninitContexts ();
+ }
+}
+
diff --git a/src/uqm/clock.c b/src/uqm/clock.c
new file mode 100644
index 0000000..6660226
--- /dev/null
+++ b/src/uqm/clock.c
@@ -0,0 +1,314 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include "gameev.h"
+#include "globdata.h"
+#include "sis.h"
+ // for DrawStatusMessage()
+#include "setup.h"
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "libs/tasklib.h"
+#include "libs/threadlib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+// the running of the game-clock is based on game framerates
+// *not* on the system (or translated) timer
+// and is hard-coded to the original 24 fps
+#define CLOCK_BASE_FRAMERATE 24
+
+// WARNING: Most of clock functions are only meant to be called by the
+// Starcon2Main thread! If you need access from other threads, examine
+// the locking system!
+// XXX: This mutex is only necessary because debugging functions
+// may access the clock and event data from a different thread
+static Mutex clock_mutex;
+
+static BOOLEAN
+IsLeapYear (COUNT year)
+{
+ // every 4th year but not 100s yet still 400s
+ return (year & 3) == 0 && ((year % 100) != 0 || (year % 400) == 0);
+}
+
+/* month is 1-based: 1=Jan, 2=Feb, etc. */
+static BYTE
+DaysInMonth (COUNT month, COUNT year)
+{
+ static const BYTE days_in_month[12] =
+ {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ };
+
+ if (month == 2 && IsLeapYear (year))
+ return 29; /* February, leap year */
+
+ return days_in_month[month - 1];
+}
+
+static void
+nextClockDay (void)
+{
+ ++GLOBAL (GameClock.day_index);
+ if (GLOBAL (GameClock.day_index) > DaysInMonth (
+ GLOBAL (GameClock.month_index),
+ GLOBAL (GameClock.year_index)))
+ {
+ GLOBAL (GameClock.day_index) = 1;
+ ++GLOBAL (GameClock.month_index);
+ if (GLOBAL (GameClock.month_index) > 12)
+ {
+ GLOBAL (GameClock.month_index) = 1;
+ ++GLOBAL (GameClock.year_index);
+ }
+ }
+
+ // update the date on screen
+ DrawStatusMessage (NULL);
+}
+
+static void
+processClockDayEvents (void)
+{
+ HEVENT hEvent;
+
+ while ((hEvent = GetHeadEvent ()))
+ {
+ EVENT *EventPtr;
+
+ LockEvent (hEvent, &EventPtr);
+
+ if (GLOBAL (GameClock.day_index) != EventPtr->day_index
+ || GLOBAL (GameClock.month_index) != EventPtr->month_index
+ || GLOBAL (GameClock.year_index) != EventPtr->year_index)
+ {
+ UnlockEvent (hEvent);
+ break;
+ }
+ RemoveEvent (hEvent);
+ EventHandler (EventPtr->func_index);
+
+ UnlockEvent (hEvent);
+ FreeEvent (hEvent);
+ }
+}
+
+BOOLEAN
+InitGameClock (void)
+{
+ if (!InitQueue (&GLOBAL (GameClock.event_q), NUM_EVENTS, sizeof (EVENT)))
+ return (FALSE);
+ clock_mutex = CreateMutex ("Clock Mutex", SYNC_CLASS_TOPLEVEL);
+ GLOBAL (GameClock.month_index) = 2;
+ GLOBAL (GameClock.day_index) = 17;
+ GLOBAL (GameClock.year_index) = START_YEAR; /* Feb 17, START_YEAR */
+ GLOBAL (GameClock.tick_count) = 0;
+ GLOBAL (GameClock.day_in_ticks) = 0;
+
+ return (TRUE);
+}
+
+BOOLEAN
+UninitGameClock (void)
+{
+ DestroyMutex (clock_mutex);
+ clock_mutex = NULL;
+
+ UninitQueue (&GLOBAL (GameClock.event_q));
+
+ return (TRUE);
+}
+
+// For debugging use only
+void
+LockGameClock (void)
+{
+ // Block the GameClockTick() for executing
+ if (clock_mutex)
+ LockMutex (clock_mutex);
+}
+
+// For debugging use only
+void
+UnlockGameClock (void)
+{
+ if (clock_mutex)
+ UnlockMutex (clock_mutex);
+}
+
+// For debugging use only
+BOOLEAN
+GameClockRunning (void)
+{
+ SIZE day_in_ticks;
+
+ if (!clock_mutex)
+ return FALSE;
+
+ LockMutex (clock_mutex);
+ day_in_ticks = GLOBAL (GameClock.day_in_ticks);
+ UnlockMutex (clock_mutex);
+
+ return day_in_ticks != 0;
+}
+
+void
+SetGameClockRate (COUNT seconds_per_day)
+{
+ SIZE new_day_in_ticks, new_tick_count;
+
+ new_day_in_ticks = (SIZE)(seconds_per_day * CLOCK_BASE_FRAMERATE);
+ if (GLOBAL (GameClock.day_in_ticks) == 0)
+ new_tick_count = new_day_in_ticks;
+ else if (GLOBAL (GameClock.tick_count) <= 0)
+ new_tick_count = 0;
+ else if ((new_tick_count = (SIZE)((DWORD)GLOBAL (GameClock.tick_count)
+ * new_day_in_ticks / GLOBAL (GameClock.day_in_ticks))) == 0)
+ new_tick_count = 1;
+ GLOBAL (GameClock.day_in_ticks) = new_day_in_ticks;
+ GLOBAL (GameClock.tick_count) = new_tick_count;
+}
+
+BOOLEAN
+ValidateEvent (EVENT_TYPE type, COUNT *pmonth_index, COUNT *pday_index,
+ COUNT *pyear_index)
+{
+ COUNT month_index, day_index, year_index;
+
+ month_index = *pmonth_index;
+ day_index = *pday_index;
+ year_index = *pyear_index;
+ if (type == RELATIVE_EVENT)
+ {
+ month_index += GLOBAL (GameClock.month_index) - 1;
+ year_index += GLOBAL (GameClock.year_index) + (month_index / 12);
+ month_index = (month_index % 12) + 1;
+
+ day_index += GLOBAL (GameClock.day_index);
+ while (day_index > DaysInMonth (month_index, year_index))
+ {
+ day_index -= DaysInMonth (month_index, year_index);
+ if (++month_index > 12)
+ {
+ month_index = 1;
+ ++year_index;
+ }
+ }
+
+ *pmonth_index = month_index;
+ *pday_index = day_index;
+ *pyear_index = year_index;
+ }
+
+ // translation: return (BOOLEAN) !(date < GLOBAL (Gameclock.date));
+ return (BOOLEAN) (!(year_index < GLOBAL (GameClock.year_index)
+ || (year_index == GLOBAL (GameClock.year_index)
+ && (month_index < GLOBAL (GameClock.month_index)
+ || (month_index == GLOBAL (GameClock.month_index)
+ && day_index < GLOBAL (GameClock.day_index))))));
+}
+
+HEVENT
+AddEvent (EVENT_TYPE type, COUNT month_index, COUNT day_index, COUNT
+ year_index, BYTE func_index)
+{
+ HEVENT hNewEvent;
+
+ if (type == RELATIVE_EVENT
+ && month_index == 0
+ && day_index == 0
+ && year_index == 0)
+ EventHandler (func_index);
+ else if (ValidateEvent (type, &month_index, &day_index, &year_index)
+ && (hNewEvent = AllocEvent ()))
+ {
+ EVENT *EventPtr;
+
+ LockEvent (hNewEvent, &EventPtr);
+ EventPtr->day_index = (BYTE)day_index;
+ EventPtr->month_index = (BYTE)month_index;
+ EventPtr->year_index = year_index;
+ EventPtr->func_index = func_index;
+ UnlockEvent (hNewEvent);
+
+ {
+ HEVENT hEvent, hSuccEvent;
+ for (hEvent = GetHeadEvent (); hEvent != 0; hEvent = hSuccEvent)
+ {
+ LockEvent (hEvent, &EventPtr);
+ if (year_index < EventPtr->year_index
+ || (year_index == EventPtr->year_index
+ && (month_index < EventPtr->month_index
+ || (month_index == EventPtr->month_index
+ && day_index < EventPtr->day_index))))
+ {
+ UnlockEvent (hEvent);
+ break;
+ }
+
+ hSuccEvent = GetSuccEvent (EventPtr);
+ UnlockEvent (hEvent);
+ }
+
+ InsertEvent (hNewEvent, hEvent);
+ }
+
+ return (hNewEvent);
+ }
+
+ return (0);
+}
+
+void
+GameClockTick (void)
+{
+ // XXX: This mutex is only necessary because debugging functions
+ // may access the clock and event data from a different thread
+ LockMutex (clock_mutex);
+
+ --GLOBAL (GameClock.tick_count);
+ if (GLOBAL (GameClock.tick_count) <= 0)
+ { // next day -- move the calendar
+ GLOBAL (GameClock.tick_count) = GLOBAL (GameClock.day_in_ticks);
+ // Do not do anything until the clock is inited
+ if (GLOBAL (GameClock.day_in_ticks) > 0)
+ {
+ nextClockDay ();
+ processClockDayEvents ();
+ }
+ }
+
+ UnlockMutex (clock_mutex);
+}
+
+void
+MoveGameClockDays (COUNT days)
+{
+ // XXX: This should theoretically hold the clock_mutex, but if
+ // someone manages to hit the debug button while this function
+ // runs, it's their own fault :-P
+
+ for ( ; days > 0; --days)
+ {
+ nextClockDay ();
+ processClockDayEvents ();
+ }
+ GLOBAL (GameClock.tick_count) = GLOBAL (GameClock.day_in_ticks);
+}
diff --git a/src/uqm/clock.h b/src/uqm/clock.h
new file mode 100644
index 0000000..5bd428c
--- /dev/null
+++ b/src/uqm/clock.h
@@ -0,0 +1,111 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_CLOCK_H_
+#define UQM_CLOCK_H_
+
+#include "libs/tasklib.h"
+#include "displist.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#define START_YEAR 2155
+
+#define UPDATE_DAY (1 << 0)
+#define UPDATE_MONTH (1 << 1)
+#define UPDATE_YEAR (1 << 2)
+
+typedef struct
+{
+ BYTE day_index, month_index;
+ COUNT year_index;
+ SIZE tick_count, day_in_ticks;
+
+ QUEUE event_q;
+ /* Queue element is EVENT */
+} CLOCK_STATE;
+
+typedef HLINK HEVENT;
+
+typedef struct event
+{
+ // LINK elements; must be first
+ HEVENT pred, succ;
+
+ BYTE day_index, month_index;
+ COUNT year_index;
+ BYTE func_index;
+} EVENT;
+
+typedef enum
+{
+ ABSOLUTE_EVENT = 0,
+ RELATIVE_EVENT
+} EVENT_TYPE;
+
+#define AllocEvent() AllocLink (&GLOBAL (GameClock.event_q))
+#define PutEvent(h) PutQueue (&GLOBAL (GameClock.event_q), (h))
+#define InsertEvent(h,i) InsertQueue (&GLOBAL (GameClock.event_q), (h), (i))
+#define GetHeadEvent() GetHeadLink (&GLOBAL (GameClock.event_q))
+#define GetTailEvent() GetTailLink (&GLOBAL (GameClock.event_q))
+#define LockEvent(h,ppe) (*(ppe) = (EVENT*)LockLink (&GLOBAL (GameClock.event_q), h))
+#define UnlockEvent(h) UnlockLink (&GLOBAL (GameClock.event_q), (h))
+#define RemoveEvent(h) RemoveQueue (&GLOBAL (GameClock.event_q), (h))
+#define FreeEvent(h) FreeLink (&GLOBAL (GameClock.event_q), (h))
+#define GetPredEvent(l) _GetPredLink (l)
+#define GetSuccEvent(l) _GetSuccLink (l)
+#define ForAllEvents(callback, arg) ForAllLinks(&GLOBAL (GameClock.event_q), \
+ (void (*)(LINK *, void *)) (callback), (arg))
+
+// Rates are in seconds per game day
+#define HYPERSPACE_CLOCK_RATE 5
+// XXX: the IP rate is based on 24 ticks/second (see SetGameClockRate),
+// however, IP runs at 30 fps right now. So in reality, the IP clock
+// rate is closer to 23 seconds per game day. The clock is faster, but
+// the flagship also moves faster.
+#define INTERPLANETARY_CLOCK_RATE 30
+
+extern BOOLEAN InitGameClock (void);
+extern BOOLEAN UninitGameClock (void);
+
+extern void SetGameClockRate (COUNT seconds_per_day);
+extern BOOLEAN ValidateEvent (EVENT_TYPE type, COUNT *pmonth_index,
+ COUNT *pday_index, COUNT *pyear_index);
+extern HEVENT AddEvent (EVENT_TYPE type, COUNT month_index, COUNT
+ day_index, COUNT year_index, BYTE func_index);
+extern void EventHandler (BYTE selector);
+extern void GameClockTick (void);
+extern void MoveGameClockDays (COUNT days);
+
+// The lock/unlock/running functions are for debugging use only
+// Locking will block the GameClockTick() function and thus
+// the thread moving the clock.
+extern void LockGameClock (void);
+extern void UnlockGameClock (void);
+// A weak indicator of the clock moving. Suitable for debugging,
+// but not much else
+extern BOOLEAN GameClockRunning (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_CLOCK_H_ */
diff --git a/src/uqm/cnctdlg.c b/src/uqm/cnctdlg.c
new file mode 100644
index 0000000..47824eb
--- /dev/null
+++ b/src/uqm/cnctdlg.c
@@ -0,0 +1,630 @@
+//Copyright Michael Martin, 2006
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef NETPLAY
+
+#include "cnctdlg.h"
+#include "controls.h"
+#include "colors.h"
+#include "gamestr.h"
+#include "setup.h"
+#include "units.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "libs/graphics/widgets.h"
+#include "supermelee/netplay/netoptions.h"
+
+#define MCD_WIDTH 260
+#define MCD_HEIGHT 110
+
+#define MENU_FRAME_RATE (ONE_SECOND / 20)
+
+typedef struct connect_dialog_state
+{
+ BOOLEAN (*InputFunc) (struct connect_dialog_state *pInputState);
+
+ DWORD NextTime;
+ BOOLEAN Initialized;
+ int which_side;
+
+ int confirmed;
+} CONNECT_DIALOG_STATE;
+
+static void DrawConnectDialog (void);
+
+static WIDGET_MENU_SCREEN menu;
+static WIDGET_BUTTON buttons[3];
+static WIDGET_SLIDER slider;
+static WIDGET_TEXTENTRY texts[2];
+
+static WIDGET *menu_widgets[] = {
+ (WIDGET *)&buttons[1],
+ (WIDGET *)&texts[0],
+ (WIDGET *)&buttons[0],
+ (WIDGET *)&slider,
+ (WIDGET *)&texts[1],
+ (WIDGET *)&buttons[2] };
+
+static BOOLEAN done;
+
+/* This kind of sucks, but the Button callbacks need access to the
+ * CONNECT_DIALOG_STATE, so we need a pointer to it */
+
+static CONNECT_DIALOG_STATE *current_state;
+
+static FONT PlayerFont;
+
+static int do_connect (WIDGET *self, int event);
+static int do_listen (WIDGET *self, int event);
+static int do_cancel (WIDGET *self, int event);
+
+static void
+MCD_DrawMenuScreen (WIDGET *_self, int x, int y)
+{
+ int widget_index, widget_y;
+
+ WIDGET_MENU_SCREEN *self = (WIDGET_MENU_SCREEN *)_self;
+
+ widget_y = y + 8;
+ for (widget_index = 0; widget_index < self->num_children; widget_index++)
+ {
+ WIDGET *c = self->child[widget_index];
+ (*c->draw)(c, x, widget_y);
+ widget_y += (*c->height)(c) + 8;
+ }
+}
+
+static void
+MCD_DrawButton (WIDGET *_self, int x, int y)
+{
+ WIDGET_BUTTON *self = (WIDGET_BUTTON *)_self;
+ Color oldtext;
+ Color inactive, selected;
+ FONT oldfont = SetContextFont (StarConFont);
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+
+ selected = MENU_HIGHLIGHT_COLOR;
+ inactive = MENU_TEXT_COLOR;
+
+ t.baseline.x = 160;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.pStr = self->name;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ SetContextFontEffect (oldFontEffect);
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+ (void) x;
+}
+
+static void
+MCD_DrawSlider (WIDGET *_self, int x, int y)
+{
+ WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
+ Color oldtext;
+ Color default_color, selected;
+ FONT oldfont = SetContextFont (PlayerFont);
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+ RECT r;
+ int tick = (MCD_WIDTH) / 8;
+
+ default_color = MENU_TEXT_COLOR;
+ selected = MENU_HIGHLIGHT_COLOR;
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ r.corner.x = t.baseline.x + 3 * tick;
+ r.corner.y = t.baseline.y - 4;
+ r.extent.height = 2;
+ r.extent.width = 3 * tick;
+ DrawFilledRectangle (&r);
+
+ r.extent.width = 3;
+ r.extent.height = 8;
+ r.corner.y = t.baseline.y - 7;
+ r.corner.x = t.baseline.x + 3 * tick + (3 * tick *
+ (self->value - self->min) / (self->max - self->min)) - 1;
+ DrawFilledRectangle (&r);
+
+ (*self->draw_value)(self, t.baseline.x + 7 * tick, t.baseline.y);
+
+ SetContextFontEffect (oldFontEffect);
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+static void
+MCD_DrawTextEntry (WIDGET *_self, int x, int y)
+{
+ WIDGET_TEXTENTRY *self = (WIDGET_TEXTENTRY *)_self;
+ Color oldtext;
+ Color inactive, default_color, selected;
+ FONT oldfont = SetContextFont (PlayerFont);
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ TEXT t;
+
+ default_color = MENU_TEXT_COLOR;
+ selected = MENU_HIGHLIGHT_COLOR;
+ inactive = MENU_TEXT_COLOR;
+
+ BatchGraphics ();
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_LEFT;
+ t.CharCount = ~0;
+ t.pStr = self->category;
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (default_color);
+ }
+ font_DrawText (&t);
+
+ /* Force string termination */
+ self->value[WIDGET_TEXTENTRY_WIDTH-1] = 0;
+
+ t.baseline.y = y;
+ t.CharCount = utf8StringCount (self->value);
+ t.pStr = self->value;
+
+ if (!(self->state & WTE_EDITING))
+ { // normal or selected state
+ t.baseline.x = 160;
+ t.align = ALIGN_CENTER;
+
+ if (widget_focus == _self)
+ {
+ oldtext = SetContextForeGroundColor (selected);
+ }
+ else
+ {
+ oldtext = SetContextForeGroundColor (inactive);
+ }
+ font_DrawText (&t);
+ }
+ else
+ { // editing state
+ COUNT i;
+ RECT text_r;
+ BYTE char_deltas[WIDGET_TEXTENTRY_WIDTH];
+ BYTE *pchar_deltas;
+ RECT r;
+ SIZE leading;
+
+ t.baseline.x = x + 90;
+ t.align = ALIGN_LEFT;
+
+ // calc background box dimensions
+ // XXX: this may need some tuning, especially if a
+ // different font is used. The font 'leading' values
+ // are not what they should be.
+#define BOX_VERT_OFFSET 2
+ GetContextFontLeading (&leading);
+ r.corner.x = t.baseline.x - 1;
+ r.corner.y = t.baseline.y - leading + BOX_VERT_OFFSET;
+ r.extent.width = MCD_WIDTH - r.corner.x - 10;
+ r.extent.height = leading + 2;
+
+ TextRect (&t, &text_r, char_deltas);
+#if 0
+ // XXX: this should potentially be used in ChangeCallback
+ if ((text_r.extent.width + 2) >= r.extent.width)
+ { // the text does not fit the input box size and so
+ // will not fit when displayed later
+ UnbatchGraphics ();
+ // disallow the change
+ return (FALSE);
+ }
+#endif
+
+ oldtext = SetContextForeGroundColor (selected);
+ DrawFilledRectangle (&r);
+
+ // calculate the cursor position and draw it
+ pchar_deltas = char_deltas;
+ for (i = self->cursor_pos; i > 0; --i)
+ r.corner.x += (SIZE)*pchar_deltas++;
+ if (self->cursor_pos < t.CharCount) /* cursor mid-line */
+ --r.corner.x;
+ if (self->state & WTE_BLOCKCUR)
+ { // Use block cursor for keyboardless systems
+ if (self->cursor_pos == t.CharCount)
+ { // cursor at end-line -- use insertion point
+ r.extent.width = 1;
+ }
+ else if (self->cursor_pos + 1 == t.CharCount)
+ { // extra pixel for last char margin
+ r.extent.width = (SIZE)*pchar_deltas + 2;
+ }
+ else
+ { // normal mid-line char
+ r.extent.width = (SIZE)*pchar_deltas + 1;
+ }
+ }
+ else
+ { // Insertion point cursor
+ r.extent.width = 1;
+ }
+ // position cursor within input field rect
+ ++r.corner.x;
+ ++r.corner.y;
+ r.extent.height -= 2;
+ SetContextForeGroundColor (MENU_CURSOR_COLOR);
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (inactive);
+ font_DrawText (&t);
+ }
+
+ UnbatchGraphics ();
+ SetContextFontEffect (oldFontEffect);
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldtext);
+}
+
+/* Text entry stuff, mostly C&Ped from setupmenu.c. Could use some
+ * refactoring, as redraw_menu () is the only real change. */
+
+static BOOLEAN
+OnTextEntryChange (TEXTENTRY_STATE *pTES)
+{
+ WIDGET_TEXTENTRY *widget = (WIDGET_TEXTENTRY *) pTES->CbParam;
+
+ widget->cursor_pos = pTES->CursorPos;
+ if (pTES->JoystickMode)
+ widget->state |= WTE_BLOCKCUR;
+ else
+ widget->state &= ~WTE_BLOCKCUR;
+
+ // XXX TODO: Here, we can examine the text entered so far
+ // to make sure it fits on the screen, for example,
+ // and return FALSE to disallow the last change
+
+ return TRUE; // allow change
+}
+
+static BOOLEAN
+OnTextEntryFrame (TEXTENTRY_STATE *pTES)
+{
+ DrawConnectDialog ();
+
+ SleepThreadUntil (pTES->NextTime);
+ pTES->NextTime = GetTimeCounter () + MENU_FRAME_RATE;
+
+ (void) pTES; // satisfying compiler
+ return TRUE; // continue
+}
+
+static int
+OnTextEntryEvent (WIDGET_TEXTENTRY *widget)
+{ // Going to edit the text
+ TEXTENTRY_STATE tes;
+ UNICODE revert_buf[256];
+
+ // position cursor at the end of text
+ widget->cursor_pos = utf8StringCount (widget->value);
+ widget->state = WTE_EDITING;
+ DrawConnectDialog ();
+
+ // make a backup copy for revert on cancel
+ utf8StringCopy (revert_buf, sizeof (revert_buf), widget->value);
+
+ // text entry setup
+ tes.Initialized = FALSE;
+ tes.NextTime = GetTimeCounter () + MENU_FRAME_RATE;
+ tes.BaseStr = widget->value;
+ tes.MaxSize = widget->maxlen;
+ tes.CursorPos = widget->cursor_pos;
+ tes.CbParam = widget;
+ tes.ChangeCallback = OnTextEntryChange;
+ tes.FrameCallback = OnTextEntryFrame;
+
+ // SetMenuSounds (0, MENU_SOUND_SELECT);
+ if (!DoTextEntry (&tes))
+ { // editing failed (canceled) -- revert the changes
+ utf8StringCopy (widget->value, widget->maxlen, revert_buf);
+ }
+ else
+ {
+ if (widget->onChange)
+ {
+ (*(widget->onChange))(widget);
+ }
+ }
+
+ widget->state = WTE_NORMAL;
+ DrawConnectDialog ();
+
+ return TRUE; // event handled
+}
+
+/* Button response routines */
+
+static int
+do_connect (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ /* These assignments are safe exactly because texts[] is file-scope) */
+ netplayOptions.peer[current_state->which_side].host = texts[0].value;
+ netplayOptions.peer[current_state->which_side].port = texts[1].value;
+ netplayOptions.peer[current_state->which_side].isServer = FALSE;
+ current_state->confirmed = TRUE;
+ netplayOptions.inputDelay = slider.value;
+
+ done = TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_listen (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ /* These assignments are safe exactly because texts[] is file-scope) */
+ netplayOptions.peer[current_state->which_side].port = texts[1].value;
+ netplayOptions.peer[current_state->which_side].isServer = TRUE;
+ netplayOptions.inputDelay = slider.value;
+ current_state->confirmed = TRUE;
+ done = TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_cancel (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ current_state->confirmed = FALSE;
+ done = TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+
+static void
+CreateWidgets (void)
+{
+ int i;
+
+ done = false;
+
+ for (i = 0; i < 3; i++)
+ {
+ buttons[i].tag = WIDGET_TYPE_BUTTON;
+ buttons[i].parent = NULL;
+ buttons[i].receiveFocus = Widget_ReceiveFocusSimple;
+ buttons[i].draw = MCD_DrawButton;
+ buttons[i].height = Widget_HeightOneLine;
+ buttons[i].width = Widget_WidthFullScreen;
+ }
+ buttons[0].name = GAME_STRING (NETMELEE_STRING_BASE + 19);
+ // "Connect to remote host"
+ buttons[1].name = GAME_STRING (NETMELEE_STRING_BASE + 20);
+ // "Wait for incoming connection"
+ buttons[2].name = GAME_STRING (NETMELEE_STRING_BASE + 21);
+ // "Cancel"
+
+ buttons[0].handleEvent = do_connect;
+ buttons[1].handleEvent = do_listen;
+ buttons[2].handleEvent = do_cancel;
+
+ menu.tag = WIDGET_TYPE_MENU_SCREEN;
+ menu.parent = NULL;
+ menu.receiveFocus = Widget_ReceiveFocusMenuScreen;
+ menu.draw = MCD_DrawMenuScreen;
+ menu.height = Widget_HeightFullScreen;
+ menu.width = Widget_WidthFullScreen;
+ menu.num_children = 6;
+ menu.child = menu_widgets;
+ menu.handleEvent = Widget_HandleEventMenuScreen;
+
+ slider.tag = WIDGET_TYPE_SLIDER;
+ slider.parent = NULL;
+ slider.handleEvent = Widget_HandleEventSlider;
+ slider.receiveFocus = Widget_ReceiveFocusSimple;
+ slider.draw = MCD_DrawSlider;
+ slider.height = Widget_HeightOneLine;
+ slider.width = Widget_WidthFullScreen;
+ slider.draw_value = Widget_Slider_DrawValue;
+ slider.min = 0;
+ slider.max = 9;
+ slider.step = 1;
+ slider.value = netplayOptions.inputDelay;
+ slider.category = GAME_STRING (NETMELEE_STRING_BASE + 24);
+ // "Net Delay"
+
+ for (i = 0; i < 2; i++)
+ {
+ texts[i].tag = WIDGET_TYPE_TEXTENTRY;
+ texts[i].parent = NULL;
+ texts[i].handleEvent = Widget_HandleEventTextEntry;
+ texts[i].receiveFocus = Widget_ReceiveFocusSimple;
+ texts[i].draw = MCD_DrawTextEntry;
+ texts[i].height = Widget_HeightOneLine;
+ texts[i].width = Widget_WidthFullScreen;
+ texts[i].handleEventSelect = OnTextEntryEvent;
+ texts[i].maxlen = WIDGET_TEXTENTRY_WIDTH-1;
+ texts[i].state = WTE_NORMAL;
+ texts[i].cursor_pos = 0;
+ }
+
+ texts[0].category = GAME_STRING (NETMELEE_STRING_BASE + 22);
+ // "Host"
+ texts[1].category = GAME_STRING (NETMELEE_STRING_BASE + 23);
+ // "Port"
+
+ /* We sometimes assign to these internals; cannot strncpy over self! */
+ if (texts[0].value != netplayOptions.peer[current_state->which_side].host)
+ {
+ strncpy (texts[0].value,
+ netplayOptions.peer[current_state->which_side].host,
+ texts[0].maxlen);
+ }
+ if (texts[1].value != netplayOptions.peer[current_state->which_side].port)
+ {
+ strncpy (texts[1].value,
+ netplayOptions.peer[current_state->which_side].port,
+ texts[1].maxlen);
+ }
+ texts[0].value[texts[0].maxlen]=0;
+ texts[1].value[texts[1].maxlen]=0;
+
+ menu.receiveFocus ((WIDGET *)&menu, WIDGET_EVENT_DOWN);
+}
+
+static void
+DrawConnectDialog (void)
+{
+ RECT r;
+
+ r.extent.width = MCD_WIDTH;
+ r.extent.height = MCD_HEIGHT;
+ r.corner.x = (SCREEN_WIDTH - r.extent.width) >> 1;
+ r.corner.y = (SCREEN_HEIGHT - r.extent.height) >> 1;
+
+
+ DrawShadowedBox (&r, SHADOWBOX_BACKGROUND_COLOR,
+ SHADOWBOX_DARK_COLOR, SHADOWBOX_MEDIUM_COLOR);
+
+ menu.draw ((WIDGET *)&menu, r.corner.x + 10, r.corner.y + 10);
+
+}
+
+static BOOLEAN
+DoMeleeConnectDialog (CONNECT_DIALOG_STATE *state)
+{
+ BOOLEAN changed;
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (!state->Initialized)
+ {
+ state->Initialized = TRUE;
+ SetDefaultMenuRepeatDelay ();
+ state->NextTime = GetTimeCounter ();
+ /* Prepare widgets, draw stuff, etc. */
+ CreateWidgets ();
+ DrawConnectDialog ();
+ }
+
+ changed = TRUE;
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ Widget_Event (WIDGET_EVENT_UP);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ Widget_Event (WIDGET_EVENT_DOWN);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ Widget_Event (WIDGET_EVENT_LEFT);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ Widget_Event (WIDGET_EVENT_RIGHT);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ Widget_Event (WIDGET_EVENT_SELECT);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ Widget_Event (WIDGET_EVENT_CANCEL);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DELETE])
+ {
+ Widget_Event (WIDGET_EVENT_DELETE);
+ }
+ else
+ {
+ changed = FALSE;
+ }
+
+ if (changed)
+ {
+ DrawConnectDialog ();
+ }
+
+ SleepThreadUntil (state->NextTime + MENU_FRAME_RATE);
+ state->NextTime = GetTimeCounter ();
+ return !((GLOBAL (CurrentActivity) & CHECK_ABORT) ||
+ done);
+}
+
+BOOLEAN
+MeleeConnectDialog (int side)
+{
+ CONNECT_DIALOG_STATE state;
+
+ PlayerFont = LoadFont (PLAYER_FONT);
+
+ state.Initialized = FALSE;
+ state.which_side = side;
+ state.InputFunc = DoMeleeConnectDialog;
+ state.confirmed = TRUE;
+
+ current_state = &state;
+
+ DoInput (&state, TRUE);
+
+ current_state = NULL;
+
+ DestroyFont (PlayerFont);
+
+ return state.confirmed;
+}
+
+#endif /* NETPLAY */
+
diff --git a/src/uqm/cnctdlg.h b/src/uqm/cnctdlg.h
new file mode 100644
index 0000000..c785e1c
--- /dev/null
+++ b/src/uqm/cnctdlg.h
@@ -0,0 +1,38 @@
+//Copyright Michael Martin, 2006
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef NETPLAY
+
+#ifndef UQM_CNCTDLG_H_
+#define UQM_CNCTDLG_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+BOOLEAN MeleeConnectDialog (int side);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_CNCTDLG_H_ */
+
+#endif /* NETPLAY */
diff --git a/src/uqm/coderes.h b/src/uqm/coderes.h
new file mode 100644
index 0000000..add0970
--- /dev/null
+++ b/src/uqm/coderes.h
@@ -0,0 +1,43 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_CODERES_H_
+#define UQM_CODERES_H_
+
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern BOOLEAN InstallCodeResType (void);
+extern void *LoadCodeResInstance (RESOURCE res);
+extern void *CaptureCodeRes (void *hCode, void *pData, void **ppLocData);
+extern void *ReleaseCodeRes (void *CodeRef);
+extern BOOLEAN DestroyCodeRes (void *hCode);
+
+typedef struct
+{
+ UWORD size;
+} CODE_REF;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_CODERES_H_ */
diff --git a/src/uqm/collide.c b/src/uqm/collide.c
new file mode 100644
index 0000000..7275e42
--- /dev/null
+++ b/src/uqm/collide.c
@@ -0,0 +1,183 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "collide.h"
+#include "element.h"
+#include "races.h"
+#include "units.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+//#define DEBUG_COLLIDE
+
+void
+collide (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1)
+{
+ SIZE speed;
+ SIZE dx0, dy0, dx1, dy1, dx_rel, dy_rel;
+ SIZE TravelAngle0, TravelAngle1, ImpactAngle0, ImpactAngle1;
+ SIZE RelTravelAngle, Directness;
+
+ dx_rel = ElementPtr0->next.location.x
+ - ElementPtr1->next.location.x;
+ dy_rel = ElementPtr0->next.location.y
+ - ElementPtr1->next.location.y;
+ ImpactAngle0 = ARCTAN (dx_rel, dy_rel);
+ ImpactAngle1 = NORMALIZE_ANGLE (ImpactAngle0 + HALF_CIRCLE);
+
+ GetCurrentVelocityComponents (&ElementPtr0->velocity, &dx0, &dy0);
+ TravelAngle0 = GetVelocityTravelAngle (&ElementPtr0->velocity);
+ GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx1, &dy1);
+ TravelAngle1 = GetVelocityTravelAngle (&ElementPtr1->velocity);
+ dx_rel = dx0 - dx1;
+ dy_rel = dy0 - dy1;
+ RelTravelAngle = ARCTAN (dx_rel, dy_rel);
+ speed = square_root ((long)dx_rel * dx_rel + (long)dy_rel * dy_rel);
+
+ Directness = NORMALIZE_ANGLE (RelTravelAngle - ImpactAngle0);
+ if (Directness <= QUADRANT || Directness >= HALF_CIRCLE + QUADRANT)
+ /* shapes just scraped each other but still collided,
+ * they will collide again unless we fudge it.
+ */
+ {
+ Directness = HALF_CIRCLE;
+ ImpactAngle0 = TravelAngle0 + HALF_CIRCLE;
+ ImpactAngle1 = TravelAngle1 + HALF_CIRCLE;
+ }
+
+#ifdef DEBUG_COLLIDE
+ log_add (log_Debug, "Centers: <%d, %d> <%d, %d>",
+ ElementPtr0->next.location.x, ElementPtr0->next.location.y,
+ ElementPtr1->next.location.x, ElementPtr1->next.location.y);
+ log_add (log_Debug, "RelTravelAngle : %d, ImpactAngles <%d, %d>",
+ RelTravelAngle, ImpactAngle0, ImpactAngle1);
+#endif /* DEBUG_COLLIDE */
+
+ if (ElementPtr0->next.location.x == ElementPtr0->current.location.x
+ && ElementPtr0->next.location.y == ElementPtr0->current.location.y
+ && ElementPtr1->next.location.x == ElementPtr1->current.location.x
+ && ElementPtr1->next.location.y == ElementPtr1->current.location.y)
+ {
+ if (ElementPtr0->state_flags & ElementPtr1->state_flags & DEFY_PHYSICS)
+ {
+ ImpactAngle0 = TravelAngle0 + (HALF_CIRCLE - OCTANT);
+ ImpactAngle1 = TravelAngle1 + (HALF_CIRCLE - OCTANT);
+ ZeroVelocityComponents (&ElementPtr0->velocity);
+ ZeroVelocityComponents (&ElementPtr1->velocity);
+ }
+ ElementPtr0->state_flags |= (DEFY_PHYSICS | COLLISION);
+ ElementPtr1->state_flags |= (DEFY_PHYSICS | COLLISION);
+#ifdef DEBUG_COLLIDE
+ log_add (log_Debug, "No movement before collision -- "
+ "<(%d, %d) = %d, (%d, %d) = %d>",
+ dx0, dy0, ImpactAngle0 - OCTANT, dx1, dy1,
+ ImpactAngle1 - OCTANT);
+#endif /* DEBUG_COLLIDE */
+ }
+
+ {
+ SIZE mass0, mass1;
+ long scalar;
+
+ mass0 = ElementPtr0->mass_points /* << 2 */;
+ mass1 = ElementPtr1->mass_points /* << 2 */;
+ scalar = (long)SINE (Directness, speed << 1) * (mass0 * mass1);
+
+ if (!GRAVITY_MASS (ElementPtr0->mass_points + 1))
+ {
+ if (ElementPtr0->state_flags & PLAYER_SHIP)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ if (!(ElementPtr0->state_flags & DEFY_PHYSICS))
+ {
+ if (ElementPtr0->turn_wait < COLLISION_TURN_WAIT)
+ ElementPtr0->turn_wait += COLLISION_TURN_WAIT;
+ if (ElementPtr0->thrust_wait < COLLISION_THRUST_WAIT)
+ ElementPtr0->thrust_wait += COLLISION_THRUST_WAIT;
+ }
+ }
+
+ speed = (SIZE)(scalar / ((long)mass0 * (mass0 + mass1)));
+ DeltaVelocityComponents (&ElementPtr0->velocity,
+ COSINE (ImpactAngle0, speed),
+ SINE (ImpactAngle0, speed));
+
+ GetCurrentVelocityComponents (&ElementPtr0->velocity, &dx0, &dy0);
+ if (dx0 < 0)
+ dx0 = -dx0;
+ if (dy0 < 0)
+ dy0 = -dy0;
+
+ if (VELOCITY_TO_WORLD (dx0 + dy0) < SCALED_ONE)
+ SetVelocityComponents (&ElementPtr0->velocity,
+ COSINE (ImpactAngle0,
+ WORLD_TO_VELOCITY (SCALED_ONE) - 1),
+ SINE (ImpactAngle0,
+ WORLD_TO_VELOCITY (SCALED_ONE) - 1));
+ }
+
+ if (!GRAVITY_MASS (ElementPtr1->mass_points + 1))
+ {
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ if (!(ElementPtr1->state_flags & DEFY_PHYSICS))
+ {
+ if (ElementPtr1->turn_wait < COLLISION_TURN_WAIT)
+ ElementPtr1->turn_wait += COLLISION_TURN_WAIT;
+ if (ElementPtr1->thrust_wait < COLLISION_THRUST_WAIT)
+ ElementPtr1->thrust_wait += COLLISION_THRUST_WAIT;
+ }
+ }
+
+ speed = (SIZE)(scalar / ((long)mass1 * (mass0 + mass1)));
+ DeltaVelocityComponents (&ElementPtr1->velocity,
+ COSINE (ImpactAngle1, speed),
+ SINE (ImpactAngle1, speed));
+
+ GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx1, &dy1);
+ if (dx1 < 0)
+ dx1 = -dx1;
+ if (dy1 < 0)
+ dy1 = -dy1;
+
+ if (VELOCITY_TO_WORLD (dx1 + dy1) < SCALED_ONE)
+ SetVelocityComponents (&ElementPtr1->velocity,
+ COSINE (ImpactAngle1,
+ WORLD_TO_VELOCITY (SCALED_ONE) - 1),
+ SINE (ImpactAngle1,
+ WORLD_TO_VELOCITY (SCALED_ONE) - 1));
+ }
+#ifdef DEBUG_COLLIDE
+ GetCurrentVelocityComponents (&ElementPtr0->velocity, &dx0, &dy0);
+ GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx1, &dy1);
+ log_add (log_Debug, "After: <%d, %d> <%d, %d>\n",
+ dx0, dy0, dx1, dy1);
+#endif /* DEBUG_COLLIDE */
+ }
+}
+
diff --git a/src/uqm/collide.h b/src/uqm/collide.h
new file mode 100644
index 0000000..50cc5f1
--- /dev/null
+++ b/src/uqm/collide.h
@@ -0,0 +1,70 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_COLLIDE_H_
+#define UQM_COLLIDE_H_
+
+#include "element.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define COLLISION_TURN_WAIT 1
+#define COLLISION_THRUST_WAIT 3
+
+#define SKIP_COLLISION (NONSOLID | DISAPPEARING)
+#define CollidingElement(e) \
+ (!((e)->state_flags & SKIP_COLLISION))
+#define CollisionPossible(e0,e1) \
+ (CollidingElement (e0) \
+ && (!(((e1)->state_flags & (e0)->state_flags) & COLLISION) \
+ && ((!(((e1)->state_flags & (e0)->state_flags) & IGNORE_SIMILAR) \
+ || (e1)->pParent != (e0)->pParent)) \
+ && ((e1)->mass_points || (e0)->mass_points)))
+
+#define InitIntersectStartPoint(eptr) \
+{ \
+ (eptr)->IntersectControl.IntersectStamp.origin.x = \
+ WORLD_TO_DISPLAY ((eptr)->current.location.x); \
+ (eptr)->IntersectControl.IntersectStamp.origin.y = \
+ WORLD_TO_DISPLAY ((eptr)->current.location.y); \
+}
+
+#define InitIntersectEndPoint(eptr) \
+{ \
+ (eptr)->IntersectControl.EndPoint.x = \
+ WORLD_TO_DISPLAY ((eptr)->next.location.x); \
+ (eptr)->IntersectControl.EndPoint.y = \
+ WORLD_TO_DISPLAY ((eptr)->next.location.y); \
+}
+
+#define InitIntersectFrame(eptr) \
+{ \
+ (eptr)->IntersectControl.IntersectStamp.frame = \
+ SetEquFrameIndex ((eptr)->next.image.farray[0], \
+ (eptr)->next.image.frame); \
+}
+
+extern void collide (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_COLLIDE_H_ */
diff --git a/src/uqm/colors.h b/src/uqm/colors.h
new file mode 100644
index 0000000..318fe49
--- /dev/null
+++ b/src/uqm/colors.h
@@ -0,0 +1,440 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef UQM_COLORS_H_
+#define UQM_COLORS_H_
+
+// To be used as an indicator that the actual value of the color does not
+// matter, for instance in structure initialisations for fields which
+// are irrelevant in the context.
+#define UNDEFINED_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+
+#if 0
+#define DEFAULT_COLOR_00 \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+#define DEFAULT_COLOR_01 \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+#define DEFAULT_COLOR_02 \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02)
+#define DEFAULT_COLOR_03 \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)
+#define DEFAULT_COLOR_04 \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x04)
+#define DEFAULT_COLOR_05 \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x14), 0x05)
+#define DEFAULT_COLOR_06 \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x00), 0x06)
+#define DEFAULT_COLOR_07 \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x07)
+#define DEFAULT_COLOR_08 \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08)
+#define DEFAULT_COLOR_09 \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+#define DEFAULT_COLOR_0A \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x0A), 0x0A)
+#define DEFAULT_COLOR_0B \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)
+#define DEFAULT_COLOR_0C \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C)
+#define DEFAULT_COLOR_0D \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x1F), 0x0D)
+#define DEFAULT_COLOR_0E \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x0E)
+#define DEFAULT_COLOR_0F \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+#endif
+
+
+#define BLACK_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00)
+#define LTGRAY_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x07)
+#define DKGRAY_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08)
+#define VDKGRAY_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x06, 0x06, 0x06), 0x00)
+#define WHITE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+#define BRIGHT_RED_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x00, 0x00), 0x04)
+#define BRIGHT_GREEN_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1F, 0x00), 0x02)
+#define BRIGHT_BLUE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x1F), 0x01)
+
+#define NORMAL_ILLUMINATED_COLOR \
+ WHITE_COLOR
+#define NORMAL_SHADOWED_COLOR \
+ DKGRAY_COLOR
+#define HIGHLIGHT_ILLUMINATED_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C)
+#define HIGHLIGHT_SHADOWED_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x04)
+#define MENU_BACKGROUND_COLOR \
+ LTGRAY_COLOR
+#define MENU_FOREGROUND_COLOR \
+ DKGRAY_COLOR
+#define MENU_TEXT_COLOR \
+ VDKGRAY_COLOR
+#define MENU_HIGHLIGHT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x0E)
+#define MENU_CURSOR_COLOR \
+ WHITE_COLOR
+
+#define STATUS_ILLUMINATED_COLOR \
+ WHITE_COLOR
+#define STATUS_SHADOWED_COLOR \
+ DKGRAY_COLOR
+#define STATUS_SHAPE_COLOR \
+ BLACK_COLOR
+#define STATUS_SHAPE_OUTLINE_COLOR \
+ WHITE_COLOR
+
+#define CONTROL_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+#define ALLIANCE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+#define HIERARCHY_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x04)
+#define ALLIANCE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)
+#define HIERARCHY_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E)
+#define ALLIANCE_BOX_HIGHLIGHT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+#define HIERARCHY_BOX_HIGHLIGHT_COLOR \
+ HIERARCHY_BACKGROUND_COLOR
+
+#define MESSAGE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+#define MESSAGE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E)
+
+
+// Not highlighted dialog options in comm.
+#define COMM_PLAYER_TEXT_NORMAL_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)
+
+// Currently highlighted dialog option in comm.
+#define COMM_PLAYER_TEXT_HIGHLIGHT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1A, 0x1A, 0x1A), 0x12)
+
+// Background color of the area containing the player's dialog options.
+#define COMM_PLAYER_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+// "(In response to your statement)"
+#define COMM_RESPONSE_INTRO_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0C, 0x1F), 0x48)
+
+// Your dialog option after choosing it.
+#define COMM_FEEDBACK_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x12, 0x14, 0x4F), 0x44)
+
+// The background when reviewing the conversation history.
+#define COMM_HISTORY_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x05, 0x00), 0x00)
+
+// The text when reviewing the conversation history.
+#define COMM_HISTORY_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x10, 0x00), 0x6B)
+
+// The text "MORE" when reviewing the conversation history.
+#define COMM_MORE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x17, 0x00), 0x00)
+
+// Default colors for System Dialog Boxes (DrawShadowedBox)
+#define SHADOWBOX_BACKGROUND_COLOR \
+ MENU_BACKGROUND_COLOR
+
+#define SHADOWBOX_MEDIUM_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19)
+
+#define SHADOWBOX_DARK_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F)
+
+
+// === SIS ===
+
+// Left border of the "SIS" view (the part in which your ship flies).
+#define SIS_LEFT_BORDER_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19)
+
+// Right and bottom border of the "SIS" view.
+#define SIS_BOTTOM_RIGHT_BORDER_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F)
+
+// Text color of the string "CAPTAIN", when using PC fonts.
+#define PC_CAPTAIN_STRING_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x02, 0x04, 0x1E), 0x38)
+
+// Background color of the string "CAPTAIN", when using PC fonts.
+#define PC_CAPTAIN_STRING_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+// Text color of the captain's name.
+#define CAPTAIN_NAME_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x16, 0x0B, 0x1F), 0x38)
+
+// Background color of the captain's name.
+#define CAPTAIN_NAME_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+// Text color of the flagship's name, when using 3DO fonts.
+#define THREEDO_FLAGSHIP_NAME_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x0A, 0x00), 0x0C)
+
+// Background color of the flagship's name.
+#define FLAGSHIP_NAME_BACKGROUND_COLOR \
+ BLACK_COLOR
+
+// Text color for the message area (at the top of the screen, on the left
+// hand side, containing the name of the solar system.
+#define SIS_MESSAGE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33)
+
+// Color of autocompleted text after the current cursor position,
+// when editing in the title area.
+#define SIS_MESSAGE_EXTRA_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x12, 0x00, 0x12), 0x33)
+
+// Background color for the message area.
+#define SIS_MESSAGE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+// Cursor color when editing in the message area.
+#define SIS_MESSAGE_CURSOR_COLOR \
+ BLACK_COLOR
+
+// Text color of the title (at the top of the screen, on the right
+// hand side, containing the coordinates in HyperSpace, or the planet name
+// in IP.
+#define SIS_TITLE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33)
+
+// Background color of the title.
+#define SIS_TITLE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+// Text color of the status message, below the flagship overview, containing
+// the date, RU, etc.
+#define STATUS_MESSAGE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x10, 0x00), 0x6B)
+
+// Background color of the status message.
+#define STATUS_MESSAGE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x08, 0x00), 0x6E)
+
+// Pulsating color of the string "AUTO-PILOT"
+#define AUTOPILOT_COLOR_CYCLE_TABLE \
+ { \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0A, 0x14, 0x18), 0x5B), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x06, 0x10, 0x16), 0x5C), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x03, 0x0E, 0x14), 0x5D), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x02, 0x0C, 0x11), 0x5E), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x01, 0x0B, 0x0F), 0x5F), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x01, 0x09, 0x0D), 0x60), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x07, 0x0B), 0x61), \
+ }
+
+// Colors for the fuel in the fuel tanks as they are filled up,
+// when viewed from the shipyard.
+#define FUEL_COLOR_TABLE \
+ { \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2C), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2A), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7F), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7E), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7D), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7C), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7B), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7A), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x18, 0x00), 0x79), \
+ }
+
+// Colors for the crew in the crew pods as they are filled up,
+// when viewed from the shipyard, when using PC fonts.
+#define PC_CREW_COLOR_TABLE \
+ { \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0A, 0x1E, 0x09), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x1E, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x1B, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x18, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x15, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x12, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x10, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x0D, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x0A, 0x00), 0x65), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x07, 0x00), 0x65), \
+ }
+
+// Colors for the crew in the crew pods as they are filled up,
+// when viewed from the shipyard, when using 3DO fonts.
+#define THREEDO_CREW_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x05, 0x10, 0x05), 0x65)
+
+// Colors for the minerals in the storage bays as they are filled up,
+// when viewed from the shipyard.
+#define STORAGE_BAY_COLOR_TABLE \
+ { \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x1F), 0x0F), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1C, 0x1C, 0x1C), 0x11), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x18, 0x18, 0x18), 0x13), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x15, 0x15, 0x15), 0x15), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x12, 0x12, 0x12), 0x17), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x10, 0x10, 0x10), 0x19), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0D, 0x0D, 0x0D), 0x1B), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0A, 0x0A, 0x0A), 0x1D), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x08, 0x08, 0x08), 0x1F), \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x05, 0x05, 0x05), 0x21), \
+ }
+
+// Color of the storage bay indicator, as shown beneath the flagship,
+// for the parts which are full.
+#define STORAGE_BAY_FULL_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08)
+
+// Color of the storage bay indicator, as shown beneath the flagship,
+// for the parts which are empty.
+#define STORAGE_BAY_EMPTY_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x06, 0x06, 0x06), 0x20)
+
+
+// === PC Menus ===
+
+// Background color of the PC-style menus.
+#define PCMENU_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x15), 0x00)
+
+// Color of the top and left segments of the border around PC-style menus.
+#define PCMENU_TOP_LEFT_BORDER_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0F, 0x0F, 0x0F), 0x00)
+
+// Color of the bottom and right segments of the border around PC-style menus.
+#define PCMENU_BOTTOM_RIGHT_BORDER_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x06, 0x06, 0x06), 0x00)
+
+// Text color of an unselected menu item.
+#define PCMENU_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x15, 0x15), 0x00)
+
+// Text color of an selected menu item.
+#define PCMENU_SELECTION_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)
+
+// Background color of a selected menu item.
+#define PCMENU_SELECTION_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+
+// === 3DO menus ===
+#define THREEDOMENU_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1F, 0x00), 0x00)
+
+
+// === Credits ===
+
+#define CREDITS_TEXT_COLOR \
+ WHITE_COLOR
+
+
+// === Cargo menu ===
+
+#define CARGO_BACK_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+#define CARGO_WORTH_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+
+#define CARGO_AMOUNT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)
+
+#define CARGO_SELECTED_BACK_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+
+#define CARGO_SELECTED_WORTH_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)
+
+#define CARGO_SELECTED_AMOUNT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)
+
+
+// === Devices menu ===
+
+#define DEVICES_BACK_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+
+#define DEVICES_NAME_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)
+
+#define DEVICES_SELECTED_BACK_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+
+#define DEVICES_SELECTED_NAME_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)
+
+
+// === Roster menu ===
+
+#define ROSTER_MODIFY_SHIP_COLOR \
+ WHITE_COLOR
+
+// === Scan menu and general ===
+
+#define SCAN_PC_TITLE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x15), 0x3B)
+
+#define SCAN_INFO_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0F, 0x00, 0x19), 0x3B)
+
+#define SCAN_MINERAL_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2C)
+
+#define SCAN_ENERGY_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0C, 0x0C, 0x0C), 0x1C)
+
+#define SCAN_BIOLOGICAL_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x0E, 0x00), 0x6C)
+
+#define SCAN_MINERAL_TINT_COLOR \
+ BRIGHT_RED_COLOR_INIT
+
+#define SCAN_ENERGY_TINT_COLOR \
+ WHITE_COLOR_INIT
+
+#define SCAN_BIOLOGICAL_TINT_COLOR \
+ BRIGHT_GREEN_COLOR_INIT
+
+
+// Temporary, until we can use C'99 features:
+#define BLACK_COLOR_INIT \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x00), 0x00)
+#define WHITE_COLOR_INIT \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x1F), 0x0F)
+#define BRIGHT_RED_COLOR_INIT \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x04)
+#define BRIGHT_GREEN_COLOR_INIT \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x1F, 0x00), 0x02)
+#define BRIGHT_BLUE_COLOR_INIT \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x1F), 0x01)
+#define UNDEFINED_COLOR_INIT \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x00), 0x00)
+
+
+#endif /* UQM_COLORS_H_ */
+
diff --git a/src/uqm/comm.c b/src/uqm/comm.c
new file mode 100644
index 0000000..ff2818c
--- /dev/null
+++ b/src/uqm/comm.c
@@ -0,0 +1,1649 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define COMM_INTERNAL
+#include "comm.h"
+
+#include "build.h"
+#include "commanim.h"
+#include "commglue.h"
+#include "controls.h"
+#include "colors.h"
+#include "sis.h"
+#include "units.h"
+#include "encount.h"
+#include "starmap.h"
+#include "endian_uqm.h"
+#include "gamestr.h"
+#include "options.h"
+#include "oscill.h"
+#include "save.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "nameref.h"
+#include "uqmdebug.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/inplib.h"
+#include "libs/sound/sound.h"
+#include "libs/sound/trackplayer.h"
+#include "libs/log.h"
+
+#include <ctype.h>
+
+#define MAX_RESPONSES 8
+#define BACKGROUND_VOL (usingSpeech ? (NORMAL_VOLUME / 2) : NORMAL_VOLUME)
+#define FOREGROUND_VOL NORMAL_VOLUME
+
+// Oscilloscope frame rate
+// Should be <= COMM_ANIM_RATE
+// XXX: was 32 picked experimentally?
+#define OSCILLOSCOPE_RATE (ONE_SECOND / 32)
+
+// Maximum comm animation frame rate (actual execution rate)
+// A gfx frame is not always produced during an execution frame,
+// and several animations are combined into one gfx frame.
+// The rate was originally 120fps which allowed for more animation
+// precision which is ultimately wasted on the human eye anyway.
+// The highest known stable animation rate is 40fps, so that's what we use.
+#define COMM_ANIM_RATE (ONE_SECOND / 40)
+
+static CONTEXT AnimContext;
+
+LOCDATA CommData;
+UNICODE shared_phrase_buf[2048];
+
+static BOOLEAN TalkingFinished;
+static CommIntroMode curIntroMode = CIM_DEFAULT;
+static TimeCount fadeTime;
+
+typedef struct response_entry
+{
+ RESPONSE_REF response_ref;
+ TEXT response_text;
+ RESPONSE_FUNC response_func;
+} RESPONSE_ENTRY;
+
+typedef struct encounter_state
+{
+ BOOLEAN (*InputFunc) (struct encounter_state *pES);
+
+ COUNT Initialized;
+ TimeCount NextTime; // framerate control
+ BYTE num_responses;
+ BYTE cur_response;
+ BYTE top_response;
+ RESPONSE_ENTRY response_list[MAX_RESPONSES];
+
+ UNICODE phrase_buf[1024];
+} ENCOUNTER_STATE;
+
+static ENCOUNTER_STATE *pCurInputState;
+
+static BOOLEAN clear_subtitles;
+static TEXT SubtitleText;
+static const UNICODE *last_subtitle;
+
+static CONTEXT TextCacheContext;
+static FRAME TextCacheFrame;
+
+
+RECT CommWndRect = {
+ // default values; actually inited by HailAlien()
+ {SIS_ORG_X, SIS_ORG_Y},
+ {0, 0}
+};
+
+static void ClearSubtitles (void);
+static void CheckSubtitles (void);
+static void RedrawSubtitles (void);
+
+
+/* _count_lines - sees how many lines a given input string would take to
+ * display given the line wrapping information
+ */
+static int
+_count_lines (TEXT *pText)
+{
+ SIZE text_width;
+ const char *pStr;
+ int numLines = 0;
+ BOOLEAN eol;
+
+ text_width = CommData.AlienTextWidth;
+ SetContextFont (CommData.AlienFont);
+
+ pStr = pText->pStr;
+ do
+ {
+ ++numLines;
+ pText->pStr = pStr;
+ eol = getLineWithinWidth (pText, &pStr, text_width, (COUNT)~0);
+ } while (!eol);
+ pText->pStr = pStr;
+
+ return numLines;
+}
+
+// status == -1: draw highlighted player dialog option
+// status == -2: draw non-highlighted player dialog option
+// status == -4: use current context, and baseline from pTextIn
+// status == 1: draw alien speech; subtitle cache is used
+static COORD
+add_text (int status, TEXT *pTextIn)
+{
+ COUNT maxchars, numchars;
+ TEXT locText;
+ TEXT *pText;
+ SIZE leading;
+ const char *pStr;
+ SIZE text_width;
+ int num_lines = 0;
+ static COORD last_baseline;
+ BOOLEAN eol;
+ CONTEXT OldContext = NULL;
+
+ BatchGraphics ();
+
+ maxchars = (COUNT)~0;
+ if (status == 1)
+ {
+ if (last_subtitle == pTextIn->pStr)
+ {
+ // draws cached subtitle
+ STAMP s;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = TextCacheFrame;
+ DrawStamp (&s);
+ UnbatchGraphics ();
+ return last_baseline;
+ }
+ else
+ {
+ // draw to subtitle cache; prepare first
+ OldContext = SetContext (TextCacheContext);
+ ClearDrawable ();
+
+ last_subtitle = pTextIn->pStr;
+ }
+
+ text_width = CommData.AlienTextWidth;
+ SetContextFont (CommData.AlienFont);
+ GetContextFontLeading (&leading);
+
+ pText = pTextIn;
+ }
+ else if (GetContextFontLeading (&leading), status <= -4)
+ {
+ text_width = (SIZE) (SIS_SCREEN_WIDTH - 8 - (TEXT_X_OFFS << 2));
+
+ pText = pTextIn;
+ }
+ else
+ {
+ text_width = (SIZE) (SIS_SCREEN_WIDTH - 8 - (TEXT_X_OFFS << 2));
+
+ switch (status)
+ {
+ case -3:
+ // Unknown. Never reached; color matches the background color.
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01));
+ break;
+ case -2:
+ // Not highlighted dialog options.
+ SetContextForeGroundColor (COMM_PLAYER_TEXT_NORMAL_COLOR);
+ break;
+ case -1:
+ // Currently highlighted dialog option.
+ SetContextForeGroundColor (COMM_PLAYER_TEXT_HIGHLIGHT_COLOR);
+ break;
+ }
+
+ maxchars = pTextIn->CharCount;
+ locText = *pTextIn;
+ locText.baseline.x -= 8;
+ locText.CharCount = (COUNT)~0;
+ locText.pStr = STR_BULLET;
+ font_DrawText (&locText);
+
+ locText = *pTextIn;
+ pText = &locText;
+ pText->baseline.y -= leading;
+ }
+
+ numchars = 0;
+ pStr = pText->pStr;
+
+ if (status > 0 && (CommData.AlienTextValign &
+ (VALIGN_MIDDLE | VALIGN_BOTTOM)))
+ {
+ num_lines = _count_lines(pText);
+ if (CommData.AlienTextValign == VALIGN_BOTTOM)
+ pText->baseline.y -= (leading * num_lines);
+ else if (CommData.AlienTextValign == VALIGN_MIDDLE)
+ pText->baseline.y -= ((leading * num_lines) / 2);
+ if (pText->baseline.y < 0)
+ pText->baseline.y = 0;
+ }
+
+ do
+ {
+ pText->pStr = pStr;
+ pText->baseline.y += leading;
+
+ eol = getLineWithinWidth (pText, &pStr, text_width, maxchars);
+
+ maxchars -= pText->CharCount;
+ if (maxchars != 0)
+ --maxchars;
+ numchars += pText->CharCount;
+
+ if (status <= 0)
+ {
+ // Player dialog option or (status == -4) other non-alien
+ // text.
+ if (pText->baseline.y < SIS_SCREEN_HEIGHT)
+ font_DrawText (pText);
+
+ if (status < -4 && pText->baseline.y >= -status - 10)
+ {
+ // Never actually reached. Status is never <-4.
+ ++pStr;
+ break;
+ }
+ }
+ else
+ {
+ // Alien speech
+ font_DrawTracedText (pText,
+ CommData.AlienTextFColor, CommData.AlienTextBColor);
+ }
+ } while (!eol && maxchars);
+ pText->pStr = pStr;
+
+ if (status == 1)
+ {
+ STAMP s;
+
+ // We were drawing to cache -- flush to screen
+ SetContext (OldContext);
+ s.origin.x = s.origin.y = 0;
+ s.frame = TextCacheFrame;
+ DrawStamp (&s);
+
+ last_baseline = pText->baseline.y;
+ }
+
+ UnbatchGraphics ();
+ return (pText->baseline.y);
+}
+
+// This function calculates how much of a string can be fitted within
+// a specific width, up to a newline or terminating \0.
+// pText is the text to be fitted. pText->CharCount will be set to the
+// number of characters that fitted.
+// startNext will be filled with the start of the first word that
+// doesn't fit in one line, or if an entire line fits, to the character
+// past the newline, or if the entire string fits, to the end of the
+// string.
+// maxWidth is the maximum number of pixels that a line may be wide
+// ASSUMPTION: there are no words in the text wider than maxWidth
+// maxChars is the maximum number of characters (not bytes) that are to
+// be fitted.
+// TRUE is returned if a complete line fitted
+// FALSE otherwise
+BOOLEAN
+getLineWithinWidth(TEXT *pText, const char **startNext,
+ SIZE maxWidth, COUNT maxChars)
+{
+ BOOLEAN eol;
+ // The end of the line of text has been reached.
+ BOOLEAN done;
+ // We cannot add any more words.
+ RECT rect;
+ COUNT oldCount;
+ const char *ptr;
+ const char *wordStart;
+ UniChar ch;
+ COUNT charCount;
+
+ //GetContextClipRect (&rect);
+
+ eol = FALSE;
+ done = FALSE;
+ oldCount = 1;
+ charCount = 0;
+ ch = '\0';
+ ptr = pText->pStr;
+ for (;;)
+ {
+ wordStart = ptr;
+
+ // Scan one word.
+ for (;;)
+ {
+ if (*ptr == '\0')
+ {
+ eol = TRUE;
+ done = TRUE;
+ break;
+ }
+ ch = getCharFromString (&ptr);
+ eol = ch == '\0' || ch == '\n' || ch == '\r';
+ done = eol || charCount >= maxChars;
+ if (done || ch == ' ')
+ break;
+ charCount++;
+ }
+
+ oldCount = pText->CharCount;
+ pText->CharCount = charCount;
+ TextRect (pText, &rect, NULL);
+
+ if (rect.extent.width >= maxWidth)
+ {
+ pText->CharCount = oldCount;
+ *startNext = wordStart;
+ return FALSE;
+ }
+
+ if (done)
+ {
+ *startNext = ptr;
+ return eol;
+ }
+ charCount++;
+ // For the space in between words.
+ }
+}
+
+static void
+DrawSISComWindow (void)
+{
+ CONTEXT OldContext;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != WON_LAST_BATTLE)
+ {
+ RECT r;
+
+ OldContext = SetContext (SpaceContext);
+
+ r.corner.x = 0;
+ r.corner.y = SLIDER_Y + SLIDER_HEIGHT;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = SIS_SCREEN_HEIGHT - r.corner.y;
+ SetContextForeGroundColor (COMM_PLAYER_BACKGROUND_COLOR);
+ DrawFilledRectangle (&r);
+
+ SetContext (OldContext);
+ }
+}
+
+void
+init_communication (void)
+{
+ // now a no-op
+}
+
+void
+uninit_communication (void)
+{
+ // now a no-op
+}
+
+static void
+RefreshResponses (ENCOUNTER_STATE *pES)
+{
+ COORD y;
+ BYTE response;
+ SIZE leading;
+ STAMP s;
+
+ SetContext (SpaceContext);
+ GetContextFontLeading (&leading);
+ BatchGraphics ();
+
+ DrawSISComWindow ();
+ y = SLIDER_Y + SLIDER_HEIGHT + 1;
+ for (response = pES->top_response; response < pES->num_responses;
+ ++response)
+ {
+ pES->response_list[response].response_text.baseline.x = TEXT_X_OFFS + 8;
+ pES->response_list[response].response_text.baseline.y = y + leading;
+ pES->response_list[response].response_text.align = ALIGN_LEFT;
+ if (response == pES->cur_response)
+ y = add_text (-1, &pES->response_list[response].response_text);
+ else
+ y = add_text (-2, &pES->response_list[response].response_text);
+ }
+
+ if (pES->top_response)
+ {
+ s.origin.y = SLIDER_Y + SLIDER_HEIGHT + 1;
+ s.frame = SetAbsFrameIndex (ActivityFrame, 6);
+ }
+ else if (y > SIS_SCREEN_HEIGHT)
+ {
+ s.origin.y = SIS_SCREEN_HEIGHT - 2;
+ s.frame = SetAbsFrameIndex (ActivityFrame, 7);
+ }
+ else
+ s.frame = 0;
+ if (s.frame)
+ {
+ RECT r;
+
+ GetFrameRect (s.frame, &r);
+ s.origin.x = SIS_SCREEN_WIDTH - r.extent.width - 1;
+ DrawStamp (&s);
+ }
+
+ UnbatchGraphics ();
+}
+
+static void
+FeedbackPlayerPhrase (UNICODE *pStr)
+{
+ SetContext (SpaceContext);
+
+ BatchGraphics ();
+ DrawSISComWindow ();
+ if (pStr[0])
+ {
+ TEXT ct;
+
+ ct.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ ct.baseline.y = SLIDER_Y + SLIDER_HEIGHT + 13;
+ ct.align = ALIGN_CENTER;
+ ct.CharCount = (COUNT)~0;
+
+ ct.pStr = GAME_STRING (FEEDBACK_STRING_BASE);
+ // "(In response to your statement)"
+ SetContextForeGroundColor (COMM_RESPONSE_INTRO_TEXT_COLOR);
+ font_DrawText (&ct);
+
+ ct.baseline.y += 16;
+ SetContextForeGroundColor (COMM_FEEDBACK_TEXT_COLOR);
+ ct.pStr = pStr;
+ add_text (-4, &ct);
+ }
+ UnbatchGraphics ();
+}
+
+static void
+InitSpeechGraphics (void)
+{
+ InitOscilloscope (SetAbsFrameIndex (ActivityFrame, 9));
+
+ InitSlider (0, SLIDER_Y, SIS_SCREEN_WIDTH,
+ SetAbsFrameIndex (ActivityFrame, 5),
+ SetAbsFrameIndex (ActivityFrame, 2));
+}
+
+static void
+UpdateSpeechGraphics (void)
+{
+ static TimeCount NextTime;
+ CONTEXT OldContext;
+
+ if (GetTimeCounter () < NextTime)
+ return; // too early
+
+ NextTime = GetTimeCounter () + OSCILLOSCOPE_RATE;
+
+ OldContext = SetContext (RadarContext);
+ DrawOscilloscope ();
+ SetContext (SpaceContext);
+ DrawSlider ();
+ SetContext (OldContext);
+}
+
+static void
+UpdateAnimations (bool paused)
+{
+ static TimeCount NextTime;
+ CONTEXT OldContext;
+ BOOLEAN change;
+
+ if (GetTimeCounter () < NextTime)
+ return; // too early
+
+ NextTime = GetTimeCounter () + COMM_ANIM_RATE;
+
+ OldContext = SetContext (AnimContext);
+ BatchGraphics ();
+ // Advance and draw ambient, transit and talk animations
+ change = ProcessCommAnimations (clear_subtitles, paused);
+ if (change || clear_subtitles)
+ RedrawSubtitles ();
+ UnbatchGraphics ();
+ clear_subtitles = FALSE;
+ SetContext (OldContext);
+}
+
+static void
+UpdateCommGraphics (void)
+{
+ UpdateAnimations (false);
+ UpdateSpeechGraphics ();
+}
+
+// Derived from INPUT_STATE_DESC
+typedef struct talking_state
+{
+ // Fields required by DoInput()
+ BOOLEAN (*InputFunc) (struct talking_state *);
+
+ TimeCount NextTime; // framerate control
+ COUNT waitTrack;
+ bool rewind;
+ bool seeking;
+ bool ended;
+
+} TALKING_STATE;
+
+static BOOLEAN
+DoTalkSegue (TALKING_STATE *pTS)
+{
+ bool left = false;
+ bool right = false;
+ COUNT curTrack;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pTS->ended = true;
+ return FALSE;
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ JumpTrack ();
+ pTS->ended = true;
+ return FALSE;
+ }
+
+ if (optSmoothScroll == OPT_PC)
+ {
+ left = PulsedInputState.menu[KEY_MENU_LEFT] != 0;
+ right = PulsedInputState.menu[KEY_MENU_RIGHT] != 0;
+ }
+ else if (optSmoothScroll == OPT_3DO)
+ {
+ left = CurrentInputState.menu[KEY_MENU_LEFT] != 0;
+ right = CurrentInputState.menu[KEY_MENU_RIGHT] != 0;
+ }
+
+#if DEMO_MODE || CREATE_JOURNAL
+ left = false;
+ right = false;
+#endif
+
+ if (right)
+ {
+ SetSliderImage (SetAbsFrameIndex (ActivityFrame, 3));
+ if (optSmoothScroll == OPT_PC)
+ FastForward_Page ();
+ else if (optSmoothScroll == OPT_3DO)
+ FastForward_Smooth ();
+ pTS->seeking = true;
+ }
+ else if (left || pTS->rewind)
+ {
+ pTS->rewind = false;
+ SetSliderImage (SetAbsFrameIndex (ActivityFrame, 4));
+ if (optSmoothScroll == OPT_PC)
+ FastReverse_Page ();
+ else if (optSmoothScroll == OPT_3DO)
+ FastReverse_Smooth ();
+ pTS->seeking = true;
+ }
+ else if (pTS->seeking)
+ {
+ // This is only done once the seeking is over (in the smooth
+ // scroll case, once the user releases the seek button)
+ pTS->seeking = false;
+ SetSliderImage (SetAbsFrameIndex (ActivityFrame, 2));
+ }
+ else
+ {
+ // This used to have a buggy guard condition, which
+ // would cause the animations to remain paused in a couple cases
+ // after seeking back to the beginning.
+ // Broken cases were: Syreen "several hours later" and Starbase
+ // VUX Beast analysis by the scientist.
+ CheckSubtitles ();
+ }
+
+ // XXX: When seeking, all animations (talking and ambient) stop
+ // progressing. This is an original 3DO behavior, and I see no
+ // reason why the animations cannot continue while seeking.
+ UpdateAnimations (pTS->seeking);
+ UpdateSpeechGraphics ();
+
+ curTrack = PlayingTrack ();
+ pTS->ended = !pTS->seeking && !curTrack;
+
+ SleepThreadUntil (pTS->NextTime);
+ // Need a high enough framerate for 3DO smooth seeking
+ pTS->NextTime = GetTimeCounter () + ONE_SECOND / 60;
+
+ return pTS->seeking || (curTrack && curTrack <= pTS->waitTrack);
+}
+
+static void
+runCommAnimFrame (void)
+{
+ UpdateCommGraphics ();
+ SleepThread (COMM_ANIM_RATE);
+}
+
+static BOOLEAN
+TalkSegue (COUNT wait_track)
+{
+ TALKING_STATE talkingState;
+
+ // Transition animation to talking state, if necessary
+ if (wantTalkingAnim () && haveTalkingAnim ())
+ {
+ if (haveTransitionAnim ())
+ setRunIntroAnim ();
+
+ setRunTalkingAnim ();
+
+ // wait until the transition finishes
+ while (runningIntroAnim ())
+ runCommAnimFrame ();
+ }
+
+ memset (&talkingState, 0, sizeof talkingState);
+
+ if (wait_track == 0)
+ { // Restarting with a rewind
+ wait_track = WAIT_TRACK_ALL;
+ talkingState.rewind = true;
+ }
+ else if (!PlayingTrack ())
+ { // initial start of player
+ PlayTrack ();
+ assert (PlayingTrack ());
+ }
+
+ // Run the talking controls
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ talkingState.InputFunc = DoTalkSegue;
+ talkingState.waitTrack = wait_track;
+ DoInput (&talkingState, FALSE);
+
+ ClearSubtitles ();
+
+ if (talkingState.ended)
+ { // reached the end; set STOP icon
+ SetSliderImage (SetAbsFrameIndex (ActivityFrame, 8));
+ }
+
+ // transition back to silent, if necessary
+ if (runningTalkingAnim ())
+ setStopTalkingAnim ();
+
+ // Wait until the animation task stops "talking"
+ while (runningTalkingAnim ())
+ runCommAnimFrame ();
+
+ return talkingState.ended;
+}
+
+static void
+CommIntroTransition (void)
+{
+ if (curIntroMode == CIM_CROSSFADE_SCREEN)
+ {
+ ScreenTransition (3, NULL);
+ UnbatchGraphics ();
+ }
+ else if (curIntroMode == CIM_CROSSFADE_SPACE)
+ {
+ RECT r;
+ r.corner.x = SIS_ORG_X;
+ r.corner.y = SIS_ORG_Y;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = SIS_SCREEN_HEIGHT;
+ ScreenTransition (3, &r);
+ UnbatchGraphics ();
+ }
+ else if (curIntroMode == CIM_CROSSFADE_WINDOW)
+ {
+ ScreenTransition (3, &CommWndRect);
+ UnbatchGraphics ();
+ }
+ else if (curIntroMode == CIM_FADE_IN_SCREEN)
+ {
+ UnbatchGraphics ();
+ FadeScreen (FadeAllToColor, fadeTime);
+ }
+ else
+ { // Uknown transition
+ // Have to unbatch anyway or no more graphics, ever
+ UnbatchGraphics ();
+ assert (0 && "Unknown comm intro transition");
+ }
+
+ // Reset the mode for next time. Everything that needs a
+ // different one will let us know.
+ curIntroMode = CIM_DEFAULT;
+}
+
+void
+AlienTalkSegue (COUNT wait_track)
+{
+ // this skips any talk segues that follow an aborted one
+ if ((GLOBAL (CurrentActivity) & CHECK_ABORT) || TalkingFinished)
+ return;
+
+ if (!pCurInputState->Initialized)
+ {
+ InitSpeechGraphics ();
+ SetColorMap (GetColorMapAddress (CommData.AlienColorMap));
+ SetContext (AnimContext);
+ DrawAlienFrame (NULL, 0, TRUE);
+ UpdateSpeechGraphics ();
+ CommIntroTransition ();
+
+ pCurInputState->Initialized = TRUE;
+
+ PlayMusic (CommData.AlienSong, TRUE, 1);
+ SetMusicVolume (BACKGROUND_VOL);
+
+ InitCommAnimations ();
+
+ LastActivity &= ~CHECK_LOAD;
+ }
+
+ TalkingFinished = TalkSegue (wait_track);
+ if (TalkingFinished)
+ FadeMusic (FOREGROUND_VOL, ONE_SECOND);
+}
+
+
+typedef struct summary_state
+{
+ // standard state required by DoInput
+ BOOLEAN (*InputFunc) (struct summary_state *pSS);
+
+ // extended state
+ BOOLEAN Initialized;
+ BOOLEAN PrintNext;
+ SUBTITLE_REF NextSub;
+ const UNICODE *LeftOver;
+
+} SUMMARY_STATE;
+
+static BOOLEAN
+DoConvSummary (SUMMARY_STATE *pSS)
+{
+#define DELTA_Y_SUMMARY 8
+#define MAX_SUMM_ROWS ((SIS_SCREEN_HEIGHT - SLIDER_Y - SLIDER_HEIGHT) \
+ / DELTA_Y_SUMMARY) - 1
+
+ if (!pSS->Initialized)
+ {
+ pSS->PrintNext = TRUE;
+ pSS->NextSub = GetFirstTrackSubtitle ();
+ pSS->LeftOver = NULL;
+ pSS->InputFunc = DoConvSummary;
+ pSS->Initialized = TRUE;
+ DoInput (pSS, FALSE);
+ }
+ else if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ return FALSE; // bail out
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT]
+ || PulsedInputState.menu[KEY_MENU_CANCEL]
+ || PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ if (pSS->NextSub)
+ { // we want the next page
+ pSS->PrintNext = TRUE;
+ }
+ else
+ { // no more, we are done
+ return FALSE;
+ }
+ }
+ else if (pSS->PrintNext)
+ { // print the next page
+ RECT r;
+ TEXT t;
+ int row;
+
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = SIS_SCREEN_HEIGHT - SLIDER_Y - SLIDER_HEIGHT + 2;
+
+ SetContext (AnimContext);
+ SetContextForeGroundColor (COMM_HISTORY_BACKGROUND_COLOR);
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (COMM_HISTORY_TEXT_COLOR);
+
+ r.extent.width -= 2 + 2;
+ t.baseline.x = 2;
+ t.align = ALIGN_LEFT;
+ t.baseline.y = DELTA_Y_SUMMARY;
+ SetContextFont (TinyFont);
+
+ for (row = 0; row < MAX_SUMM_ROWS && pSS->NextSub;
+ ++row, pSS->NextSub = GetNextTrackSubtitle (pSS->NextSub))
+ {
+ const char *next = NULL;
+
+ if (pSS->LeftOver)
+ { // some text left from last subtitle
+ t.pStr = pSS->LeftOver;
+ pSS->LeftOver = NULL;
+ }
+ else
+ {
+ t.pStr = GetTrackSubtitleText (pSS->NextSub);
+ if (!t.pStr)
+ continue;
+ }
+
+ t.CharCount = (COUNT)~0;
+ for ( ; row < MAX_SUMM_ROWS &&
+ !getLineWithinWidth (&t, &next, r.extent.width, (COUNT)~0);
+ ++row)
+ {
+ font_DrawText (&t);
+ t.baseline.y += DELTA_Y_SUMMARY;
+ t.pStr = next;
+ t.CharCount = (COUNT)~0;
+ }
+
+ if (row >= MAX_SUMM_ROWS)
+ { // no more space on screen, but some text left over
+ // from the current subtitle
+ pSS->LeftOver = next;
+ break;
+ }
+
+ // this subtitle fit completely
+ font_DrawText (&t);
+ t.baseline.y += DELTA_Y_SUMMARY;
+ }
+
+ if (row >= MAX_SUMM_ROWS && (pSS->NextSub || pSS->LeftOver))
+ { // draw *MORE*
+ TEXT mt;
+ UNICODE buffer[80];
+
+ mt.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ mt.baseline.y = t.baseline.y;
+ mt.align = ALIGN_CENTER;
+ snprintf (buffer, sizeof (buffer), "%s%s%s", // "MORE"
+ STR_MIDDLE_DOT, GAME_STRING (FEEDBACK_STRING_BASE + 1),
+ STR_MIDDLE_DOT);
+ mt.pStr = buffer;
+ SetContextForeGroundColor (COMM_MORE_TEXT_COLOR);
+ font_DrawText (&mt);
+ }
+
+
+ pSS->PrintNext = FALSE;
+ }
+ else
+ {
+ SleepThread (ONE_SECOND / 20);
+ }
+
+ return TRUE; // keep going
+}
+
+// Called when the player presses the select button on a response.
+static void
+SelectResponse (ENCOUNTER_STATE *pES)
+{
+ TEXT *response_text =
+ &pES->response_list[pES->cur_response].response_text;
+ utf8StringCopy (pES->phrase_buf, sizeof pES->phrase_buf,
+ response_text->pStr);
+ FeedbackPlayerPhrase (pES->phrase_buf);
+ StopTrack ();
+ ClearSubtitles ();
+ SetSliderImage (SetAbsFrameIndex (ActivityFrame, 2));
+
+ FadeMusic (BACKGROUND_VOL, ONE_SECOND);
+
+ TalkingFinished = FALSE;
+ pES->num_responses = 0;
+ (*pES->response_list[pES->cur_response].response_func)
+ (pES->response_list[pES->cur_response].response_ref);
+}
+
+// Called when the player presses the cancel button in comm.
+static void
+SelectConversationSummary (ENCOUNTER_STATE *pES)
+{
+ SUMMARY_STATE SummaryState;
+
+ if (pES)
+ FeedbackPlayerPhrase (pES->phrase_buf);
+
+ SummaryState.Initialized = FALSE;
+ DoConvSummary (&SummaryState);
+
+ if (pES)
+ RefreshResponses (pES);
+ clear_subtitles = TRUE;
+}
+
+static void
+SelectReplay (ENCOUNTER_STATE *pES)
+{
+ FadeMusic (BACKGROUND_VOL, ONE_SECOND);
+ if (pES)
+ FeedbackPlayerPhrase (pES->phrase_buf);
+
+ TalkSegue (0);
+}
+
+static void
+PlayerResponseInput (ENCOUNTER_STATE *pES)
+{
+ BYTE response;
+
+ if (pES->top_response == (BYTE)~0)
+ {
+ pES->top_response = 0;
+ RefreshResponses (pES);
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ SelectResponse (pES);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_CANCEL] &&
+ LOBYTE (GLOBAL (CurrentActivity)) != WON_LAST_BATTLE)
+ {
+ SelectConversationSummary (pES);
+ }
+ else
+ {
+ response = pES->cur_response;
+ if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ SelectReplay (pES);
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ RefreshResponses (pES);
+ FadeMusic (FOREGROUND_VOL, ONE_SECOND);
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_UP])
+ response = (BYTE)((response + (BYTE)(pES->num_responses - 1))
+ % pES->num_responses);
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ response = (BYTE)((BYTE)(response + 1) % pES->num_responses);
+
+ if (response != pES->cur_response)
+ {
+ COORD y;
+
+ BatchGraphics ();
+ add_text (-2,
+ &pES->response_list[pES->cur_response].response_text);
+
+ pES->cur_response = response;
+
+ y = add_text (-1,
+ &pES->response_list[pES->cur_response].response_text);
+ if (response < pES->top_response)
+ {
+ pES->top_response = 0;
+ RefreshResponses (pES);
+ }
+ else if (y > SIS_SCREEN_HEIGHT)
+ {
+ pES->top_response = response;
+ RefreshResponses (pES);
+ }
+ UnbatchGraphics ();
+ }
+
+ UpdateCommGraphics ();
+
+ SleepThreadUntil (pES->NextTime);
+ pES->NextTime = GetTimeCounter () + COMM_ANIM_RATE;
+ }
+}
+
+// Derived from INPUT_STATE_DESC
+typedef struct last_replay_state
+{
+ // Fields required by DoInput()
+ BOOLEAN (*InputFunc) (struct last_replay_state *);
+
+ TimeCount NextTime; // framerate control
+ TimeCount TimeOut;
+
+} LAST_REPLAY_STATE;
+
+static BOOLEAN
+DoLastReplay (LAST_REPLAY_STATE *pLRS)
+{
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (GetTimeCounter () > pLRS->TimeOut)
+ return FALSE; // timed out and done
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL] &&
+ LOBYTE (GLOBAL (CurrentActivity)) != WON_LAST_BATTLE)
+ {
+ FadeMusic (BACKGROUND_VOL, ONE_SECOND);
+ SelectConversationSummary (NULL);
+ pLRS->TimeOut = FadeMusic (0, ONE_SECOND * 2) + ONE_SECOND / 60;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ SelectReplay (NULL);
+ pLRS->TimeOut = FadeMusic (0, ONE_SECOND * 2) + ONE_SECOND / 60;
+ }
+
+ UpdateCommGraphics ();
+
+ SleepThreadUntil (pLRS->NextTime);
+ pLRS->NextTime = GetTimeCounter () + COMM_ANIM_RATE;
+
+ return TRUE;
+}
+
+static BOOLEAN
+DoCommunication (ENCOUNTER_STATE *pES)
+{
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN, MENU_SOUND_SELECT);
+
+ // First, finish playing all queued tracks if not done yet
+ if (!TalkingFinished)
+ {
+ AlienTalkSegue (WAIT_TRACK_ALL);
+ return TRUE;
+ }
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ ;
+ else if (pES->num_responses == 0)
+ {
+ // The player doesn't get a chance to say anything,
+ // but can still review alien's last phrases.
+ LAST_REPLAY_STATE replayState;
+
+ memset (&replayState, 0, sizeof replayState);
+ replayState.TimeOut = FadeMusic (0, ONE_SECOND * 3) + ONE_SECOND / 60;
+ replayState.InputFunc = DoLastReplay;
+ DoInput (&replayState, FALSE);
+ }
+ else
+ {
+ PlayerResponseInput (pES);
+ return TRUE;
+ }
+
+ SetContext (SpaceContext);
+ DestroyContext (AnimContext);
+ AnimContext = NULL;
+
+ FlushColorXForms ();
+ ClearSubtitles ();
+
+ StopMusic ();
+ StopSound ();
+ StopTrack ();
+ SleepThreadUntil (FadeMusic (NORMAL_VOLUME, 0) + ONE_SECOND / 60);
+
+ return FALSE;
+}
+
+void
+DoResponsePhrase (RESPONSE_REF R, RESPONSE_FUNC response_func,
+ UNICODE *ConstructStr)
+{
+ ENCOUNTER_STATE *pES = pCurInputState;
+ RESPONSE_ENTRY *pEntry;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return;
+
+ if (pES->num_responses == 0)
+ {
+ pES->cur_response = 0;
+ pES->top_response = (BYTE)~0;
+ }
+
+ pEntry = &pES->response_list[pES->num_responses];
+ pEntry->response_ref = R;
+ pEntry->response_text.pStr = ConstructStr;
+ if (pEntry->response_text.pStr)
+ pEntry->response_text.CharCount = (COUNT)~0;
+ else
+ {
+ STRING locString;
+
+ locString = SetAbsStringTableIndex (CommData.ConversationPhrases,
+ (COUNT) (R - 1));
+ pEntry->response_text.pStr =
+ (UNICODE *) GetStringAddress (locString);
+ pEntry->response_text.CharCount = GetStringLength (locString);
+//#define BVT_PROBLEM
+#ifdef BVT_PROBLEM
+ if (pEntry->response_text.pStr[pEntry->response_text.CharCount - 1]
+ == '\0')
+ --pEntry->response_text.CharCount;
+#endif /* BVT_PROBLEM */
+ }
+ pEntry->response_func = response_func;
+ ++pES->num_responses;
+}
+
+static void
+HailAlien (void)
+{
+ ENCOUNTER_STATE ES;
+ FONT PlayerFont, OldFont;
+ MUSIC_REF SongRef = 0;
+ Color TextBack;
+
+ pCurInputState = &ES;
+ memset (pCurInputState, 0, sizeof (*pCurInputState));
+
+ TalkingFinished = FALSE;
+
+ ES.InputFunc = DoCommunication;
+ PlayerFont = LoadFont (PLAYER_FONT);
+
+ CommData.AlienFrame = CaptureDrawable (
+ LoadGraphic (CommData.AlienFrameRes));
+ CommData.AlienFont = LoadFont (CommData.AlienFontRes);
+ CommData.AlienColorMap = CaptureColorMap (
+ LoadColorMap (CommData.AlienColorMapRes));
+ if ((CommData.AlienSongFlags & LDASF_USE_ALTERNATE)
+ && CommData.AlienAltSongRes)
+ SongRef = LoadMusic (CommData.AlienAltSongRes);
+ if (SongRef)
+ CommData.AlienSong = SongRef;
+ else
+ CommData.AlienSong = LoadMusic (CommData.AlienSongRes);
+
+ CommData.ConversationPhrases = CaptureStringTable (
+ LoadStringTable (CommData.ConversationPhrasesRes));
+
+ SubtitleText.baseline = CommData.AlienTextBaseline;
+ SubtitleText.align = CommData.AlienTextAlign;
+
+
+ // init subtitle cache context
+ TextCacheContext = CreateContext ("TextCacheContext");
+ TextCacheFrame = CaptureDrawable (
+ CreateDrawable (WANT_PIXMAP, SIS_SCREEN_WIDTH,
+ SIS_SCREEN_HEIGHT - SLIDER_Y - SLIDER_HEIGHT + 2, 1));
+ SetContext (TextCacheContext);
+ SetContextFGFrame (TextCacheFrame);
+ TextBack = BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x10), 0x00);
+ // Color key for the background.
+ SetContextBackGroundColor (TextBack);
+ ClearDrawable ();
+ SetFrameTransparentColor (TextCacheFrame, TextBack);
+
+ ES.phrase_buf[0] = '\0';
+
+ SetContext (SpaceContext);
+ OldFont = SetContextFont (PlayerFont);
+
+ {
+ RECT r;
+
+ AnimContext = CreateContext ("AnimContext");
+ SetContext (AnimContext);
+ SetContextFGFrame (Screen);
+ GetFrameRect (CommData.AlienFrame, &r);
+ r.extent.width = SIS_SCREEN_WIDTH;
+ CommWndRect.extent = r.extent;
+
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ r.corner = CommWndRect.corner;
+ SetContextClipRect (&r);
+ }
+ else
+ {
+ r.corner.x = SIS_ORG_X;
+ r.corner.y = SIS_ORG_Y;
+ SetContextClipRect (&r);
+ CommWndRect.corner = r.corner;
+
+ DrawSISFrame ();
+ // TODO: find a better way to do this, perhaps set the titles
+ // forward from callers.
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) == (BYTE)~0
+ && GET_GAME_STATE (STARBASE_AVAILABLE))
+ { // Talking to allied Starbase
+ DrawSISMessage (GAME_STRING (STARBASE_STRING_BASE + 1));
+ // "Starbase Commander"
+ DrawSISTitle (GAME_STRING (STARBASE_STRING_BASE + 0));
+ // "Starbase"
+ }
+ else
+ { // Default titles: star name + planet name
+ DrawSISMessage (NULL);
+ DrawSISTitle (GLOBAL_SIS (PlanetName));
+ }
+ }
+
+ DrawSISComWindow ();
+ }
+
+
+ LastActivity |= CHECK_LOAD; /* prevent spurious input */
+ (*CommData.init_encounter_func) ();
+ DoInput (&ES, FALSE);
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ (*CommData.post_encounter_func) ();
+ (*CommData.uninit_encounter_func) ();
+
+ SetContext (SpaceContext);
+ SetContextFont (OldFont);
+
+ DestroyStringTable (ReleaseStringTable (CommData.ConversationPhrases));
+ DestroyMusic (CommData.AlienSong);
+ DestroyColorMap (ReleaseColorMap (CommData.AlienColorMap));
+ DestroyFont (CommData.AlienFont);
+ DestroyDrawable (ReleaseDrawable (CommData.AlienFrame));
+
+ DestroyContext (TextCacheContext);
+ DestroyDrawable (ReleaseDrawable (TextCacheFrame));
+
+ DestroyFont (PlayerFont);
+
+ // Some support code tests either of these to see if the
+ // game is currently in comm or encounter
+ CommData.ConversationPhrasesRes = 0;
+ CommData.ConversationPhrases = 0;
+ pCurInputState = 0;
+}
+
+void
+SetCommIntroMode (CommIntroMode newMode, TimeCount howLong)
+{
+ curIntroMode = newMode;
+ fadeTime = howLong;
+}
+
+COUNT
+InitCommunication (CONVERSATION which_comm)
+{
+ COUNT status;
+ LOCDATA *LocDataPtr;
+
+#ifdef DEBUG
+ if (disableInteractivity)
+ return 0;
+#endif
+
+
+ if (LastActivity & CHECK_LOAD)
+ {
+ LastActivity &= ~CHECK_LOAD;
+ if (which_comm != COMMANDER_CONVERSATION)
+ {
+ if (LOBYTE (LastActivity) == 0)
+ {
+ DrawSISFrame ();
+ }
+ else
+ {
+ ClearSISRect (DRAW_SIS_DISPLAY);
+ RepairSISBorder ();
+ }
+ DrawSISMessage (NULL);
+ if (inHQSpace ())
+ DrawHyperCoords (GLOBAL (ShipStamp.origin));
+ else if (GLOBAL (ip_planet) == 0)
+ DrawHyperCoords (CurStarDescPtr->star_pt);
+ else
+ DrawSISTitle (GLOBAL_SIS (PlanetName));
+ }
+ }
+
+
+ if (which_comm == URQUAN_DRONE_CONVERSATION)
+ {
+ status = URQUAN_DRONE_SHIP;
+ which_comm = URQUAN_CONVERSATION;
+ }
+ else
+ {
+ if (which_comm == YEHAT_REBEL_CONVERSATION)
+ {
+ status = YEHAT_REBEL_SHIP;
+ which_comm = YEHAT_CONVERSATION;
+ }
+ else
+ {
+ COUNT commToShip[] = {
+ RACE_SHIP_FOR_COMM
+ };
+ status = commToShip[which_comm];
+ if (status >= YEHAT_REBEL_SHIP) {
+ /* conversation exception, set to self */
+ status = HUMAN_SHIP;
+ }
+ }
+ StartSphereTracking (status);
+
+ if (which_comm == ORZ_CONVERSATION
+ || (which_comm == TALKING_PET_CONVERSATION
+ && (!GET_GAME_STATE (TALKING_PET_ON_SHIP)
+ || LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE))
+ || (which_comm != CHMMR_CONVERSATION
+ && which_comm != SYREEN_CONVERSATION
+ ))//&& CheckAlliance (status) == BAD_GUY))
+ BuildBattle (NPC_PLAYER_NUM);
+ }
+
+ LocDataPtr = init_race (
+ status != YEHAT_REBEL_SHIP ? which_comm :
+ YEHAT_REBEL_CONVERSATION);
+ if (LocDataPtr)
+ { // We make a copy here
+ CommData = *LocDataPtr;
+ }
+
+ if (GET_GAME_STATE (BATTLE_SEGUE) == 0)
+ {
+ // Not offered the chance to attack.
+ status = HAIL;
+ }
+ else if ((status = InitEncounter ()) == HAIL && LocDataPtr)
+ {
+ // The player chose to talk.
+ SET_GAME_STATE (BATTLE_SEGUE, 0);
+ }
+ else
+ {
+ // The player chose to attack.
+ status = ATTACK;
+ SET_GAME_STATE (BATTLE_SEGUE, 1);
+ }
+
+ if (status == HAIL)
+ {
+ HailAlien ();
+ }
+ else if (LocDataPtr)
+ { // only when comm initied successfully
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ (*CommData.post_encounter_func) (); // process states
+
+ (*CommData.uninit_encounter_func) (); // cleanup
+ }
+
+ status = 0;
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ // The Sa-Matra battle is skipped when Cyborg is enabled.
+ // Most likely because the Cyborg is too dumb to know what
+ // to do in this battle.
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE
+ && (GLOBAL (glob_flags) & CYBORG_ENABLED))
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+
+ // Clear the location flags
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 0);
+ status = (GET_GAME_STATE (BATTLE_SEGUE)
+ && GetHeadLink (&GLOBAL (npc_built_ship_q)));
+ if (status)
+ {
+ // Start combat
+ BuildBattle (RPG_PLAYER_NUM);
+ EncounterBattle ();
+ }
+ else
+ {
+ SET_GAME_STATE (BATTLE_SEGUE, 0);
+ }
+ }
+
+ UninitEncounter ();
+
+ return (status);
+}
+
+void
+RaceCommunication (void)
+{
+ COUNT i, status;
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+ HENCOUNTER hEncounter = 0;
+ CONVERSATION RaceComm[] =
+ {
+ RACE_COMMUNICATION
+ };
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ {
+ /* Going into talking pet conversation */
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ CloneShipFragment (SAMATRA_SHIP, &GLOBAL (npc_built_ship_q), 0);
+ InitCommunication (TALKING_PET_CONVERSATION);
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
+ {
+ GLOBAL (CurrentActivity) = WON_LAST_BATTLE;
+ }
+ return;
+ }
+ else if (NextActivity & CHECK_LOAD)
+ {
+ BYTE ec;
+
+ ec = GET_GAME_STATE (ESCAPE_COUNTER);
+
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI) == 1)
+ InitCommunication (SPATHI_CONVERSATION);
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) == 0)
+ InitCommunication (TALKING_PET_CONVERSATION);
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) &
+ ((1 << 4) | (1 << 5)))
+ // Communicate with the Ilwrath using a Hyperwave Broadcaster.
+ InitCommunication (ILWRATH_CONVERSATION);
+ else
+ InitCommunication (CHMMR_CONVERSATION);
+ if (GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
+ {
+ NextActivity = GLOBAL (CurrentActivity) & ~START_ENCOUNTER;
+ if (LOBYTE (NextActivity) == IN_INTERPLANETARY)
+ NextActivity |= START_INTERPLANETARY;
+ GLOBAL (CurrentActivity) |= CHECK_LOAD; /* fake a load game */
+ }
+
+ SET_GAME_STATE (ESCAPE_COUNTER, ec);
+ return;
+ }
+ else if (inHQSpace ())
+ {
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) >= 2)
+ {
+ InitCommunication (ARILOU_CONVERSATION);
+ return;
+ }
+ else
+ {
+ /* Encounter with a black globe in HS, prepare enemy ship list */
+ ENCOUNTER *EncounterPtr;
+
+ // The encounter globe that the flagship collided with is moved
+ // to the head of the queue in hyper.c:cleanup_hyperspace()
+ hEncounter = GetHeadEncounter ();
+ LockEncounter (hEncounter, &EncounterPtr);
+
+ for (i = 0; i < EncounterPtr->num_ships; ++i)
+ {
+ CloneShipFragment (EncounterPtr->race_id,
+ &GLOBAL (npc_built_ship_q),
+ EncounterPtr->ShipList[i].crew_level);
+ }
+
+ // XXX: Bug: CurStarDescPtr was abused to point within
+ // an ENCOUNTER struct, which is immediately unlocked
+ //CurStarDescPtr = (STAR_DESC*)&EncounterPtr->SD;
+ UnlockEncounter (hEncounter);
+ }
+ }
+
+ // First ship in the npc queue defines which alien race
+ // the player will be talking to
+ hStarShip = GetHeadLink (&GLOBAL (npc_built_ship_q));
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ i = FragPtr->race_id;
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+
+ status = InitCommunication (RaceComm[i]);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return;
+
+ if (i == CHMMR_SHIP)
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
+ {
+ /* if used destruct code in interplanetary */
+ if (i == SLYLANDRO_SHIP && status == 0)
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ }
+ else if (hEncounter)
+ {
+ /* Update HSpace encounter info, ships lefts, etc. */
+ BYTE i, NumShips;
+ ENCOUNTER *EncounterPtr;
+
+ LockEncounter (hEncounter, &EncounterPtr);
+
+ NumShips = CountLinks (&GLOBAL (npc_built_ship_q));
+ EncounterPtr->num_ships = NumShips;
+ EncounterPtr->flags |= ENCOUNTER_REFORMING;
+ if (status == 0)
+ EncounterPtr->flags |= ONE_SHOT_ENCOUNTER;
+
+ for (i = 0; i < NumShips; ++i)
+ {
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+ BRIEF_SHIP_INFO *BSIPtr;
+
+ hStarShip = GetStarShipFromIndex (&GLOBAL (npc_built_ship_q), i);
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ BSIPtr = &EncounterPtr->ShipList[i];
+ BSIPtr->race_id = FragPtr->race_id;
+ BSIPtr->crew_level = FragPtr->crew_level;
+ BSIPtr->max_crew = FragPtr->max_crew;
+ BSIPtr->max_energy = FragPtr->max_energy;
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+
+ UnlockEncounter (hEncounter);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ }
+}
+
+static void
+RedrawSubtitles (void)
+{
+ TEXT t;
+
+ if (!optSubtitles)
+ return;
+
+ if (SubtitleText.pStr)
+ {
+ t = SubtitleText;
+ add_text (1, &t);
+ }
+}
+
+static void
+ClearSubtitles (void)
+{
+ clear_subtitles = TRUE;
+ last_subtitle = NULL;
+ SubtitleText.pStr = NULL;
+ SubtitleText.CharCount = 0;
+}
+
+static void
+CheckSubtitles (void)
+{
+ const UNICODE *pStr;
+ POINT baseline;
+ TEXT_ALIGN align;
+
+ pStr = GetTrackSubtitle ();
+ baseline = CommData.AlienTextBaseline;
+ align = CommData.AlienTextAlign;
+
+ if (pStr != SubtitleText.pStr ||
+ SubtitleText.baseline.x != baseline.x ||
+ SubtitleText.baseline.y != baseline.y ||
+ SubtitleText.align != align)
+ { // Subtitles changed
+ clear_subtitles = TRUE;
+ // Baseline may be updated by the ZFP
+ SubtitleText.baseline = baseline;
+ SubtitleText.align = align;
+ // Make a note in the logs if the update was multiframe
+ if (SubtitleText.pStr == pStr)
+ {
+ log_add (log_Warning, "Dialog text and location changed out of sync");
+ }
+
+ SubtitleText.pStr = pStr;
+ // may have been cleared too
+ if (pStr)
+ SubtitleText.CharCount = (COUNT)~0;
+ else
+ SubtitleText.CharCount = 0;
+ }
+}
+
+void
+EnableTalkingAnim (BOOLEAN enable)
+{
+ if (enable)
+ CommData.AlienTalkDesc.AnimFlags &= ~PAUSE_TALKING;
+ else
+ CommData.AlienTalkDesc.AnimFlags |= PAUSE_TALKING;
+}
diff --git a/src/uqm/comm.h b/src/uqm/comm.h
new file mode 100644
index 0000000..e8f0182
--- /dev/null
+++ b/src/uqm/comm.h
@@ -0,0 +1,142 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_COMM_H_
+#define UQM_COMM_H_
+
+#include "globdata.h"
+#include "units.h"
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "commglue.h"
+ // for CONVERSATION
+
+#ifdef COMM_INTERNAL
+
+#define SLIDER_Y 107
+#define SLIDER_HEIGHT 15
+
+#include "commanim.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern LOCDATA CommData;
+
+static inline BOOLEAN
+haveTalkingAnim (void)
+{
+ return CommData.AlienTalkDesc.NumFrames > 0;
+}
+
+static inline BOOLEAN
+haveTransitionAnim (void)
+{
+ return CommData.AlienTransitionDesc.NumFrames > 0;
+}
+
+static inline BOOLEAN
+wantTalkingAnim (void)
+{
+ return !(CommData.AlienTalkDesc.AnimFlags & PAUSE_TALKING);
+}
+
+static inline void
+setRunTalkingAnim (void)
+{
+ CommData.AlienTalkDesc.AnimFlags |= WAIT_TALKING;
+}
+
+static inline void
+clearRunTalkingAnim (void)
+{
+ CommData.AlienTalkDesc.AnimFlags &= ~WAIT_TALKING;
+}
+
+static inline BOOLEAN
+runningTalkingAnim (void)
+{
+ return (CommData.AlienTalkDesc.AnimFlags & WAIT_TALKING);
+}
+
+static inline void
+setRunIntroAnim (void)
+{
+ CommData.AlienTransitionDesc.AnimFlags |= TALK_INTRO;
+}
+
+static inline BOOLEAN
+runningIntroAnim (void)
+{
+ return (CommData.AlienTransitionDesc.AnimFlags & TALK_INTRO);
+}
+
+static inline void
+setStopTalkingAnim (void)
+{
+ CommData.AlienTalkDesc.AnimFlags |= TALK_DONE;
+}
+
+static inline void
+clearStopTalkingAnim (void)
+{
+ CommData.AlienTalkDesc.AnimFlags &= ~TALK_DONE;
+}
+
+static inline BOOLEAN
+signaledStopTalkingAnim (void)
+{
+ return CommData.AlienTalkDesc.AnimFlags & TALK_DONE;
+}
+
+#endif
+
+#define TEXT_X_OFFS 1
+#define TEXT_Y_OFFS 1
+#define SIS_TEXT_WIDTH (SIS_SCREEN_WIDTH - (TEXT_X_OFFS << 1))
+
+extern void init_communication (void);
+extern void uninit_communication (void);
+
+extern COUNT InitCommunication (CONVERSATION which_comm);
+extern void RaceCommunication (void);
+
+#define WAIT_TRACK_ALL ((COUNT)~0)
+extern void AlienTalkSegue (COUNT wait_track);
+BOOLEAN getLineWithinWidth(TEXT *pText, const char **startNext,
+ SIZE maxWidth, COUNT maxChars);
+
+extern RECT CommWndRect; /* comm window rect */
+
+typedef enum
+{
+ CIM_CROSSFADE_SPACE,
+ CIM_CROSSFADE_WINDOW,
+ CIM_CROSSFADE_SCREEN,
+ CIM_FADE_IN_SCREEN,
+
+ CIM_DEFAULT = CIM_CROSSFADE_SPACE,
+} CommIntroMode;
+extern void SetCommIntroMode (CommIntroMode, TimeCount howLong);
+
+extern void EnableTalkingAnim (BOOLEAN enable);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_COMM_H_ */
diff --git a/src/uqm/comm/Makeinfo b/src/uqm/comm/Makeinfo
new file mode 100644
index 0000000..3f87e5b
--- /dev/null
+++ b/src/uqm/comm/Makeinfo
@@ -0,0 +1,4 @@
+uqm_SUBDIRS="arilou blackur chmmr comandr druuge ilwrath melnorm mycon
+ orz pkunk rebel shofixt slyhome slyland spahome spathi starbas supox
+ syreen talkpet thradd umgah urquan utwig vux yehat zoqfot"
+uqm_HFILES="commall.h"
diff --git a/src/uqm/comm/arilou/Makeinfo b/src/uqm/comm/arilou/Makeinfo
new file mode 100644
index 0000000..27cb053
--- /dev/null
+++ b/src/uqm/comm/arilou/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="arilouc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/arilou/arilouc.c b/src/uqm/comm/arilou/arilouc.c
new file mode 100644
index 0000000..13f2d72
--- /dev/null
+++ b/src/uqm/comm/arilou/arilouc.c
@@ -0,0 +1,855 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/gameev.h"
+
+
+static LOCDATA arilou_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ ARILOU_PMAP_ANIM, /* AlienFrame */
+ ARILOU_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ ARILOU_COLOR_MAP, /* AlienColorMap */
+ ARILOU_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ ARILOU_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 20, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 4, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 1) | (1L << 16)
+ },
+ {
+ 13, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 0) | (1L << 16)
+ },
+ {
+ 22, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1L << 16)
+ },
+ {
+ 31, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4)
+ },
+ {
+ 40, /* StartIndex */
+ 10, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 3)
+ },
+ {
+ 50, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 7)
+ },
+ {
+ 59, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 67, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5)
+ },
+ {
+ 76, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 9)
+ },
+ {
+ 85, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 8)
+ },
+ {
+ 94, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 103, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 13)
+ },
+ {
+ 112, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 121, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 11)
+ },
+ {
+ 129, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1L << 15)
+ },
+ {
+ 138, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 14)
+ },
+ {
+ 146, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 0) | (1 << 1) | (1 << 2)
+ },
+ { /* Hands moving (right up) */
+ 155, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 19), /* BlockMask */
+ },
+ { /* Hands moving (left up) */
+ 157, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 19), /* BlockMask */
+ },
+ { /* Stars flashing next to the head */
+ 159, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 17) | (1 << 18), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, bye_angry_space))
+ NPCPhrase (GOODBYE_ANGRY_SPACE);
+ else if (PLAYER_SAID (R, bye_friendly_space))
+ NPCPhrase (GOODBYE_FRIENDLY_SPACE);
+ else if (PLAYER_SAID (R, bye_friendly_homeworld))
+ NPCPhrase (GOODBYE_FRDLY_HOMEWORLD);
+ else if (PLAYER_SAID (R, lets_fight))
+ NPCPhrase (NO_FIGHT);
+ else if (PLAYER_SAID (R, bug_eyed_fruitcakes))
+ {
+ NPCPhrase (WE_NEVER_FRIENDS);
+
+ SET_GAME_STATE (ARILOU_MANNER, 2);
+ }
+ else if (PLAYER_SAID (R, best_if_i_killed_you))
+ {
+ NPCPhrase (WICKED_HUMAN);
+
+ SET_GAME_STATE (ARILOU_MANNER, 2);
+ }
+}
+
+static void
+ArilouHome (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[4];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = pStr[3] = 0;
+ if (PLAYER_SAID (R, confused_by_hello))
+ NPCPhrase (CONFUSED_RESPONSE);
+ else if (PLAYER_SAID (R, happy_by_hello))
+ NPCPhrase (HAPPY_RESPONSE);
+ else if (PLAYER_SAID (R, miffed_by_hello))
+ NPCPhrase (MIFFED_RESPONSE);
+ else if (PLAYER_SAID (R, ok_lets_be_friends))
+ NPCPhrase (NO_ALLY_BUT_MUCH_GIVE);
+ else if (PLAYER_SAID (R, what_about_war))
+ {
+ NPCPhrase (ABOUT_WAR);
+
+ SET_GAME_STATE (ARILOU_STACK_1, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_urquan))
+ {
+ NPCPhrase (ABOUT_URQUAN);
+
+ SET_GAME_STATE (ARILOU_STACK_1, 2);
+ }
+ else if (PLAYER_SAID (R, tell_arilou_about_tpet))
+ {
+ NPCPhrase (BAD_NEWS_ABOUT_TPET);
+
+ LastStack = 1;
+ SET_GAME_STATE (ARILOU_STACK_2, 1);
+ }
+ else if (PLAYER_SAID (R, what_do_about_tpet))
+ {
+ NPCPhrase (DANGEROUS_BUT_USEFUL);
+
+ LastStack = 1;
+ SET_GAME_STATE (ARILOU_STACK_2, 2);
+ }
+ else if (PLAYER_SAID (R, learned_about_umgah))
+ {
+ if (GET_GAME_STATE (ARILOU_CHECKED_UMGAH) != 2)
+ NPCPhrase (NO_NEWS_YET);
+ else
+ {
+ NPCPhrase (UMGAH_UNDER_COMPULSION);
+
+ LastStack = 1;
+ }
+
+ DISABLE_PHRASE (learned_about_umgah);
+ }
+ else if (PLAYER_SAID (R, umgah_acting_weird))
+ {
+ NPCPhrase (WELL_GO_CHECK);
+
+ SET_GAME_STATE (ARILOU_CHECKED_UMGAH, 1);
+ AddEvent (RELATIVE_EVENT, 0, 10, 0, ARILOU_UMGAH_CHECK);
+ DISABLE_PHRASE (umgah_acting_weird);
+ }
+ else if (PLAYER_SAID (R, what_do_now))
+ {
+ NPCPhrase (GO_FIND_OUT);
+
+ SET_GAME_STATE (ARILOU_CHECKED_UMGAH, 3);
+ }
+ else if (PLAYER_SAID (R, what_did_on_earth))
+ {
+ NPCPhrase (DID_THIS);
+
+ LastStack = 2;
+ SET_GAME_STATE (ARILOU_STACK_3, 1);
+ }
+ else if (PLAYER_SAID (R, why_did_this))
+ {
+ NPCPhrase (IDF_PARASITES);
+
+ LastStack = 2;
+ SET_GAME_STATE (ARILOU_STACK_3, 2);
+ }
+ else if (PLAYER_SAID (R, tell_more))
+ {
+ NPCPhrase (NOT_NOW);
+
+ LastStack = 2;
+ SET_GAME_STATE (ARILOU_STACK_3, 3);
+ }
+ else if (PLAYER_SAID (R, what_give_me))
+ {
+ NPCPhrase (ABOUT_PORTAL);
+
+ LastStack = 3;
+ SET_GAME_STATE (KNOW_ARILOU_WANT_WRECK, 1);
+
+ R = about_portal_again;
+ DISABLE_PHRASE (what_give_me);
+ }
+ else if (PLAYER_SAID (R, what_about_tpet))
+ {
+ NPCPhrase (ABOUT_TPET);
+
+ SET_GAME_STATE (ARILOU_STACK_4, 1);
+ }
+ else if (PLAYER_SAID (R, about_portal_again))
+ {
+ NPCPhrase (PORTAL_AGAIN);
+
+ DISABLE_PHRASE (about_portal_again);
+ }
+ else if (PLAYER_SAID (R, got_it))
+ {
+ if (GET_GAME_STATE (ARILOU_HOME_VISITS) == 1)
+ NPCPhrase (CLEVER_HUMAN);
+ NPCPhrase (GIVE_PORTAL);
+
+ SET_GAME_STATE (PORTAL_KEY_ON_SHIP, 0);
+ SET_GAME_STATE (PORTAL_SPAWNER, 1);
+ SET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP, 1);
+ }
+#ifdef NEVER
+ else if (PLAYER_SAID (R, got_tpet))
+ {
+ NPCPhrase (OK_GOT_TPET);
+
+ SET_GAME_STATE (ARILOU_STACK_2, 1);
+ }
+#endif /* NEVER */
+
+ switch (GET_GAME_STATE (ARILOU_STACK_1))
+ {
+ case 0:
+ pStr[0] = what_about_war;
+ break;
+ case 1:
+ pStr[0] = what_about_urquan;
+ break;
+ }
+ if (GET_GAME_STATE (TALKING_PET))
+ {
+#ifdef NEVER
+ if (GET_GAME_STATE (ARILOU_STACK_2) == 0)
+ pStr[1] = got_tpet;
+#endif /* NEVER */
+ }
+ else
+ {
+ if (GET_GAME_STATE (TALKING_PET_VISITS))
+ {
+ switch (GET_GAME_STATE (ARILOU_STACK_2))
+ {
+ case 0:
+ pStr[1] = tell_arilou_about_tpet;
+ break;
+ case 1:
+ pStr[1] = what_do_about_tpet;
+ break;
+ }
+ }
+ else if (GET_GAME_STATE (KNOW_UMGAH_ZOMBIES))
+ {
+ if (!GET_GAME_STATE (ARILOU_CHECKED_UMGAH))
+ pStr[1] = umgah_acting_weird;
+ else if (PHRASE_ENABLED (learned_about_umgah) && PHRASE_ENABLED (umgah_acting_weird))
+ pStr[1] = learned_about_umgah;
+ else if (GET_GAME_STATE (ARILOU_CHECKED_UMGAH) == 2)
+ pStr[1] = what_do_now;
+ }
+ }
+ switch (GET_GAME_STATE (ARILOU_STACK_3))
+ {
+ case 0:
+ pStr[2] = what_did_on_earth;
+ break;
+ case 1:
+ pStr[2] = why_did_this;
+ break;
+ case 2:
+ pStr[2] = tell_more;
+ break;
+ }
+ if (!GET_GAME_STATE (KNOW_ARILOU_WANT_WRECK))
+ pStr[3] = what_give_me;
+ else if (!GET_GAME_STATE (ARILOU_STACK_4))
+ pStr[3] = what_about_tpet;
+
+ if (pStr[LastStack])
+ Response (pStr[LastStack], ArilouHome);
+ for (i = 0; i < 4; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ Response (pStr[i], ArilouHome);
+ }
+
+ if (GET_GAME_STATE (KNOW_ARILOU_WANT_WRECK))
+ {
+ if (GET_GAME_STATE (PORTAL_KEY_ON_SHIP))
+ Response (got_it, ArilouHome);
+ else if (PHRASE_ENABLED (about_portal_again) && !GET_GAME_STATE (PORTAL_SPAWNER))
+ Response (about_portal_again, ArilouHome);
+ }
+ if (GET_GAME_STATE (ARILOU_MANNER) != 3)
+ Response (best_if_i_killed_you, ExitConversation);
+ Response (bye_friendly_homeworld, ExitConversation);
+}
+
+static void
+AngryHomeArilou (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, invaders_from_mars))
+ {
+ NPCPhrase (HAD_OUR_REASONS);
+
+ DISABLE_PHRASE (invaders_from_mars);
+ }
+ else if (PLAYER_SAID (R, why_should_i_trust))
+ {
+ NPCPhrase (TRUST_BECAUSE);
+
+ DISABLE_PHRASE (why_should_i_trust);
+ }
+ else if (PLAYER_SAID (R, what_about_interference))
+ {
+ NPCPhrase (INTERFERENCE_NECESSARY);
+
+ DISABLE_PHRASE (what_about_interference);
+ }
+ else if (PLAYER_SAID (R, i_just_like_to_leave))
+ {
+ NPCPhrase (SORRY_NO_LEAVE);
+
+ DISABLE_PHRASE (i_just_like_to_leave);
+ }
+
+ if (PHRASE_ENABLED (invaders_from_mars))
+ Response (invaders_from_mars, AngryHomeArilou);
+ else
+ {
+ Response (bug_eyed_fruitcakes, ExitConversation);
+ }
+ if (PHRASE_ENABLED (why_should_i_trust))
+ Response (why_should_i_trust, AngryHomeArilou);
+ else if (PHRASE_ENABLED (what_about_interference))
+ Response (what_about_interference, AngryHomeArilou);
+ Response (ok_lets_be_friends, ArilouHome);
+ Response (i_just_like_to_leave, AngryHomeArilou);
+}
+
+static void
+AngrySpaceArilou (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, im_sorry))
+ {
+ NPCPhrase (APOLOGIZE_AT_HOMEWORLD);
+
+ DISABLE_PHRASE (im_sorry);
+ }
+
+ Response (lets_fight, ExitConversation);
+ if (PHRASE_ENABLED (im_sorry))
+ {
+ Response (im_sorry, AngrySpaceArilou);
+ }
+ Response (bye_angry_space, ExitConversation);
+}
+
+static void
+FriendlySpaceArilou (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, confused_by_hello))
+ NPCPhrase (CONFUSED_RESPONSE);
+ else if (PLAYER_SAID (R, happy_by_hello))
+ NPCPhrase (HAPPY_RESPONSE);
+ else if (PLAYER_SAID (R, miffed_by_hello))
+ NPCPhrase (MIFFED_RESPONSE);
+ else if (PLAYER_SAID (R, whats_up_1)
+ || PLAYER_SAID (R, whats_up_2))
+ {
+ NumVisits = GET_GAME_STATE (ARILOU_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ARILOU_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_2);
+ }
+ else if (PLAYER_SAID (R, why_you_here))
+ {
+ NPCPhrase (LEARN_THINGS);
+
+ SET_GAME_STATE (ARILOU_STACK_5, 1);
+ }
+ else if (PLAYER_SAID (R, what_things))
+ {
+ NPCPhrase (THESE_THINGS);
+
+ SET_GAME_STATE (ARILOU_STACK_5, 2);
+ }
+ else if (PLAYER_SAID (R, why_do_it))
+ {
+ NPCPhrase (DO_IT_BECAUSE);
+
+ SET_GAME_STATE (ARILOU_STACK_5, 3);
+ }
+ else if (PLAYER_SAID (R, give_me_info_1)
+ || PLAYER_SAID (R, give_me_info_2))
+ {
+ NumVisits = GET_GAME_STATE (ARILOU_HINTS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ARILOU_HINTS_1);
+ break;
+ case 1:
+ NPCPhrase (ARILOU_HINTS_2);
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) < 2)
+ {
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 2);
+ }
+ break;
+ case 2:
+ NPCPhrase (ARILOU_HINTS_3);
+ SET_GAME_STATE (KNOW_URQUAN_STORY, 1);
+ SET_GAME_STATE (KNOW_KOHR_AH_STORY, 1);
+ break;
+ case 3:
+ NPCPhrase (ARILOU_HINTS_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ARILOU_HINTS, NumVisits);
+
+ DISABLE_PHRASE (give_me_info_2);
+ }
+
+ switch (GET_GAME_STATE (ARILOU_STACK_5))
+ {
+ case 0:
+ Response (why_you_here, FriendlySpaceArilou);
+ break;
+ case 1:
+ Response (what_things, FriendlySpaceArilou);
+ break;
+ case 2:
+ Response (why_do_it, FriendlySpaceArilou);
+ break;
+ }
+ if (PHRASE_ENABLED (whats_up_2))
+ {
+ if (GET_GAME_STATE (ARILOU_INFO) == 0)
+ Response (whats_up_1, FriendlySpaceArilou);
+ else
+ Response (whats_up_2, FriendlySpaceArilou);
+ }
+ if (PHRASE_ENABLED (give_me_info_2))
+ {
+ if (GET_GAME_STATE (ARILOU_HINTS) == 0)
+ Response (give_me_info_1, FriendlySpaceArilou);
+ else
+ Response (give_me_info_2, FriendlySpaceArilou);
+ }
+ Response (bye_friendly_space, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits, Manner;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+ else if (!GET_GAME_STATE (MET_ARILOU))
+ {
+ RESPONSE_FUNC RespFunc;
+
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ NPCPhrase (INIT_HELLO);
+ RespFunc = (RESPONSE_FUNC)FriendlySpaceArilou;
+ }
+ else
+ {
+ NPCPhrase (FRDLY_HOMEWORLD_HELLO_1);
+ RespFunc = (RESPONSE_FUNC)ArilouHome;
+ SET_GAME_STATE (ARILOU_HOME_VISITS, 1);
+ }
+ Response (confused_by_hello, RespFunc);
+ Response (happy_by_hello, RespFunc);
+ Response (miffed_by_hello, RespFunc);
+ SET_GAME_STATE (MET_ARILOU, 1);
+ return;
+ }
+
+ Manner = GET_GAME_STATE (ARILOU_MANNER);
+ if (Manner == 2)
+ {
+ NumVisits = GET_GAME_STATE (ARILOU_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_GOODBYE_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_GOODBYE_2);
+ break;
+ case 2:
+ NPCPhrase (HOSTILE_GOODBYE_3);
+ break;
+ case 3:
+ NPCPhrase (HOSTILE_GOODBYE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ARILOU_VISITS, NumVisits);
+
+ setSegue (Segue_peace);
+ }
+ else if (Manner == 1)
+ {
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1)
+ {
+ NPCPhrase (INIT_ANGRY_HWLD_HELLO);
+ SET_GAME_STATE (ARILOU_HOME_VISITS, 1);
+
+ AngryHomeArilou ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (ARILOU_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ANGRY_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ANGRY_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ARILOU_VISITS, NumVisits);
+
+ AngrySpaceArilou ((RESPONSE_REF)0);
+ }
+ }
+ else
+ {
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ NumVisits = GET_GAME_STATE (ARILOU_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ARILOU_VISITS, NumVisits);
+
+ FriendlySpaceArilou ((RESPONSE_REF)0);
+ }
+ else
+ {
+ if (!GET_GAME_STATE (PORTAL_SPAWNER)
+ && GET_GAME_STATE (KNOW_ARILOU_WANT_WRECK))
+ {
+ NumVisits = GET_GAME_STATE (NO_PORTAL_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GOT_PART_YET_1);
+ break;
+ case 1:
+ NPCPhrase (GOT_PART_YET_1);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (NO_PORTAL_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (ARILOU_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FRDLY_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (FRDLY_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (FRDLY_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (FRDLY_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ARILOU_HOME_VISITS, NumVisits);
+ }
+
+ ArilouHome ((RESPONSE_REF)0);
+ }
+ }
+}
+
+static COUNT
+uninit_arilou (void)
+{
+ return (0);
+}
+
+static void
+post_arilou_enc (void)
+{
+ BYTE Manner;
+
+ if (getSegue () == Segue_hostile
+ && (Manner = GET_GAME_STATE (ARILOU_MANNER)) != 2)
+ {
+ SET_GAME_STATE (ARILOU_MANNER, 1);
+ if (Manner != 1)
+ {
+ SET_GAME_STATE (ARILOU_VISITS, 0);
+ SET_GAME_STATE (ARILOU_HOME_VISITS, 0);
+ }
+ }
+
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1
+ && GET_GAME_STATE (ARILOU_HOME_VISITS) <= 1)
+ {
+ SET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES, 1);
+ SET_GAME_STATE (UMGAH_VISITS, 0);
+ SET_GAME_STATE (UMGAH_HOME_VISITS, 0);
+
+ if (GET_GAME_STATE (ARILOU_MANNER) < 2)
+ {
+ SET_GAME_STATE (ARILOU_MANNER, 3);
+ }
+ }
+}
+
+LOCDATA*
+init_arilou_comm (void)
+{
+ LOCDATA *retval;
+
+ arilou_desc.init_encounter_func = Intro;
+ arilou_desc.post_encounter_func = post_arilou_enc;
+ arilou_desc.uninit_encounter_func = uninit_arilou;
+
+ arilou_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ arilou_desc.AlienTextBaseline.y = 0;
+ arilou_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1
+ || GET_GAME_STATE (ARILOU_MANNER) == 3
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &arilou_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/arilou/resinst.h b/src/uqm/comm/arilou/resinst.h
new file mode 100644
index 0000000..991ea37
--- /dev/null
+++ b/src/uqm/comm/arilou/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ARILOU_COLOR_MAP "comm.arilou.colortable"
+#define ARILOU_CONVERSATION_PHRASES "comm.arilou.dialogue"
+#define ARILOU_FONT "comm.arilou.font"
+#define ARILOU_MUSIC "comm.arilou.music"
+#define ARILOU_PMAP_ANIM "comm.arilou.graphics"
diff --git a/src/uqm/comm/arilou/strings.h b/src/uqm/comm/arilou/strings.h
new file mode 100644
index 0000000..1f80468
--- /dev/null
+++ b/src/uqm/comm/arilou/strings.h
@@ -0,0 +1,123 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ARILOU_STRINGS_H
+#define ARILOU_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ INIT_HELLO,
+ confused_by_hello,
+ CONFUSED_RESPONSE,
+ happy_by_hello,
+ HAPPY_RESPONSE,
+ miffed_by_hello,
+ MIFFED_RESPONSE,
+ FRIENDLY_SPACE_HELLO_1,
+ FRIENDLY_SPACE_HELLO_2,
+ FRIENDLY_SPACE_HELLO_3,
+ FRIENDLY_SPACE_HELLO_4,
+ FRDLY_HOMEWORLD_HELLO_1,
+ FRDLY_HOMEWORLD_HELLO_2,
+ FRDLY_HOMEWORLD_HELLO_3,
+ FRDLY_HOMEWORLD_HELLO_4,
+ whats_up_1,
+ whats_up_2,
+ GENERAL_INFO_1,
+ GENERAL_INFO_2,
+ GENERAL_INFO_3,
+ GENERAL_INFO_4,
+ why_you_here,
+ LEARN_THINGS,
+ what_things,
+ THESE_THINGS,
+ why_do_it,
+ DO_IT_BECAUSE,
+ give_me_info_1,
+ ARILOU_HINTS_1,
+ give_me_info_2,
+ ARILOU_HINTS_2,
+ ARILOU_HINTS_3,
+ ARILOU_HINTS_4,
+ bye_friendly_space,
+ GOODBYE_FRIENDLY_SPACE,
+ GOT_PART_YET_1,
+ GOT_PART_YET_2,
+ INIT_ANGRY_HWLD_HELLO,
+ invaders_from_mars,
+ HAD_OUR_REASONS,
+ bug_eyed_fruitcakes,
+ WE_NEVER_FRIENDS,
+ ok_lets_be_friends,
+ NO_ALLY_BUT_MUCH_GIVE,
+ why_should_i_trust,
+ TRUST_BECAUSE,
+ what_about_interference,
+ INTERFERENCE_NECESSARY,
+ i_just_like_to_leave,
+ SORRY_NO_LEAVE,
+ what_about_war,
+ ABOUT_WAR,
+ what_about_urquan,
+ ABOUT_URQUAN,
+ best_if_i_killed_you,
+ WICKED_HUMAN,
+ what_did_on_earth,
+ DID_THIS,
+ why_did_this,
+ IDF_PARASITES,
+ tell_more,
+ NOT_NOW,
+ umgah_acting_weird,
+ learned_about_umgah,
+ WELL_GO_CHECK,
+ NO_NEWS_YET,
+ UMGAH_UNDER_COMPULSION,
+ what_do_now,
+ GO_FIND_OUT,
+ tell_arilou_about_tpet,
+ BAD_NEWS_ABOUT_TPET,
+ what_do_about_tpet,
+ DANGEROUS_BUT_USEFUL,
+ what_give_me,
+ ABOUT_PORTAL,
+ what_about_tpet,
+ ABOUT_TPET,
+ about_portal_again,
+ PORTAL_AGAIN,
+ got_it,
+ CLEVER_HUMAN,
+ GIVE_PORTAL,
+ bye_friendly_homeworld,
+ GOODBYE_FRDLY_HOMEWORLD,
+ HOSTILE_GOODBYE_1,
+ HOSTILE_GOODBYE_2,
+ HOSTILE_GOODBYE_3,
+ HOSTILE_GOODBYE_4,
+ ANGRY_SPACE_HELLO_1,
+ ANGRY_SPACE_HELLO_2,
+ lets_fight,
+ NO_FIGHT,
+ im_sorry,
+ APOLOGIZE_AT_HOMEWORLD,
+ bye_angry_space,
+ GOODBYE_ANGRY_SPACE,
+ OUT_TAKES,
+};
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/blackur/Makeinfo b/src/uqm/comm/blackur/Makeinfo
new file mode 100644
index 0000000..1927a56
--- /dev/null
+++ b/src/uqm/comm/blackur/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="blackurc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/blackur/blackurc.c b/src/uqm/comm/blackur/blackurc.c
new file mode 100644
index 0000000..1b47a7b
--- /dev/null
+++ b/src/uqm/comm/blackur/blackurc.c
@@ -0,0 +1,567 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+static LOCDATA blackurq_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ BLACKURQ_PMAP_ANIM, /* AlienFrame */
+ BLACKURQ_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ BLACKURQ_COLOR_MAP, /* AlienColorMap */
+ BLACKURQ_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ BLACKURQ_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 8, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 7, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 13, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 20, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 23, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 26, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 29, /* StartIndex */
+ 4, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND / 10, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 33, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 38, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 1, /* StartIndex */
+ 2, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 6, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 2, /* StartIndex */
+ 5, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+CombatIsInevitable (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, bye))
+ {
+ if (GET_GAME_STATE (KOHR_AH_BYES) == 0)
+ NPCPhrase (GOODBYE_AND_DIE);
+ else
+ NPCPhrase (DIE_HUMAN /* GOODBYE_AND_DIE_2 */);
+
+ SET_GAME_STATE (KOHR_AH_BYES, 1);
+ }
+ else if (PLAYER_SAID (R, guess_thats_all))
+ NPCPhrase (THEN_DIE);
+ else if (PLAYER_SAID (R, what_are_you_hovering_over))
+ {
+ NPCPhrase (BONE_PILE);
+
+ SET_GAME_STATE (KOHR_AH_INFO, 1);
+ }
+ else if (PLAYER_SAID (R, you_sure_are_creepy))
+ {
+ NPCPhrase (YES_CREEPY);
+
+ SET_GAME_STATE (KOHR_AH_INFO, 2);
+ }
+ else if (PLAYER_SAID (R, stop_that_gross_blinking))
+ {
+ NPCPhrase (DIE_HUMAN);
+
+ SET_GAME_STATE (KOHR_AH_INFO, 3);
+ }
+ else if (PLAYER_SAID (R, threat_1)
+ || PLAYER_SAID (R, threat_2)
+ || PLAYER_SAID (R, threat_3)
+ || PLAYER_SAID (R, threat_4))
+ {
+ NumVisits = GET_GAME_STATE (KOHR_AH_REASONS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (RESISTANCE_IS_USELESS_1);
+ break;
+ case 1:
+ NPCPhrase (RESISTANCE_IS_USELESS_2);
+ break;
+ case 2:
+ NPCPhrase (RESISTANCE_IS_USELESS_3);
+ break;
+ case 3:
+ NPCPhrase (RESISTANCE_IS_USELESS_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (KOHR_AH_REASONS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, plead_1)
+ || PLAYER_SAID (R, plead_2)
+ || PLAYER_SAID (R, plead_3)
+ || PLAYER_SAID (R, plead_4))
+ {
+ NumVisits = GET_GAME_STATE (KOHR_AH_PLEAD);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (PLEADING_IS_USELESS_1);
+ break;
+ case 1:
+ NPCPhrase (PLEADING_IS_USELESS_2);
+ break;
+ case 2:
+ // This response disabled due to lack of a speech file.
+ // NPCPhrase (PLEADING_IS_USELESS_3);
+ // break;
+ case 3:
+ NPCPhrase (PLEADING_IS_USELESS_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (KOHR_AH_PLEAD, NumVisits);
+ }
+ else if (PLAYER_SAID (R, why_kill_all_1)
+ || PLAYER_SAID (R, why_kill_all_2)
+ || PLAYER_SAID (R, why_kill_all_3)
+ || PLAYER_SAID (R, why_kill_all_4))
+ {
+ NumVisits = GET_GAME_STATE (KOHR_AH_REASONS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (KILL_BECAUSE_1);
+ break;
+ case 1:
+ NPCPhrase (KILL_BECAUSE_2);
+ break;
+ case 2:
+ NPCPhrase (KILL_BECAUSE_3);
+ break;
+ case 3:
+ NPCPhrase (KILL_BECAUSE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (KOHR_AH_REASONS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, please_dont_kill_1)
+ || PLAYER_SAID (R, please_dont_kill_2)
+ || PLAYER_SAID (R, please_dont_kill_3)
+ || PLAYER_SAID (R, please_dont_kill_4))
+ {
+ NumVisits = GET_GAME_STATE (KOHR_AH_PLEAD);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (WILL_KILL_1);
+ break;
+ case 1:
+ NPCPhrase (WILL_KILL_2);
+ break;
+ case 2:
+ NPCPhrase (WILL_KILL_3);
+ break;
+ case 3:
+ NPCPhrase (WILL_KILL_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (KOHR_AH_PLEAD, NumVisits);
+ }
+ else if (PLAYER_SAID (R, bye_frenzy_1)
+ || PLAYER_SAID (R, bye_frenzy_2)
+ || PLAYER_SAID (R, bye_frenzy_3)
+ || PLAYER_SAID (R, bye_frenzy_4))
+ {
+ NumVisits = GET_GAME_STATE (KOHR_AH_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GOODBYE_AND_DIE_FRENZY_1);
+ break;
+ case 1:
+ NPCPhrase (GOODBYE_AND_DIE_FRENZY_2);
+ break;
+ case 2:
+ NPCPhrase (GOODBYE_AND_DIE_FRENZY_3);
+ break;
+ case 3:
+ NPCPhrase (GOODBYE_AND_DIE_FRENZY_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (KOHR_AH_INFO, NumVisits);
+ }
+}
+
+static void
+Frenzy (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ switch (GET_GAME_STATE (KOHR_AH_REASONS))
+ {
+ case 0:
+ Response (why_kill_all_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (why_kill_all_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (why_kill_all_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (why_kill_all_4, CombatIsInevitable);
+ break;
+ }
+ switch (GET_GAME_STATE (KOHR_AH_PLEAD))
+ {
+ case 0:
+ Response (please_dont_kill_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (please_dont_kill_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (please_dont_kill_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (please_dont_kill_4, CombatIsInevitable);
+ break;
+ }
+ switch (GET_GAME_STATE (KOHR_AH_INFO))
+ {
+ case 0:
+ Response (bye_frenzy_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (bye_frenzy_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (bye_frenzy_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (bye_frenzy_4, CombatIsInevitable);
+ break;
+ }
+}
+
+static void
+KohrAhStory (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, key_phrase))
+ {
+ NPCPhrase (RESPONSE_TO_KEY_PHRASE);
+
+ SET_GAME_STATE (KNOW_KOHR_AH_STORY, 2);
+ }
+ else if (PLAYER_SAID (R, why_do_you_destroy))
+ {
+ NPCPhrase (WE_WERE_SLAVES);
+
+ DISABLE_PHRASE (why_do_you_destroy);
+ }
+ else if (PLAYER_SAID (R, relationship_with_urquan))
+ {
+ NPCPhrase (WE_ARE_URQUAN_TOO);
+
+ DISABLE_PHRASE (relationship_with_urquan);
+ }
+ else if (PLAYER_SAID (R, what_about_culture))
+ {
+ NPCPhrase (BONE_GARDENS);
+
+ DISABLE_PHRASE (what_about_culture);
+ }
+ else if (PLAYER_SAID (R, how_leave_me_alone))
+ {
+ NPCPhrase (YOU_DIE);
+
+ DISABLE_PHRASE (how_leave_me_alone);
+ }
+
+ if (PHRASE_ENABLED (why_do_you_destroy))
+ Response (why_do_you_destroy, KohrAhStory);
+ if (PHRASE_ENABLED (relationship_with_urquan))
+ Response (relationship_with_urquan, KohrAhStory);
+ if (PHRASE_ENABLED (what_about_culture))
+ Response (what_about_culture, KohrAhStory);
+ if (PHRASE_ENABLED (how_leave_me_alone))
+ Response (how_leave_me_alone, KohrAhStory);
+ Response (guess_thats_all, CombatIsInevitable);
+}
+
+static void
+DieHuman (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ switch (GET_GAME_STATE (KOHR_AH_REASONS))
+ {
+ case 0:
+ Response (threat_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (threat_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (threat_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (threat_4, CombatIsInevitable);
+ break;
+ }
+ if (GET_GAME_STATE (KNOW_KOHR_AH_STORY) == 1)
+ {
+ Response (key_phrase, KohrAhStory);
+ }
+ switch (GET_GAME_STATE (KOHR_AH_INFO))
+ {
+ case 0:
+ Response (what_are_you_hovering_over, CombatIsInevitable);
+ break;
+ case 1:
+ Response (you_sure_are_creepy, CombatIsInevitable);
+ break;
+ case 2:
+ Response (stop_that_gross_blinking, CombatIsInevitable);
+ break;
+ }
+ switch (GET_GAME_STATE (KOHR_AH_PLEAD))
+ {
+ case 0:
+ Response (plead_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (plead_2, CombatIsInevitable);
+ break;
+ case 2:
+ // This response disabled due to lack of a speech file.
+ // Response (plead_3, CombatIsInevitable);
+ // break;
+ case 3:
+ Response (plead_4, CombatIsInevitable);
+ break;
+ }
+ Response (bye, CombatIsInevitable);
+}
+
+static void
+Intro (void)
+{
+ DWORD GrpOffs;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (KOHR_AH_KILLED_ALL))
+ {
+ NPCPhrase (GAME_OVER_DUDE);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (!GET_GAME_STATE (KOHR_AH_SENSES_EVIL)
+ && GET_GAME_STATE (TALKING_PET_ON_SHIP))
+ {
+ NPCPhrase (SENSE_EVIL);
+ SET_GAME_STATE (KOHR_AH_SENSES_EVIL, 1);
+ }
+
+ GrpOffs = GET_GAME_STATE_32 (SAMATRA_GRPOFFS0);
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && GLOBAL (BattleGroupRef)
+ && GLOBAL (BattleGroupRef) == GrpOffs)
+ {
+ NPCPhrase (HELLO_SAMATRA);
+
+ SET_GAME_STATE (AWARE_OF_SAMATRA, 1);
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (KOHR_AH_VISITS);
+ if (GET_GAME_STATE (KOHR_AH_FRENZY))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (WE_KILL_ALL_1);
+ break;
+ case 1:
+ NPCPhrase (WE_KILL_ALL_2);
+ break;
+ case 2:
+ NPCPhrase (WE_KILL_ALL_3);
+ break;
+ case 3:
+ NPCPhrase (WE_KILL_ALL_4);
+ --NumVisits;
+ break;
+ }
+
+ Frenzy ((RESPONSE_REF)0);
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_AND_DIE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_AND_DIE_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_AND_DIE_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_AND_DIE_4);
+ --NumVisits;
+ break;
+ }
+
+ DieHuman ((RESPONSE_REF)0);
+ }
+ SET_GAME_STATE (KOHR_AH_VISITS, NumVisits);
+ }
+}
+
+static COUNT
+uninit_blackurq (void)
+{
+ return (0);
+}
+
+static void
+post_blackurq_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_blackurq_comm (void)
+{
+ LOCDATA *retval;
+
+ blackurq_desc.init_encounter_func = Intro;
+ blackurq_desc.post_encounter_func = post_blackurq_enc;
+ blackurq_desc.uninit_encounter_func = uninit_blackurq;
+
+ blackurq_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ blackurq_desc.AlienTextBaseline.y = 0;
+ blackurq_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (!GET_GAME_STATE (KOHR_AH_KILLED_ALL)
+ && LOBYTE (GLOBAL (CurrentActivity)) != WON_LAST_BATTLE)
+ {
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ setSegue (Segue_peace);
+ }
+ retval = &blackurq_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/blackur/resinst.h b/src/uqm/comm/blackur/resinst.h
new file mode 100644
index 0000000..d0c2313
--- /dev/null
+++ b/src/uqm/comm/blackur/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define BLACKURQ_COLOR_MAP "comm.kohrah.colortable"
+#define BLACKURQ_CONVERSATION_PHRASES "comm.kohrah.dialogue"
+#define BLACKURQ_FONT "comm.kohrah.font"
+#define BLACKURQ_MUSIC "comm.kohrah.music"
+#define BLACKURQ_PMAP_ANIM "comm.kohrah.graphics"
diff --git a/src/uqm/comm/blackur/strings.h b/src/uqm/comm/blackur/strings.h
new file mode 100644
index 0000000..06a9351
--- /dev/null
+++ b/src/uqm/comm/blackur/strings.h
@@ -0,0 +1,103 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef BLACKUR_STRINGS_H
+#define BLACKUR_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ SENSE_EVIL,
+ HELLO_AND_DIE_1,
+ HELLO_AND_DIE_2,
+ HELLO_AND_DIE_3,
+ HELLO_AND_DIE_4,
+ HELLO_AND_DIE_5,
+ HELLO_AND_DIE_6,
+ HELLO_AND_DIE_7,
+ HELLO_AND_DIE_8,
+ HELLO_SAMATRA,
+ WE_KILL_ALL_1,
+ WE_KILL_ALL_2,
+ WE_KILL_ALL_3,
+ WE_KILL_ALL_4,
+ why_kill_all_1,
+ why_kill_all_2,
+ why_kill_all_3,
+ why_kill_all_4,
+ KILL_BECAUSE_1,
+ KILL_BECAUSE_2,
+ KILL_BECAUSE_3,
+ KILL_BECAUSE_4,
+ please_dont_kill_1,
+ WILL_KILL_1,
+ please_dont_kill_2,
+ WILL_KILL_2,
+ please_dont_kill_3,
+ WILL_KILL_3,
+ please_dont_kill_4,
+ WILL_KILL_4,
+ bye_frenzy_1,
+ bye_frenzy_2,
+ bye_frenzy_3,
+ bye_frenzy_4,
+ GOODBYE_AND_DIE_FRENZY_1,
+ GOODBYE_AND_DIE_FRENZY_2,
+ GOODBYE_AND_DIE_FRENZY_3,
+ GOODBYE_AND_DIE_FRENZY_4,
+ threat_1,
+ RESISTANCE_IS_USELESS_1,
+ threat_2,
+ RESISTANCE_IS_USELESS_2,
+ threat_3,
+ RESISTANCE_IS_USELESS_3,
+ threat_4,
+ RESISTANCE_IS_USELESS_4,
+ key_phrase,
+ RESPONSE_TO_KEY_PHRASE,
+ why_do_you_destroy,
+ WE_WERE_SLAVES,
+ relationship_with_urquan,
+ WE_ARE_URQUAN_TOO,
+ what_about_culture,
+ BONE_GARDENS,
+ how_leave_me_alone,
+ YOU_DIE,
+ guess_thats_all,
+ THEN_DIE,
+ what_are_you_hovering_over,
+ BONE_PILE,
+ you_sure_are_creepy,
+ YES_CREEPY,
+ stop_that_gross_blinking,
+ DIE_HUMAN,
+ plead_1,
+ PLEADING_IS_USELESS_1,
+ plead_2,
+ PLEADING_IS_USELESS_2,
+ plead_3,
+ PLEADING_IS_USELESS_3,
+ plead_4,
+ PLEADING_IS_USELESS_4,
+ bye,
+ GOODBYE_AND_DIE,
+ GAME_OVER_DUDE,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/chmmr/Makeinfo b/src/uqm/comm/chmmr/Makeinfo
new file mode 100644
index 0000000..f01e2b8
--- /dev/null
+++ b/src/uqm/comm/chmmr/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="chmmrc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/chmmr/chmmrc.c b/src/uqm/comm/chmmr/chmmrc.c
new file mode 100644
index 0000000..1b35fc0
--- /dev/null
+++ b/src/uqm/comm/chmmr/chmmrc.c
@@ -0,0 +1,641 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/hyper.h"
+ // for SOL_X/SOL_Y
+
+
+static LOCDATA chmmr_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ CHMMR_PMAP_ANIM, /* AlienFrame */
+ CHMMR_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ CHMMR_COLOR_MAP, /* AlienColorMap */
+ CHMMR_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ CHMMR_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 6, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 12, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 17, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 27, /* StartIndex */
+ 20, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 47, /* StartIndex */
+ 14, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 61, /* StartIndex */
+ 24, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 11, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 60, 0, /* FrameRate */
+ ONE_SECOND / 60, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, bye))
+ NPCPhrase (GOODBYE);
+ else if (PLAYER_SAID (R, bye_shielded))
+ NPCPhrase (GOODBYE_SHIELDED);
+ else if (PLAYER_SAID (R, bye_after_bomb))
+ NPCPhrase (GOODBYE_AFTER_BOMB);
+ else if (PLAYER_SAID (R, proceed))
+ {
+ int i;
+
+ NPCPhrase (TAKE_2_WEEKS);
+
+ SetRaceAllied (CHMMR_SHIP, TRUE);
+
+ SET_GAME_STATE (CHMMR_HOME_VISITS, 0);
+ SET_GAME_STATE (CHMMR_STACK, 0);
+ SET_GAME_STATE (CHMMR_BOMB_STATE, 2);
+ SET_GAME_STATE (UTWIG_BOMB_ON_SHIP, 0);
+ GLOBAL_SIS (ResUnits) = 1000000L;
+ GLOBAL_SIS (NumLanders) = 0;
+ GLOBAL (ModuleCost[PLANET_LANDER]) = 0;
+
+#define EARTH_INDEX 2 /* earth is 3rd planet --> 3 - 1 = 2 */
+/* Magic numbers for Earth */
+#define EARTH_OUTER_X (-725)
+#define EARTH_OUTER_Y (597)
+#define EARTH_INNER_X (121)
+#define EARTH_INNER_Y (113)
+/* Magic numbers for Earth Starbase */
+#define STARBASE_INNER_X (86)
+#define STARBASE_INNER_Y (113)
+
+ /* transport player to Earth */
+ GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (SOL_X);
+ GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (SOL_Y);
+ GLOBAL (ShipFacing) = 1;
+ /* At Earth or at Starbase */
+ GLOBAL (ip_planet) = EARTH_INDEX + 1;
+ GLOBAL (in_orbit) = 0;
+ /* XXX : this should be unhardcoded eventually */
+ GLOBAL (ip_location.x) = EARTH_OUTER_X;
+ GLOBAL (ip_location.y) = EARTH_OUTER_Y;
+
+ if (GET_GAME_STATE (STARBASE_AVAILABLE))
+ { /* Normal game mode - you are transported to Starbase */
+ GLOBAL_SIS (FuelOnBoard) = FUEL_RESERVE;
+ GLOBAL_SIS (CrewEnlisted) = 0;
+ GLOBAL_SIS (TotalElementMass) = 0;
+ GLOBAL (ModuleCost[STORAGE_BAY]) = 0; /* disable Storage Bay */
+ for (i = 0; i < NUM_ELEMENT_CATEGORIES; ++i)
+ GLOBAL_SIS (ElementAmounts[i]) = 0;
+ for (i = NUM_BOMB_MODULES; i < NUM_MODULE_SLOTS; ++i)
+ GLOBAL_SIS (ModuleSlots[i]) = EMPTY_SLOT + 2;
+
+ /* XXX : this should be unhardcoded eventually */
+ /* transport to Starbase */
+ GLOBAL (ShipStamp.origin.x) = STARBASE_INNER_X - SAFE_X;
+ GLOBAL (ShipStamp.origin.y) = STARBASE_INNER_Y - SAFE_Y;
+ }
+ else
+ { /* 'Beating Game Differently' mode - never visited Starbase,
+ * so you are transported to Earth */
+ /* compress the layout -- move all to front */
+ for (i = NUM_MODULE_SLOTS - 1; i > 0; --i)
+ {
+ int m;
+
+ /* find next unused slot */
+ for (; i > 0
+ && GLOBAL_SIS (ModuleSlots[i]) != EMPTY_SLOT + 2;
+ --i)
+ ;
+ if (i == 0)
+ break;
+ /* find next module to move */
+ for (m = i - 1; m >= 0
+ && GLOBAL_SIS (ModuleSlots[m]) == EMPTY_SLOT + 2;
+ --m)
+ ;
+ if (m < 0)
+ break;
+
+ /* move the module */
+ GLOBAL_SIS (ModuleSlots[i]) = GLOBAL_SIS (ModuleSlots[m]);
+ GLOBAL_SIS (ModuleSlots[m]) = EMPTY_SLOT + 2;
+ }
+
+ /* XXX : this should be unhardcoded eventually */
+ /* transport to Earth itself */
+ GLOBAL (ShipStamp.origin.x) = EARTH_INNER_X - SAFE_X;
+ GLOBAL (ShipStamp.origin.y) = EARTH_INNER_Y - SAFE_Y;
+ }
+
+ /* install Chmmr-supplied modules */
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ GLOBAL_SIS (DriveSlots[i]) = FUSION_THRUSTER;
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ GLOBAL_SIS (JetSlots[i]) = TURNING_JETS;
+ GLOBAL_SIS (ModuleSlots[0]) = BOMB_MODULE_4;
+ GLOBAL_SIS (ModuleSlots[1]) = BOMB_MODULE_5;
+ GLOBAL_SIS (ModuleSlots[2]) = BOMB_MODULE_3;
+ GLOBAL_SIS (ModuleSlots[3]) = BOMB_MODULE_1;
+ GLOBAL_SIS (ModuleSlots[4]) = BOMB_MODULE_0;
+ GLOBAL_SIS (ModuleSlots[5]) = BOMB_MODULE_1;
+ GLOBAL_SIS (ModuleSlots[6]) = BOMB_MODULE_3;
+ GLOBAL_SIS (ModuleSlots[7]) = BOMB_MODULE_4;
+ GLOBAL_SIS (ModuleSlots[8]) = BOMB_MODULE_5;
+ GLOBAL_SIS (ModuleSlots[9]) = BOMB_MODULE_2;
+ }
+}
+
+static void
+NotReady (RESPONSE_REF R)
+{
+ if (R == 0)
+ NPCPhrase (RETURN_WHEN_READY);
+ else if (PLAYER_SAID (R, further_assistance))
+ {
+ NPCPhrase (NO_FURTHER_ASSISTANCE);
+
+ DISABLE_PHRASE (further_assistance);
+ }
+ else if (PLAYER_SAID (R, tech_help))
+ {
+ NPCPhrase (USE_OUR_SHIPS_BEFORE);
+
+ SetRaceAllied (CHMMR_SHIP, TRUE);
+ }
+ else if (PLAYER_SAID (R, where_weapon))
+ {
+ NPCPhrase (PRECURSOR_WEAPON);
+
+ DISABLE_PHRASE (where_weapon);
+ }
+ else if (PLAYER_SAID (R, where_distraction))
+ {
+ NPCPhrase (PSYCHIC_WEAPONRY);
+
+ DISABLE_PHRASE (where_distraction);
+ }
+
+ if (CheckAlliance (CHMMR_SHIP) != GOOD_GUY)
+ Response (tech_help, NotReady);
+ else if (PHRASE_ENABLED (further_assistance))
+ Response (further_assistance, NotReady);
+ if (PHRASE_ENABLED (where_weapon) && !GET_GAME_STATE (UTWIG_BOMB_ON_SHIP))
+ Response (where_weapon, NotReady);
+ if (PHRASE_ENABLED (where_distraction) && !GET_GAME_STATE (TALKING_PET_ON_SHIP))
+ Response (where_distraction, NotReady);
+ Response (bye, ExitConversation);
+}
+
+static void
+ImproveBomb (RESPONSE_REF R)
+{
+ if (R == 0)
+ NPCPhrase (WE_WILL_IMPROVE_BOMB);
+ else if (PLAYER_SAID (R, what_now))
+ {
+ NPCPhrase (MODIFY_VESSEL);
+
+ DISABLE_PHRASE (what_now);
+ }
+ else if (PLAYER_SAID (R, wont_hurt_my_ship))
+ {
+ NPCPhrase (WILL_DESTROY_IT);
+
+ DISABLE_PHRASE (wont_hurt_my_ship);
+ }
+ else if (PLAYER_SAID (R, bummer_about_my_ship))
+ {
+ NPCPhrase (DEAD_SILENCE);
+
+ DISABLE_PHRASE (bummer_about_my_ship);
+ }
+ else if (PLAYER_SAID (R, other_assistance))
+ {
+ NPCPhrase (USE_OUR_SHIPS_AFTER);
+
+ SetRaceAllied (CHMMR_SHIP, TRUE);
+ }
+
+ if (PHRASE_ENABLED (what_now))
+ Response (what_now, ImproveBomb);
+ else if (PHRASE_ENABLED (wont_hurt_my_ship))
+ Response (wont_hurt_my_ship, ImproveBomb);
+ else if (PHRASE_ENABLED (bummer_about_my_ship))
+ Response (bummer_about_my_ship, ImproveBomb);
+ if (CheckAlliance (CHMMR_SHIP) != GOOD_GUY)
+ Response (other_assistance, ImproveBomb);
+ Response (proceed, ExitConversation);
+}
+
+static void
+ChmmrFree (RESPONSE_REF R)
+{
+ if (R == 0
+ || PLAYER_SAID (R, i_am_captain0)
+ || PLAYER_SAID (R, i_am_savior)
+ || PLAYER_SAID (R, i_am_silly))
+ {
+ NPCPhrase (WHY_HAVE_YOU_FREED_US);
+ AlienTalkSegue ((COUNT)~0);
+ SET_GAME_STATE (CHMMR_EMERGING, 0);
+
+ Response (serious_1, ChmmrFree);
+ Response (serious_2, ChmmrFree);
+ Response (silly, ChmmrFree);
+ }
+ else
+ {
+ NPCPhrase (WILL_HELP_ANALYZE_LOGS);
+
+ if (GET_GAME_STATE (AWARE_OF_SAMATRA))
+ NPCPhrase (YOU_KNOW_SAMATRA);
+ else
+ {
+ NPCPhrase (DONT_KNOW_ABOUT_SAMATRA);
+
+ SET_GAME_STATE (AWARE_OF_SAMATRA, 1);
+ }
+
+ if (GET_GAME_STATE (TALKING_PET_ON_SHIP))
+ NPCPhrase (HAVE_TALKING_PET);
+ else
+ NPCPhrase (NEED_DISTRACTION);
+
+ if (GET_GAME_STATE (UTWIG_BOMB_ON_SHIP))
+ NPCPhrase (HAVE_BOMB);
+ else
+ NPCPhrase (NEED_WEAPON);
+
+ if (!GET_GAME_STATE (TALKING_PET_ON_SHIP)
+ || !GET_GAME_STATE (UTWIG_BOMB_ON_SHIP))
+ NotReady ((RESPONSE_REF)0);
+ else
+ ImproveBomb ((RESPONSE_REF)0);
+ }
+}
+
+static void ChmmrShielded (RESPONSE_REF R);
+
+static void
+ChmmrAdvice (RESPONSE_REF R)
+{
+ BYTE AdviceLeft;
+
+ if (PLAYER_SAID (R, need_advice))
+ NPCPhrase (WHAT_ADVICE);
+ else if (PLAYER_SAID (R, how_defeat_urquan))
+ {
+ NPCPhrase (DEFEAT_LIKE_SO);
+
+ SET_GAME_STATE (CHMMR_BOMB_STATE, 1);
+ DISABLE_PHRASE (how_defeat_urquan);
+ }
+ else if (PLAYER_SAID (R, what_about_tpet))
+ {
+ NPCPhrase (SCARY_BUT_USEFUL);
+
+ DISABLE_PHRASE (what_about_tpet);
+ }
+ else if (PLAYER_SAID (R, what_about_bomb))
+ {
+ NPCPhrase (ABOUT_BOMB);
+
+ DISABLE_PHRASE (what_about_bomb);
+ }
+ else if (PLAYER_SAID (R, what_about_sun_device))
+ {
+ NPCPhrase (ABOUT_SUN_DEVICE);
+
+ DISABLE_PHRASE (what_about_sun_device);
+ }
+ else if (PLAYER_SAID (R, what_about_samatra))
+ {
+ NPCPhrase (ABOUT_SAMATRA);
+
+ DISABLE_PHRASE (what_about_samatra);
+ }
+
+ AdviceLeft = 0;
+ if (PHRASE_ENABLED (how_defeat_urquan))
+ {
+ Response (how_defeat_urquan, ChmmrAdvice);
+ AdviceLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_tpet) && GET_GAME_STATE (TALKING_PET_ON_SHIP))
+ {
+ Response (what_about_tpet, ChmmrAdvice);
+ AdviceLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_bomb) && GET_GAME_STATE (UTWIG_BOMB_ON_SHIP))
+ {
+ Response (what_about_bomb, ChmmrAdvice);
+ AdviceLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_sun_device) && GET_GAME_STATE (SUN_DEVICE_ON_SHIP))
+ {
+ Response (what_about_sun_device, ChmmrAdvice);
+ AdviceLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_samatra) && GET_GAME_STATE (AWARE_OF_SAMATRA))
+ {
+ Response (what_about_samatra, ChmmrAdvice);
+ AdviceLeft = TRUE;
+ }
+ Response (enough_advice, ChmmrShielded);
+
+ if (!AdviceLeft)
+ DISABLE_PHRASE (need_advice);
+}
+
+static void
+ChmmrShielded (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, find_out_whats_up))
+ {
+ NPCPhrase (HYBRID_PROCESS);
+
+ DISABLE_PHRASE (find_out_whats_up);
+ }
+ else if (PLAYER_SAID (R, need_help))
+ {
+ NPCPhrase (CANT_HELP);
+
+ SET_GAME_STATE (CHMMR_STACK, 1);
+ }
+ else if (PLAYER_SAID (R, why_no_help))
+ {
+ NPCPhrase (LONG_TIME);
+
+ SET_GAME_STATE (CHMMR_STACK, 2);
+ }
+ else if (PLAYER_SAID (R, what_if_more_energy))
+ {
+ NPCPhrase (DANGER_TO_US);
+
+ SET_GAME_STATE (CHMMR_STACK, 3);
+ }
+ else if (PLAYER_SAID (R, enough_advice))
+ NPCPhrase (OK_ENOUGH_ADVICE);
+
+ switch (GET_GAME_STATE (CHMMR_STACK))
+ {
+ case 0:
+ Response (need_help, ChmmrShielded);
+ break;
+ case 1:
+ Response (why_no_help, ChmmrShielded);
+ break;
+ case 2:
+ Response (what_if_more_energy, ChmmrShielded);
+ break;
+ }
+ if (PHRASE_ENABLED (find_out_whats_up))
+ Response (find_out_whats_up, ChmmrShielded);
+ if (PHRASE_ENABLED (need_advice))
+ {
+ Response (need_advice, ChmmrAdvice);
+ }
+ Response (bye_shielded, ExitConversation);
+}
+
+static void
+AfterBomb (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_after_bomb))
+ {
+ if (GET_GAME_STATE (CHMMR_STACK))
+ NPCPhrase (GENERAL_INFO_AFTER_BOMB_2);
+ else
+ {
+ NPCPhrase (GENERAL_INFO_AFTER_BOMB_1);
+
+ SET_GAME_STATE (CHMMR_STACK, 1);
+ }
+
+ DISABLE_PHRASE (whats_up_after_bomb);
+ }
+ else if (PLAYER_SAID (R, what_do_after_bomb))
+ {
+ NPCPhrase (DO_AFTER_BOMB);
+
+ DISABLE_PHRASE (what_do_after_bomb);
+ }
+
+ if (PHRASE_ENABLED (whats_up_after_bomb))
+ Response (whats_up_after_bomb, AfterBomb);
+ if (PHRASE_ENABLED (what_do_after_bomb))
+ Response (what_do_after_bomb, AfterBomb);
+ Response (bye_after_bomb, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) >= 2)
+ {
+ NumVisits = GET_GAME_STATE (CHMMR_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_AFTER_BOMB_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_AFTER_BOMB_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (CHMMR_HOME_VISITS, NumVisits);
+
+ AfterBomb ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (CHMMR_UNLEASHED))
+ {
+ if (!GET_GAME_STATE (TALKING_PET_ON_SHIP)
+ || !GET_GAME_STATE (UTWIG_BOMB_ON_SHIP))
+ NotReady ((RESPONSE_REF)0);
+ else
+ {
+ NPCPhrase (YOU_ARE_READY);
+
+ ImproveBomb ((RESPONSE_REF)0);
+ }
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (CHMMR_HOME_VISITS);
+ if (!GET_GAME_STATE (CHMMR_EMERGING))
+ {
+ CommData.AlienColorMap = SetAbsColorMapIndex (
+ CommData.AlienColorMap, 1
+ );
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (WHY_YOU_HERE_1);
+ break;
+ case 1:
+ NPCPhrase (WHY_YOU_HERE_2);
+ break;
+ case 2:
+ NPCPhrase (WHY_YOU_HERE_3);
+ break;
+ case 3:
+ NPCPhrase (WHY_YOU_HERE_4);
+ --NumVisits;
+ break;
+ }
+
+ ChmmrShielded ((RESPONSE_REF)0);
+ }
+ else
+ {
+ SetCommIntroMode (CIM_FADE_IN_SCREEN, ONE_SECOND * 2);
+ NPCPhrase (WE_ARE_FREE);
+
+ if (NumVisits)
+ {
+ ChmmrFree ((RESPONSE_REF)0);
+ NumVisits = 0;
+ }
+ else
+ {
+ NPCPhrase (WHO_ARE_YOU);
+
+ construct_response (shared_phrase_buf,
+ i_am_captain0,
+ GLOBAL_SIS (CommanderName),
+ i_am_captain1,
+ GLOBAL_SIS (ShipName),
+ i_am_captain2,
+ (UNICODE*)NULL);
+ DoResponsePhrase (i_am_captain0, ChmmrFree, shared_phrase_buf);
+ Response (i_am_savior, ChmmrFree);
+ Response (i_am_silly, ChmmrFree);
+ }
+
+ SET_GAME_STATE (CHMMR_UNLEASHED, 1);
+ }
+ SET_GAME_STATE (CHMMR_HOME_VISITS, NumVisits);
+ }
+}
+
+static COUNT
+uninit_chmmr (void)
+{
+ return (0);
+}
+
+static void
+post_chmmr_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_chmmr_comm (void)
+{
+ LOCDATA *retval;
+
+ chmmr_desc.init_encounter_func = Intro;
+ chmmr_desc.post_encounter_func = post_chmmr_enc;
+ chmmr_desc.uninit_encounter_func = uninit_chmmr;
+
+ chmmr_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ chmmr_desc.AlienTextBaseline.y = 0;
+ chmmr_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ setSegue (Segue_peace);
+ retval = &chmmr_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/chmmr/resinst.h b/src/uqm/comm/chmmr/resinst.h
new file mode 100644
index 0000000..3365c97
--- /dev/null
+++ b/src/uqm/comm/chmmr/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define CHMMR_COLOR_MAP "comm.chmmr.colortable"
+#define CHMMR_CONVERSATION_PHRASES "comm.chmmr.dialogue"
+#define CHMMR_FONT "comm.chmmr.font"
+#define CHMMR_MUSIC "comm.chmmr.music"
+#define CHMMR_PMAP_ANIM "comm.chmmr.graphics"
diff --git a/src/uqm/comm/chmmr/strings.h b/src/uqm/comm/chmmr/strings.h
new file mode 100644
index 0000000..0952891
--- /dev/null
+++ b/src/uqm/comm/chmmr/strings.h
@@ -0,0 +1,105 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CHMMR_STRINGS_H
+#define CHMMR_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ WHY_YOU_HERE_1,
+ WHY_YOU_HERE_2,
+ WHY_YOU_HERE_3,
+ WHY_YOU_HERE_4,
+ find_out_whats_up,
+ HYBRID_PROCESS,
+ need_help,
+ CANT_HELP,
+ why_no_help,
+ LONG_TIME,
+ what_if_more_energy,
+ DANGER_TO_US,
+ need_advice,
+ WHAT_ADVICE,
+ how_defeat_urquan,
+ DEFEAT_LIKE_SO,
+ what_about_tpet,
+ SCARY_BUT_USEFUL,
+ what_about_bomb,
+ ABOUT_BOMB,
+ what_about_sun_device,
+ ABOUT_SUN_DEVICE,
+ what_about_samatra,
+ ABOUT_SAMATRA,
+ enough_advice,
+ OK_ENOUGH_ADVICE,
+ bye_shielded,
+ GOODBYE_SHIELDED,
+ WE_ARE_FREE,
+ WHO_ARE_YOU,
+ i_am_captain0,
+ i_am_captain1,
+ i_am_captain2,
+ i_am_savior,
+ i_am_silly,
+ WHY_HAVE_YOU_FREED_US,
+ serious_1,
+ serious_2,
+ silly,
+ WILL_HELP_ANALYZE_LOGS,
+ YOU_KNOW_SAMATRA,
+ DONT_KNOW_ABOUT_SAMATRA,
+ NEED_DISTRACTION,
+ HAVE_TALKING_PET,
+ NEED_WEAPON,
+ HAVE_BOMB,
+ RETURN_WHEN_READY,
+ YOU_ARE_READY,
+ further_assistance,
+ NO_FURTHER_ASSISTANCE,
+ tech_help,
+ USE_OUR_SHIPS_BEFORE,
+ where_weapon,
+ PRECURSOR_WEAPON,
+ where_distraction,
+ PSYCHIC_WEAPONRY,
+ what_now,
+ WE_WILL_IMPROVE_BOMB,
+ MODIFY_VESSEL,
+ wont_hurt_my_ship,
+ WILL_DESTROY_IT,
+ bummer_about_my_ship,
+ DEAD_SILENCE,
+ other_assistance,
+ USE_OUR_SHIPS_AFTER,
+ proceed,
+ TAKE_2_WEEKS,
+ HELLO_AFTER_BOMB_1,
+ HELLO_AFTER_BOMB_2,
+ whats_up_after_bomb,
+ GENERAL_INFO_AFTER_BOMB_1,
+ GENERAL_INFO_AFTER_BOMB_2,
+ what_do_after_bomb,
+ DO_AFTER_BOMB,
+ bye_after_bomb,
+ GOODBYE_AFTER_BOMB,
+ bye,
+ GOODBYE,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/comandr/Makeinfo b/src/uqm/comm/comandr/Makeinfo
new file mode 100644
index 0000000..36b63ed
--- /dev/null
+++ b/src/uqm/comm/comandr/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="comandr.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/comandr/comandr.c b/src/uqm/comm/comandr/comandr.c
new file mode 100644
index 0000000..57df233
--- /dev/null
+++ b/src/uqm/comm/comandr/comandr.c
@@ -0,0 +1,694 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/setup.h"
+#include "uqm/sis.h"
+ // for DeltaSISGauges(), DrawLanders()
+#include "libs/graphics/gfx_common.h"
+
+static LOCDATA commander_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ COMMANDER_PMAP_ANIM, /* AlienFrame */
+ COMMANDER_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ COMMANDER_COLOR_MAP, /* AlienColorMap */
+ COMMANDER_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ COMMANDER_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 3, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ { /* Blink */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Running light */
+ 10, /* StartIndex */
+ 30, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ ONE_SECOND * 2, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM | COLORXFORM_ANIM,/* AnimFlags */
+ 0, ONE_SECOND / 30, /* FrameRate */
+ 0, ONE_SECOND / 15, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 4, /* StartIndex */
+ 6, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 7 / 60, ONE_SECOND / 12, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ByeBye (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, ok_i_will_get_radios))
+ NPCPhrase (THANKS_FOR_HELPING);
+ else if (PLAYER_SAID (R, well_go_get_them_now))
+ NPCPhrase (GLAD_WHEN_YOU_COME_BACK);
+ else if (PLAYER_SAID (R, we_will_take_care_of_base))
+ {
+ NPCPhrase (GOOD_LUCK_WITH_BASE);
+
+ SET_GAME_STATE (WILL_DESTROY_BASE, 1);
+ }
+ else if (PLAYER_SAID (R, take_care_of_base_again))
+ NPCPhrase (GOOD_LUCK_AGAIN);
+ else if (PLAYER_SAID (R, base_was_abandoned)
+ || PLAYER_SAID (R, i_lied_it_was_abandoned))
+ {
+ NPCPhrase (IT_WAS_ABANDONED);
+ NPCPhrase (HERE_COMES_ILWRATH);
+
+ SET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER, 1);
+ }
+ else if (PLAYER_SAID (R, oh_yes_big_fight))
+ {
+ NPCPhrase (IM_GLAD_YOU_WON);
+ NPCPhrase (HERE_COMES_ILWRATH);
+
+ SET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER, 1);
+ }
+ else if (PLAYER_SAID (R, i_cant_talk_about_it))
+ {
+ NPCPhrase (IM_SURE_IT_WAS_DIFFICULT);
+ NPCPhrase (HERE_COMES_ILWRATH);
+
+ SET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER, 1);
+ }
+ else if (PLAYER_SAID (R, cook_their_butts)
+ || PLAYER_SAID (R, overthrow_evil_aliens)
+ || PLAYER_SAID (R, annihilate_those_monsters))
+ {
+ SET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER, 0);
+
+ if (PLAYER_SAID (R, cook_their_butts))
+ NPCPhrase (COOK_BUTTS);
+ else if (PLAYER_SAID (R, overthrow_evil_aliens))
+ NPCPhrase (OVERTHROW_ALIENS);
+ else /* if (R == annihilate_those_monsters) */
+ NPCPhrase (KILL_MONSTERS);
+
+ construct_response (shared_phrase_buf,
+ name_40,
+ GLOBAL_SIS (CommanderName),
+ name_41,
+ (UNICODE*)NULL);
+
+ NPCPhrase (THIS_MAY_SEEM_SILLY);
+
+ Response (name_1, ByeBye);
+ Response (name_2, ByeBye);
+ Response (name_3, ByeBye);
+ DoResponsePhrase (name_40, ByeBye, shared_phrase_buf);
+
+ SET_GAME_STATE (STARBASE_AVAILABLE, 1);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, name_1))
+ {
+ NPCPhrase (OK_THE_NAFS);
+
+ SET_GAME_STATE (NEW_ALLIANCE_NAME, 0);
+ }
+ else if (PLAYER_SAID (R, name_2))
+ {
+ NPCPhrase (OK_THE_CAN);
+
+ SET_GAME_STATE (NEW_ALLIANCE_NAME, 1);
+ }
+ else if (PLAYER_SAID (R, name_3))
+ {
+ NPCPhrase (OK_THE_UFW);
+
+ SET_GAME_STATE (NEW_ALLIANCE_NAME, 2);
+ }
+ else /* if (PLAYER_SAID (R, name_4)) */
+ {
+ NPCPhrase (OK_THE_NAME_IS_EMPIRE0);
+ NPCPhrase (GLOBAL_PLAYER_NAME);
+ NPCPhrase (OK_THE_NAME_IS_EMPIRE1);
+
+ SET_GAME_STATE (NEW_ALLIANCE_NAME, 3);
+ }
+
+ NPCPhrase (STARBASE_WILL_BE_READY);
+ }
+}
+
+static void GiveRadios (RESPONSE_REF R);
+
+static void
+NoRadioactives (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, yes_this_is_supply_ship))
+ {
+ NPCPhrase (ABOUT_TIME);
+
+ if (GLOBAL_SIS (ElementAmounts[RADIOACTIVE]))
+ GiveRadios (0);
+ else
+ {
+ Response (i_lied, NoRadioactives);
+ Response (plumb_out, NoRadioactives);
+ }
+ }
+ else
+ {
+ if (PLAYER_SAID (R, where_can_i_get_radios))
+ {
+ NPCPhrase (RADIOS_ON_MERCURY);
+
+ DISABLE_PHRASE (where_can_i_get_radios);
+ }
+ else if (PLAYER_SAID (R, no_but_well_help0))
+ NPCPhrase (THE_WHAT_FROM_WHERE);
+ else if (PLAYER_SAID (R, what_slave_planet)
+ || PLAYER_SAID (R, i_lied))
+ NPCPhrase (DONT_KNOW_WHO_YOU_ARE);
+ else if (PLAYER_SAID (R, plumb_out))
+ NPCPhrase (WHAT_KIND_OF_IDIOT);
+ else if (PLAYER_SAID (R, i_lost_my_lander))
+ {
+ NPCPhrase (HERE_IS_A_NEW_LANDER);
+ ++GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+ DeltaSISGauges (4, 0, 0);
+
+ SET_GAME_STATE (LANDERS_LOST, 1);
+ }
+ else if (PLAYER_SAID (R, i_lost_another_lander))
+ {
+ NPCPhrase (HERE_IS_ANOTHER_LANDER);
+ ++GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+ DeltaSISGauges (4, 0, 0);
+ }
+ else if (PLAYER_SAID (R, need_fuel_mercury) ||
+ PLAYER_SAID (R, need_fuel_luna))
+ {
+ NPCPhrase (GIVE_FUEL);
+ DeltaSISGauges (0, 5 * FUEL_TANK_SCALE, 0);
+
+ SET_GAME_STATE (GIVEN_FUEL_BEFORE, 1);
+ }
+ else if (PLAYER_SAID (R, need_fuel_again))
+ {
+ NPCPhrase (GIVE_FUEL_AGAIN);
+ DeltaSISGauges (0, 5 * FUEL_TANK_SCALE, 0);
+ }
+
+ if (GLOBAL_SIS (ElementAmounts[RADIOACTIVE]))
+ GiveRadios (0);
+ else
+ {
+ if (GLOBAL_SIS (NumLanders) == 0
+ && GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
+ {
+ if (GET_GAME_STATE (LANDERS_LOST))
+ Response (i_lost_another_lander, NoRadioactives);
+ else
+ Response (i_lost_my_lander, NoRadioactives);
+ }
+ if (GLOBAL_SIS (FuelOnBoard) < 2 * FUEL_TANK_SCALE)
+ {
+ if (GET_GAME_STATE (GIVEN_FUEL_BEFORE))
+ Response (need_fuel_again, NoRadioactives);
+ else
+ Response (need_fuel_mercury, NoRadioactives);
+ }
+
+ Response (ok_i_will_get_radios, ByeBye);
+ if (PHRASE_ENABLED (where_can_i_get_radios))
+ {
+ Response (where_can_i_get_radios, NoRadioactives);
+ }
+ }
+ }
+}
+
+static void
+AskAfterRadios (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, i_lost_my_lander))
+ {
+ NPCPhrase (HERE_IS_A_NEW_LANDER);
+ ++GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+ DeltaSISGauges (4, 0, 0);
+
+ SET_GAME_STATE (LANDERS_LOST, 1);
+ }
+ else if (PLAYER_SAID (R, i_lost_another_lander))
+ {
+ NPCPhrase (HERE_IS_ANOTHER_LANDER);
+ ++GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+ DeltaSISGauges (4, 0, 0);
+ }
+ else if (PLAYER_SAID (R, need_fuel_mercury) ||
+ PLAYER_SAID (R, need_fuel_luna))
+ {
+ NPCPhrase (GIVE_FUEL);
+ DeltaSISGauges (0, 5 * FUEL_TANK_SCALE, 0);
+
+ SET_GAME_STATE (GIVEN_FUEL_BEFORE, 1);
+ }
+ else if (PLAYER_SAID (R, need_fuel_again))
+ {
+ NPCPhrase (GIVE_FUEL_AGAIN);
+ DeltaSISGauges (0, 5 * FUEL_TANK_SCALE, 0);
+ }
+ else if (PLAYER_SAID (R, where_get_radios))
+ {
+ NPCPhrase (RADIOS_ON_MERCURY);
+
+ DISABLE_PHRASE (where_get_radios);
+ }
+
+ {
+ if (GLOBAL_SIS (NumLanders) == 0
+ && GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
+ {
+ if (GET_GAME_STATE (LANDERS_LOST))
+ Response (i_lost_another_lander, AskAfterRadios);
+ else
+ Response (i_lost_my_lander, AskAfterRadios);
+ }
+ if (GLOBAL_SIS (FuelOnBoard) < 2 * FUEL_TANK_SCALE)
+ {
+ if (GET_GAME_STATE (GIVEN_FUEL_BEFORE))
+ Response (need_fuel_again, AskAfterRadios);
+ else
+ Response (need_fuel_mercury, AskAfterRadios);
+ }
+ Response (well_go_get_them_now, ByeBye);
+ if (PHRASE_ENABLED (where_get_radios))
+ {
+ Response (where_get_radios, AskAfterRadios);
+ }
+ }
+}
+
+static void
+BaseDestroyed (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, we_fought_them))
+ {
+ NPCPhrase (YOU_REALLY_FOUGHT_BASE);
+
+ Response (oh_yes_big_fight, ByeBye);
+ Response (i_lied_it_was_abandoned, ByeBye);
+ Response (i_cant_talk_about_it, ByeBye);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, we_are_here_to_help))
+ {
+ NPCPhrase (BASE_ON_MOON);
+ }
+ else
+ {
+ NPCPhrase (DEALT_WITH_BASE_YET);
+ }
+
+ Response (base_was_abandoned, ByeBye);
+ Response (we_fought_them, BaseDestroyed);
+ }
+}
+
+static void
+TellMoonBase (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ NPCPhrase (DEALT_WITH_BASE_YET);
+ }
+ else if (PLAYER_SAID (R, i_lost_my_lander))
+ {
+ NPCPhrase (HERE_IS_A_NEW_LANDER);
+ ++GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+ DeltaSISGauges (4, 0, 0);
+
+ SET_GAME_STATE (LANDERS_LOST, 1);
+ }
+ else if (PLAYER_SAID (R, i_lost_another_lander))
+ {
+ NPCPhrase (HERE_IS_ANOTHER_LANDER);
+ ++GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+ DeltaSISGauges (4, 0, 0);
+ }
+ else if (PLAYER_SAID (R, need_fuel_mercury) ||
+ PLAYER_SAID (R, need_fuel_luna))
+ {
+ NPCPhrase (GIVE_FUEL);
+ DeltaSISGauges (0, 5 * FUEL_TANK_SCALE, 0);
+
+ SET_GAME_STATE (GIVEN_FUEL_BEFORE, 1);
+ }
+ else if (PLAYER_SAID (R, need_fuel_again))
+ {
+ NPCPhrase (GIVE_FUEL_AGAIN);
+ DeltaSISGauges (0, 5 * FUEL_TANK_SCALE, 0);
+ }
+ else if (PLAYER_SAID (R, we_are_here_to_help))
+ {
+ NPCPhrase (BASE_ON_MOON);
+ }
+ else if (GET_GAME_STATE (STARBASE_YACK_STACK1) == 0)
+ {
+ NPCPhrase (ABOUT_BASE);
+
+ SET_GAME_STATE (STARBASE_YACK_STACK1, 1);
+ }
+ else
+ {
+ NPCPhrase (ABOUT_BASE_AGAIN);
+ }
+
+ if (GLOBAL_SIS (NumLanders) == 0
+ && GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
+ {
+ if (GET_GAME_STATE (LANDERS_LOST))
+ Response (i_lost_another_lander, TellMoonBase);
+ else
+ Response (i_lost_my_lander, TellMoonBase);
+ }
+ if (GLOBAL_SIS (FuelOnBoard) < 2 * FUEL_TANK_SCALE)
+ {
+ if (GET_GAME_STATE (GIVEN_FUEL_BEFORE))
+ Response (need_fuel_again, TellMoonBase);
+ else
+ Response (need_fuel_luna, TellMoonBase);
+ }
+ if (GET_GAME_STATE (WILL_DESTROY_BASE) == 0)
+ Response (we_will_take_care_of_base, ByeBye);
+ else
+ Response (take_care_of_base_again, ByeBye);
+ if (GET_GAME_STATE (STARBASE_YACK_STACK1) == 0)
+ Response (tell_me_about_base, TellMoonBase);
+ else
+ Response (tell_me_again, TellMoonBase);
+}
+
+static void RevealSelf (RESPONSE_REF R);
+
+static void
+TellProbe (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ NPCPhrase (THAT_WAS_PROBE);
+ DISABLE_PHRASE (what_was_red_thing);
+
+ Response (it_went_away, RevealSelf);
+ Response (we_destroyed_it, RevealSelf);
+ Response (what_probe, RevealSelf);
+}
+
+static void
+RevealSelf (RESPONSE_REF R)
+{
+ BYTE i, stack;
+
+ stack = 0;
+ if (PLAYER_SAID (R, we_are_vindicator0))
+ {
+ NPCPhrase (THATS_IMPOSSIBLE);
+
+ DISABLE_PHRASE (we_are_vindicator0);
+ }
+ else if (PLAYER_SAID (R, our_mission_was_secret))
+ {
+ NPCPhrase (ACKNOWLEDGE_SECRET);
+
+ DISABLE_PHRASE (our_mission_was_secret);
+ }
+ else if (PLAYER_SAID (R, first_give_info))
+ {
+ NPCPhrase (ASK_AWAY);
+
+ stack = 1;
+ DISABLE_PHRASE (first_give_info);
+ }
+ else if (PLAYER_SAID (R, whats_this_starbase))
+ {
+ NPCPhrase (STARBASE_IS);
+
+ stack = 1;
+ DISABLE_PHRASE (whats_this_starbase);
+ }
+ else if (PLAYER_SAID (R, what_about_earth))
+ {
+ NPCPhrase (HAPPENED_TO_EARTH);
+
+ stack = 1;
+ DISABLE_PHRASE (what_about_earth);
+ }
+ else if (PLAYER_SAID (R, where_are_urquan))
+ {
+ NPCPhrase (URQUAN_LEFT);
+
+ stack = 1;
+ DISABLE_PHRASE (where_are_urquan);
+ }
+ else if (PLAYER_SAID (R, it_went_away))
+ NPCPhrase (DEEP_TROUBLE);
+ else if (PLAYER_SAID (R, we_destroyed_it))
+ NPCPhrase (GOOD_NEWS);
+ else if (PLAYER_SAID (R, what_probe))
+ NPCPhrase (SURE_HOPE);
+
+ for (i = 0; i < 2; ++i, stack ^= 1)
+ {
+ if (stack == 1)
+ {
+ if (PHRASE_ENABLED (first_give_info))
+ Response (first_give_info, RevealSelf);
+ else if (PHRASE_ENABLED (whats_this_starbase))
+ Response (whats_this_starbase, RevealSelf);
+ else if (PHRASE_ENABLED (what_about_earth))
+ Response (what_about_earth, RevealSelf);
+ else if (PHRASE_ENABLED (where_are_urquan))
+ Response (where_are_urquan, RevealSelf);
+ else if (PHRASE_ENABLED (what_was_red_thing))
+ {
+ Response (what_was_red_thing, TellProbe);
+ }
+ }
+ else
+ {
+ if (PHRASE_ENABLED (we_are_vindicator0))
+ {
+ construct_response (shared_phrase_buf,
+ we_are_vindicator0,
+ GLOBAL_SIS (CommanderName),
+ we_are_vindicator1,
+ GLOBAL_SIS (ShipName),
+ we_are_vindicator2,
+ (UNICODE*)NULL);
+ DoResponsePhrase (we_are_vindicator0, RevealSelf, shared_phrase_buf);
+ }
+ else if (PHRASE_ENABLED (our_mission_was_secret))
+ Response (our_mission_was_secret, RevealSelf);
+ else
+ {
+ if (GET_GAME_STATE (MOONBASE_DESTROYED) == 0)
+ Response (we_are_here_to_help, TellMoonBase);
+ else
+ Response (we_are_here_to_help, BaseDestroyed);
+ }
+ }
+ }
+}
+
+static void
+GiveRadios (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, we_will_transfer_now))
+ {
+ SET_GAME_STATE (RADIOACTIVES_PROVIDED, 1);
+
+ NPCPhrase (FUEL_UP0);
+ NPCPhrase (FUEL_UP1);
+ AlienTalkSegue (1);
+
+ CommData.AlienAmbientArray[2].AnimFlags |= ANIM_DISABLED;
+
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 0)
+ ), ONE_SECOND / 2);
+
+ AlienTalkSegue ((COUNT)~0);
+
+ RevealSelf (0);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, what_will_you_give_us))
+ NPCPhrase (MESSAGE_GARBLED_1);
+ else if (PLAYER_SAID (R, before_radios_we_need_info))
+ NPCPhrase (MESSAGE_GARBLED_2);
+
+ Response (we_will_transfer_now, GiveRadios);
+ Response (what_will_you_give_us, GiveRadios);
+ Response (before_radios_we_need_info, GiveRadios);
+ }
+}
+
+static void
+Intro (void)
+{
+ if (GET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER))
+ {
+ NPCPhrase (VERY_IMPRESSIVE);
+
+ Response (cook_their_butts, ByeBye);
+ Response (overthrow_evil_aliens, ByeBye);
+ Response (annihilate_those_monsters, ByeBye);
+ }
+ else if (GET_GAME_STATE (STARBASE_VISITED))
+ {
+ if (GET_GAME_STATE (RADIOACTIVES_PROVIDED))
+ {
+ if (GET_GAME_STATE (MOONBASE_DESTROYED) == 0)
+ {
+ TellMoonBase (0);
+ }
+ else
+ {
+ BaseDestroyed (0);
+ }
+ }
+ else
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+ NPCPhrase (DO_YOU_HAVE_RADIO_THIS_TIME);
+
+ if (GLOBAL_SIS (ElementAmounts[RADIOACTIVE]))
+ GiveRadios (0);
+ else
+ AskAfterRadios (0);
+ }
+ }
+ else /* first visit */
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+
+ SET_GAME_STATE (STARBASE_VISITED, 1);
+
+ NPCPhrase (ARE_YOU_SUPPLY_SHIP);
+ construct_response (
+ shared_phrase_buf,
+ no_but_well_help0,
+ GLOBAL_SIS (ShipName),
+ no_but_well_help1,
+ (UNICODE*)NULL);
+ DoResponsePhrase (no_but_well_help0, NoRadioactives, shared_phrase_buf);
+ Response (yes_this_is_supply_ship, NoRadioactives);
+ Response (what_slave_planet, NoRadioactives);
+ }
+}
+
+static COUNT
+uninit_commander (void)
+{
+ return (0);
+}
+
+static void
+post_commander_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_commander_comm ()
+{
+ LOCDATA *retval;
+
+ commander_desc.init_encounter_func = Intro;
+ commander_desc.post_encounter_func = post_commander_enc;
+ commander_desc.uninit_encounter_func = uninit_commander;
+
+ if (GET_GAME_STATE (RADIOACTIVES_PROVIDED))
+ {
+ commander_desc.AlienAmbientArray[2].AnimFlags |= ANIM_DISABLED;
+ // regular track -- let's make sure
+ commander_desc.AlienSongFlags &= ~LDASF_USE_ALTERNATE;
+ }
+ else
+ {
+ commander_desc.AlienAmbientArray[2].AnimFlags &= ~ANIM_DISABLED;
+ // use alternate 'low-power' track if available
+ commander_desc.AlienAltSongRes = COMMANDER_LOWPOW_MUSIC;
+ commander_desc.AlienSongFlags |= LDASF_USE_ALTERNATE;
+ }
+
+ commander_desc.AlienTextWidth = 143;
+ commander_desc.AlienTextBaseline.x = 164;
+ commander_desc.AlienTextBaseline.y = 20;
+
+ setSegue (Segue_peace);
+ retval = &commander_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/comandr/resinst.h b/src/uqm/comm/comandr/resinst.h
new file mode 100644
index 0000000..7798214
--- /dev/null
+++ b/src/uqm/comm/comandr/resinst.h
@@ -0,0 +1,12 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define COMMANDER_COLOR_MAP "comm.commander.colortable"
+#define COMMANDER_CONVERSATION_PHRASES "comm.commander.dialogue"
+#define COMMANDER_FONT "comm.commander.font"
+#define COMMANDER_LOWPOW_MUSIC "comm.commander.lowpower.music"
+#define COMMANDER_MUSIC "comm.commander.music"
+#define COMMANDER_PMAP_ANIM "comm.commander.graphics"
+#define STARBASE_ALT_MUSIC "comm.starbase.music"
+#define STARBASE_CONVERSATION_PHRASES "comm.starbase.dialogue"
diff --git a/src/uqm/comm/comandr/strings.h b/src/uqm/comm/comandr/strings.h
new file mode 100644
index 0000000..fcc330e
--- /dev/null
+++ b/src/uqm/comm/comandr/strings.h
@@ -0,0 +1,127 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef COMANDR_STRINGS_H
+#define COMANDR_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ GLAD_WHEN_YOU_COME_BACK,
+ GIVE_FUEL,
+ GIVE_FUEL_AGAIN,
+ ARE_YOU_SUPPLY_SHIP,
+ DO_YOU_HAVE_RADIO_THIS_TIME,
+ HERE_IS_ANOTHER_LANDER,
+ THE_WHAT_FROM_WHERE,
+ ABOUT_TIME,
+ MESSAGE_GARBLED_1,
+ MESSAGE_GARBLED_2,
+ HERE_IS_A_NEW_LANDER,
+ THIS_MAY_SEEM_SILLY,
+ OK_THE_NAFS,
+ OK_THE_CAN,
+ OK_THE_UFW,
+ OK_THE_NAME_IS_EMPIRE0,
+ OK_THE_NAME_IS_EMPIRE1,
+ FUEL_UP0,
+ FUEL_UP1,
+ WHAT_KIND_OF_IDIOT,
+ DONT_KNOW_WHO_YOU_ARE,
+ THATS_IMPOSSIBLE,
+ ASK_AWAY,
+ RADIOS_ON_MERCURY,
+ THANKS_FOR_HELPING,
+ STARBASE_IS,
+ HAPPENED_TO_EARTH,
+ URQUAN_LEFT,
+ BASE_ON_MOON,
+ ACKNOWLEDGE_SECRET,
+ ABOUT_BASE,
+ GOOD_LUCK_WITH_BASE,
+ DEALT_WITH_BASE_YET,
+ HERE_COMES_ILWRATH,
+ VERY_IMPRESSIVE,
+ IT_WAS_ABANDONED,
+ YOU_REALLY_FOUGHT_BASE,
+ IM_GLAD_YOU_WON,
+ IM_SURE_IT_WAS_DIFFICULT,
+ THAT_WAS_PROBE,
+ DEEP_TROUBLE,
+ GOOD_NEWS,
+ SURE_HOPE,
+ ABOUT_BASE_AGAIN,
+ COOK_BUTTS,
+ OVERTHROW_ALIENS,
+ KILL_MONSTERS,
+ GOOD_LUCK_AGAIN,
+ STARBASE_WILL_BE_READY,
+
+ overthrow_evil_aliens,
+ annihilate_those_monsters,
+ cook_their_butts,
+ where_get_radios,
+ well_go_get_them_now,
+ we_will_transfer_now,
+ what_will_you_give_us,
+ before_radios_we_need_info,
+ no_but_well_help0,
+ no_but_well_help1,
+ yes_this_is_supply_ship,
+ what_slave_planet,
+ i_lied,
+ plumb_out,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ we_are_vindicator2,
+ first_give_info,
+ we_must_go_now,
+ where_can_i_get_radios,
+ ok_i_will_get_radios,
+ whats_this_starbase,
+ what_about_earth,
+ where_are_urquan,
+ our_mission_was_secret,
+ we_are_here_to_help,
+ tell_me_about_base,
+ we_will_take_care_of_base,
+ tell_me_again,
+ base_was_abandoned,
+ we_fought_them,
+ oh_yes_big_fight,
+ i_lied_it_was_abandoned,
+ i_cant_talk_about_it,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ i_lost_my_lander,
+ i_lost_another_lander,
+ need_fuel_mercury,
+ need_fuel_luna,
+ need_fuel_again,
+ what_was_red_thing,
+ it_went_away,
+ we_destroyed_it,
+ what_probe,
+ take_care_of_base_again,
+ goodbye_commander,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/commall.h b/src/uqm/comm/commall.h
new file mode 100644
index 0000000..4f8ebed
--- /dev/null
+++ b/src/uqm/comm/commall.h
@@ -0,0 +1,26 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_COMM_COMMALL_H_
+#define UQM_COMM_COMMALL_H_
+
+#include "uqm/colors.h"
+#include "uqm/comm.h"
+#include "uqm/commglue.h"
+#include "libs/reslib.h"
+
+#endif /* UQM_COMM_COMMALL_H_ */
+
diff --git a/src/uqm/comm/druuge/Makeinfo b/src/uqm/comm/druuge/Makeinfo
new file mode 100644
index 0000000..733ed6c
--- /dev/null
+++ b/src/uqm/comm/druuge/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="druugec.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/druuge/druugec.c b/src/uqm/comm/druuge/druugec.c
new file mode 100644
index 0000000..9a082c1
--- /dev/null
+++ b/src/uqm/comm/druuge/druugec.c
@@ -0,0 +1,926 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/setup.h"
+#include "uqm/sis.h"
+ // for DeltaSISGauges()
+
+
+static LOCDATA druuge_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ DRUUGE_PMAP_ANIM, /* AlienFrame */
+ DRUUGE_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ DRUUGE_COLOR_MAP, /* AlienColorMap */
+ DRUUGE_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ DRUUGE_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 11, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 5, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 9, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 13, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 19, /* StartIndex */
+ 3, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 25, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 28, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 31, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 33, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND * 7, ONE_SECOND * 3,/* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 40, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 10, 0, /* FrameRate */
+ ONE_SECOND * 3 / 10, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 44, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 5, 0, /* FrameRate */
+ ONE_SECOND / 5, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 4, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 12, ONE_SECOND / 12, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static COUNT SlaveryCount = 0;
+static BOOLEAN AttemptedSalvage = FALSE;
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, bye))
+ {
+ setSegue (Segue_peace);
+
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ if (SlaveryCount)
+ {
+ UWORD PreviousSlaves;
+
+ PreviousSlaves = MAKE_WORD (
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE0),
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE1)
+ );
+ SlaveryCount += PreviousSlaves;
+ if (SlaveryCount > 250 && PreviousSlaves <= 250)
+ {
+ if (PreviousSlaves > 100)
+ GLOBAL (CrewCost) += (22 - 7);
+ else
+ GLOBAL (CrewCost) += 22;
+ }
+ else if (SlaveryCount > 100 && PreviousSlaves <= 100)
+ GLOBAL (CrewCost) += 7;
+
+ SET_GAME_STATE (CREW_SOLD_TO_DRUUGE0, LOBYTE (SlaveryCount));
+ SET_GAME_STATE (CREW_SOLD_TO_DRUUGE1, HIBYTE (SlaveryCount));
+ }
+
+ switch (GET_GAME_STATE (DRUUGE_HOME_VISITS))
+ {
+ case 1:
+ NPCPhrase (BYE_FROM_TRADE_WORLD_1);
+ break;
+ default:
+ NPCPhrase (BYE_FROM_TRADE_WORLD_2);
+ break;
+ }
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ NPCPhrase (GOODBYE_FROM_BOMB_PLANET);
+ else
+ NPCPhrase (GOODBYE_FROM_SPACE);
+ }
+ else /* if (R == then_we_take_bomb) */
+ {
+ setSegue (Segue_hostile);
+
+ NPCPhrase (FIGHT_FOR_BOMB);
+ }
+}
+
+static void TradeWorld (RESPONSE_REF R);
+
+static void
+Buy (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, want_to_buy)
+ || PLAYER_SAID (R, im_ready_to_buy))
+ {
+ NPCPhrase (READY_TO_SELL);
+ if (!GET_GAME_STATE (ROSY_SPHERE))
+ NPCPhrase (HAVE_SPHERE);
+ if (!GET_GAME_STATE (ARTIFACT_2_ON_SHIP))
+ NPCPhrase (HAVE_ART_1);
+ if (!GET_GAME_STATE (ARTIFACT_3_ON_SHIP))
+ NPCPhrase (HAVE_ART_2);
+ NPCPhrase (SHIPS_AND_FUEL);
+
+ SET_GAME_STATE (KNOW_DRUUGE_SLAVERS, 3);
+ }
+ else if (PLAYER_SAID (R, buy_druuge_ship))
+ {
+#define SHIP_CREW_COST 100
+ if (GLOBAL_SIS (CrewEnlisted) < SHIP_CREW_COST)
+ NPCPhrase (NOT_ENOUGH_CREW);
+ else if (EscortFeasibilityStudy (DRUUGE_SHIP) == 0)
+ NPCPhrase (NOT_ENOUGH_ROOM);
+ else
+ {
+ DeltaSISGauges (-SHIP_CREW_COST, 0, 0);
+ SlaveryCount += SHIP_CREW_COST;
+ AddEscortShips (DRUUGE_SHIP, 1);
+
+ NPCPhrase (BOUGHT_SHIP);
+ }
+ }
+#define ARTIFACT_CREW_COST 100
+ else if (PLAYER_SAID (R, buy_rosy_sphere))
+ {
+ if (GLOBAL_SIS (CrewEnlisted) < ARTIFACT_CREW_COST)
+ NPCPhrase (NOT_ENOUGH_CREW);
+ else
+ {
+ DeltaSISGauges (-ARTIFACT_CREW_COST, 0, 0);
+ SlaveryCount += ARTIFACT_CREW_COST;
+ SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 1);
+ SET_GAME_STATE (ROSY_SPHERE, 1);
+
+ NPCPhrase (BOUGHT_SPHERE);
+ }
+ }
+ else if (PLAYER_SAID (R, buy_art_1))
+ {
+ if (GLOBAL_SIS (CrewEnlisted) < ARTIFACT_CREW_COST)
+ NPCPhrase (NOT_ENOUGH_CREW);
+ else
+ {
+ DeltaSISGauges (-ARTIFACT_CREW_COST, 0, 0);
+ SlaveryCount += ARTIFACT_CREW_COST;
+ SET_GAME_STATE (ARTIFACT_2_ON_SHIP, 1);
+
+ NPCPhrase (BOUGHT_ART_1);
+ }
+ }
+ else if (PLAYER_SAID (R, buy_art_2))
+ {
+ if (GLOBAL_SIS (CrewEnlisted) < ARTIFACT_CREW_COST)
+ NPCPhrase (NOT_ENOUGH_CREW);
+ else
+ {
+ DeltaSISGauges (-ARTIFACT_CREW_COST, 0, 0);
+ SlaveryCount += ARTIFACT_CREW_COST;
+ SET_GAME_STATE (ARTIFACT_3_ON_SHIP, 1);
+
+ NPCPhrase (BOUGHT_ART_2);
+ }
+ }
+ else if (PLAYER_SAID (R, buy_fuel))
+ {
+#define FUEL_CREW_COST 10
+ if (GLOBAL_SIS (CrewEnlisted) < FUEL_CREW_COST)
+ NPCPhrase (NOT_ENOUGH_CREW);
+ else
+ {
+ DeltaSISGauges (-FUEL_CREW_COST,
+ FUEL_CREW_COST * FUEL_TANK_SCALE, 0);
+ SlaveryCount += FUEL_CREW_COST;
+
+ NPCPhrase (BOUGHT_FUEL);
+ }
+ }
+
+ Response (buy_druuge_ship, Buy);
+ if (!GET_GAME_STATE (ROSY_SPHERE))
+ Response (buy_rosy_sphere, Buy);
+ if (!GET_GAME_STATE (ARTIFACT_2_ON_SHIP))
+ Response (buy_art_1, Buy);
+ if (!GET_GAME_STATE (ARTIFACT_3_ON_SHIP))
+ Response (buy_art_2, Buy);
+ Response (buy_fuel, Buy);
+ Response (done_buying, TradeWorld);
+}
+
+static void Sell (RESPONSE_REF R);
+
+static RESPONSE_REF LastResponse = 0;
+
+static void
+Trade (RESPONSE_REF R)
+{
+ if (!PLAYER_SAID (R, whats_the_sphere_again))
+ {
+ NPCPhrase (TRADE_FOR_SPHERE);
+ LastResponse = R;
+ }
+ else
+ {
+ NPCPhrase (SPHERE_IS);
+
+ DISABLE_PHRASE (whats_the_sphere_again);
+ }
+
+ Response (no_way, Sell);
+ Response (way, Sell);
+ if (PHRASE_ENABLED (whats_the_sphere_again))
+ {
+ Response (whats_the_sphere_again, Trade);
+ }
+}
+
+static void
+DoTransaction (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, sell_maidens))
+ {
+ SET_GAME_STATE (MAIDENS_ON_SHIP, 0);
+ }
+ else if (PLAYER_SAID (R, sell_fragments))
+ {
+ BYTE num_frags;
+
+ if (GET_GAME_STATE (EGG_CASE0_ON_SHIP))
+ {
+ SET_GAME_STATE (EGG_CASE0_ON_SHIP, 0);
+ }
+ else if (GET_GAME_STATE (EGG_CASE1_ON_SHIP))
+ {
+ SET_GAME_STATE (EGG_CASE1_ON_SHIP, 0);
+ }
+ else if (GET_GAME_STATE (EGG_CASE2_ON_SHIP))
+ {
+ SET_GAME_STATE (EGG_CASE2_ON_SHIP, 0);
+ }
+
+ num_frags = GET_GAME_STATE (FRAGMENTS_BOUGHT) + 1;
+ SET_GAME_STATE (FRAGMENTS_BOUGHT, num_frags);
+ }
+ else if (PLAYER_SAID (R, sell_caster))
+ {
+ SET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP, 0);
+ }
+ else if (PLAYER_SAID (R, sell_spawner))
+ {
+ SET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP, 0);
+ }
+
+ if (!GET_GAME_STATE (ROSY_SPHERE)
+ && GET_GAME_STATE (ROSY_SPHERE_ON_SHIP))
+ {
+ SET_GAME_STATE (ROSY_SPHERE, 1);
+ }
+ else
+ {
+ BYTE trade_gas;
+ BYTE ship_slots, ships_to_trade;
+
+ trade_gas = 0;
+ ships_to_trade = 0;
+ ship_slots = EscortFeasibilityStudy (DRUUGE_SHIP);
+ if (PLAYER_SAID (R, sell_maidens))
+ {
+ NPCPhrase (BOUGHT_MAIDENS);
+ ships_to_trade = 6;
+ }
+ else if (PLAYER_SAID (R, sell_fragments))
+ {
+ NPCPhrase (BOUGHT_FRAGMENTS);
+ ships_to_trade = 1;
+ }
+ else if (PLAYER_SAID (R, sell_caster))
+ {
+ NPCPhrase (BOUGHT_CASTER);
+ ships_to_trade = 0;
+ trade_gas = 1;
+ }
+ else if (PLAYER_SAID (R, sell_spawner))
+ {
+ NPCPhrase (BOUGHT_SPAWNER);
+ ships_to_trade = 3;
+ trade_gas = 1;
+ }
+
+ NPCPhrase (YOU_GET);
+ if (ships_to_trade)
+ {
+ AddEscortShips (DRUUGE_SHIP, ships_to_trade);
+
+ if (ship_slots >= ships_to_trade)
+ NPCPhrase (DEAL_FOR_STATED_SHIPS);
+ else if (ship_slots == 0)
+ NPCPhrase (DEAL_FOR_NO_SHIPS);
+ else
+ NPCPhrase (DEAL_FOR_LESS_SHIPS);
+
+ if (trade_gas)
+ NPCPhrase (YOU_ALSO_GET);
+ }
+
+ if (trade_gas)
+ {
+ BYTE slot;
+ COUNT f;
+ DWORD capacity;
+
+ capacity = FUEL_RESERVE;
+ slot = NUM_MODULE_SLOTS - 1;
+ do
+ {
+ if (GLOBAL_SIS (ModuleSlots[slot]) == FUEL_TANK
+ || GLOBAL_SIS (ModuleSlots[slot]) == HIGHEFF_FUELSYS)
+ {
+ COUNT volume;
+
+ volume = GLOBAL_SIS (ModuleSlots[slot]) == FUEL_TANK
+ ? FUEL_TANK_CAPACITY : HEFUEL_TANK_CAPACITY;
+ capacity += volume;
+ }
+ } while (slot--);
+ capacity -= GLOBAL_SIS (FuelOnBoard);
+ f = (COUNT)((capacity + (FUEL_TANK_SCALE >> 1)) / FUEL_TANK_SCALE);
+
+ while (capacity > 0x3FFFL)
+ {
+ DeltaSISGauges (0, 0x3FFF, 0);
+ capacity -= 0x3FFF;
+ }
+ DeltaSISGauges (0, (SIZE)capacity, 0);
+
+ NPCPhrase (FUEL0);
+ NPCNumber (f, NULL);
+ NPCPhrase (FUEL1);
+
+ if (f >= 250)
+ NPCPhrase (HIDEOUS_DEAL);
+ else if (f >= 100)
+ NPCPhrase (BAD_DEAL);
+ else if (f >= 50)
+ NPCPhrase (FAIR_DEAL);
+ else if (f >= 10)
+ NPCPhrase (GOOD_DEAL);
+ else
+ NPCPhrase (FINE_DEAL);
+ }
+ }
+}
+
+static void
+Sell (RESPONSE_REF R)
+{
+ RESPONSE_FUNC RespFunc;
+
+ if (PLAYER_SAID (R, want_to_sell))
+ NPCPhrase (READY_TO_BUY);
+ else if (PLAYER_SAID (R, no_way)
+ || PLAYER_SAID (R, way))
+ {
+ if (PLAYER_SAID (R, no_way))
+ NPCPhrase (OK_REGULAR_DEAL);
+ else
+ {
+ NPCPhrase (OK_HERES_SPHERE);
+
+ SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 1);
+ }
+
+ DoTransaction (LastResponse);
+ }
+ else if (PLAYER_SAID (R, sell_maidens)
+ || PLAYER_SAID (R, sell_fragments)
+ || PLAYER_SAID (R, sell_caster)
+ || PLAYER_SAID (R, sell_spawner))
+ {
+ DoTransaction (R);
+ }
+
+ if (!GET_GAME_STATE (ROSY_SPHERE))
+ RespFunc = (RESPONSE_FUNC)Trade;
+ else
+ RespFunc = (RESPONSE_FUNC)Sell;
+ if (GET_GAME_STATE (MAIDENS_ON_SHIP))
+ Response (sell_maidens, RespFunc);
+ if ((GET_GAME_STATE (EGG_CASE0_ON_SHIP)
+ || GET_GAME_STATE (EGG_CASE1_ON_SHIP)
+ || GET_GAME_STATE (EGG_CASE2_ON_SHIP))
+ && GET_GAME_STATE (FRAGMENTS_BOUGHT) < 2)
+ Response (sell_fragments, RespFunc);
+ if (GET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP))
+ Response (sell_caster, RespFunc);
+ if (GET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP))
+ Response (sell_spawner, RespFunc);
+ Response (done_selling, TradeWorld);
+}
+
+static void
+ExplainSlaveTrade (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, want_to_buy))
+ NPCPhrase (WE_SELL_FOR_CREW);
+ else if (PLAYER_SAID (R, isnt_this_slave_trading))
+ {
+ NPCPhrase (NO_SLAVE_TRADE);
+
+ SET_GAME_STATE (KNOW_DRUUGE_SLAVERS, 1);
+ }
+ else if (PLAYER_SAID (R, what_do_with_crew))
+ {
+ NPCPhrase (HAVE_FUN);
+
+ SET_GAME_STATE (KNOW_DRUUGE_SLAVERS, 2);
+ }
+
+ switch (GET_GAME_STATE (KNOW_DRUUGE_SLAVERS))
+ {
+ case 0:
+ Response (isnt_this_slave_trading, ExplainSlaveTrade);
+ break;
+ case 1:
+ Response (what_do_with_crew, ExplainSlaveTrade);
+ break;
+ }
+ Response (i_will_never_trade_crew, TradeWorld);
+ Response (im_ready_to_buy, Buy);
+}
+
+static void
+TradeWorld (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_at_trade_world))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (DRUUGE_HOME_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GEN_INFO_AT_TRADE_WORLD_1);
+ break;
+ case 1:
+ NPCPhrase (GEN_INFO_AT_TRADE_WORLD_2);
+ break;
+ case 2:
+ NPCPhrase (GEN_INFO_AT_TRADE_WORLD_3);
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) < 2)
+ {
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 2);
+ }
+ break;
+ case 3:
+ NPCPhrase (GEN_INFO_AT_TRADE_WORLD_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_HOME_INFO, NumVisits);
+ DISABLE_PHRASE (whats_up_at_trade_world);
+ }
+ else if (PLAYER_SAID (R, done_selling))
+ NPCPhrase (OK_DONE_SELLING);
+ else if (PLAYER_SAID (R, done_buying))
+ NPCPhrase (OK_DONE_BUYING);
+ else if (PLAYER_SAID (R, i_will_never_trade_crew))
+ NPCPhrase (YOUR_LOSS);
+
+ if (PHRASE_ENABLED (whats_up_at_trade_world))
+ {
+ Response (whats_up_at_trade_world, TradeWorld);
+ }
+ Response (want_to_sell, Sell);
+ if (GET_GAME_STATE (KNOW_DRUUGE_SLAVERS) == 3)
+ Response (want_to_buy, Buy);
+ else
+ Response (want_to_buy, ExplainSlaveTrade);
+ Response (bye, ExitConversation);
+}
+
+static void
+BombAmbush (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_at_bomb_planet))
+ {
+ NPCPhrase (GEN_INFO_AT_BOMB_PLANET);
+ SET_GAME_STATE (BOMB_VISITS, 2);
+ }
+ else if (PLAYER_SAID (R, we_get_bomb))
+ {
+ NPCPhrase (NOT_GET_BOMB);
+ SET_GAME_STATE (BOMB_VISITS, 3);
+ }
+
+ switch (GET_GAME_STATE (BOMB_VISITS))
+ {
+ case 1:
+ Response (whats_up_at_bomb_planet, BombAmbush);
+ break;
+ case 2:
+ Response (we_get_bomb, BombAmbush);
+ break;
+ default:
+ Response (then_we_take_bomb, ExitConversation);
+ break;
+ }
+ Response (bye, ExitConversation);
+}
+
+static void
+Space (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_in_space))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (DRUUGE_SPACE_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_IN_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_IN_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_IN_SPACE_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_IN_SPACE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_SPACE_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_in_space);
+ }
+
+ if (PHRASE_ENABLED (whats_up_in_space))
+ {
+ Response (whats_up_in_space, Space);
+ }
+ Response (bye, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (DRUUGE_MANNER))
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (DRUUGE_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HSTL_TRADE_WORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HSTL_TRADE_WORLD_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (DRUUGE_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_VISITS, NumVisits);
+ }
+
+ setSegue (Segue_hostile);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ {
+ NumVisits = GET_GAME_STATE (DRUUGE_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INITIAL_TRADE_WORLD_HELLO);
+ break;
+ case 1:
+ NPCPhrase (SSQ_TRADE_WORLD_HELLO_1);
+ break;
+ case 2:
+ NPCPhrase (SSQ_TRADE_WORLD_HELLO_2);
+ break;
+ case 3:
+ NPCPhrase (SSQ_TRADE_WORLD_HELLO_3);
+ break;
+ case 4:
+ NPCPhrase (SSQ_TRADE_WORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_HOME_VISITS, NumVisits);
+ }
+ if (GET_GAME_STATE (ATTACKED_DRUUGE)
+ && !GET_GAME_STATE (DRUUGE_DISCLAIMER))
+ {
+ // There is no HOSTILE_TRADE voice track that we know of
+ // so this is currently disabled
+ //NPCPhrase (HOSTILE_TRADE);
+ SET_GAME_STATE (DRUUGE_DISCLAIMER, 1);
+ }
+ if (GET_GAME_STATE (MAIDENS_ON_SHIP)
+ && !GET_GAME_STATE (SCANNED_MAIDENS))
+ {
+ NPCPhrase (SCAN_MAIDENS);
+ SET_GAME_STATE (SCANNED_MAIDENS, 1);
+ }
+ if ((GET_GAME_STATE (EGG_CASE0_ON_SHIP)
+ || GET_GAME_STATE (EGG_CASE1_ON_SHIP)
+ || GET_GAME_STATE (EGG_CASE2_ON_SHIP))
+ && !GET_GAME_STATE (SCANNED_FRAGMENTS))
+ {
+ if (GET_GAME_STATE (FRAGMENTS_BOUGHT) < 2)
+ NPCPhrase (SCAN_FRAGMENTS);
+ else
+ NPCPhrase (ENOUGH_FRAGMENTS);
+ SET_GAME_STATE (SCANNED_FRAGMENTS, 1);
+ }
+ if (GET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP)
+ && !GET_GAME_STATE (SCANNED_CASTER))
+ {
+ NPCPhrase (SCAN_DRUUGE_CASTER);
+ SET_GAME_STATE (SCANNED_CASTER, 1);
+ }
+ if (GET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP)
+ && !GET_GAME_STATE (SCANNED_SPAWNER))
+ {
+ NPCPhrase (SCAN_ARILOU_SPAWNER);
+ SET_GAME_STATE (SCANNED_SPAWNER, 1);
+ }
+
+ TradeWorld ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ if (GET_GAME_STATE (BOMB_VISITS))
+ NPCPhrase (SUBSEQ_BOMB_WORLD_HELLO);
+ else
+ {
+ NPCPhrase (INIT_BOMB_WORLD_HELLO);
+ SET_GAME_STATE (BOMB_VISITS, 1);
+ }
+
+ BombAmbush ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (ATTACKED_DRUUGE))
+ {
+ NumVisits = GET_GAME_STATE (DRUUGE_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_VISITS, NumVisits);
+
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ NumVisits = 0;
+ if (GetHeadLink (&GLOBAL (built_ship_q)) == 0)
+ {
+ for (NumVisits = 0; NumVisits < NUM_MODULE_SLOTS; ++NumVisits)
+ {
+ BYTE which_module;
+
+ which_module = GLOBAL_SIS (ModuleSlots[NumVisits]);
+ if (which_module >= GUN_WEAPON
+ && which_module <= CANNON_WEAPON)
+ {
+ NumVisits = 0;
+ break;
+ }
+ }
+ }
+
+ if (NumVisits)
+ {
+ NumVisits = GET_GAME_STATE (DRUUGE_SALVAGE);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (SALVAGE_YOUR_SHIP_1);
+ break;
+ case 1:
+ NPCPhrase (SALVAGE_YOUR_SHIP_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_SALVAGE, NumVisits);
+
+ setSegue (Segue_hostile);
+ AttemptedSalvage = TRUE;
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (DRUUGE_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_SPACE_HELLO);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_SPACE_HELLO);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (DRUUGE_VISITS, NumVisits);
+
+ Space ((RESPONSE_REF)0);
+ }
+ }
+}
+
+static COUNT
+uninit_druuge (void)
+{
+ return (0);
+}
+
+static void
+post_druuge_enc (void)
+{
+ if (getSegue () == Segue_hostile
+ && !AttemptedSalvage
+ && !GET_GAME_STATE (DRUUGE_MANNER))
+ {
+ if (!GET_GAME_STATE (ATTACKED_DRUUGE))
+ {
+ SET_GAME_STATE (ATTACKED_DRUUGE, 1);
+ SET_GAME_STATE (DRUUGE_VISITS, 0);
+ }
+ }
+}
+
+LOCDATA*
+init_druuge_comm (void)
+{
+ LOCDATA *retval;
+
+ SlaveryCount = 0;
+ AttemptedSalvage = FALSE;
+
+ druuge_desc.init_encounter_func = Intro;
+ druuge_desc.post_encounter_func = post_druuge_enc;
+ druuge_desc.uninit_encounter_func = uninit_druuge;
+
+ druuge_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ druuge_desc.AlienTextBaseline.y = 70;
+ druuge_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if ((GET_GAME_STATE (DRUUGE_MANNER) == 0
+ && (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7)))
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &druuge_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/druuge/resinst.h b/src/uqm/comm/druuge/resinst.h
new file mode 100644
index 0000000..ca82a22
--- /dev/null
+++ b/src/uqm/comm/druuge/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define DRUUGE_COLOR_MAP "comm.druuge.colortable"
+#define DRUUGE_CONVERSATION_PHRASES "comm.druuge.dialogue"
+#define DRUUGE_FONT "comm.druuge.font"
+#define DRUUGE_MUSIC "comm.druuge.music"
+#define DRUUGE_PMAP_ANIM "comm.druuge.graphics"
diff --git a/src/uqm/comm/druuge/strings.h b/src/uqm/comm/druuge/strings.h
new file mode 100644
index 0000000..e9dfde9
--- /dev/null
+++ b/src/uqm/comm/druuge/strings.h
@@ -0,0 +1,132 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DRUUGE_STRINGS_H
+#define DRUUGE_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ AMBUSH_IS_FIRST_HELLO,
+ INIT_BOMB_WORLD_HELLO,
+ SUBSEQ_BOMB_WORLD_HELLO,
+ whats_up_at_bomb_planet,
+ GEN_INFO_AT_BOMB_PLANET,
+ we_get_bomb,
+ NOT_GET_BOMB,
+ then_we_take_bomb,
+ FIGHT_FOR_BOMB,
+ GOODBYE_FROM_BOMB_PLANET,
+ NOT_ENOUGH_ROOM,
+ TRADE_FOR_SPHERE,
+ no_way,
+ OK_REGULAR_DEAL,
+ way,
+ OK_HERES_SPHERE,
+ whats_the_sphere_again,
+ SPHERE_IS,
+ WE_SELL_FOR_CREW,
+ i_will_never_trade_crew,
+ YOUR_LOSS,
+ isnt_this_slave_trading,
+ NO_SLAVE_TRADE,
+ what_do_with_crew,
+ HAVE_FUN,
+ im_ready_to_buy,
+ THIS_FOR_SALE,
+ HAVE_SPHERE,
+ HAVE_ART_2,
+ HAVE_ART_1,
+ SHIPS_AND_FUEL,
+ BOUGHT_SHIP,
+ BOUGHT_FUEL,
+ BOUGHT_ART_2,
+ BOUGHT_ART_1,
+ BOUGHT_SPHERE,
+ repeat_what_to_sell,
+ INIT_SPACE_HELLO,
+ SUBSEQUENT_SPACE_HELLO,
+ whats_up_in_space,
+ GENERAL_INFO_IN_SPACE_1,
+ GENERAL_INFO_IN_SPACE_2,
+ GENERAL_INFO_IN_SPACE_3,
+ GENERAL_INFO_IN_SPACE_4,
+ GOODBYE_FROM_SPACE,
+ HSTL_TRADE_WORLD_HELLO_1,
+ HSTL_TRADE_WORLD_HELLO_2,
+ HOSTILE_SPACE_HELLO_1,
+ HOSTILE_SPACE_HELLO_2,
+ INITIAL_TRADE_WORLD_HELLO,
+ SSQ_TRADE_WORLD_HELLO_1,
+ SSQ_TRADE_WORLD_HELLO_2,
+ SSQ_TRADE_WORLD_HELLO_3,
+ SSQ_TRADE_WORLD_HELLO_4,
+ whats_up_at_trade_world,
+ GEN_INFO_AT_TRADE_WORLD_1,
+ GEN_INFO_AT_TRADE_WORLD_2,
+ GEN_INFO_AT_TRADE_WORLD_3,
+ GEN_INFO_AT_TRADE_WORLD_4,
+ SCAN_MAIDENS,
+ SCAN_FRAGMENTS,
+ SCAN_DRUUGE_CASTER,
+ SCAN_ARILOU_SPAWNER,
+ ENOUGH_FRAGMENTS,
+ READY_TO_BUY,
+ READY_TO_SELL,
+ BYE_FROM_TRADE_WORLD_1,
+ BYE_FROM_TRADE_WORLD_2,
+ NOT_ENOUGH_CREW,
+ EXCHANGE_MADE,
+ OK_DONE_BUYING,
+ OK_DONE_SELLING,
+ bye,
+ want_to_sell,
+ want_to_buy,
+ buy_druuge_ship,
+ buy_fuel,
+ buy_art_1,
+ buy_art_2,
+ buy_rosy_sphere,
+ done_buying,
+ done_selling,
+ sell_maidens,
+ sell_caster,
+ sell_fragments,
+ sell_spawner,
+ BOUGHT_MAIDENS,
+ BOUGHT_FRAGMENTS,
+ BOUGHT_CASTER,
+ YOU_GET,
+ YOU_ALSO_GET,
+ BOUGHT_SPAWNER,
+ SALVAGE_YOUR_SHIP_1,
+ SALVAGE_YOUR_SHIP_2,
+ DEAL_FOR_STATED_SHIPS,
+ DEAL_FOR_LESS_SHIPS,
+ DEAL_FOR_NO_SHIPS,
+ FUEL0,
+ FUEL1,
+ HIDEOUS_DEAL,
+ BAD_DEAL,
+ FAIR_DEAL,
+ GOOD_DEAL,
+ FINE_DEAL,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/ilwrath/Makeinfo b/src/uqm/comm/ilwrath/Makeinfo
new file mode 100644
index 0000000..3f40e79
--- /dev/null
+++ b/src/uqm/comm/ilwrath/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="ilwrathc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/ilwrath/ilwrathc.c b/src/uqm/comm/ilwrath/ilwrathc.c
new file mode 100644
index 0000000..1afd812
--- /dev/null
+++ b/src/uqm/comm/ilwrath/ilwrathc.c
@@ -0,0 +1,649 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/gameev.h"
+
+
+static LOCDATA ilwrath_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ ILWRATH_PMAP_ANIM, /* AlienFrame */
+ ILWRATH_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ ILWRATH_COLOR_MAP, /* AlienColorMap */
+ ILWRATH_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ ILWRATH_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 4, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 6, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 11, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 16, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 21, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 5, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+CombatIsInevitable (RESPONSE_REF R)
+{
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, you_are_weak))
+ NPCPhrase (STRENGTH_NOT_ALL);
+ else if (PLAYER_SAID (R, slay_by_thousands))
+ NPCPhrase (NO_SLAY_BY_THOUSANDS);
+ else if (PLAYER_SAID (R, ease_up))
+ NPCPhrase (NO_EASE_UP);
+ else if (PLAYER_SAID (R, bye_space))
+ NPCPhrase (GOODBYE_AND_DIE_SPACE);
+ else if (PLAYER_SAID (R, bye_homeworld))
+ NPCPhrase (GOODBYE_AND_DIE_HOMEWORLD);
+ else if (PLAYER_SAID (R, want_peace))
+ NPCPhrase (NO_PEACE);
+ else if (PLAYER_SAID (R, want_alliance))
+ NPCPhrase (NO_ALLIANCE);
+ else if (PLAYER_SAID (R, but_evil_is_defined))
+ NPCPhrase (DONT_CONFUSE_US);
+ else if (PLAYER_SAID (R, bye_gods))
+ {
+ NPCPhrase (GOODBYE_GODS);
+
+ setSegue (Segue_peace);
+ }
+ if (PLAYER_SAID (R, whats_up))
+ {
+ NPCPhrase (GENERAL_INFO);
+ Response (bye, CombatIsInevitable);
+ }
+ else if (PLAYER_SAID (R, whats_up_space_1)
+ || PLAYER_SAID (R, whats_up_space_2)
+ || PLAYER_SAID (R, whats_up_space_3)
+ || PLAYER_SAID (R, whats_up_space_4)
+ || PLAYER_SAID (R, whats_up_space_5))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (ILWRATH_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_SPACE_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_SPACE_4);
+ break;
+ case 4:
+ NPCPhrase (GENERAL_INFO_SPACE_5);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ILWRATH_INFO, NumVisits);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, bye))
+ NPCPhrase (GOODBYE_AND_DIE);
+ else if (PLAYER_SAID (R, where_you_come_from))
+ NPCPhrase (CAME_FROM);
+ if (PLAYER_SAID (R, it_will_be_a_pleasure))
+ NPCPhrase (WHO_BLASTS_WHO);
+ if (PLAYER_SAID (R, surrender))
+ NPCPhrase (NO_SURRENDER);
+ if (PLAYER_SAID (R, be_reasonable))
+ NPCPhrase (NOT_REASONABLE);
+ }
+}
+
+static void IlwrathHome (RESPONSE_REF R);
+
+static void
+IlwrathGods (RESPONSE_REF R)
+{
+ BYTE GodsLeft;
+
+ GodsLeft = FALSE;
+ if (PLAYER_SAID (R, want_info_on_gods))
+ NPCPhrase (SO_MUCH_TO_KNOW);
+ else if (PLAYER_SAID (R, when_start_worship))
+ {
+ NPCPhrase (LONG_AGO);
+
+ DISABLE_PHRASE (when_start_worship);
+ }
+ else if (PLAYER_SAID (R, any_good_gods))
+ {
+ NPCPhrase (KILLED_GOOD_GODS);
+
+ DISABLE_PHRASE (any_good_gods);
+ }
+ else if (PLAYER_SAID (R, how_talk_with_gods))
+ {
+ NPCPhrase (CHANNEL_44);
+
+ DISABLE_PHRASE (how_talk_with_gods);
+ }
+ else if (PLAYER_SAID (R, why_44))
+ {
+ NPCPhrase (BECAUSE_44);
+
+ DISABLE_PHRASE (why_44);
+ }
+
+ if (PHRASE_ENABLED (when_start_worship))
+ {
+ Response (when_start_worship, IlwrathGods);
+ GodsLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (any_good_gods))
+ {
+ Response (any_good_gods, IlwrathGods);
+ GodsLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (how_talk_with_gods))
+ {
+ Response (how_talk_with_gods, IlwrathGods);
+ GodsLeft = TRUE;
+ }
+ else if (PHRASE_ENABLED (why_44))
+ {
+ Response (why_44, IlwrathGods);
+ GodsLeft = TRUE;
+ }
+ Response (enough_gods, IlwrathHome);
+
+ if (!GodsLeft)
+ DISABLE_PHRASE (want_info_on_gods);
+}
+
+static void
+IlwrathInfo (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ InfoLeft = FALSE;
+ if (PLAYER_SAID (R, want_info_on_ilwrath))
+ NPCPhrase (WHAT_ABOUT_ILWRATH);
+ else if (PLAYER_SAID (R, what_about_physio))
+ {
+ NPCPhrase (ABOUT_PHYSIO);
+
+ DISABLE_PHRASE (what_about_physio);
+ }
+ else if (PLAYER_SAID (R, what_about_history))
+ {
+ NPCPhrase (ABOUT_HISTORY);
+
+ DISABLE_PHRASE (what_about_history);
+ }
+ else if (PLAYER_SAID (R, what_about_culture))
+ {
+ NPCPhrase (ABOUT_CULTURE);
+
+ DISABLE_PHRASE (what_about_culture);
+ }
+ else if (PLAYER_SAID (R, what_about_urquan))
+ {
+ NPCPhrase (URQUAN_TOO_NICE);
+
+ DISABLE_PHRASE (what_about_urquan);
+ }
+ else if (PLAYER_SAID (R, are_you_evil))
+ {
+ NPCPhrase (OF_COURSE_WERE_EVIL);
+
+ DISABLE_PHRASE (are_you_evil);
+ }
+
+ if (PHRASE_ENABLED (what_about_physio))
+ {
+ Response (what_about_physio, IlwrathInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_history))
+ {
+ Response (what_about_history, IlwrathInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_culture))
+ {
+ Response (what_about_culture, IlwrathInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_urquan))
+ {
+ Response (what_about_urquan, IlwrathInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (are_you_evil))
+ {
+ Response (are_you_evil, IlwrathInfo);
+ InfoLeft = TRUE;
+ }
+ else
+ {
+ Response (but_evil_is_defined, CombatIsInevitable);
+ InfoLeft = TRUE;
+ }
+ Response (enough_ilwrath, IlwrathHome);
+
+ if (!InfoLeft)
+ DISABLE_PHRASE (want_info_on_ilwrath);
+}
+
+static void
+IlwrathHome (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (ILWRATH_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_HOME_HELLO);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_HOME_HELLO);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ILWRATH_HOME_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, enough_gods))
+ NPCPhrase (OK_ENOUGH_GODS);
+ else if (PLAYER_SAID (R, enough_ilwrath))
+ NPCPhrase (OK_ENOUGH_ILWRATH);
+
+ if (PHRASE_ENABLED (want_info_on_gods))
+ {
+ Response (want_info_on_gods, IlwrathGods);
+ }
+ if (PHRASE_ENABLED (want_info_on_ilwrath))
+ {
+ Response (want_info_on_ilwrath, IlwrathInfo);
+ }
+ Response (want_peace, CombatIsInevitable);
+ Response (want_alliance, CombatIsInevitable);
+ Response (bye_homeworld, CombatIsInevitable);
+}
+
+static void GodsSpeak (RESPONSE_REF R);
+
+static void
+GodsOrder (RESPONSE_REF R)
+{
+ BYTE OrdersLeft;
+
+ OrdersLeft = FALSE;
+ if (PLAYER_SAID (R, other_divine_orders))
+ NPCPhrase (WHAT_ORDERS);
+ else if (PLAYER_SAID (R, say_warship))
+ {
+ NPCPhrase (OK_WARSHIP);
+
+ DISABLE_PHRASE (say_warship);
+ }
+ else if (PLAYER_SAID (R, say_dwe))
+ {
+ NPCPhrase (OK_DWE);
+
+ DISABLE_PHRASE (say_dwe);
+ }
+ else if (PLAYER_SAID (R, say_youboo))
+ {
+ NPCPhrase (OK_YOUBOO);
+
+ DISABLE_PHRASE (say_youboo);
+ }
+ else if (PLAYER_SAID (R, say_dillrat))
+ {
+ NPCPhrase (OK_DILRAT);
+
+ DISABLE_PHRASE (say_dillrat);
+ }
+
+ if (PHRASE_ENABLED (say_warship))
+ {
+ Response (say_warship, GodsOrder);
+ OrdersLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (say_dwe))
+ {
+ Response (say_dwe, GodsOrder);
+ OrdersLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (say_youboo))
+ {
+ Response (say_youboo, GodsOrder);
+ OrdersLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (say_dillrat))
+ {
+ Response (say_dillrat, GodsOrder);
+ OrdersLeft = TRUE;
+ }
+ Response (enough_orders, GodsSpeak);
+
+ if (!OrdersLeft)
+ DISABLE_PHRASE (other_divine_orders);
+}
+
+static void
+GodsSpeak (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (R == 0)
+ {
+ if (GET_GAME_STATE (ILWRATH_FIGHT_THRADDASH))
+ NPCPhrase (GLORIOUS_WORSHIP);
+ else if (GET_GAME_STATE (ILWRATH_DECEIVED))
+ NPCPhrase (ON_WAY);
+ else
+ {
+ NumVisits = GET_GAME_STATE (ILWRATH_GODS_SPOKEN);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ILWRATH_BELIEVE);
+ break;
+ case 1:
+ NPCPhrase (GODS_RETURN_1);
+ break;
+ case 2:
+ NPCPhrase (GODS_RETURN_2);
+ break;
+ case 3:
+ NPCPhrase (GODS_RETURN_3);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ILWRATH_GODS_SPOKEN, NumVisits);
+ }
+ }
+ else if (PLAYER_SAID (R, go_kill_thraddash))
+ {
+ NPCPhrase (OK_KILL_THRADDASH);
+
+ SET_GAME_STATE (ILWRATH_DECEIVED, 1);
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, ADVANCE_ILWRATH_MISSION);
+ }
+ else if (PLAYER_SAID (R, worship_us))
+ {
+ NumVisits = GET_GAME_STATE (ILWRATH_WORSHIP);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (WE_WORSHIP_1);
+ break;
+ case 1:
+ NPCPhrase (WE_WORSHIP_2);
+ break;
+ case 2:
+ NPCPhrase (WE_WORSHIP_3);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ILWRATH_WORSHIP, NumVisits);
+
+ DISABLE_PHRASE (worship_us);
+ }
+ else if (PLAYER_SAID (R, enough_orders))
+ NPCPhrase (NEVER_ENOUGH);
+
+ if (!GET_GAME_STATE (ILWRATH_DECEIVED))
+ Response (go_kill_thraddash, GodsSpeak);
+ if (PHRASE_ENABLED (worship_us))
+ Response (worship_us, GodsSpeak);
+ if (PHRASE_ENABLED (other_divine_orders))
+ {
+ Response (other_divine_orders, GodsOrder);
+ }
+ Response (bye_gods, CombatIsInevitable);
+}
+
+static void
+IlwrathSpace (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (R == 0)
+ {
+ NumVisits = GET_GAME_STATE (ILWRATH_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_HELLO_SPACE);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_HELLO_SPACE_1);
+ break;
+ case 2:
+ NPCPhrase (SUBSEQUENT_HELLO_SPACE_2);
+ break;
+ case 3:
+ NPCPhrase (SUBSEQUENT_HELLO_SPACE_3);
+ break;
+ case 4:
+ NPCPhrase (SUBSEQUENT_HELLO_SPACE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ILWRATH_VISITS, NumVisits);
+ }
+
+ NumVisits = GET_GAME_STATE (ILWRATH_INFO);
+ switch (NumVisits)
+ {
+ case 0:
+ Response (whats_up_space_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (whats_up_space_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (whats_up_space_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (whats_up_space_4, CombatIsInevitable);
+ break;
+ case 4:
+ Response (whats_up_space_5, CombatIsInevitable);
+ break;
+ }
+ Response (you_are_weak, CombatIsInevitable);
+ Response (slay_by_thousands, CombatIsInevitable);
+ Response (ease_up, CombatIsInevitable);
+ Response (bye_space, CombatIsInevitable);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (GET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER))
+ {
+ NPCPhrase (SEND_MESSAGE);
+
+ Response (where_you_come_from, CombatIsInevitable);
+ Response (it_will_be_a_pleasure, CombatIsInevitable);
+ Response (surrender, CombatIsInevitable);
+ Response (be_reasonable, CombatIsInevitable);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ IlwrathHome ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ NumVisits = GET_GAME_STATE (ILWRATH_CHMMR_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_CHMMR_HELLO);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_CHMMR_HELLO);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ILWRATH_CHMMR_VISITS, NumVisits);
+
+ Response (whats_up, CombatIsInevitable);
+ Response (bye, CombatIsInevitable);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 5))
+ {
+ // Communicating with an Ilwrath ship using a HyperWave Broadcaster.
+ if (GET_GAME_STATE (ILWRATH_FIGHT_THRADDASH))
+ NPCPhrase (BIG_FUN);
+ else if (GET_GAME_STATE (ILWRATH_DECEIVED))
+ NPCPhrase (FAST_AS_CAN);
+ else
+ NPCPhrase (JUST_GRUNTS);
+
+ setSegue (Segue_peace);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 4))
+ {
+ // Communicating with the Ilwrath homeworld using a
+ // Hyperwave Broadcaster.
+ GodsSpeak ((RESPONSE_REF)0);
+ }
+ else
+ {
+ setSegue (Segue_peace);
+
+ if (GET_GAME_STATE (ILWRATH_FIGHT_THRADDASH))
+ NPCPhrase (HAPPY_FIGHTING_THRADDASH);
+ else if (GET_GAME_STATE (ILWRATH_DECEIVED))
+ NPCPhrase (ON_WAY_TO_THRADDASH);
+ else
+ IlwrathSpace ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_ilwrath (void)
+{
+ return (0);
+}
+
+static void
+post_ilwrath_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_ilwrath_comm (void)
+{
+ LOCDATA *retval;
+
+ ilwrath_desc.init_encounter_func = Intro;
+ ilwrath_desc.post_encounter_func = post_ilwrath_enc;
+ ilwrath_desc.uninit_encounter_func = uninit_ilwrath;
+
+ ilwrath_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ ilwrath_desc.AlienTextBaseline.y = 70;
+ ilwrath_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (GET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER)
+ || (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA)
+ & ((1 << 4) | (1 << 5)))
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &ilwrath_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/ilwrath/resinst.h b/src/uqm/comm/ilwrath/resinst.h
new file mode 100644
index 0000000..7063e39
--- /dev/null
+++ b/src/uqm/comm/ilwrath/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ILWRATH_COLOR_MAP "comm.ilwrath.colortable"
+#define ILWRATH_CONVERSATION_PHRASES "comm.ilwrath.dialogue"
+#define ILWRATH_FONT "comm.ilwrath.font"
+#define ILWRATH_MUSIC "comm.ilwrath.music"
+#define ILWRATH_PMAP_ANIM "comm.ilwrath.graphics"
diff --git a/src/uqm/comm/ilwrath/strings.h b/src/uqm/comm/ilwrath/strings.h
new file mode 100644
index 0000000..9cd4d26
--- /dev/null
+++ b/src/uqm/comm/ilwrath/strings.h
@@ -0,0 +1,135 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ILWRATH_STRINGS_H
+#define ILWRATH_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ NEVER_ENOUGH,
+ OK_WARSHIP,
+ OK_DWE,
+ OK_YOUBOO,
+ OK_DILRAT,
+ BIG_FUN,
+ FAST_AS_CAN,
+ GLORIOUS_WORSHIP,
+ ON_WAY,
+ GODS_RETURN_1,
+ GODS_RETURN_2,
+ GODS_RETURN_3,
+ JUST_GRUNTS,
+ GRUNTS_AGAIN,
+ WHAT_ORDERS,
+ WE_WORSHIP_1,
+ WE_WORSHIP_2,
+ WE_WORSHIP_3,
+ SUBSEQUENT_CHMMR_HELLO,
+ INIT_CHMMR_HELLO,
+ OK_ENOUGH_ILWRATH,
+ OK_ENOUGH_GODS,
+ SEND_MESSAGE,
+ CAME_FROM,
+ WHO_BLASTS_WHO,
+ NO_SURRENDER,
+ NOT_REASONABLE,
+ SUBSEQUENT_HOME_HELLO,
+ GENERAL_INFO,
+ GOODBYE_AND_DIE,
+ DECEIVERS,
+ NO_PEACE,
+ NO_ALLIANCE,
+ ILWRATH_BELIEVE,
+ OK_KILL_THRADDASH,
+ GOODBYE_GODS,
+ INIT_HELLO_SPACE,
+ SUBSEQUENT_HELLO_SPACE_1,
+ SUBSEQUENT_HELLO_SPACE_2,
+ SUBSEQUENT_HELLO_SPACE_3,
+ SUBSEQUENT_HELLO_SPACE_4,
+ GENERAL_INFO_SPACE_1,
+ GENERAL_INFO_SPACE_2,
+ GENERAL_INFO_SPACE_3,
+ GENERAL_INFO_SPACE_4,
+ GENERAL_INFO_SPACE_5,
+ STRENGTH_NOT_ALL,
+ NO_SLAY_BY_THOUSANDS,
+ NO_EASE_UP,
+ GOODBYE_AND_DIE_SPACE,
+ INIT_HOME_HELLO,
+ GOODBYE_AND_DIE_HOMEWORLD,
+ SO_MUCH_TO_KNOW,
+ LONG_AGO,
+ KILLED_GOOD_GODS,
+ CHANNEL_44,
+ BECAUSE_44,
+ WHAT_ABOUT_ILWRATH,
+ ABOUT_PHYSIO,
+ ABOUT_HISTORY,
+ ABOUT_CULTURE,
+ ABOUT_URQUAN,
+ URQUAN_TOO_NICE,
+ OF_COURSE_WERE_EVIL,
+ DONT_CONFUSE_US,
+ ON_WAY_TO_THRADDASH,
+ HAPPY_FIGHTING_THRADDASH,
+
+ say_warship,
+ say_dwe,
+ say_youboo,
+ say_dillrat,
+ enough_orders,
+ other_divine_orders,
+ worship_us,
+ bye_gods,
+ enough_ilwrath,
+ enough_gods,
+ where_you_come_from,
+ it_will_be_a_pleasure,
+ be_reasonable,
+ surrender,
+ whats_up,
+ bye,
+ want_peace,
+ want_alliance,
+ go_kill_thraddash,
+ whats_up_space_1,
+ whats_up_space_2,
+ whats_up_space_3,
+ whats_up_space_4,
+ whats_up_space_5,
+ you_are_weak,
+ slay_by_thousands,
+ ease_up,
+ bye_space,
+ bye_homeworld,
+ want_info_on_gods,
+ when_start_worship,
+ any_good_gods,
+ how_talk_with_gods,
+ why_44,
+ want_info_on_ilwrath,
+ what_about_physio,
+ what_about_history,
+ what_about_culture,
+ what_about_urquan,
+ are_you_evil,
+ but_evil_is_defined,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/melnorm/Makeinfo b/src/uqm/comm/melnorm/Makeinfo
new file mode 100644
index 0000000..5e3a214
--- /dev/null
+++ b/src/uqm/comm/melnorm/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="melnorm.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/melnorm/melnorm.c b/src/uqm/comm/melnorm/melnorm.c
new file mode 100644
index 0000000..00b6a07
--- /dev/null
+++ b/src/uqm/comm/melnorm/melnorm.c
@@ -0,0 +1,1855 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/gameev.h"
+#include "uqm/shipcont.h"
+#include "libs/inplib.h"
+#include "libs/mathlib.h"
+
+#include "uqm/hyper.h"
+ // for SOL_X/SOL_Y
+#include "uqm/planets/planets.h"
+ // for xxx_DISASTER
+#include "uqm/sis.h"
+
+
+static const NUMBER_SPEECH_DESC melnorme_numbers_english;
+
+static LOCDATA melnorme_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ MELNORME_PMAP_ANIM, /* AlienFrame */
+ MELNORME_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ MELNORME_COLOR_MAP, /* AlienColorMap */
+ MELNORME_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ MELNORME_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 4, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 6, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND * 4, ONE_SECOND * 4,/* RestartRate */
+ (1 << 1), /* BlockMask */
+ },
+ {
+ 11, /* StartIndex */
+ 9, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND * 4, ONE_SECOND * 4,/* RestartRate */
+ (1 << 0), /* BlockMask */
+ },
+ {
+ 20, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 5, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ &melnorme_numbers_english, /* AlienNumberSpeech - default */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static COUNT melnorme_digit_names[] =
+{
+ ENUMERATE_ZERO,
+ ENUMERATE_ONE,
+ ENUMERATE_TWO,
+ ENUMERATE_THREE,
+ ENUMERATE_FOUR,
+ ENUMERATE_FIVE,
+ ENUMERATE_SIX,
+ ENUMERATE_SEVEN,
+ ENUMERATE_EIGHT,
+ ENUMERATE_NINE
+};
+
+static COUNT melnorme_teen_names[] =
+{
+ ENUMERATE_TEN,
+ ENUMERATE_ELEVEN,
+ ENUMERATE_TWELVE,
+ ENUMERATE_THIRTEEN,
+ ENUMERATE_FOURTEEN,
+ ENUMERATE_FIFTEEN,
+ ENUMERATE_SIXTEEN,
+ ENUMERATE_SEVENTEEN,
+ ENUMERATE_EIGHTEEN,
+ ENUMERATE_NINETEEN
+};
+
+static COUNT melnorme_tens_names[] =
+{
+ 0, /* invalid */
+ 0, /* skip digit */
+ ENUMERATE_TWENTY,
+ ENUMERATE_THIRTY,
+ ENUMERATE_FOURTY,
+ ENUMERATE_FIFTY,
+ ENUMERATE_SIXTY,
+ ENUMERATE_SEVENTY,
+ ENUMERATE_EIGHTY,
+ ENUMERATE_NINETY
+};
+
+static const NUMBER_SPEECH_DESC melnorme_numbers_english =
+{
+ 5, /* NumDigits */
+ {
+ { /* 1000-999999 */
+ 1000, /* Divider */
+ 0, /* Subtrahend */
+ NULL, /* StrDigits - recurse */
+ NULL, /* Names - not used */
+ ENUMERATE_THOUSAND /* CommonIndex */
+ },
+ { /* 100-999 */
+ 100, /* Divider */
+ 0, /* Subtrahend */
+ melnorme_digit_names, /* StrDigits */
+ NULL, /* Names - not used */
+ ENUMERATE_HUNDRED /* CommonIndex */
+ },
+ { /* 20-99 */
+ 10, /* Divider */
+ 0, /* Subtrahend */
+ melnorme_tens_names, /* StrDigits */
+ NULL, /* Names - not used */
+ 0 /* CommonIndex - not used */
+ },
+ { /* 10-19 */
+ 1, /* Divider */
+ 10, /* Subtrahend */
+ melnorme_teen_names, /* StrDigits */
+ NULL, /* Names - not used */
+ 0 /* CommonIndex - not used */
+ },
+ { /* 0-9 */
+ 1, /* Divider */
+ 0, /* Subtrahend */
+ melnorme_digit_names, /* StrDigits */
+ NULL, /* Names - not used */
+ 0 /* CommonIndex - not used */
+ }
+ }
+};
+
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof (*array))
+
+
+//////////////Technology System///////////////////////
+// This section deals with enabling and checking for
+// various technologies. It should probably be
+// migrated to its own file.
+
+// Identifiers for the various technologies
+typedef enum
+{
+ TECH_MODULE_BLASTER,
+ TECH_LANDER_SPEED,
+ TECH_MODULE_ANTIMISSILE,
+ TECH_LANDER_SHIELD_BIO,
+ TECH_LANDER_CARGO,
+ TECH_MODULE_BIGFUELTANK,
+ TECH_LANDER_RAPIDFIRE,
+ TECH_LANDER_SHIELD_QUAKE,
+ TECH_MODULE_TRACKING,
+ TECH_LANDER_SHIELD_LIGHTNING,
+ TECH_LANDER_SHIELD_HEAT,
+ TECH_MODULE_CANNON,
+ TECH_MODULE_FURNACE,
+} TechId_t;
+
+// Group the technologies into three subtypes
+typedef enum
+{
+ TECH_TYPE_MODULE, // Flagship modules
+ // subtype = moduleId, info = cost
+ // Cost will be scaled by MODULE_COST_SCALE.
+ TECH_TYPE_LANDER_SHIELD, // Lander shield enhancements
+ // subtype = disaster type, info = unused
+ TECH_TYPE_STATE // Other game state changes
+ // subtype = stateId, info = state value
+} TechType_t;
+
+
+// Define the information specifying a particular technology
+typedef struct
+{
+ TechId_t id; // ID of the technology
+ TechType_t type; // Type of the technology
+ int subtype; // Subtype of the technology
+ int info; // Supplemental information
+} TechData;
+
+
+// A table of the available technologies.
+// This should really be an associative map of TechIds to tech data records,
+// but implementing that would be excessive.
+static const TechData tech_data_table[] =
+{
+ // Tech ID Tech Type, Supplemental info
+ { TECH_MODULE_BLASTER, TECH_TYPE_MODULE, BLASTER_WEAPON, 4000 },
+ { TECH_LANDER_SPEED, TECH_TYPE_STATE, IMPROVED_LANDER_SPEED, 1 },
+ { TECH_MODULE_ANTIMISSILE, TECH_TYPE_MODULE, ANTIMISSILE_DEFENSE, 4000 },
+ { TECH_LANDER_SHIELD_BIO, TECH_TYPE_LANDER_SHIELD, BIOLOGICAL_DISASTER, -1 },
+ { TECH_LANDER_CARGO, TECH_TYPE_STATE, IMPROVED_LANDER_CARGO, 1 },
+ { TECH_MODULE_BIGFUELTANK, TECH_TYPE_MODULE, HIGHEFF_FUELSYS, 1000 },
+ { TECH_LANDER_RAPIDFIRE, TECH_TYPE_STATE, IMPROVED_LANDER_SHOT, 1 },
+ { TECH_LANDER_SHIELD_QUAKE, TECH_TYPE_LANDER_SHIELD, EARTHQUAKE_DISASTER, -1 },
+ { TECH_MODULE_TRACKING, TECH_TYPE_MODULE, TRACKING_SYSTEM, 5000 },
+ { TECH_LANDER_SHIELD_LIGHTNING, TECH_TYPE_LANDER_SHIELD, LIGHTNING_DISASTER, -1 },
+ { TECH_LANDER_SHIELD_HEAT, TECH_TYPE_LANDER_SHIELD, LAVASPOT_DISASTER, -1 },
+ { TECH_MODULE_CANNON, TECH_TYPE_MODULE, CANNON_WEAPON, 6000 },
+ { TECH_MODULE_FURNACE, TECH_TYPE_MODULE, SHIVA_FURNACE, 4000 },
+};
+const size_t NUM_TECHNOLOGIES = ARRAY_SIZE (tech_data_table);
+
+// Lookup function to get the data for a particular tech
+static const TechData*
+GetTechData (TechId_t techId)
+{
+ size_t i = 0;
+ for (i = 0; i < NUM_TECHNOLOGIES; ++i)
+ {
+ if (tech_data_table[i].id == techId)
+ return &tech_data_table[i];
+ }
+ return NULL;
+}
+
+
+// We have to explicitly switch on the state ID because the xxx_GAME_STATE
+// macros use preprocessor stringizing.
+static bool
+HasStateTech (int stateId)
+{
+ switch (stateId)
+ {
+ case IMPROVED_LANDER_SPEED:
+ return GET_GAME_STATE (IMPROVED_LANDER_SPEED);
+ case IMPROVED_LANDER_CARGO:
+ return GET_GAME_STATE (IMPROVED_LANDER_CARGO);
+ case IMPROVED_LANDER_SHOT:
+ return GET_GAME_STATE (IMPROVED_LANDER_SHOT);
+ }
+ return false;
+}
+
+static void
+GrantStateTech (int stateId, BYTE value)
+{
+ switch (stateId)
+ {
+ case IMPROVED_LANDER_SPEED:
+ SET_GAME_STATE (IMPROVED_LANDER_SPEED, value);
+ return;
+ case IMPROVED_LANDER_CARGO:
+ SET_GAME_STATE (IMPROVED_LANDER_CARGO, value);
+ return;
+ case IMPROVED_LANDER_SHOT:
+ SET_GAME_STATE (IMPROVED_LANDER_SHOT, value);
+ return;
+ }
+}
+
+static bool
+HasTech (TechId_t techId)
+{
+ const TechData* techData = GetTechData (techId);
+ if (!techData)
+ return false;
+
+ switch (techData->type)
+ {
+ case TECH_TYPE_MODULE:
+ return GLOBAL (ModuleCost[techData->subtype]) != 0;
+ case TECH_TYPE_LANDER_SHIELD:
+ return (GET_GAME_STATE (LANDER_SHIELDS) & (1 << techData->subtype)) != 0;
+ case TECH_TYPE_STATE:
+ return HasStateTech (techData->subtype);
+ }
+ return false;
+}
+
+static void
+GrantTech (TechId_t techId)
+{
+ const TechData* techData = GetTechData (techId);
+ if (!techData)
+ return;
+
+ switch (techData->type)
+ {
+ case TECH_TYPE_MODULE:
+ GLOBAL (ModuleCost[techData->subtype]) = techData->info / MODULE_COST_SCALE;
+ return;
+ case TECH_TYPE_LANDER_SHIELD:
+ {
+ COUNT state = GET_GAME_STATE (LANDER_SHIELDS) | (1 << techData->subtype);
+ SET_GAME_STATE (LANDER_SHIELDS, state);
+ return;
+ }
+ case TECH_TYPE_STATE:
+ GrantStateTech (techData->subtype, techData->info);
+ return;
+ }
+}
+
+
+////////////Melnorme Sales System///////////
+// This section contains code related to Melnorme sales
+
+// Many of the conversation lines in strings.h fall into groups
+// of sequential responses. These structures allow those
+// responses to be interated through.
+static const int ok_buy_event_lines[] =
+{
+ OK_BUY_EVENT_1, OK_BUY_EVENT_2, OK_BUY_EVENT_3, OK_BUY_EVENT_4,
+ OK_BUY_EVENT_5, OK_BUY_EVENT_6, OK_BUY_EVENT_7, OK_BUY_EVENT_8
+};
+const size_t NUM_EVENT_ITEMS = ARRAY_SIZE (ok_buy_event_lines);
+
+static const int ok_buy_alien_race_lines[] =
+{
+ OK_BUY_ALIEN_RACE_1, OK_BUY_ALIEN_RACE_2, OK_BUY_ALIEN_RACE_3,
+ OK_BUY_ALIEN_RACE_4, OK_BUY_ALIEN_RACE_5, OK_BUY_ALIEN_RACE_6,
+ OK_BUY_ALIEN_RACE_7, OK_BUY_ALIEN_RACE_8, OK_BUY_ALIEN_RACE_9,
+ OK_BUY_ALIEN_RACE_10, OK_BUY_ALIEN_RACE_11, OK_BUY_ALIEN_RACE_12,
+ OK_BUY_ALIEN_RACE_13, OK_BUY_ALIEN_RACE_14, OK_BUY_ALIEN_RACE_15,
+ OK_BUY_ALIEN_RACE_16
+};
+const size_t NUM_ALIEN_RACE_ITEMS = ARRAY_SIZE (ok_buy_alien_race_lines);
+
+static const int ok_buy_history_lines[] =
+{
+ OK_BUY_HISTORY_1, OK_BUY_HISTORY_2, OK_BUY_HISTORY_3,
+ OK_BUY_HISTORY_4, OK_BUY_HISTORY_5, OK_BUY_HISTORY_6,
+ OK_BUY_HISTORY_7, OK_BUY_HISTORY_8, OK_BUY_HISTORY_9
+};
+const size_t NUM_HISTORY_ITEMS = ARRAY_SIZE (ok_buy_history_lines);
+
+static const int hello_and_down_to_business_lines[] =
+{
+ HELLO_AND_DOWN_TO_BUSINESS_1, HELLO_AND_DOWN_TO_BUSINESS_2,
+ HELLO_AND_DOWN_TO_BUSINESS_3, HELLO_AND_DOWN_TO_BUSINESS_4,
+ HELLO_AND_DOWN_TO_BUSINESS_5, HELLO_AND_DOWN_TO_BUSINESS_6,
+ HELLO_AND_DOWN_TO_BUSINESS_7, HELLO_AND_DOWN_TO_BUSINESS_8,
+ HELLO_AND_DOWN_TO_BUSINESS_9, HELLO_AND_DOWN_TO_BUSINESS_10
+};
+const size_t NUM_HELLO_LINES = ARRAY_SIZE (hello_and_down_to_business_lines);
+
+static const int rescue_lines[] =
+{
+ RESCUE_EXPLANATION, RESCUE_AGAIN_1, RESCUE_AGAIN_2,
+ RESCUE_AGAIN_3, RESCUE_AGAIN_4, RESCUE_AGAIN_5
+};
+const size_t NUM_RESCUE_LINES = ARRAY_SIZE (rescue_lines);
+
+// How many lines are available in the given array?
+static size_t
+GetNumLines (const int array[])
+{
+ if (array == ok_buy_event_lines)
+ return NUM_EVENT_ITEMS;
+ else if (array == ok_buy_alien_race_lines)
+ return NUM_ALIEN_RACE_ITEMS;
+ else if (array == ok_buy_history_lines)
+ return NUM_HISTORY_ITEMS;
+ else if (array == hello_and_down_to_business_lines)
+ return NUM_HELLO_LINES;
+ else if (array == rescue_lines)
+ return NUM_RESCUE_LINES;
+ return 0;
+}
+
+// Get the line, with range checking.
+// Returns the last line if the desired one is out of range.
+static int
+GetLineSafe (const int array[], size_t linenum)
+{
+ const size_t array_size = GetNumLines (array);
+ assert (array_size > 0);
+ if (linenum >= array_size)
+ linenum = array_size - 1;
+ return array[linenum];
+}
+
+// Data structure to hold the Melnorme's info on a technology
+typedef struct
+{
+ TechId_t techId; // ID of technology
+ int price; // Melnorme's price to sell
+ int sale_line; // Sales pitch line ID
+ int sold_line; // Post-sale line ID
+} TechSaleData;
+
+// Right now, all techs have the same price.
+#define TECHPRICE (75 * BIO_CREDIT_VALUE)
+
+static const TechSaleData tech_sale_catalog[] =
+{
+ { TECH_MODULE_BLASTER, TECHPRICE, NEW_TECH_1, OK_BUY_NEW_TECH_1 },
+ { TECH_LANDER_SPEED, TECHPRICE, NEW_TECH_2, OK_BUY_NEW_TECH_2 },
+ { TECH_MODULE_ANTIMISSILE, TECHPRICE, NEW_TECH_3, OK_BUY_NEW_TECH_3 },
+ { TECH_LANDER_SHIELD_BIO, TECHPRICE, NEW_TECH_4, OK_BUY_NEW_TECH_4 },
+ { TECH_LANDER_CARGO, TECHPRICE, NEW_TECH_5, OK_BUY_NEW_TECH_5 },
+ { TECH_MODULE_BIGFUELTANK, TECHPRICE, NEW_TECH_6, OK_BUY_NEW_TECH_6 },
+ { TECH_LANDER_RAPIDFIRE, TECHPRICE, NEW_TECH_7, OK_BUY_NEW_TECH_7 },
+ { TECH_LANDER_SHIELD_QUAKE, TECHPRICE, NEW_TECH_8, OK_BUY_NEW_TECH_8 },
+ { TECH_MODULE_TRACKING, TECHPRICE, NEW_TECH_9, OK_BUY_NEW_TECH_9 },
+ { TECH_LANDER_SHIELD_LIGHTNING, TECHPRICE, NEW_TECH_10, OK_BUY_NEW_TECH_10 },
+ { TECH_LANDER_SHIELD_HEAT, TECHPRICE, NEW_TECH_11, OK_BUY_NEW_TECH_11 },
+ { TECH_MODULE_CANNON, TECHPRICE, NEW_TECH_12, OK_BUY_NEW_TECH_12 },
+ { TECH_MODULE_FURNACE, TECHPRICE, NEW_TECH_13, OK_BUY_NEW_TECH_13 },
+};
+const size_t NUM_TECH_ITEMS = ARRAY_SIZE (tech_sale_catalog);
+
+// Return the next tech for sale that the player doesn't already have.
+// Returns NULL if the player has all the techs.
+static const TechSaleData*
+GetNextTechForSale (void)
+{
+ size_t i = 0;
+ for (i = 0; i < NUM_TECH_ITEMS; ++i)
+ {
+ if (!HasTech (tech_sale_catalog[i].techId))
+ return &tech_sale_catalog[i];
+ }
+
+ return NULL;
+}
+
+///////////End Melnorme Sales Section//////////////////
+
+static StatMsgMode prevMsgMode;
+
+static void DoFirstMeeting (RESPONSE_REF R);
+
+static COUNT
+ShipWorth (void)
+{
+ BYTE i;
+ SBYTE crew_pods;
+ COUNT worth;
+
+ worth = GLOBAL_SIS (NumLanders)
+ * GLOBAL (ModuleCost[PLANET_LANDER]);
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (DriveSlots[i]) < EMPTY_SLOT)
+ worth += GLOBAL (ModuleCost[FUSION_THRUSTER]);
+ }
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (JetSlots[i]) < EMPTY_SLOT)
+ worth += GLOBAL (ModuleCost[TURNING_JETS]);
+ }
+
+ crew_pods = -(SBYTE)(
+ (GLOBAL_SIS (CrewEnlisted) + CREW_POD_CAPACITY - 1)
+ / CREW_POD_CAPACITY
+ );
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ BYTE which_module;
+
+ which_module = GLOBAL_SIS (ModuleSlots[i]);
+ if (which_module < BOMB_MODULE_0
+ && (which_module != CREW_POD || ++crew_pods > 0))
+ {
+ worth += GLOBAL (ModuleCost[which_module]);
+ }
+ }
+
+ return (worth);
+}
+
+static COUNT rescue_fuel;
+static SIS_STATE SIS_copy;
+
+// Extract method to return the response string index
+// for stripping a given module.
+static int
+GetStripModuleRef (int moduleID)
+{
+ switch (moduleID)
+ {
+ case PLANET_LANDER: return LANDERS;
+ case FUSION_THRUSTER: return THRUSTERS;
+ case TURNING_JETS: return JETS;
+ case CREW_POD: return PODS;
+ case STORAGE_BAY: return BAYS;
+ case DYNAMO_UNIT: return DYNAMOS;
+ case SHIVA_FURNACE: return FURNACES;
+ case GUN_WEAPON: return GUNS;
+ case BLASTER_WEAPON: return BLASTERS;
+ case CANNON_WEAPON: return CANNONS;
+ case TRACKING_SYSTEM: return TRACKERS;
+ case ANTIMISSILE_DEFENSE: return DEFENSES;
+ // If a modder has added new modules, should it really
+ // be a fatal error if the Melnorme don't know about
+ // them?
+ default:
+ assert (0 && "Unknown module");
+ }
+ return 0;
+}
+
+static DWORD
+getStripRandomSeed (void)
+{
+ DWORD x, y;
+ // We truncate the location because encounters move the ship slightly in
+ // HSpace, and throw some other relatively immutable values in the mix to
+ // vary the deal when stuck at the same general location again.
+ // It is still possible but unlikely for encounters to move the ship into
+ // another truncation sector so the player could choose from 2 deals.
+ x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x)) / 100;
+ y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y)) / 100;
+ // prime numbers help randomness
+ return y * 1013 + x + GLOBAL_SIS (NumLanders)
+ + GLOBAL_SIS (ModuleSlots[1]) + GLOBAL_SIS (ModuleSlots[4])
+ + GLOBAL_SIS (ModuleSlots[7]) + GLOBAL_SIS (ModuleSlots[10]);
+}
+
+static BOOLEAN
+StripShip (COUNT fuel_required)
+{
+ BYTE i, which_module;
+ SBYTE crew_pods;
+
+ SET_GAME_STATE (MELNORME_RESCUE_REFUSED, 0);
+
+ crew_pods = -(SBYTE)(
+ (GLOBAL_SIS (CrewEnlisted) + CREW_POD_CAPACITY - 1)
+ / CREW_POD_CAPACITY
+ );
+ if (fuel_required == 0)
+ {
+ GlobData.SIS_state = SIS_copy;
+ DeltaSISGauges (UNDEFINED_DELTA, rescue_fuel, UNDEFINED_DELTA);
+ }
+ else if (fuel_required == (COUNT)~0)
+ {
+ GLOBAL_SIS (NumLanders) = 0;
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ GLOBAL_SIS (DriveSlots[i]) = EMPTY_SLOT + 0;
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ GLOBAL_SIS (JetSlots[i]) = EMPTY_SLOT + 1;
+ if (GLOBAL_SIS (FuelOnBoard) > FUEL_RESERVE)
+ GLOBAL_SIS (FuelOnBoard) = FUEL_RESERVE;
+ GLOBAL_SIS (TotalBioMass) = 0;
+ GLOBAL_SIS (TotalElementMass) = 0;
+ for (i = 0; i < NUM_ELEMENT_CATEGORIES; ++i)
+ GLOBAL_SIS (ElementAmounts[i]) = 0;
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ which_module = GLOBAL_SIS (ModuleSlots[i]);
+ if (which_module < BOMB_MODULE_0
+ && (which_module != CREW_POD
+ || ++crew_pods > 0))
+ GLOBAL_SIS (ModuleSlots[i]) = EMPTY_SLOT + 2;
+ }
+
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+ }
+ else if (fuel_required)
+ {
+ SBYTE bays;
+ BYTE num_searches, beg_mod, end_mod;
+ COUNT worth, total;
+ BYTE module_count[BOMB_MODULE_0];
+ BYTE slot;
+ DWORD capacity;
+ RandomContext *rc;
+
+ // Bug #567
+ // In order to offer the same deal each time if it is refused, we seed
+ // the random number generator with our location, thus making the deal
+ // a repeatable pseudo-random function of where we got stuck and what,
+ // exactly, is on our ship.
+ rc = RandomContext_New();
+ RandomContext_SeedRandom (rc, getStripRandomSeed ());
+
+ SIS_copy = GlobData.SIS_state;
+ for (i = PLANET_LANDER; i < BOMB_MODULE_0; ++i)
+ module_count[i] = 0;
+
+ capacity = FUEL_RESERVE;
+ slot = NUM_MODULE_SLOTS - 1;
+ do
+ {
+ if (SIS_copy.ModuleSlots[slot] == FUEL_TANK
+ || SIS_copy.ModuleSlots[slot] == HIGHEFF_FUELSYS)
+ {
+ COUNT volume;
+
+ volume = SIS_copy.ModuleSlots[slot] == FUEL_TANK
+ ? FUEL_TANK_CAPACITY : HEFUEL_TANK_CAPACITY;
+ capacity += volume;
+ }
+ } while (slot--);
+ if (fuel_required > capacity)
+ fuel_required = capacity;
+
+ bays = -(SBYTE)(
+ (SIS_copy.TotalElementMass + STORAGE_BAY_CAPACITY - 1)
+ / STORAGE_BAY_CAPACITY
+ );
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ which_module = SIS_copy.ModuleSlots[i];
+ if (which_module == CREW_POD)
+ ++crew_pods;
+ else if (which_module == STORAGE_BAY)
+ ++bays;
+ }
+
+ worth = fuel_required / FUEL_TANK_SCALE;
+ total = 0;
+ num_searches = 0;
+ beg_mod = end_mod = (BYTE)~0;
+ while (total < worth && ShipWorth () && ++num_searches)
+ {
+ DWORD rand_val;
+
+ rand_val = RandomContext_Random (rc);
+ switch (which_module = LOBYTE (LOWORD (rand_val)) % (CREW_POD + 1))
+ {
+ case PLANET_LANDER:
+ if (SIS_copy.NumLanders == 0)
+ continue;
+ --SIS_copy.NumLanders;
+ break;
+ case FUSION_THRUSTER:
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ if (SIS_copy.DriveSlots[i] < EMPTY_SLOT)
+ break;
+ }
+ if (i == NUM_DRIVE_SLOTS)
+ continue;
+ SIS_copy.DriveSlots[i] = EMPTY_SLOT + 0;
+ break;
+ case TURNING_JETS:
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ if (SIS_copy.JetSlots[i] < EMPTY_SLOT)
+ break;
+ }
+ if (i == NUM_JET_SLOTS)
+ continue;
+ SIS_copy.JetSlots[i] = EMPTY_SLOT + 1;
+ break;
+ case CREW_POD:
+ i = HIBYTE (LOWORD (rand_val)) % NUM_MODULE_SLOTS;
+ which_module = SIS_copy.ModuleSlots[i];
+ if (which_module >= BOMB_MODULE_0
+ || which_module == FUEL_TANK
+ || which_module == HIGHEFF_FUELSYS
+ || (which_module == STORAGE_BAY
+ && module_count[STORAGE_BAY] >= bays)
+ || (which_module == CREW_POD
+ && module_count[CREW_POD] >= crew_pods))
+ continue;
+ SIS_copy.ModuleSlots[i] = EMPTY_SLOT + 2;
+ break;
+ }
+
+ if (beg_mod == (BYTE)~0)
+ beg_mod = end_mod = which_module;
+ else if (which_module > end_mod)
+ end_mod = which_module;
+ ++module_count[which_module];
+ total += GLOBAL (ModuleCost[which_module]);
+ }
+ RandomContext_Delete (rc);
+
+ if (total == 0)
+ {
+ NPCPhrase (CHARITY);
+ DeltaSISGauges (0, fuel_required, 0);
+ return (FALSE);
+ }
+ else
+ {
+ NPCPhrase (RESCUE_OFFER);
+ rescue_fuel = fuel_required;
+ if (rescue_fuel == capacity)
+ NPCPhrase (RESCUE_TANKS);
+ else
+ NPCPhrase (RESCUE_HOME);
+ for (i = PLANET_LANDER; i < BOMB_MODULE_0; ++i)
+ {
+ if (module_count[i])
+ {
+ if (i == end_mod && i != beg_mod)
+ NPCPhrase (END_LIST_WITH_AND);
+ NPCPhrase (ENUMERATE_ONE + (module_count[i] - 1));
+ NPCPhrase (GetStripModuleRef (i));
+ }
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, no_trade_now))
+ NPCPhrase (OK_NO_TRADE_NOW_BYE);
+ else if (PLAYER_SAID (R, youre_on))
+ {
+ NPCPhrase (YOU_GIVE_US_NO_CHOICE);
+
+ SET_GAME_STATE (MELNORME_ANGER, 1);
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, so_we_can_attack))
+ {
+ NPCPhrase (DECEITFUL_HUMAN);
+
+ SET_GAME_STATE (MELNORME_ANGER, 2);
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, bye_melnorme_slightly_angry))
+ NPCPhrase (MELNORME_SLIGHTLY_ANGRY_GOODBYE);
+ else if (PLAYER_SAID (R, ok_strip_me))
+ {
+ if (ShipWorth () < 4000 / MODULE_COST_SCALE)
+ /* is ship worth stripping */
+ NPCPhrase (NOT_WORTH_STRIPPING);
+ else
+ {
+ SET_GAME_STATE (MELNORME_ANGER, 0);
+
+ StripShip ((COUNT)~0);
+ NPCPhrase (FAIR_JUSTICE);
+ }
+ }
+ else if (PLAYER_SAID (R, fight_some_more))
+ {
+ NPCPhrase (OK_FIGHT_SOME_MORE);
+
+ SET_GAME_STATE (MELNORME_ANGER, 3);
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, bye_melnorme_pissed_off))
+ NPCPhrase (MELNORME_PISSED_OFF_GOODBYE);
+ else if (PLAYER_SAID (R, well_if_thats_the_way_you_feel))
+ {
+ NPCPhrase (WE_FIGHT_AGAIN);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, you_hate_us_so_we_go_away))
+ NPCPhrase (HATE_YOU_GOODBYE);
+ else if (PLAYER_SAID (R, take_it))
+ {
+ StripShip (0);
+ NPCPhrase (HAPPY_TO_HAVE_RESCUED);
+ }
+ else if (PLAYER_SAID (R, leave_it))
+ {
+ SET_GAME_STATE (MELNORME_RESCUE_REFUSED, 1);
+ NPCPhrase (MAYBE_SEE_YOU_LATER);
+ }
+ else if (PLAYER_SAID (R, no_help))
+ {
+ SET_GAME_STATE (MELNORME_RESCUE_REFUSED, 1);
+ NPCPhrase (GOODBYE_AND_GOODLUCK);
+ }
+ else if (PLAYER_SAID (R, no_changed_mind))
+ {
+ NPCPhrase (GOODBYE_AND_GOODLUCK_AGAIN);
+ }
+ else if (PLAYER_SAID (R, be_leaving_now)
+ || PLAYER_SAID (R, goodbye))
+ {
+ NPCPhrase (FRIENDLY_GOODBYE);
+ }
+}
+
+static void
+DoRescue (RESPONSE_REF R)
+{
+ SIZE dx, dy;
+ COUNT fuel_required;
+
+ (void) R; // ignored
+ dx = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x))
+ - SOL_X;
+ dy = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y))
+ - SOL_Y;
+ fuel_required = square_root (
+ (DWORD)((long)dx * dx + (long)dy * dy)
+ ) + (2 * FUEL_TANK_SCALE);
+
+ if (StripShip (fuel_required))
+ {
+ Response (take_it, ExitConversation);
+ Response (leave_it, ExitConversation);
+ }
+}
+
+// Extract method for getting the player's current credits.
+static COUNT
+GetAvailableCredits (void)
+{
+ return MAKE_WORD (GET_GAME_STATE (MELNORME_CREDIT0),
+ GET_GAME_STATE (MELNORME_CREDIT1));
+}
+
+// Extract method for setting the player's current credits.
+static void
+SetAvailableCredits (COUNT credits)
+{
+ SET_GAME_STATE (MELNORME_CREDIT0, LOBYTE (credits));
+ SET_GAME_STATE (MELNORME_CREDIT1, HIBYTE (credits));
+}
+
+// Now returns whether the purchase succeeded instead of the remaining
+// credit balance. Use GetAvailableCredits() to get the latter.
+static bool
+DeltaCredit (SIZE delta_credit)
+{
+ COUNT Credit = GetAvailableCredits ();
+
+ // Can they afford it?
+ if ((int)delta_credit >= 0 || ((int)(-delta_credit) <= (int)(Credit)))
+ {
+ Credit += delta_credit;
+ SetAvailableCredits (Credit);
+ DrawStatusMessage (NULL);
+ return true;
+ }
+
+ // Fail
+ NPCPhrase (NEED_MORE_CREDIT0);
+ NPCNumber (-delta_credit - Credit, NULL);
+ NPCPhrase (NEED_MORE_CREDIT1);
+
+ return false;
+}
+
+
+// Extract methods to process the giving of various bits of information to the
+// player. Ideally, we'd want to merge these three into a single parameterized
+// function, but the nature of the XXX_GAME_STATE() code makes that tricky.
+static void
+CurrentEvents (void)
+{
+ BYTE stack = GET_GAME_STATE (MELNORME_EVENTS_INFO_STACK);
+ const int phraseId = GetLineSafe (ok_buy_event_lines, stack);
+ NPCPhrase (phraseId);
+ SET_GAME_STATE (MELNORME_EVENTS_INFO_STACK, stack + 1);
+}
+
+static void
+AlienRaces (void)
+{
+ BYTE stack = GET_GAME_STATE (MELNORME_ALIEN_INFO_STACK);
+ const int phraseId = GetLineSafe (ok_buy_alien_race_lines, stack);
+ // Two pieces of alien knowledge trigger state changes.
+ switch (phraseId)
+ {
+ case OK_BUY_ALIEN_RACE_14:
+ if (!GET_GAME_STATE (FOUND_PLUTO_SPATHI))
+ {
+ SET_GAME_STATE (KNOW_SPATHI_PASSWORD, 1);
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 7);
+ }
+ break;
+ case OK_BUY_ALIEN_RACE_15:
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) < 2)
+ {
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 2);
+ }
+ SET_GAME_STATE (KNOW_SYREEN_WORLD_SHATTERED, 1);
+ break;
+ }
+ NPCPhrase (phraseId);
+ SET_GAME_STATE (MELNORME_ALIEN_INFO_STACK, stack + 1);
+}
+
+static void
+History (void)
+{
+ BYTE stack = GET_GAME_STATE (MELNORME_HISTORY_INFO_STACK);
+ const int phraseId = GetLineSafe (ok_buy_history_lines, stack);
+ NPCPhrase (phraseId);
+ SET_GAME_STATE (MELNORME_HISTORY_INFO_STACK, stack + 1);
+}
+
+// extract method to tell if we have any information left to sell to the player.
+static bool AnyInfoLeftToSell (void)
+{
+ return GET_GAME_STATE (MELNORME_EVENTS_INFO_STACK) < NUM_EVENT_ITEMS
+ || GET_GAME_STATE (MELNORME_ALIEN_INFO_STACK) < NUM_ALIEN_RACE_ITEMS
+ || GET_GAME_STATE (MELNORME_HISTORY_INFO_STACK) < NUM_HISTORY_ITEMS;
+}
+
+static void NatureOfConversation (RESPONSE_REF R);
+
+static BYTE AskedToBuy;
+
+
+static void
+DoBuy (RESPONSE_REF R)
+{
+ COUNT credit;
+ SIZE needed_credit;
+ BYTE slot;
+ DWORD capacity;
+
+ credit = GetAvailableCredits ();
+
+ capacity = FUEL_RESERVE;
+ slot = NUM_MODULE_SLOTS - 1;
+ do
+ {
+ if (GLOBAL_SIS (ModuleSlots[slot]) == FUEL_TANK
+ || GLOBAL_SIS (ModuleSlots[slot]) == HIGHEFF_FUELSYS)
+ {
+ COUNT volume;
+
+ volume = GLOBAL_SIS (ModuleSlots[slot]) == FUEL_TANK
+ ? FUEL_TANK_CAPACITY : HEFUEL_TANK_CAPACITY;
+ capacity += volume;
+ }
+ } while (slot--);
+
+ // If they're out of credits, educate them on how commerce works.
+ if (credit == 0)
+ {
+ AskedToBuy = TRUE;
+ NPCPhrase (NEED_CREDIT);
+
+ NatureOfConversation (R);
+ }
+ else if (PLAYER_SAID (R, buy_fuel)
+ || PLAYER_SAID (R, buy_1_fuel)
+ || PLAYER_SAID (R, buy_5_fuel)
+ || PLAYER_SAID (R, buy_10_fuel)
+ || PLAYER_SAID (R, buy_25_fuel)
+ || PLAYER_SAID (R, fill_me_up))
+ {
+ needed_credit = 0;
+ if (PLAYER_SAID (R, buy_1_fuel))
+ needed_credit = 1;
+ else if (PLAYER_SAID (R, buy_5_fuel))
+ needed_credit = 5;
+ else if (PLAYER_SAID (R, buy_10_fuel))
+ needed_credit = 10;
+ else if (PLAYER_SAID (R, buy_25_fuel))
+ needed_credit = 25;
+ else if (PLAYER_SAID (R, fill_me_up))
+ needed_credit = (capacity - GLOBAL_SIS (FuelOnBoard)
+ + FUEL_TANK_SCALE - 1)
+ / FUEL_TANK_SCALE;
+
+ if (needed_credit == 0)
+ {
+ if (!GET_GAME_STATE (MELNORME_FUEL_PROCEDURE))
+ {
+ NPCPhrase (BUY_FUEL_INTRO);
+ SET_GAME_STATE (MELNORME_FUEL_PROCEDURE, 1);
+ }
+ }
+ else
+ {
+ if (GLOBAL_SIS (FuelOnBoard) / FUEL_TANK_SCALE
+ + needed_credit > capacity / FUEL_TANK_SCALE)
+ {
+ NPCPhrase (NO_ROOM_FOR_FUEL);
+ goto TryFuelAgain;
+ }
+
+ if ((int)(needed_credit * (BIO_CREDIT_VALUE / 2)) <= (int)credit)
+ {
+ DWORD f;
+
+ NPCPhrase (GOT_FUEL);
+
+ f = (DWORD)needed_credit * FUEL_TANK_SCALE;
+ while (f > 0x3FFFL)
+ {
+ DeltaSISGauges (0, 0x3FFF, 0);
+ f -= 0x3FFF;
+ }
+ DeltaSISGauges (0, (SIZE)f, 0);
+ }
+ needed_credit *= (BIO_CREDIT_VALUE / 2);
+ }
+ if (needed_credit)
+ {
+ DeltaCredit (-needed_credit);
+ if (GLOBAL_SIS (FuelOnBoard) >= capacity)
+ goto BuyBuyBuy;
+ }
+TryFuelAgain:
+ NPCPhrase (HOW_MUCH_FUEL);
+
+ Response (buy_1_fuel, DoBuy);
+ Response (buy_5_fuel, DoBuy);
+ Response (buy_10_fuel, DoBuy);
+ Response (buy_25_fuel, DoBuy);
+ Response (fill_me_up, DoBuy);
+ Response (done_buying_fuel, DoBuy);
+ }
+ else if (PLAYER_SAID (R, buy_technology)
+ || PLAYER_SAID (R, buy_new_tech))
+ {
+ // Note that this code no longer uses the MELNORME_TECH_STACK state
+ // buts, as they're not needed; we can tell what technologies the
+ // player has by using the technology API above. This opens the
+ // possibility of the player acquiring tech from someplace other than
+ // the Melnorme.
+ const TechSaleData* nextTech;
+
+ // If it's our first time, give an introduction.
+ if (!GET_GAME_STATE (MELNORME_TECH_PROCEDURE))
+ {
+ NPCPhrase (BUY_NEW_TECH_INTRO);
+ SET_GAME_STATE (MELNORME_TECH_PROCEDURE, 1);
+ }
+
+ // Did the player just attempt to buy a tech?
+ if (PLAYER_SAID (R, buy_new_tech))
+ {
+ nextTech = GetNextTechForSale ();
+ if (!nextTech)
+ goto BuyBuyBuy; // No tech left to buy
+
+ if (!DeltaCredit (-nextTech->price))
+ goto BuyBuyBuy; // Can't afford it
+
+ // Make the sale
+ GrantTech (nextTech->techId);
+ NPCPhrase (nextTech->sold_line);
+ }
+
+ nextTech = GetNextTechForSale ();
+ if (!nextTech)
+ {
+ NPCPhrase (NEW_TECH_ALL_GONE);
+ goto BuyBuyBuy; // No tech left to buy
+ }
+
+ NPCPhrase (nextTech->sale_line);
+
+ Response (buy_new_tech, DoBuy);
+ Response (no_buy_new_tech, DoBuy);
+ }
+ else if (PLAYER_SAID (R, buy_info)
+ || PLAYER_SAID (R, buy_current_events)
+ || PLAYER_SAID (R, buy_alien_races)
+ || PLAYER_SAID (R, buy_history))
+ {
+ if (!GET_GAME_STATE (MELNORME_INFO_PROCEDURE))
+ {
+ NPCPhrase (BUY_INFO_INTRO);
+ SET_GAME_STATE (MELNORME_INFO_PROCEDURE, 1);
+ }
+ else if (PLAYER_SAID (R, buy_info))
+ {
+ NPCPhrase (OK_BUY_INFO);
+ }
+ else
+ {
+#define INFO_COST 75
+ if (!DeltaCredit (-INFO_COST))
+ goto BuyBuyBuy;
+
+ if (PLAYER_SAID (R, buy_current_events))
+ CurrentEvents ();
+ else if (PLAYER_SAID (R, buy_alien_races))
+ AlienRaces ();
+ else if (PLAYER_SAID (R, buy_history))
+ History ();
+ }
+
+ if (!AnyInfoLeftToSell ())
+ {
+ NPCPhrase (INFO_ALL_GONE);
+ goto BuyBuyBuy;
+ }
+
+ if (GET_GAME_STATE (MELNORME_EVENTS_INFO_STACK) < NUM_EVENT_ITEMS)
+ Response (buy_current_events, DoBuy);
+ if (GET_GAME_STATE (MELNORME_ALIEN_INFO_STACK) < NUM_ALIEN_RACE_ITEMS)
+ Response (buy_alien_races, DoBuy);
+ if (GET_GAME_STATE (MELNORME_HISTORY_INFO_STACK) < NUM_HISTORY_ITEMS)
+ Response (buy_history, DoBuy);
+ Response (done_buying_info, DoBuy);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, done_buying_fuel))
+ NPCPhrase (OK_DONE_BUYING_FUEL);
+ else if (PLAYER_SAID (R, no_buy_new_tech))
+ NPCPhrase (OK_NO_BUY_NEW_TECH);
+ else if (PLAYER_SAID (R, done_buying_info))
+ NPCPhrase (OK_DONE_BUYING_INFO);
+ else
+ NPCPhrase (WHAT_TO_BUY);
+
+BuyBuyBuy:
+ if (GLOBAL_SIS (FuelOnBoard) < capacity)
+ Response (buy_fuel, DoBuy);
+ if (GetNextTechForSale ())
+ Response (buy_technology, DoBuy);
+ if (AnyInfoLeftToSell ())
+ Response (buy_info, DoBuy);
+
+ Response (done_buying, NatureOfConversation);
+ Response (be_leaving_now, ExitConversation);
+ }
+}
+
+static void
+DoSell (RESPONSE_REF R)
+{
+ BYTE num_new_rainbows;
+ UWORD rainbow_mask;
+ SIZE added_credit;
+ int what_to_sell_queued = 0;
+
+ rainbow_mask = MAKE_WORD (
+ GET_GAME_STATE (RAINBOW_WORLD0),
+ GET_GAME_STATE (RAINBOW_WORLD1)
+ );
+ num_new_rainbows = (BYTE)(-GET_GAME_STATE (MELNORME_RAINBOW_COUNT));
+ while (rainbow_mask)
+ {
+ if (rainbow_mask & 1)
+ ++num_new_rainbows;
+
+ rainbow_mask >>= 1;
+ }
+
+ if (!PLAYER_SAID (R, sell))
+ {
+ if (PLAYER_SAID (R, sell_life_data))
+ {
+ DWORD TimeIn;
+
+ added_credit = GLOBAL_SIS (TotalBioMass) * BIO_CREDIT_VALUE;
+
+ NPCPhrase (SOLD_LIFE_DATA1);
+ NPCNumber (GLOBAL_SIS (TotalBioMass), NULL);
+ NPCPhrase (SOLD_LIFE_DATA2);
+ NPCNumber (added_credit, NULL);
+ NPCPhrase (SOLD_LIFE_DATA3);
+ // queue WHAT_TO_SELL before talk-segue
+ if (num_new_rainbows)
+ {
+ NPCPhrase (WHAT_TO_SELL);
+ what_to_sell_queued = 1;
+ }
+ AlienTalkSegue (1);
+
+ DrawCargoStrings ((BYTE)~0, (BYTE)~0);
+ SleepThread (ONE_SECOND / 2);
+ TimeIn = GetTimeCounter ();
+ DrawCargoStrings (
+ (BYTE)NUM_ELEMENT_CATEGORIES,
+ (BYTE)NUM_ELEMENT_CATEGORIES
+ );
+ do
+ {
+ TimeIn = GetTimeCounter ();
+ if (AnyButtonPress (TRUE))
+ {
+ DeltaCredit (GLOBAL_SIS (TotalBioMass) * BIO_CREDIT_VALUE);
+ GLOBAL_SIS (TotalBioMass) = 0;
+ }
+ else
+ {
+ --GLOBAL_SIS (TotalBioMass);
+ DeltaCredit (BIO_CREDIT_VALUE);
+ }
+ DrawCargoStrings (
+ (BYTE)NUM_ELEMENT_CATEGORIES,
+ (BYTE)NUM_ELEMENT_CATEGORIES
+ );
+ } while (GLOBAL_SIS (TotalBioMass));
+ SleepThread (ONE_SECOND / 2);
+
+ ClearSISRect (DRAW_SIS_DISPLAY);
+ }
+ else /* if (R == sell_rainbow_locations) */
+ {
+ added_credit = num_new_rainbows * (250 * BIO_CREDIT_VALUE);
+
+ NPCPhrase (SOLD_RAINBOW_LOCATIONS1);
+ NPCNumber (num_new_rainbows, NULL);
+ NPCPhrase (SOLD_RAINBOW_LOCATIONS2);
+ NPCNumber (added_credit, NULL);
+ NPCPhrase (SOLD_RAINBOW_LOCATIONS3);
+
+ num_new_rainbows += GET_GAME_STATE (MELNORME_RAINBOW_COUNT);
+ SET_GAME_STATE (MELNORME_RAINBOW_COUNT, num_new_rainbows);
+ num_new_rainbows = 0;
+
+ DeltaCredit (added_credit);
+ }
+
+ AskedToBuy = FALSE;
+ }
+
+ if (GLOBAL_SIS (TotalBioMass) || num_new_rainbows)
+ {
+ if (!what_to_sell_queued)
+ NPCPhrase (WHAT_TO_SELL);
+
+ if (GLOBAL_SIS (TotalBioMass))
+ Response (sell_life_data, DoSell);
+ if (num_new_rainbows)
+ Response (sell_rainbow_locations, DoSell);
+ Response (done_selling, NatureOfConversation);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, sell))
+ NPCPhrase (NOTHING_TO_SELL);
+ DISABLE_PHRASE (sell);
+
+ NatureOfConversation (R);
+ }
+}
+
+
+static void
+NatureOfConversation (RESPONSE_REF R)
+{
+ BYTE num_new_rainbows;
+ UWORD rainbow_mask;
+ COUNT Credit;
+
+ if (PLAYER_SAID (R, get_on_with_business))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK2, 5);
+ R = 0;
+ }
+
+ // Draw credits display
+ DeltaCredit (0);
+ Credit = GetAvailableCredits ();
+ if (R == 0)
+ {
+ BYTE stack = GET_GAME_STATE (MELNORME_YACK_STACK2) - 5;
+ NPCPhrase (GetLineSafe (hello_and_down_to_business_lines, stack));
+ if (stack < (NUM_HELLO_LINES - 1))
+ ++stack;
+ SET_GAME_STATE (MELNORME_YACK_STACK2, stack + 5);
+ }
+
+ rainbow_mask = MAKE_WORD (
+ GET_GAME_STATE (RAINBOW_WORLD0),
+ GET_GAME_STATE (RAINBOW_WORLD1)
+ );
+ num_new_rainbows = (BYTE)(-GET_GAME_STATE (MELNORME_RAINBOW_COUNT));
+ while (rainbow_mask)
+ {
+ if (rainbow_mask & 1)
+ ++num_new_rainbows;
+
+ rainbow_mask >>= 1;
+ }
+
+ if (GLOBAL_SIS (FuelOnBoard) > 0
+ || GLOBAL_SIS (TotalBioMass)
+ || Credit
+ || num_new_rainbows)
+ {
+ if (!GET_GAME_STATE (TRADED_WITH_MELNORME))
+ {
+ SET_GAME_STATE (TRADED_WITH_MELNORME, 1);
+
+ NPCPhrase (TRADING_INFO);
+ }
+
+ if (R == 0)
+ {
+ /* Melnorme reports any news and turns purple */
+ NPCPhrase (BUY_OR_SELL);
+ AlienTalkSegue (1);
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1)
+ ), ONE_SECOND / 2);
+ AlienTalkSegue ((COUNT)~0);
+ }
+ else if (PLAYER_SAID (R, why_turned_purple))
+ {
+ SET_GAME_STATE (WHY_MELNORME_PURPLE, 1);
+
+ NPCPhrase (TURNED_PURPLE_BECAUSE);
+ }
+ else if (PLAYER_SAID (R, done_selling))
+ {
+ NPCPhrase (OK_DONE_SELLING);
+ }
+ else if (PLAYER_SAID (R, done_buying))
+ {
+ NPCPhrase (OK_DONE_BUYING);
+ }
+
+ if (!GET_GAME_STATE (WHY_MELNORME_PURPLE))
+ {
+ Response (why_turned_purple, NatureOfConversation);
+ }
+ if (!AskedToBuy)
+ Response (buy, DoBuy);
+ if (PHRASE_ENABLED (sell))
+ Response (sell, DoSell);
+ Response (goodbye, ExitConversation);
+ }
+ else /* needs to be rescued */
+ {
+ if (GET_GAME_STATE (MELNORME_RESCUE_REFUSED))
+ {
+ NPCPhrase (CHANGED_MIND);
+
+ Response (yes_changed_mind, DoRescue);
+ Response (no_changed_mind, ExitConversation);
+ }
+ else
+ {
+ BYTE num_rescues = GET_GAME_STATE (MELNORME_RESCUE_COUNT);
+ NPCPhrase (GetLineSafe (rescue_lines, num_rescues));
+
+ if (num_rescues < NUM_RESCUE_LINES - 1)
+ {
+ ++num_rescues;
+ SET_GAME_STATE (MELNORME_RESCUE_COUNT, num_rescues);
+ }
+
+ NPCPhrase (SHOULD_WE_HELP_YOU);
+
+ Response (yes_help, DoRescue);
+ Response (no_help, ExitConversation);
+ }
+ }
+}
+
+static BYTE local_stack0, local_stack1;
+
+static void
+DoBluster (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, trade_is_for_the_weak))
+ {
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 2)
+ ), ONE_SECOND / 2);
+
+ SET_GAME_STATE (MELNORME_YACK_STACK2, 4);
+ NPCPhrase (WERE_NOT_AFRAID);
+ }
+ else if (PLAYER_SAID (R, why_blue_light))
+ {
+ SET_GAME_STATE (WHY_MELNORME_BLUE, 1);
+
+ NPCPhrase (BLUE_IS_MAD);
+ }
+ else if (PLAYER_SAID (R, we_strong_1))
+ {
+ local_stack0 = 1;
+ NPCPhrase (YOU_NOT_STRONG_1);
+ }
+ else if (PLAYER_SAID (R, we_strong_2))
+ {
+ local_stack0 = 2;
+ NPCPhrase (YOU_NOT_STRONG_2);
+ }
+ else if (PLAYER_SAID (R, we_strong_3))
+ {
+ local_stack0 = 3;
+ NPCPhrase (YOU_NOT_STRONG_3);
+ }
+ else if (PLAYER_SAID (R, just_testing))
+ {
+ local_stack1 = 1;
+ NPCPhrase (REALLY_TESTING);
+ }
+
+ if (!GET_GAME_STATE (WHY_MELNORME_BLUE))
+ Response (why_blue_light, DoBluster);
+ switch (local_stack0)
+ {
+ case 0:
+ Response (we_strong_1, DoBluster);
+ break;
+ case 1:
+ Response (we_strong_2, DoBluster);
+ break;
+ case 2:
+ Response (we_strong_3, DoBluster);
+ break;
+ }
+ switch (local_stack1)
+ {
+ case 0:
+ Response (just_testing, DoBluster);
+ break;
+ case 1:
+ {
+ Response (yes_really_testing, DoFirstMeeting);
+ break;
+ }
+ }
+ Response (youre_on, ExitConversation);
+}
+
+static void
+yack0_respond (void)
+{
+
+ switch (GET_GAME_STATE (MELNORME_YACK_STACK0))
+ {
+ case 0:
+ {
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ we_are_from_alliance0,
+ buf,
+ (RESPONSE_REF)-1);
+ DoResponsePhrase (we_are_from_alliance0, DoFirstMeeting, shared_phrase_buf);
+ break;
+ }
+ case 1:
+ Response (how_know, DoFirstMeeting);
+ break;
+ }
+}
+
+static void
+yack1_respond (void)
+{
+ switch (GET_GAME_STATE (MELNORME_YACK_STACK1))
+ {
+ case 0:
+ Response (what_about_yourselves, DoFirstMeeting);
+ break;
+ case 1:
+ Response (what_factors, DoFirstMeeting);
+ case 2:
+ Response (get_on_with_business, NatureOfConversation);
+ break;
+ }
+}
+
+static void
+yack2_respond (void)
+{
+ switch (GET_GAME_STATE (MELNORME_YACK_STACK2))
+ {
+ case 0:
+ Response (what_about_universe, DoFirstMeeting);
+ break;
+ case 1:
+ Response (giving_is_good_1, DoFirstMeeting);
+ break;
+ case 2:
+ Response (giving_is_good_2, DoFirstMeeting);
+ break;
+ case 3:
+ Response (trade_is_for_the_weak, DoBluster);
+ break;
+ }
+}
+
+static void
+DoFirstMeeting (RESPONSE_REF R)
+{
+ BYTE last_stack = 0;
+ PVOIDFUNC temp_func, stack_func[] =
+ {
+ yack0_respond,
+ yack1_respond,
+ yack2_respond,
+ };
+
+ if (R == 0)
+ {
+ BYTE business_count;
+
+ business_count = GET_GAME_STATE (MELNORME_BUSINESS_COUNT);
+ switch (business_count++)
+ {
+ case 0:
+ NPCPhrase (HELLO_NOW_DOWN_TO_BUSINESS_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_NOW_DOWN_TO_BUSINESS_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_NOW_DOWN_TO_BUSINESS_3);
+ --business_count;
+ break;
+ }
+ SET_GAME_STATE (MELNORME_BUSINESS_COUNT, business_count);
+ }
+ else if (PLAYER_SAID (R, we_are_from_alliance0))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK0, 1);
+ NPCPhrase (KNOW_OF_YOU);
+ }
+ else if (PLAYER_SAID (R, how_know))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK0, 2);
+ NPCPhrase (KNOW_BECAUSE);
+ }
+ else if (PLAYER_SAID (R, what_about_yourselves))
+ {
+ last_stack = 1;
+ SET_GAME_STATE (MELNORME_YACK_STACK1, 1);
+ NPCPhrase (NO_TALK_ABOUT_OURSELVES);
+ }
+ else if (PLAYER_SAID (R, what_factors))
+ {
+ last_stack = 1;
+ SET_GAME_STATE (MELNORME_YACK_STACK1, 2);
+ NPCPhrase (FACTORS_ARE);
+ }
+ else if (PLAYER_SAID (R, what_about_universe))
+ {
+ last_stack = 2;
+ SET_GAME_STATE (MELNORME_YACK_STACK2, 1);
+ NPCPhrase (NO_FREE_LUNCH);
+ }
+ else if (PLAYER_SAID (R, giving_is_good_1))
+ {
+ last_stack = 2;
+ SET_GAME_STATE (MELNORME_YACK_STACK2, 2);
+ NPCPhrase (GIVING_IS_BAD_1);
+ }
+ else if (PLAYER_SAID (R, giving_is_good_2))
+ {
+ last_stack = 2;
+ SET_GAME_STATE (MELNORME_YACK_STACK2, 3);
+ NPCPhrase (GIVING_IS_BAD_2);
+ }
+ else if (PLAYER_SAID (R, yes_really_testing))
+ {
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 0)
+ ), ONE_SECOND / 2);
+
+ NPCPhrase (TEST_RESULTS);
+ }
+ else if (PLAYER_SAID (R, we_apologize))
+ {
+ SET_GAME_STATE (MELNORME_ANGER, 0);
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 0)
+ ), ONE_SECOND / 2);
+
+ NPCPhrase (APOLOGY_ACCEPTED);
+ }
+
+ temp_func = stack_func[0];
+ stack_func[0] = stack_func[last_stack];
+ stack_func[last_stack] = temp_func;
+ (*stack_func[0]) ();
+ (*stack_func[1]) ();
+ (*stack_func[2]) ();
+ Response (no_trade_now, ExitConversation);
+}
+
+static void
+DoMelnormeMiffed (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE miffed_count;
+
+ miffed_count = GET_GAME_STATE (MELNORME_MIFFED_COUNT);
+ switch (miffed_count++)
+ {
+ case 0:
+ NPCPhrase (HELLO_SLIGHTLY_ANGRY_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_SLIGHTLY_ANGRY_2);
+ break;
+ default:
+ --miffed_count;
+ NPCPhrase (HELLO_SLIGHTLY_ANGRY_3);
+ break;
+ }
+ SET_GAME_STATE (MELNORME_MIFFED_COUNT, miffed_count);
+
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 2)
+ ), ONE_SECOND / 2);
+ }
+ else if (PLAYER_SAID (R, explore_relationship))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK3, 1);
+
+ NPCPhrase (EXAMPLE_OF_RELATIONSHIP);
+ }
+ else if (PLAYER_SAID (R, excuse_1))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK3, 2);
+
+ NPCPhrase (NO_EXCUSE_1);
+ }
+ else if (PLAYER_SAID (R, excuse_2))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK3, 3);
+
+ NPCPhrase (NO_EXCUSE_2);
+ }
+ else if (PLAYER_SAID (R, excuse_3))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK3, 4);
+
+ NPCPhrase (NO_EXCUSE_3);
+ }
+
+ switch (GET_GAME_STATE (MELNORME_YACK_STACK3))
+ {
+ case 0:
+ Response (explore_relationship, DoMelnormeMiffed);
+ break;
+ case 1:
+ Response (excuse_1, DoMelnormeMiffed);
+ break;
+ case 2:
+ Response (excuse_2, DoMelnormeMiffed);
+ break;
+ case 3:
+ Response (excuse_3, DoMelnormeMiffed);
+ break;
+ }
+ Response (we_apologize, DoFirstMeeting);
+ Response (so_we_can_attack, ExitConversation);
+ Response (bye_melnorme_slightly_angry, ExitConversation);
+}
+
+static void
+DoMelnormePissed (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE pissed_count;
+
+ pissed_count = GET_GAME_STATE (MELNORME_PISSED_COUNT);
+ switch (pissed_count++)
+ {
+ case 0:
+ NPCPhrase (HELLO_PISSED_OFF_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_PISSED_OFF_2);
+ break;
+ default:
+ --pissed_count;
+ NPCPhrase (HELLO_PISSED_OFF_3);
+ break;
+ }
+ SET_GAME_STATE (MELNORME_PISSED_COUNT, pissed_count);
+
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 2)
+ ), ONE_SECOND / 2);
+ }
+ else if (PLAYER_SAID (R, beg_forgiveness))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK4, 1);
+
+ NPCPhrase (LOTS_TO_MAKE_UP_FOR);
+ }
+ else if (PLAYER_SAID (R, you_are_so_right))
+ {
+ SET_GAME_STATE (MELNORME_YACK_STACK4, 2);
+
+ NPCPhrase (ONE_LAST_CHANCE);
+ }
+
+ switch (GET_GAME_STATE (MELNORME_YACK_STACK4))
+ {
+ case 0:
+ Response (beg_forgiveness, DoMelnormePissed);
+ break;
+ case 1:
+ Response (you_are_so_right, DoMelnormePissed);
+ break;
+ case 2:
+ Response (ok_strip_me, ExitConversation);
+ break;
+ }
+ Response (fight_some_more, ExitConversation);
+ Response (bye_melnorme_pissed_off, ExitConversation);
+}
+
+static void
+DoMelnormeHate (RESPONSE_REF R)
+{
+ BYTE hate_count;
+
+ (void) R; // ignored
+ hate_count = GET_GAME_STATE (MELNORME_HATE_COUNT);
+ switch (hate_count++)
+ {
+ case 0:
+ NPCPhrase (HELLO_HATE_YOU_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_HATE_YOU_2);
+ break;
+ default:
+ --hate_count;
+ NPCPhrase (HELLO_HATE_YOU_3);
+ break;
+ }
+ SET_GAME_STATE (MELNORME_HATE_COUNT, hate_count);
+
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 2)
+ ), ONE_SECOND / 2);
+
+ Response (well_if_thats_the_way_you_feel, ExitConversation);
+ Response (you_hate_us_so_we_go_away, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ prevMsgMode = SetStatusMessageMode (SMM_CREDITS);
+
+ if (GET_GAME_STATE (MET_MELNORME) == 0)
+ {
+ SET_GAME_STATE (MET_MELNORME, 1);
+ DoFirstMeeting (0);
+ }
+ else
+ {
+ switch (GET_GAME_STATE (MELNORME_ANGER))
+ {
+ case 0:
+ if (GET_GAME_STATE (MELNORME_YACK_STACK2) <= 5)
+ DoFirstMeeting (0);
+ else
+ NatureOfConversation (0);
+ break;
+ case 1:
+ DoMelnormeMiffed (0);
+ break;
+ case 2:
+ DoMelnormePissed (0);
+ break;
+ default:
+ DoMelnormeHate (0);
+ break;
+ }
+ }
+}
+
+static COUNT
+uninit_melnorme (void)
+{
+ return 0;
+}
+
+static void
+post_melnorme_enc (void)
+{
+ if (prevMsgMode != SMM_UNDEFINED)
+ SetStatusMessageMode (prevMsgMode);
+ DrawStatusMessage (NULL);
+}
+
+LOCDATA*
+init_melnorme_comm (void)
+{
+ LOCDATA *retval;
+
+ melnorme_desc.init_encounter_func = Intro;
+ melnorme_desc.post_encounter_func = post_melnorme_enc;
+ melnorme_desc.uninit_encounter_func = uninit_melnorme;
+
+ melnorme_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ melnorme_desc.AlienTextBaseline.y = 0;
+ melnorme_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ local_stack0 = 0;
+ local_stack1 = 0;
+
+ prevMsgMode = SMM_UNDEFINED;
+
+ setSegue (Segue_peace);
+ AskedToBuy = FALSE;
+ retval = &melnorme_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/melnorm/resinst.h b/src/uqm/comm/melnorm/resinst.h
new file mode 100644
index 0000000..cda8d5a
--- /dev/null
+++ b/src/uqm/comm/melnorm/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define MELNORME_COLOR_MAP "comm.melnorme.colortable"
+#define MELNORME_CONVERSATION_PHRASES "comm.melnorme.dialogue"
+#define MELNORME_FONT "comm.melnorme.font"
+#define MELNORME_MUSIC "comm.melnorme.music"
+#define MELNORME_PMAP_ANIM "comm.melnorme.graphics"
diff --git a/src/uqm/comm/melnorm/strings.h b/src/uqm/comm/melnorm/strings.h
new file mode 100644
index 0000000..14ad322
--- /dev/null
+++ b/src/uqm/comm/melnorm/strings.h
@@ -0,0 +1,309 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MELNORME_STRINGS_H
+#define MELNORME_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HELLO_NOW_DOWN_TO_BUSINESS_1,
+ HELLO_NOW_DOWN_TO_BUSINESS_2,
+ HELLO_NOW_DOWN_TO_BUSINESS_3,
+ KNOW_OF_YOU,
+ how_know,
+ KNOW_BECAUSE,
+ what_about_yourselves,
+ NO_TALK_ABOUT_OURSELVES,
+ what_factors,
+ FACTORS_ARE,
+ what_about_universe,
+ NO_FREE_LUNCH,
+ giving_is_good_1,
+ GIVING_IS_BAD_1,
+ giving_is_good_2,
+ GIVING_IS_BAD_2,
+ get_on_with_business,
+ trade_is_for_the_weak,
+ WERE_NOT_AFRAID,
+ no_trade_now,
+ OK_NO_TRADE_NOW_BYE,
+ HELLO_AND_DOWN_TO_BUSINESS_1,
+ HELLO_AND_DOWN_TO_BUSINESS_2,
+ HELLO_AND_DOWN_TO_BUSINESS_3,
+ HELLO_AND_DOWN_TO_BUSINESS_4,
+ HELLO_AND_DOWN_TO_BUSINESS_5,
+ HELLO_AND_DOWN_TO_BUSINESS_6,
+ HELLO_AND_DOWN_TO_BUSINESS_7,
+ HELLO_AND_DOWN_TO_BUSINESS_8,
+ HELLO_AND_DOWN_TO_BUSINESS_9,
+ HELLO_AND_DOWN_TO_BUSINESS_10,
+ whats_my_credit,
+ HELLO_SLIGHTLY_ANGRY_1,
+ HELLO_SLIGHTLY_ANGRY_2,
+ HELLO_SLIGHTLY_ANGRY_3,
+ explore_relationship,
+ EXAMPLE_OF_RELATIONSHIP,
+ excuse_1,
+ NO_EXCUSE_1,
+ excuse_2,
+ NO_EXCUSE_2,
+ excuse_3,
+ NO_EXCUSE_3,
+ we_apologize,
+ APOLOGY_ACCEPTED,
+ so_we_can_attack,
+ DECEITFUL_HUMAN,
+ bye_melnorme_slightly_angry,
+ MELNORME_SLIGHTLY_ANGRY_GOODBYE,
+ HELLO_HATE_YOU_1,
+ HELLO_HATE_YOU_2,
+ HELLO_HATE_YOU_3,
+ well_if_thats_the_way_you_feel,
+ you_hate_us_so_we_go_away,
+ HATE_YOU_GOODBYE,
+ WE_FIGHT_AGAIN,
+ RESCUE_EXPLANATION,
+ RESCUE_AGAIN_1,
+ RESCUE_AGAIN_2,
+ RESCUE_AGAIN_3,
+ RESCUE_AGAIN_4,
+ RESCUE_AGAIN_5,
+ CHANGED_MIND,
+ no_changed_mind,
+ yes_changed_mind,
+ SHOULD_WE_HELP_YOU,
+ yes_help,
+ no_help,
+ RESCUE_OFFER,
+ RESCUE_TANKS,
+ RESCUE_HOME,
+ take_it,
+ leave_it,
+ HAPPY_TO_HAVE_RESCUED,
+ MAYBE_SEE_YOU_LATER,
+ GOODBYE_AND_GOODLUCK,
+ GOODBYE_AND_GOODLUCK_AGAIN,
+ HELLO_PISSED_OFF_1,
+ HELLO_PISSED_OFF_2,
+ HELLO_PISSED_OFF_3,
+ beg_forgiveness,
+ LOTS_TO_MAKE_UP_FOR,
+ you_are_so_right,
+ ONE_LAST_CHANCE,
+ ok_strip_me,
+ no_strip_now,
+ NOT_WORTH_STRIPPING,
+ FAIR_JUSTICE,
+ bye_melnorme_pissed_off,
+ MELNORME_PISSED_OFF_GOODBYE,
+ fight_some_more,
+ OK_FIGHT_SOME_MORE,
+ why_blue_light,
+ BLUE_IS_MAD,
+ we_strong_1,
+ YOU_NOT_STRONG_1,
+ we_strong_2,
+ YOU_NOT_STRONG_2,
+ we_strong_3,
+ YOU_NOT_STRONG_3,
+ just_testing,
+ REALLY_TESTING,
+ yes_really_testing,
+ TEST_RESULTS,
+ youre_on,
+ YOU_GIVE_US_NO_CHOICE,
+ TRADING_INFO,
+ BUY_OR_SELL,
+ goodbye,
+ why_turned_purple,
+ buy,
+ sell,
+ TURNED_PURPLE_BECAUSE,
+ NOTHING_TO_SELL,
+ WHAT_TO_SELL,
+ OK_DONE_SELLING,
+ sell_life_data,
+ SOLD_LIFE_DATA1,
+ SOLD_LIFE_DATA2,
+ SOLD_LIFE_DATA3,
+ sell_rainbow_locations,
+ SOLD_RAINBOW_LOCATIONS1,
+ SOLD_RAINBOW_LOCATIONS2,
+ SOLD_RAINBOW_LOCATIONS3,
+ sell_precursor_find,
+ SOLD_PRECURSOR_FIND,
+ changed_mind_no_sell,
+ done_selling,
+ NEED_CREDIT,
+ WHAT_TO_BUY,
+ WHAT_MORE_TO_BUY,
+ OK_DONE_BUYING,
+ buy_fuel,
+ done_buying,
+ be_leaving_now,
+ HOW_MUCH_FUEL,
+ buy_1_fuel,
+ GOT_FUEL,
+ buy_5_fuel,
+ buy_10_fuel,
+ buy_25_fuel,
+ done_buying_fuel,
+ FRIENDLY_GOODBYE,
+ CREDIT_IS0,
+ CREDIT_IS1,
+ NEED_MORE_CREDIT0,
+ NEED_MORE_CREDIT1,
+ BUY_FUEL_INTRO,
+ NO_ROOM_FOR_FUEL,
+ buy_info,
+ buy_technology,
+ buy_current_events,
+ buy_alien_races,
+ buy_history,
+ done_buying_info,
+ no_buy_info,
+ BUY_INFO_INTRO,
+ OK_BUY_INFO,
+ OK_NO_BUY_INFO,
+ OK_DONE_BUYING_INFO,
+ OK_BUY_EVENT_1,
+ OK_BUY_EVENT_2,
+ OK_BUY_EVENT_3,
+ OK_BUY_EVENT_4,
+ OK_BUY_EVENT_5,
+ OK_BUY_EVENT_6,
+ OK_BUY_EVENT_7,
+ OK_BUY_EVENT_8,
+ OK_BUY_ALIEN_RACE_1,
+ OK_BUY_ALIEN_RACE_2,
+ OK_BUY_ALIEN_RACE_3,
+ OK_BUY_ALIEN_RACE_4,
+ OK_BUY_ALIEN_RACE_5,
+ OK_BUY_ALIEN_RACE_6,
+ OK_BUY_ALIEN_RACE_7,
+ OK_BUY_ALIEN_RACE_8,
+ OK_BUY_ALIEN_RACE_9,
+ OK_BUY_ALIEN_RACE_10,
+ OK_BUY_ALIEN_RACE_11,
+ OK_BUY_ALIEN_RACE_12,
+ OK_BUY_ALIEN_RACE_13,
+ OK_BUY_ALIEN_RACE_14,
+ OK_BUY_ALIEN_RACE_15,
+ OK_BUY_ALIEN_RACE_16,
+ OK_BUY_HISTORY_1,
+ OK_BUY_HISTORY_2,
+ OK_BUY_HISTORY_3,
+ OK_BUY_HISTORY_4,
+ OK_BUY_HISTORY_5,
+ OK_BUY_HISTORY_6,
+ OK_BUY_HISTORY_7,
+ OK_BUY_HISTORY_8,
+ OK_BUY_HISTORY_9,
+ INFO_ALL_GONE,
+ buy_new_tech,
+ no_buy_new_tech,
+ done_buying_new_tech,
+ fill_me_up,
+ OK_FILL_YOU_UP,
+ BUY_NEW_TECH_INTRO,
+ OK_BUY_NEW_TECH,
+ OK_NO_BUY_NEW_TECH,
+ OK_DONE_BUYING_NEW_TECH,
+ OK_DONE_BUYING_FUEL,
+ NEW_TECH_1,
+ NEW_TECH_2,
+ NEW_TECH_3,
+ NEW_TECH_4,
+ NEW_TECH_5,
+ NEW_TECH_6,
+ NEW_TECH_7,
+ NEW_TECH_8,
+ NEW_TECH_9,
+ NEW_TECH_10,
+ NEW_TECH_11,
+ NEW_TECH_12,
+ NEW_TECH_13,
+ OK_BUY_NEW_TECH_1,
+ OK_BUY_NEW_TECH_2,
+ OK_BUY_NEW_TECH_3,
+ OK_BUY_NEW_TECH_4,
+ OK_BUY_NEW_TECH_5,
+ OK_BUY_NEW_TECH_6,
+ OK_BUY_NEW_TECH_7,
+ OK_BUY_NEW_TECH_8,
+ OK_BUY_NEW_TECH_9,
+ OK_BUY_NEW_TECH_10,
+ OK_BUY_NEW_TECH_11,
+ OK_BUY_NEW_TECH_12,
+ OK_BUY_NEW_TECH_13,
+ CHARITY,
+ NEW_TECH_ALL_GONE,
+ we_are_from_alliance0,
+ STRIP_HEAD,
+ LANDERS,
+ THRUSTERS,
+ JETS,
+ PODS,
+ BAYS,
+ DYNAMOS,
+ FURNACES,
+ GUNS,
+ BLASTERS,
+ CANNONS,
+ TRACKERS,
+ DEFENSES,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ ENUMERATE_ONE,
+ ENUMERATE_TWO,
+ ENUMERATE_THREE,
+ ENUMERATE_FOUR,
+ ENUMERATE_FIVE,
+ ENUMERATE_SIX,
+ ENUMERATE_SEVEN,
+ ENUMERATE_EIGHT,
+ ENUMERATE_NINE,
+ ENUMERATE_TEN,
+ ENUMERATE_ELEVEN,
+ ENUMERATE_TWELVE,
+ ENUMERATE_THIRTEEN,
+ ENUMERATE_FOURTEEN,
+ ENUMERATE_FIFTEEN,
+ ENUMERATE_SIXTEEN,
+ END_LIST_WITH_AND,
+ ENUMERATE_ZERO,
+ ENUMERATE_SEVENTEEN,
+ ENUMERATE_EIGHTEEN,
+ ENUMERATE_NINETEEN,
+ ENUMERATE_TWENTY,
+ ENUMERATE_THIRTY,
+ ENUMERATE_FOURTY,
+ ENUMERATE_FIFTY,
+ ENUMERATE_SIXTY,
+ ENUMERATE_SEVENTY,
+ ENUMERATE_EIGHTY,
+ ENUMERATE_NINETY,
+ ENUMERATE_HUNDRED,
+ ENUMERATE_THOUSAND
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/mycon/Makeinfo b/src/uqm/comm/mycon/Makeinfo
new file mode 100644
index 0000000..df0ef72
--- /dev/null
+++ b/src/uqm/comm/mycon/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="myconc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/mycon/myconc.c b/src/uqm/comm/mycon/myconc.c
new file mode 100644
index 0000000..6490904
--- /dev/null
+++ b/src/uqm/comm/mycon/myconc.c
@@ -0,0 +1,643 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/gameev.h"
+#include "libs/mathlib.h"
+
+
+static LOCDATA mycon_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ MYCON_PMAP_ANIM, /* AlienFrame */
+ MYCON_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ MYCON_COLOR_MAP, /* AlienColorMap */
+ MYCON_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ MYCON_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 5, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 12, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ (1 << 1), /* BlockMask */
+ },
+ {
+ 18, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ (1 << 0), /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 28, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 33, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND * 3 / 40, 0, /* FrameRate */
+ ONE_SECOND * 3 / 40, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 11, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static BYTE MadeChoice;
+
+static void
+DoRamble (RESPONSE_REF R)
+{
+ BYTE Counter;
+
+ Counter = GET_GAME_STATE (MYCON_RAMBLE);
+ switch (Counter++)
+ {
+ case 0:
+ NPCPhrase (RAMBLE_1);
+ break;
+ case 1:
+ NPCPhrase (RAMBLE_2);
+ break;
+ case 2:
+ NPCPhrase (RAMBLE_3);
+ break;
+ case 3:
+ NPCPhrase (RAMBLE_4);
+ break;
+ case 4:
+ NPCPhrase (RAMBLE_5);
+ break;
+ case 5:
+ NPCPhrase (RAMBLE_6);
+ break;
+ case 6:
+ NPCPhrase (RAMBLE_7);
+ break;
+ case 7:
+ NPCPhrase (RAMBLE_8);
+ break;
+ case 8:
+ NPCPhrase (RAMBLE_9);
+ break;
+ case 9:
+ NPCPhrase (RAMBLE_10);
+ break;
+ case 10:
+ NPCPhrase (RAMBLE_11);
+ break;
+ case 11:
+ NPCPhrase (RAMBLE_12);
+ break;
+ case 12:
+ NPCPhrase (RAMBLE_13);
+ break;
+ case 13:
+ NPCPhrase (RAMBLE_14);
+ break;
+ case 14:
+ NPCPhrase (RAMBLE_15);
+ break;
+ case 15:
+ NPCPhrase (RAMBLE_16);
+ break;
+ case 16:
+ NPCPhrase (RAMBLE_17);
+ break;
+ case 17:
+ NPCPhrase (RAMBLE_18);
+ break;
+ case 18:
+ NPCPhrase (RAMBLE_19);
+ break;
+ case 19:
+ NPCPhrase (RAMBLE_20);
+ break;
+ case 20:
+ NPCPhrase (RAMBLE_21);
+ break;
+ case 21:
+ NPCPhrase (RAMBLE_22);
+ break;
+ case 22:
+ NPCPhrase (RAMBLE_23);
+ break;
+ case 23:
+ NPCPhrase (RAMBLE_24);
+ break;
+ case 24:
+ NPCPhrase (RAMBLE_25);
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) < 2)
+ {
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 2);
+ }
+ break;
+ case 25:
+ NPCPhrase (RAMBLE_26);
+ break;
+ case 26:
+ NPCPhrase (RAMBLE_27);
+ break;
+ case 27:
+ NPCPhrase (RAMBLE_28);
+ break;
+ case 28:
+ NPCPhrase (RAMBLE_29);
+ break;
+ case 29:
+ NPCPhrase (RAMBLE_30);
+ break;
+ case 30:
+ NPCPhrase (RAMBLE_31);
+ break;
+ case 31:
+ NPCPhrase (RAMBLE_32);
+ Counter = 0;
+ break;
+ }
+ SET_GAME_STATE (MYCON_RAMBLE, Counter);
+
+ if (!(GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7)))
+ {
+ if (!PLAYER_SAID (R, come_in_peace)
+ && !PLAYER_SAID (R, gonna_die))
+ {
+ Counter = (GET_GAME_STATE (MYCON_INSULTS) + 1) & 7;
+ SET_GAME_STATE (MYCON_INSULTS, Counter);
+ MadeChoice = 1;
+ }
+ }
+ else if (!PLAYER_SAID (R, lets_be_friends)
+ && !PLAYER_SAID (R, came_to_homeworld)
+ && !PLAYER_SAID (R, submit_to_us))
+ {
+ Counter = (GET_GAME_STATE (MYCON_INFO) + 1) & 15;
+ SET_GAME_STATE (MYCON_INFO, Counter);
+ MadeChoice = 1;
+ }
+}
+
+static void
+CombatIsInevitable (RESPONSE_REF R)
+{
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, bye_space))
+ NPCPhrase (BYE_AND_DIE_SPACE);
+ else if (PLAYER_SAID (R, bye_homeworld))
+ NPCPhrase (BYE_AND_DIE_HOMEWORLD);
+ else if (PLAYER_SAID (R, like_to_land))
+ NPCPhrase (NEVER_LET_LAND);
+ else if (PLAYER_SAID (R, bye_sun_device))
+ {
+ NPCPhrase (GOODBYE_SUN_DEVICE);
+
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ DoRamble (R);
+ if (!(GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7)))
+ NPCPhrase (BYE_AND_DIE_SPACE);
+ else
+ NPCPhrase (BYE_AND_DIE_HOMEWORLD);
+ }
+ MadeChoice = 0;
+}
+
+static void
+SunDevice (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_sun_device))
+ {
+ NPCPhrase (GENERAL_INFO_SUN_DEVICE);
+
+ DISABLE_PHRASE (whats_up_sun_device);
+ }
+ else if (PLAYER_SAID (R, how_goes_implanting))
+ {
+ NPCPhrase (UNFORSEEN_DELAYS);
+
+ DISABLE_PHRASE (how_goes_implanting);
+ }
+ else if (PLAYER_SAID (R, i_have_a_cunning_plan))
+ {
+ NPCPhrase (WONT_FALL_FOR_TRICK);
+
+ SET_GAME_STATE (NO_TRICK_AT_SUN, 1);
+ }
+
+ if (PHRASE_ENABLED (whats_up_sun_device))
+ Response (whats_up_sun_device, SunDevice);
+ if (GET_GAME_STATE (MYCON_FELL_FOR_AMBUSH))
+ {
+ if (PHRASE_ENABLED (how_goes_implanting) && GET_GAME_STATE (MYCON_FELL_FOR_AMBUSH))
+ Response (how_goes_implanting, SunDevice);
+ Response (like_to_land, CombatIsInevitable);
+ }
+ else if (GET_GAME_STATE (MYCON_AMBUSH)
+ && !GET_GAME_STATE (NO_TRICK_AT_SUN))
+ Response (i_have_a_cunning_plan, SunDevice);
+ Response (bye_sun_device, CombatIsInevitable);
+}
+
+static void
+TrickMycon (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, i_have_a_cunning_plan))
+ {
+ NPCPhrase (TELL_US_ABOUT_WORLD);
+
+ DISABLE_PHRASE (i_have_a_cunning_plan);
+ }
+ else if (PLAYER_SAID (R, clue_1))
+ {
+ NPCPhrase (RESPONSE_1);
+
+ DISABLE_PHRASE (clue_1);
+ }
+ else if (PLAYER_SAID (R, clue_2))
+ {
+ NPCPhrase (RESPONSE_2);
+
+ DISABLE_PHRASE (clue_2);
+ }
+ else if (PLAYER_SAID (R, clue_3))
+ {
+ NPCPhrase (RESPONSE_3);
+
+ DISABLE_PHRASE (clue_3);
+ }
+
+ if (PHRASE_ENABLED (clue_1) == 0
+ && PHRASE_ENABLED (clue_2) == 0
+ && PHRASE_ENABLED (clue_3) == 0)
+ {
+ NPCPhrase (WE_GO_TO_IMPLANT);
+
+ setSegue (Segue_peace);
+ SET_GAME_STATE (MYCON_FELL_FOR_AMBUSH, 1);
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, ADVANCE_MYCON_MISSION);
+ }
+ else
+ NPCPhrase (AMBUSH_TAIL);
+
+ if (PHRASE_ENABLED (clue_1))
+ Response (clue_1, TrickMycon);
+ if (PHRASE_ENABLED (clue_2))
+ Response (clue_2, TrickMycon);
+ if (PHRASE_ENABLED (clue_3))
+ Response (clue_3, TrickMycon);
+}
+
+static void
+NormalMycon (RESPONSE_REF R)
+{
+ RESPONSE_FUNC RespFunc;
+
+ if (PLAYER_SAID (R, what_about_shattered))
+ {
+ NPCPhrase (ABOUT_SHATTERED);
+
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 2);
+ }
+ else if (R)
+ {
+ DoRamble (R);
+ NPCPhrase (RAMBLE_TAIL);
+
+ DISABLE_PHRASE (R);
+ }
+
+ if ((BYTE)TFB_Random () < 256 * 30 / 100)
+ RespFunc = (RESPONSE_FUNC)CombatIsInevitable;
+ else
+ RespFunc = (RESPONSE_FUNC)NormalMycon;
+ if (!(GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7)))
+ {
+ if (PHRASE_ENABLED (come_in_peace))
+ Response (come_in_peace, RespFunc);
+ if (PHRASE_ENABLED (gonna_die))
+ Response (gonna_die, RespFunc);
+ if (!MadeChoice) switch (GET_GAME_STATE (MYCON_INSULTS))
+ {
+ case 0:
+ Response (insult_1, RespFunc);
+ break;
+ case 1:
+ Response (insult_2, RespFunc);
+ break;
+ case 2:
+ Response (insult_3, RespFunc);
+ break;
+ case 3:
+ Response (insult_4, RespFunc);
+ break;
+ case 4:
+ Response (insult_5, RespFunc);
+ break;
+ case 5:
+ Response (insult_6, RespFunc);
+ break;
+ case 6:
+ Response (insult_7, RespFunc);
+ break;
+ case 7:
+ Response (insult_8, RespFunc);
+ break;
+ }
+ Response (bye_space, CombatIsInevitable);
+ }
+ else
+ {
+ if (!MadeChoice) switch (GET_GAME_STATE (MYCON_INFO))
+ {
+ case 0:
+ Response (question_1, RespFunc);
+ break;
+ case 1:
+ Response (question_2, RespFunc);
+ break;
+ case 2:
+ Response (question_3, RespFunc);
+ break;
+ case 3:
+ Response (question_4, RespFunc);
+ break;
+ case 4:
+ Response (question_5, RespFunc);
+ break;
+ case 5:
+ Response (question_6, RespFunc);
+ break;
+ case 6:
+ Response (question_7, RespFunc);
+ break;
+ case 7:
+ Response (question_8, RespFunc);
+ break;
+ case 8:
+ Response (question_9, RespFunc);
+ break;
+ case 9:
+ Response (question_10, RespFunc);
+ break;
+ case 10:
+ Response (question_11, RespFunc);
+ break;
+ case 11:
+ Response (question_12, RespFunc);
+ break;
+ case 12:
+ Response (question_13, RespFunc);
+ break;
+ case 13:
+ Response (question_14, RespFunc);
+ break;
+ case 14:
+ Response (question_15, RespFunc);
+ break;
+ case 15:
+ Response (question_16, RespFunc);
+ break;
+ }
+ if (PHRASE_ENABLED (lets_be_friends))
+ Response (lets_be_friends, RespFunc);
+ if (PHRASE_ENABLED (came_to_homeworld))
+ Response (came_to_homeworld, RespFunc);
+ if (PHRASE_ENABLED (submit_to_us))
+ Response (submit_to_us, RespFunc);
+ if (!GET_GAME_STATE (MYCON_FELL_FOR_AMBUSH))
+ {
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) == 1)
+ Response (what_about_shattered, NormalMycon);
+ if (GET_GAME_STATE (MYCON_AMBUSH))
+ {
+ Response (i_have_a_cunning_plan, TrickMycon);
+ }
+ }
+ Response (bye_homeworld, CombatIsInevitable);
+ }
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (GET_GAME_STATE (SUN_DEVICE))
+ {
+ NumVisits = GET_GAME_STATE (MYCON_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (DIE_THIEF);
+ break;
+ case 1:
+ NPCPhrase (DIE_THIEF_AGAIN);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (MYCON_VISITS, NumVisits);
+
+ setSegue (Segue_hostile);
+ }
+ else if (GET_GAME_STATE (MYCON_KNOW_AMBUSH))
+ {
+ NPCPhrase (DIE_LIAR);
+
+ setSegue (Segue_hostile);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ NumVisits = GET_GAME_STATE (MYCON_SUN_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_SUN_DEVICE_WORLD_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_SUN_DEVICE_WORLD_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (MYCON_SUN_VISITS, NumVisits);
+
+ SunDevice ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (MYCON_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_HOMEWORLD_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_HOMEWORLD_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_HOMEWORLD_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_HOMEWORLD_4);
+ break;
+ case 4:
+ NPCPhrase (HELLO_HOMEWORLD_5);
+ break;
+ case 5:
+ NPCPhrase (HELLO_HOMEWORLD_6);
+ break;
+ case 6:
+ NPCPhrase (HELLO_HOMEWORLD_7);
+ break;
+ case 7:
+ NPCPhrase (HELLO_HOMEWORLD_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (MYCON_HOME_VISITS, NumVisits);
+
+ NormalMycon ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (MYCON_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_SPACE_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_SPACE_4);
+ break;
+ case 4:
+ NPCPhrase (HELLO_SPACE_5);
+ break;
+ case 5:
+ NPCPhrase (HELLO_SPACE_6);
+ break;
+ case 6:
+ NPCPhrase (HELLO_SPACE_7);
+ break;
+ case 7:
+ NPCPhrase (HELLO_SPACE_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (MYCON_VISITS, NumVisits);
+
+ NormalMycon ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_mycon (void)
+{
+ return (0);
+}
+
+static void
+post_mycon_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_mycon_comm (void)
+{
+ LOCDATA *retval;
+
+ mycon_desc.init_encounter_func = Intro;
+ mycon_desc.post_encounter_func = post_mycon_enc;
+ mycon_desc.uninit_encounter_func = uninit_mycon;
+
+ mycon_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ mycon_desc.AlienTextBaseline.y = 0;
+ mycon_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ MadeChoice = 0;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != WON_LAST_BATTLE)
+ {
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ setSegue (Segue_peace);
+ }
+ retval = &mycon_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/mycon/resinst.h b/src/uqm/comm/mycon/resinst.h
new file mode 100644
index 0000000..65a6a33
--- /dev/null
+++ b/src/uqm/comm/mycon/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define MYCON_COLOR_MAP "comm.mycon.colortable"
+#define MYCON_CONVERSATION_PHRASES "comm.mycon.dialogue"
+#define MYCON_FONT "comm.mycon.font"
+#define MYCON_MUSIC "comm.mycon.music"
+#define MYCON_PMAP_ANIM "comm.mycon.graphics"
diff --git a/src/uqm/comm/mycon/strings.h b/src/uqm/comm/mycon/strings.h
new file mode 100644
index 0000000..8b71dfb
--- /dev/null
+++ b/src/uqm/comm/mycon/strings.h
@@ -0,0 +1,136 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MYCON_STRINGS_H
+#define MYCON_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ TELL_US_ABOUT_WORLD,
+ BYE_AND_DIE_HOMEWORLD,
+ RAMBLE_1,
+ RAMBLE_2,
+ RAMBLE_3,
+ RAMBLE_4,
+ RAMBLE_5,
+ RAMBLE_6,
+ RAMBLE_7,
+ RAMBLE_8,
+ RAMBLE_9,
+ RAMBLE_10,
+ RAMBLE_11,
+ RAMBLE_12,
+ RAMBLE_13,
+ RAMBLE_14,
+ RAMBLE_15,
+ RAMBLE_16,
+ RAMBLE_17,
+ RAMBLE_18,
+ RAMBLE_19,
+ RAMBLE_20,
+ RAMBLE_21,
+ RAMBLE_22,
+ RAMBLE_23,
+ RAMBLE_24,
+ RAMBLE_25,
+ RAMBLE_26,
+ RAMBLE_27,
+ RAMBLE_28,
+ RAMBLE_29,
+ RAMBLE_30,
+ RAMBLE_31,
+ RAMBLE_32,
+ question_1,
+ question_2,
+ question_3,
+ question_4,
+ question_5,
+ question_6,
+ question_7,
+ question_8,
+ question_9,
+ question_10,
+ question_11,
+ question_12,
+ question_13,
+ question_14,
+ question_15,
+ question_16,
+ bye_space,
+ BYE_AND_DIE_SPACE,
+ gonna_die,
+ insult_1,
+ insult_2,
+ insult_3,
+ insult_4,
+ insult_5,
+ insult_6,
+ insult_7,
+ insult_8,
+ come_in_peace,
+ HELLO_HOMEWORLD_1,
+ HELLO_HOMEWORLD_2,
+ HELLO_HOMEWORLD_3,
+ HELLO_HOMEWORLD_4,
+ HELLO_HOMEWORLD_5,
+ HELLO_HOMEWORLD_6,
+ HELLO_HOMEWORLD_7,
+ HELLO_HOMEWORLD_8,
+ HELLO_SPACE_1,
+ HELLO_SPACE_2,
+ HELLO_SPACE_3,
+ HELLO_SPACE_4,
+ HELLO_SPACE_5,
+ HELLO_SPACE_6,
+ HELLO_SPACE_7,
+ HELLO_SPACE_8,
+ lets_be_friends,
+ came_to_homeworld,
+ submit_to_us,
+ bye_sun_device,
+ GOODBYE_SUN_DEVICE,
+ RESPONSE_1,
+ RESPONSE_2,
+ RESPONSE_3,
+ clue_1,
+ clue_2,
+ clue_3,
+ what_about_shattered,
+ ABOUT_SHATTERED,
+ HELLO_SUN_DEVICE_WORLD_1,
+ HELLO_SUN_DEVICE_WORLD_2,
+ whats_up_sun_device,
+ GENERAL_INFO_SUN_DEVICE,
+ like_to_land,
+ NEVER_LET_LAND,
+ bye_homeworld,
+ i_have_a_cunning_plan,
+ DIE_LIAR,
+ how_goes_implanting,
+ UNFORSEEN_DELAYS,
+ DIE_THIEF,
+ DIE_THIEF_AGAIN,
+ GOODBYE_AND_DIE,
+ AMBUSH_TAIL,
+ RAMBLE_TAIL,
+ WE_GO_TO_IMPLANT,
+ WONT_FALL_FOR_TRICK,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/orz/Makeinfo b/src/uqm/comm/orz/Makeinfo
new file mode 100644
index 0000000..b5c56d2
--- /dev/null
+++ b/src/uqm/comm/orz/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="orzc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/orz/orzc.c b/src/uqm/comm/orz/orzc.c
new file mode 100644
index 0000000..72076a6
--- /dev/null
+++ b/src/uqm/comm/orz/orzc.c
@@ -0,0 +1,898 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+
+
+static LOCDATA orz_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ ORZ_PMAP_ANIM, /* AlienFrame */
+ ORZ_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ ORZ_COLOR_MAP, /* AlienColorMap */
+ ORZ_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ ORZ_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 12 /* 13 */, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 4, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 10, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 15, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 10, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 17, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 10, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 20, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND / 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 7), /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND / 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 6), /* BlockMask */
+ },
+ {
+ 30, /* StartIndex */
+ 3, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND / 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5), /* BlockMask */
+ },
+ {
+ 33, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND / 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4), /* BlockMask */
+ },
+ {
+ 36, /* StartIndex */
+ 25, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 60, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 61, /* StartIndex */
+ 15, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 60, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 76, /* StartIndex */
+ 17, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 60, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 12), /* BlockMask */
+ },
+ {
+ 93, /* StartIndex */
+ 25, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 60, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 118, /* StartIndex */
+ 11, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 60, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 10), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 12, ONE_SECOND * 3 / 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, bye_ally))
+ NPCPhrase (GOODBYE_ALLY);
+ else if (PLAYER_SAID (R, bye_neutral))
+ NPCPhrase (GOODBYE_NEUTRAL);
+ else if (PLAYER_SAID (R, bye_angry))
+ NPCPhrase (GOODBYE_ANGRY);
+ else if (PLAYER_SAID (R, bye_taalo))
+ {
+ if (GET_GAME_STATE (ORZ_MANNER) == 1)
+ NPCPhrase (ANGRY_TAALO_GOODBYE);
+ else
+ NPCPhrase (FRIENDLY_TAALO_GOODBYE);
+ }
+ else if (PLAYER_SAID (R, hostile_2))
+ {
+ NPCPhrase (HOSTILITY_IS_BAD_2);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, may_we_land))
+ {
+ NPCPhrase (SURE_LAND);
+
+ SET_GAME_STATE (TAALO_UNPROTECTED, 1);
+ }
+ else if (PLAYER_SAID (R, yes_alliance)
+ || PLAYER_SAID (R, were_sorry))
+ {
+ if (PLAYER_SAID (R, yes_alliance))
+ NPCPhrase (GREAT);
+ else
+ NPCPhrase (APOLOGY_ACCEPTED);
+
+ SET_GAME_STATE (ORZ_ANDRO_STATE, 0);
+ SET_GAME_STATE (ORZ_GENERAL_INFO, 0);
+ SET_GAME_STATE (ORZ_PERSONAL_INFO, 0);
+ SET_GAME_STATE (ORZ_MANNER, 3);
+ SetRaceAllied (ORZ_SHIP, TRUE);
+ }
+ else if (PLAYER_SAID (R, demand_to_land))
+ {
+ NPCPhrase (NO_DEMAND);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, about_andro_3)
+ || PLAYER_SAID (R, must_know_about_androsyn))
+ {
+ if (PLAYER_SAID (R, about_andro_3))
+ NPCPhrase (BLEW_IT);
+ else
+ NPCPhrase (KNOW_TOO_MUCH);
+
+ SET_GAME_STATE (ORZ_VISITS, 0);
+ SET_GAME_STATE (ORZ_MANNER, 2);
+ setSegue (Segue_hostile);
+ if (PLAYER_SAID (R, about_andro_3))
+ {
+ SetRaceAllied (ORZ_SHIP, FALSE);
+ RemoveEscortShips (ORZ_SHIP);
+ }
+
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1)
+ ), ONE_SECOND / 2);
+ }
+ else /* insults */
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (ORZ_PERSONAL_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INSULTED_1);
+ break;
+ case 1:
+ NPCPhrase (INSULTED_2);
+ break;
+ case 2:
+ NPCPhrase (INSULTED_3);
+ setSegue (Segue_hostile);
+ break;
+ case 7:
+ --NumVisits;
+ default:
+ NPCPhrase (INSULTED_4);
+ setSegue (Segue_hostile);
+ break;
+ }
+ SET_GAME_STATE (ORZ_PERSONAL_INFO, NumVisits);
+ }
+}
+
+static void
+TaaloWorld (RESPONSE_REF R)
+{
+ // We can only get here when ORZ_MANNER != HOSTILE (2)
+ BYTE Manner;
+
+ Manner = GET_GAME_STATE (ORZ_MANNER);
+ if (PLAYER_SAID (R, demand_to_land))
+ {
+ NPCPhrase (ASK_NICELY);
+
+ DISABLE_PHRASE (demand_to_land);
+ }
+ else if (PLAYER_SAID (R, why_you_here))
+ {
+ if (Manner != 1)
+ NPCPhrase (FRIENDLY_EXPLANATION);
+ else
+ NPCPhrase (ANGRY_EXPLANATION);
+
+ DISABLE_PHRASE (why_you_here);
+ }
+ else if (PLAYER_SAID (R, what_is_this_place))
+ {
+ if (Manner != 1)
+ NPCPhrase (FRIENDLY_PLACE);
+ else
+ NPCPhrase (ANGRY_PLACE);
+
+ DISABLE_PHRASE (what_is_this_place);
+ }
+ else if (PLAYER_SAID (R, may_we_land))
+ {
+ NPCPhrase (ALLIES_CAN_VISIT);
+
+ DISABLE_PHRASE (may_we_land);
+ }
+ else if (PLAYER_SAID (R, make_alliance))
+ {
+ NPCPhrase (CANT_ALLY_HERE);
+
+ DISABLE_PHRASE (make_alliance);
+ }
+ else if (PLAYER_SAID (R, why_busy))
+ {
+ NPCPhrase (BUSY_BECAUSE);
+
+ DISABLE_PHRASE (why_busy);
+ }
+
+ if (PHRASE_ENABLED (may_we_land))
+ {
+ if (Manner == 3 && CheckAlliance (ORZ_SHIP) == GOOD_GUY)
+ Response (may_we_land, ExitConversation);
+ else
+ Response (may_we_land, TaaloWorld);
+ }
+ else if (PHRASE_ENABLED (make_alliance))
+ Response (make_alliance, TaaloWorld);
+ else if (PHRASE_ENABLED (why_busy))
+ Response (why_busy, TaaloWorld);
+ if (PHRASE_ENABLED (demand_to_land))
+ {
+ if (Manner == 1)
+ Response (demand_to_land, ExitConversation);
+ else
+ Response (demand_to_land, TaaloWorld);
+ }
+ if (PHRASE_ENABLED (why_you_here))
+ Response (why_you_here, TaaloWorld);
+ if (PHRASE_ENABLED (what_is_this_place))
+ Response (what_is_this_place, TaaloWorld);
+ Response (bye_taalo, ExitConversation);
+}
+
+static void
+OrzAllied (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_ally))
+ {
+ NumVisits = GET_GAME_STATE (ORZ_GENERAL_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_ALLY_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_ALLY_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_ALLY_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_ALLY_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_GENERAL_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_ally);
+ }
+ else if (PLAYER_SAID (R, more_about_you))
+ {
+ NumVisits = GET_GAME_STATE (ORZ_PERSONAL_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ABOUT_US_1);
+ break;
+ case 1:
+ NPCPhrase (ABOUT_US_2);
+ break;
+ case 2:
+ NPCPhrase (ABOUT_US_3);
+ break;
+ case 3:
+ NPCPhrase (ABOUT_US_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_PERSONAL_INFO, NumVisits);
+
+ DISABLE_PHRASE (more_about_you);
+ }
+ else if (PLAYER_SAID (R, about_andro_1))
+ {
+ NPCPhrase (FORGET_ANDRO_1);
+
+ SET_GAME_STATE (ORZ_ANDRO_STATE, 1);
+ }
+ else if (PLAYER_SAID (R, about_andro_2))
+ {
+ NPCPhrase (FORGET_ANDRO_2);
+
+ SET_GAME_STATE (ORZ_ANDRO_STATE, 2);
+ }
+
+ if (GET_GAME_STATE (ORZ_ANDRO_STATE) == 0)
+ Response (about_andro_1, OrzAllied);
+ else if (GET_GAME_STATE (ORZ_ANDRO_STATE) == 1)
+ Response (about_andro_2, OrzAllied);
+ else
+ {
+ Response (about_andro_3, ExitConversation);
+ }
+ if (PHRASE_ENABLED (whats_up_ally))
+ Response (whats_up_ally, OrzAllied);
+ if (PHRASE_ENABLED (more_about_you))
+ Response (more_about_you, OrzAllied);
+ Response (bye_ally, ExitConversation);
+}
+
+static void OrzNeutral (RESPONSE_REF R);
+
+static void
+WhereAndrosyn (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ NPCPhrase (DISEMBLE_ABOUT_ANDROSYN);
+ DISABLE_PHRASE (where_androsyn);
+
+ Response (must_know_about_androsyn, ExitConversation);
+ Response (dont_really_care, OrzNeutral);
+}
+
+static void
+OfferAlliance (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, seem_like_nice_guys))
+ NPCPhrase (ARE_NICE_WANT_ALLY);
+ else if (PLAYER_SAID (R, talk_about_alliance))
+ NPCPhrase (OK_TALK_ALLIANCE);
+ else if (PLAYER_SAID (R, why_so_trusting))
+ {
+ NPCPhrase (TRUSTING_BECAUSE);
+
+ SET_GAME_STATE (ORZ_STACK1, 1);
+ }
+
+ Response (no_alliance, OrzNeutral);
+ Response (decide_later, OrzNeutral);
+ if (GET_GAME_STATE (ORZ_STACK1) == 0)
+ {
+ Response (why_so_trusting, OfferAlliance);
+ }
+ Response (yes_alliance, ExitConversation);
+}
+
+static void
+OrzNeutral (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[3];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = 0;
+ if (PLAYER_SAID (R, hostile_1))
+ {
+ NPCPhrase (HOSTILITY_IS_BAD_1);
+
+ DISABLE_PHRASE (hostile_1);
+ LastStack = 2;
+ }
+ else if (PLAYER_SAID (R, we_are_vindicator0))
+ {
+ NPCPhrase (NICE_TO_MEET_YOU);
+
+ SET_GAME_STATE (ORZ_STACK0, 1);
+ LastStack = 1;
+ }
+ else if (PLAYER_SAID (R, who_you))
+ {
+ NPCPhrase (WE_ARE_ORZ);
+
+ SET_GAME_STATE (ORZ_ANDRO_STATE, 1);
+ }
+ else if (PLAYER_SAID (R, why_here))
+ {
+ NPCPhrase (HERE_BECAUSE);
+
+ SET_GAME_STATE (ORZ_ANDRO_STATE, 2);
+ }
+ else if (PLAYER_SAID (R, no_alliance))
+ {
+ NPCPhrase (MAYBE_LATER);
+
+ DISABLE_PHRASE (talk_about_alliance);
+ SET_GAME_STATE (REFUSED_ORZ_ALLIANCE, 1);
+ }
+ else if (PLAYER_SAID (R, decide_later))
+ {
+ NPCPhrase (OK_LATER);
+
+ DISABLE_PHRASE (talk_about_alliance);
+ SET_GAME_STATE (REFUSED_ORZ_ALLIANCE, 1);
+ }
+ else if (PLAYER_SAID (R, dont_really_care))
+ NPCPhrase (YOU_ARE_OUR_FRIENDS);
+ else if (PLAYER_SAID (R, where_androsyn))
+ {
+ WhereAndrosyn (R);
+ return;
+ }
+ else if (PLAYER_SAID (R, talk_about_alliance)
+ || PLAYER_SAID (R, seem_like_nice_guys))
+ {
+ OfferAlliance (R);
+ return;
+ }
+ else if (PLAYER_SAID (R, hostile_2))
+ {
+ ExitConversation (R);
+ return;
+ }
+
+ if (GET_GAME_STATE (ORZ_ANDRO_STATE) == 0)
+ pStr[0] = who_you;
+ else if (GET_GAME_STATE (ORZ_ANDRO_STATE) == 1)
+ pStr[0] = why_here;
+ else if (PHRASE_ENABLED (where_androsyn) && GET_GAME_STATE (ORZ_ANDRO_STATE) == 2)
+ pStr[0] = where_androsyn;
+ if (GET_GAME_STATE (REFUSED_ORZ_ALLIANCE))
+ {
+ if (PHRASE_ENABLED (talk_about_alliance))
+ pStr[1] = talk_about_alliance;
+ }
+ else if (GET_GAME_STATE (ORZ_STACK0) == 0)
+ {
+ construct_response (shared_phrase_buf,
+ we_are_vindicator0,
+ GLOBAL_SIS (CommanderName),
+ we_are_vindicator1,
+ GLOBAL_SIS (ShipName),
+ we_are_vindicator2,
+ (UNICODE*)NULL);
+ pStr[1] = we_are_vindicator0;
+ }
+ else
+ pStr[1] = seem_like_nice_guys;
+ if (PHRASE_ENABLED (hostile_1))
+ pStr[2] = hostile_1;
+ else
+ pStr[2] = hostile_2;
+
+ if (pStr[LastStack])
+ {
+ if (pStr[LastStack] != we_are_vindicator0)
+ Response (pStr[LastStack], OrzNeutral);
+ else
+ DoResponsePhrase (pStr[LastStack], OrzNeutral, shared_phrase_buf);
+ }
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ if (pStr[i] != we_are_vindicator0)
+ Response (pStr[i], OrzNeutral);
+ else
+ DoResponsePhrase (pStr[i], OrzNeutral, shared_phrase_buf);
+ }
+ }
+ Response (bye_neutral, ExitConversation);
+}
+
+static void
+OrzAngry (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_angry))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (ORZ_GENERAL_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_ANGRY_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_ANGRY_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_GENERAL_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_angry);
+ }
+
+ if (PHRASE_ENABLED (whats_up_angry))
+ {
+ Response (whats_up_angry, OrzAngry);
+ }
+ Response (were_sorry, ExitConversation);
+ switch (GET_GAME_STATE (ORZ_PERSONAL_INFO))
+ {
+ case 0:
+ Response (insult_1, ExitConversation);
+ break;
+ case 1:
+ Response (insult_2, ExitConversation);
+ break;
+ case 2:
+ Response (insult_3, ExitConversation);
+ break;
+ case 3:
+ Response (insult_4, ExitConversation);
+ break;
+ case 4:
+ Response (insult_5, ExitConversation);
+ break;
+ case 5:
+ Response (insult_6, ExitConversation);
+ break;
+ case 6:
+ Response (insult_7, ExitConversation);
+ break;
+ case 7:
+ Response (insult_8, ExitConversation);
+ break;
+ }
+ Response (bye_angry, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits, Manner;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (!GET_GAME_STATE (MET_ORZ_BEFORE))
+ NPCPhrase (INIT_HELLO);
+
+ Manner = GET_GAME_STATE (ORZ_MANNER);
+ if (Manner == 2)
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+
+ NumVisits = GET_GAME_STATE (ORZ_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_VISITS, NumVisits);
+
+ setSegue (Segue_hostile);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ NumVisits = GET_GAME_STATE (TAALO_VISITS);
+ if (Manner != 1)
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FRIENDLY_ALLIED_TAALO_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (FRIENDLY_ALLIED_TAALO_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ANGRY_TAALO_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ANGRY_TAALO_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (TAALO_VISITS, NumVisits);
+
+ TaaloWorld ((RESPONSE_REF)0);
+ }
+ else if (Manner == 3 && CheckAlliance (ORZ_SHIP) == GOOD_GUY)
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (ORZ_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (ORZ_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ALLIED_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ALLIED_SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (ALLIED_SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (ALLIED_SPACE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_VISITS, NumVisits);
+ }
+
+ OrzAllied ((RESPONSE_REF)0);
+ }
+ else if (Manner != 1)
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (ORZ_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (ORZ_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_VISITS, NumVisits);
+ }
+
+ OrzNeutral ((RESPONSE_REF)0);
+ }
+ else
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (ORZ_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ANGRY_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ANGRY_HOMEWORLD_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (ORZ_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ANGRY_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ANGRY_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ORZ_VISITS, NumVisits);
+ }
+
+ OrzAngry ((RESPONSE_REF)0);
+ }
+
+ if (!GET_GAME_STATE (MET_ORZ_BEFORE))
+ {
+ SET_GAME_STATE (MET_ORZ_BEFORE, 1);
+
+ // Disable talking anim and run the computer report
+ EnableTalkingAnim (FALSE);
+ AlienTalkSegue (1);
+ // Run whatever is left with talking anim
+ EnableTalkingAnim (TRUE);
+ }
+}
+
+static COUNT
+uninit_orz (void)
+{
+ return (0);
+}
+
+static void
+post_orz_enc (void)
+{
+ BYTE Manner;
+
+ if (getSegue () == Segue_hostile
+ && (Manner = GET_GAME_STATE (ORZ_MANNER)) != 2)
+ {
+ SET_GAME_STATE (ORZ_MANNER, 1);
+ if (Manner != 1)
+ {
+ SET_GAME_STATE (ORZ_VISITS, 0);
+ SET_GAME_STATE (ORZ_HOME_VISITS, 0);
+ SET_GAME_STATE (TAALO_VISITS, 0);
+ }
+ }
+}
+
+LOCDATA*
+init_orz_comm (void)
+{
+ LOCDATA *retval;
+
+ orz_desc.init_encounter_func = Intro;
+ orz_desc.post_encounter_func = post_orz_enc;
+ orz_desc.uninit_encounter_func = uninit_orz;
+
+ orz_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ orz_desc.AlienTextBaseline.y = 0;
+ orz_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (GET_GAME_STATE (ORZ_MANNER) == 3
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &orz_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/orz/resinst.h b/src/uqm/comm/orz/resinst.h
new file mode 100644
index 0000000..a526cef
--- /dev/null
+++ b/src/uqm/comm/orz/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ORZ_COLOR_MAP "comm.orz.colortable"
+#define ORZ_CONVERSATION_PHRASES "comm.orz.dialogue"
+#define ORZ_FONT "comm.orz.font"
+#define ORZ_MUSIC "comm.orz.music"
+#define ORZ_PMAP_ANIM "comm.orz.graphics"
diff --git a/src/uqm/comm/orz/strings.h b/src/uqm/comm/orz/strings.h
new file mode 100644
index 0000000..7eabebe
--- /dev/null
+++ b/src/uqm/comm/orz/strings.h
@@ -0,0 +1,143 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ORZ_STRINGS_H
+#define ORZ_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ INIT_HELLO,
+ who_you,
+ WE_ARE_ORZ,
+ why_here,
+ HERE_BECAUSE,
+ ALLIED_HOMEWORLD_HELLO_1,
+ ALLIED_HOMEWORLD_HELLO_2,
+ ALLIED_HOMEWORLD_HELLO_3,
+ ALLIED_HOMEWORLD_HELLO_4,
+ ALLIED_SPACE_HELLO_1,
+ ALLIED_SPACE_HELLO_2,
+ ALLIED_SPACE_HELLO_3,
+ ALLIED_SPACE_HELLO_4,
+ whats_up_ally,
+ GENERAL_INFO_ALLY_1,
+ GENERAL_INFO_ALLY_2,
+ GENERAL_INFO_ALLY_3,
+ GENERAL_INFO_ALLY_4,
+ more_about_you,
+ ABOUT_US_1,
+ ABOUT_US_2,
+ ABOUT_US_3,
+ ABOUT_US_4,
+ where_androsyn,
+ DISEMBLE_ABOUT_ANDROSYN,
+ must_know_about_androsyn,
+ KNOW_TOO_MUCH,
+ dont_really_care,
+ YOU_ARE_OUR_FRIENDS,
+ about_andro_1,
+ FORGET_ANDRO_1,
+ about_andro_2,
+ FORGET_ANDRO_2,
+ about_andro_3,
+ BLEW_IT,
+ NEUTRAL_HOMEWORLD_HELLO_1,
+ NEUTRAL_HOMEWORLD_HELLO_2,
+ NEUTRAL_HOMEWORLD_HELLO_3,
+ NEUTRAL_HOMEWORLD_HELLO_4,
+ NEUTRAL_SPACE_HELLO_1,
+ NEUTRAL_SPACE_HELLO_2,
+ NEUTRAL_SPACE_HELLO_3,
+ NEUTRAL_SPACE_HELLO_4,
+ hostile_1,
+ HOSTILITY_IS_BAD_1,
+ hostile_2,
+ HOSTILITY_IS_BAD_2,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ we_are_vindicator2,
+ NICE_TO_MEET_YOU,
+ seem_like_nice_guys,
+ ARE_NICE_WANT_ALLY,
+ talk_about_alliance,
+ OK_TALK_ALLIANCE,
+ yes_alliance,
+ GREAT,
+ no_alliance,
+ MAYBE_LATER,
+ decide_later,
+ OK_LATER,
+ why_so_trusting,
+ TRUSTING_BECAUSE,
+ bye_neutral,
+ GOODBYE_NEUTRAL,
+ ANGRY_SPACE_HELLO_1,
+ ANGRY_SPACE_HELLO_2,
+ ANGRY_HOMEWORLD_HELLO_1,
+ ANGRY_HOMEWORLD_HELLO_2,
+ whats_up_angry,
+ GENERAL_INFO_ANGRY_1,
+ GENERAL_INFO_ANGRY_2,
+ were_sorry,
+ APOLOGY_ACCEPTED,
+ insult_1,
+ insult_2,
+ insult_3,
+ insult_4,
+ insult_5,
+ insult_6,
+ insult_7,
+ insult_8,
+ INSULTED_1,
+ INSULTED_2,
+ INSULTED_3,
+ INSULTED_4,
+ bye_angry,
+ GOODBYE_ANGRY,
+ ANGRY_TAALO_HELLO_1,
+ ANGRY_TAALO_HELLO_2,
+ FRIENDLY_ALLIED_TAALO_HELLO_1,
+ FRIENDLY_ALLIED_TAALO_HELLO_2,
+ demand_to_land,
+ NO_DEMAND,
+ ASK_NICELY,
+ why_you_here,
+ ANGRY_EXPLANATION,
+ FRIENDLY_EXPLANATION,
+ what_is_this_place,
+ FRIENDLY_PLACE,
+ ANGRY_PLACE,
+ may_we_land,
+ SURE_LAND,
+ ALLIES_CAN_VISIT,
+ make_alliance,
+ CANT_ALLY_HERE,
+ why_busy,
+ BUSY_BECAUSE,
+ bye_taalo,
+ bye_ally,
+ GOODBYE_ALLY,
+ FRIENDLY_TAALO_GOODBYE,
+ ANGRY_TAALO_GOODBYE,
+ HOSTILE_HELLO_1,
+ HOSTILE_HELLO_2,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/pkunk/Makeinfo b/src/uqm/comm/pkunk/Makeinfo
new file mode 100644
index 0000000..67dc511
--- /dev/null
+++ b/src/uqm/comm/pkunk/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="pkunkc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/pkunk/pkunkc.c b/src/uqm/comm/pkunk/pkunkc.c
new file mode 100644
index 0000000..5db575d
--- /dev/null
+++ b/src/uqm/comm/pkunk/pkunkc.c
@@ -0,0 +1,1148 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/gameev.h"
+
+
+static LOCDATA pkunk_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ PKUNK_PMAP_ANIM, /* AlienFrame */
+ PKUNK_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ PKUNK_COLOR_MAP, /* AlienColorMap */
+ PKUNK_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ PKUNK_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 3, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 3, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 7, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 2), /* BlockMask */
+ },
+ {
+ 11, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 1), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 2, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 6, /* FrameRate */
+ ONE_SECOND / 12, ONE_SECOND / 2, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static BOOLEAN
+ShipsReady (void)
+{
+ SIZE i;
+
+ return (GET_GAME_STATE (PKUNK_MANNER) == 3
+ && !((i = (GLOBAL (GameClock.year_index) - START_YEAR) - GET_GAME_STATE (PKUNK_SHIP_YEAR)) < 0
+ || ((i == 0 && (i = GLOBAL (GameClock.month_index) - GET_GAME_STATE (PKUNK_SHIP_MONTH)) < 0)
+ || (i == 0 && GLOBAL (GameClock.day_index) < GET_GAME_STATE (PKUNK_SHIP_DAY)))));
+}
+
+static void
+PrepareShip (void)
+{
+#define MAX_PKUNK_SHIPS 4
+ if (AddEscortShips (PKUNK_SHIP, MAX_PKUNK_SHIPS))
+ {
+ BYTE mi, di, yi;
+
+ mi = GLOBAL (GameClock.month_index);
+ SET_GAME_STATE (PKUNK_SHIP_MONTH, mi);
+ if ((di = GLOBAL (GameClock.day_index)) > 28)
+ di = 28;
+ SET_GAME_STATE (PKUNK_SHIP_DAY, di);
+ yi = (BYTE)(GLOBAL (GameClock.year_index) - START_YEAR) + 1;
+ SET_GAME_STATE (PKUNK_SHIP_YEAR, yi);
+ }
+}
+
+#define GOOD_REASON_1 (1 << 0)
+#define GOOD_REASON_2 (1 << 1)
+#define BAD_REASON_1 (1 << 2)
+#define BAD_REASON_2 (1 << 3)
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, friendly_bye_space))
+ NPCPhrase (FRIENDLY_GOODBYE_SPACE);
+ else if (PLAYER_SAID (R, neutral_bye_space))
+ NPCPhrase (NEUTRAL_GOODBYE_SPACE);
+ else if (PLAYER_SAID (R, bye_angry))
+ NPCPhrase (GOODBYE_ANGRY);
+ else if (PLAYER_SAID (R, bye_friendly))
+ NPCPhrase (GOODBYE_FRIENDLY);
+ else if (PLAYER_SAID (R, we_here_to_help)
+ || PLAYER_SAID (R, we_need_help))
+ {
+ if (PLAYER_SAID (R, we_here_to_help))
+ NPCPhrase (NEED_HELP);
+ else
+ NPCPhrase (GIVE_HELP);
+ NPCPhrase (ALMOST_ALLIANCE);
+
+ SET_GAME_STATE (PKUNK_MANNER, 3);
+ SET_GAME_STATE (PKUNK_VISITS, 0);
+ SET_GAME_STATE (PKUNK_HOME_VISITS, 0);
+ SET_GAME_STATE (PKUNK_INFO, 0);
+
+ AddEvent (RELATIVE_EVENT, 6, 0, 0, ADVANCE_PKUNK_MISSION);
+ if (EscortFeasibilityStudy (PKUNK_SHIP) == 0)
+ NPCPhrase (INIT_NO_ROOM);
+ else
+ {
+ NPCPhrase (INIT_SHIP_GIFT);
+ AlienTalkSegue ((COUNT)~0);
+ PrepareShip ();
+ }
+ }
+ else if (PLAYER_SAID (R, try_to_be_nicer))
+ {
+ NPCPhrase (CANT_ASK_FOR_MORE);
+ NPCPhrase (VISIT_OUR_HOMEWORLD);
+
+ SET_GAME_STATE (PKUNK_MANNER, 3);
+ SET_GAME_STATE (PKUNK_VISITS, 0);
+ SET_GAME_STATE (PKUNK_HOME_VISITS, 0);
+ SET_GAME_STATE (PKUNK_INFO, 0);
+ }
+ else if (PLAYER_SAID (R, must_conquer)
+ || PLAYER_SAID (R, obey))
+ {
+ if (PLAYER_SAID (R, obey))
+ NPCPhrase (NO_OBEY);
+ else
+ {
+ NPCPhrase (BAD_IDEA);
+
+ SET_GAME_STATE (PKUNK_MANNER, 2);
+ }
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, die_idiot_fools))
+ {
+ NPCPhrase (VERY_WELL);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, suit_yourself))
+ NPCPhrase (GOODBYE_MIGRATION);
+ else
+ {
+ BYTE ReasonMask;
+
+ ReasonMask = GET_GAME_STATE (PKUNK_REASONS);
+ if (PLAYER_SAID (R, good_reason_1))
+ {
+ NPCPhrase (WE_GO_HOME_1);
+ ReasonMask |= GOOD_REASON_1;
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, ADVANCE_PKUNK_MISSION);
+ }
+ else if (PLAYER_SAID (R, good_reason_2))
+ {
+ NPCPhrase (WE_GO_HOME_2);
+ ReasonMask |= GOOD_REASON_2;
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, ADVANCE_PKUNK_MISSION);
+ }
+ else if (PLAYER_SAID (R, bad_reason_1))
+ {
+ NPCPhrase (NO_GO_HOME_1);
+ ReasonMask |= BAD_REASON_1;
+ }
+ else if (PLAYER_SAID (R, bad_reason_2))
+ {
+ NPCPhrase (NO_GO_HOME_2);
+ ReasonMask |= BAD_REASON_2;
+ }
+ SET_GAME_STATE (PKUNK_REASONS, ReasonMask);
+ }
+}
+
+static void PkunkHome (RESPONSE_REF R);
+
+static void
+PkunkAngry (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, why_insults))
+ {
+ NPCPhrase (RELEASE_TENSION);
+
+ DISABLE_PHRASE (why_insults);
+ }
+ else if (PLAYER_SAID (R, what_about_you))
+ {
+ NPCPhrase (ABOUT_US);
+
+ DISABLE_PHRASE (what_about_you);
+ }
+ else if (PLAYER_SAID (R, should_be_friends))
+ {
+ NPCPhrase (YES_FRIENDS);
+
+ DISABLE_PHRASE (should_be_friends);
+ }
+
+ if (PHRASE_ENABLED (should_be_friends))
+ {
+ Response (should_be_friends, PkunkAngry);
+ }
+ else
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ Response (try_to_be_nicer, PkunkHome);
+ else
+ Response (try_to_be_nicer, ExitConversation);
+ }
+ Response (die_idiot_fools, ExitConversation);
+ if (PHRASE_ENABLED (why_insults))
+ Response (why_insults, PkunkAngry);
+ if (PHRASE_ENABLED (what_about_you))
+ Response (what_about_you, PkunkAngry);
+ Response (bye_angry, ExitConversation);
+}
+
+static void
+DiscussConquer (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, we_conquer))
+ {
+ NPCPhrase (WHY_CONQUER);
+
+ DISABLE_PHRASE (we_conquer);
+ }
+ else if (PLAYER_SAID (R, conquer_because_1))
+ {
+#if 0
+ NPCPhrase (NOT_CONQUER_10);
+ NPCPhrase (GLOBAL_ALLIANCE_NAME + name_1);
+ NPCPhrase (NOT_CONQUER_11);
+ NPCPhrase (GLOBAL_ALLIANCE_NAME + name_1);
+ NPCPhrase (NOT_CONQUER_12);
+#endif
+ NPCPhrase (NOT_CONQUER_1);
+
+ DISABLE_PHRASE (conquer_because_1);
+ }
+ else if (PLAYER_SAID (R, conquer_because_2))
+ {
+ NPCPhrase (NOT_CONQUER_2);
+
+ DISABLE_PHRASE (conquer_because_2);
+ }
+
+ if (PHRASE_ENABLED (conquer_because_1))
+ {
+#if 0
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ conquer_because_1,
+ buf,
+ (RESPONSE_REF)-1);
+ DoResponsePhrase (conquer_because_1, DiscussConquer, shared_phrase_buf);
+#endif
+ Response(conquer_because_1, DiscussConquer);
+ }
+ if (PHRASE_ENABLED (conquer_because_2))
+ Response (conquer_because_2, DiscussConquer);
+ Response (must_conquer, ExitConversation);
+ Response (no_conquest, PkunkHome);
+}
+
+static void
+OfferAlliance (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, we_are_vindicator0))
+ NPCPhrase (WHY_YOU_HERE);
+ else if (PLAYER_SAID (R, exploring_universe))
+ {
+ NPCPhrase (SENSE_DEEPER_CONFLICT);
+
+ DISABLE_PHRASE (exploring_universe);
+ }
+ else if (PLAYER_SAID (R, fun_cruise))
+ {
+ NPCPhrase (REPRESS);
+
+ DISABLE_PHRASE (fun_cruise);
+ }
+
+ Response (we_here_to_help, ExitConversation);
+ Response (we_need_help, ExitConversation);
+ if (PHRASE_ENABLED (exploring_universe))
+ Response (exploring_universe, OfferAlliance);
+ if (PHRASE_ENABLED (fun_cruise))
+ Response (fun_cruise, OfferAlliance);
+}
+
+static void
+AboutPkunk (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ InfoLeft = FALSE;
+ if (PLAYER_SAID (R, what_about_you))
+ NPCPhrase (ABOUT_US);
+ else if (PLAYER_SAID (R, what_about_history))
+ {
+ NPCPhrase (ABOUT_HISTORY);
+
+ DISABLE_PHRASE (what_about_history);
+ }
+ else if (PLAYER_SAID (R, what_about_yehat))
+ {
+ NPCPhrase (ABOUT_YEHAT);
+
+ DISABLE_PHRASE (what_about_yehat);
+ }
+ else if (PLAYER_SAID (R, what_about_culture))
+ {
+ NPCPhrase (ABOUT_CULTURE);
+
+ DISABLE_PHRASE (what_about_culture);
+ }
+ else if (PLAYER_SAID (R, elaborate_culture))
+ {
+ NPCPhrase (OK_ELABORATE_CULTURE);
+
+ DISABLE_PHRASE (elaborate_culture);
+ }
+ else if (PLAYER_SAID (R, what_about_future))
+ {
+ NPCPhrase (ABOUT_FUTURE);
+
+ DISABLE_PHRASE (what_about_future);
+ }
+
+ if (PHRASE_ENABLED (what_about_history))
+ {
+ Response (what_about_history, AboutPkunk);
+ InfoLeft = TRUE;
+ }
+ else if (PHRASE_ENABLED (what_about_yehat))
+ {
+ Response (what_about_yehat, AboutPkunk);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_culture))
+ {
+ Response (what_about_culture, AboutPkunk);
+ InfoLeft = TRUE;
+ }
+ else if (PHRASE_ENABLED (elaborate_culture))
+ {
+ Response (elaborate_culture, AboutPkunk);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_future))
+ {
+ Response (what_about_future, AboutPkunk);
+ InfoLeft = TRUE;
+ }
+ Response (enough_about_you, PkunkHome);
+
+ if (!InfoLeft)
+ {
+ DISABLE_PHRASE (what_about_you);
+ }
+}
+
+static void
+AboutIlwrath (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ InfoLeft = FALSE;
+ if (PLAYER_SAID (R, what_about_ilwrath))
+ NPCPhrase (ABOUT_ILWRATH);
+ else if (PLAYER_SAID (R, why_ilwrath_fight))
+ {
+ NPCPhrase (ILWRATH_FIGHT_BECAUSE);
+
+ DISABLE_PHRASE (why_ilwrath_fight);
+ }
+ else if (PLAYER_SAID (R, when_fight_start))
+ {
+ NPCPhrase (FIGHT_START_WHEN);
+
+ DISABLE_PHRASE (when_fight_start);
+ }
+ else if (PLAYER_SAID (R, how_goes_fight))
+ {
+ NPCPhrase (FIGHT_GOES);
+
+ DISABLE_PHRASE (how_goes_fight);
+ }
+ else if (PLAYER_SAID (R, how_stop_fight))
+ {
+ NPCPhrase (STOP_FIGHT_LIKE_SO);
+
+ DISABLE_PHRASE (how_stop_fight);
+ }
+
+ if (PHRASE_ENABLED (why_ilwrath_fight))
+ {
+ Response (why_ilwrath_fight, AboutIlwrath);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (when_fight_start))
+ {
+ Response (when_fight_start, AboutIlwrath);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (how_goes_fight))
+ {
+ Response (how_goes_fight, AboutIlwrath);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (how_stop_fight))
+ {
+ Response (how_stop_fight, AboutIlwrath);
+ InfoLeft = TRUE;
+ }
+ Response (enough_ilwrath, PkunkHome);
+
+ if (!InfoLeft)
+ {
+ DISABLE_PHRASE (what_about_ilwrath);
+ }
+}
+
+static void
+PkunkHome (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, no_conquest))
+ NPCPhrase (GOOD_IDEA);
+ else if (PLAYER_SAID (R, enough_ilwrath))
+ NPCPhrase (OK_ENOUGH_ILWRATH);
+ else if (PLAYER_SAID (R, enough_about_you))
+ NPCPhrase (OK_ENOUGH_ABOUT_US);
+ else if (PLAYER_SAID (R, where_fleet_1)
+ || PLAYER_SAID (R, where_fleet_2)
+ || PLAYER_SAID (R, where_fleet_3))
+ {
+ SET_GAME_STATE (PKUNK_SWITCH, 1);
+ if (!(GET_GAME_STATE (PKUNK_MISSION) & 1))
+ {
+ NumVisits = GET_GAME_STATE (PKUNK_RETURN);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (RETURNING_FROM_YEHAT_1);
+ break;
+ case 1:
+ NPCPhrase (RETURNING_FROM_YEHAT_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_RETURN, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (PKUNK_MIGRATE);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (MIGRATING_HOMEWORLD_1);
+ break;
+ case 1:
+ NPCPhrase (MIGRATING_HOMEWORLD_2);
+ break;
+ case 2:
+ NPCPhrase (MIGRATING_HOMEWORLD_3);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_MIGRATE, NumVisits);
+ }
+
+ NumVisits = GET_GAME_STATE (PKUNK_FLEET) + 1;
+ SET_GAME_STATE (PKUNK_FLEET, NumVisits);
+
+ DISABLE_PHRASE (where_fleet_1);
+ }
+ else if (PLAYER_SAID (R, am_worried_1)
+ || PLAYER_SAID (R, am_worried_2)
+ || PLAYER_SAID (R, am_worried_3))
+ {
+ NumVisits = GET_GAME_STATE (PKUNK_WORRY);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (DONT_WORRY_1);
+ break;
+ case 1:
+ NPCPhrase (DONT_WORRY_2);
+ break;
+ case 2:
+ NPCPhrase (DONT_WORRY_3);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_WORRY, NumVisits);
+
+ DISABLE_PHRASE (am_worried_1);
+ }
+ else if (PLAYER_SAID (R, try_to_be_nicer))
+ {
+ NPCPhrase (CANT_ASK_FOR_MORE);
+ if (!GET_GAME_STATE (CLEAR_SPINDLE))
+ {
+ NPCPhrase (GIVE_SPINDLE);
+
+ SET_GAME_STATE (CLEAR_SPINDLE, 1);
+ SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 1);
+ }
+ NPCPhrase (CAN_BE_FRIENDS);
+
+ SET_GAME_STATE (PKUNK_MANNER, 3);
+ SET_GAME_STATE (PKUNK_VISITS, 0);
+ SET_GAME_STATE (PKUNK_HOME_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, what_about_ilwrath))
+ {
+ NPCPhrase (ABOUT_ILWRATH /* ILWRATH_GONE */);
+
+ DISABLE_PHRASE (what_about_ilwrath);
+ }
+
+ if (PHRASE_ENABLED (we_conquer) && GET_GAME_STATE (PKUNK_MANNER) == 0)
+ {
+ Response (we_conquer, DiscussConquer);
+ }
+ if (GET_GAME_STATE (PKUNK_ON_THE_MOVE))
+ {
+ if (PHRASE_ENABLED (where_fleet_1) && !GET_GAME_STATE (PKUNK_SWITCH))
+ {
+ switch (GET_GAME_STATE (PKUNK_FLEET))
+ {
+ case 0:
+ Response (where_fleet_1, PkunkHome);
+ break;
+ case 1:
+ Response (where_fleet_2, PkunkHome);
+ break;
+ case 2:
+ Response (where_fleet_3, PkunkHome);
+ break;
+ }
+ }
+ else if (!PHRASE_ENABLED (where_fleet_1)
+ && PHRASE_ENABLED (am_worried_1)
+ && (GET_GAME_STATE (PKUNK_MISSION) & 1))
+ {
+ switch (GET_GAME_STATE (PKUNK_WORRY))
+ {
+ case 0:
+ Response (am_worried_1, PkunkHome);
+ break;
+ case 1:
+ Response (am_worried_2, PkunkHome);
+ break;
+ case 2:
+ Response (am_worried_3, PkunkHome);
+ break;
+ }
+ }
+ }
+ if (!GET_GAME_STATE (PKUNK_SHIP_MONTH))
+ {
+ construct_response (shared_phrase_buf,
+ we_are_vindicator0,
+ GLOBAL_SIS (CommanderName),
+ we_are_vindicator1,
+ GLOBAL_SIS (ShipName),
+ we_are_vindicator2,
+ (UNICODE*)NULL);
+ DoResponsePhrase (we_are_vindicator0, OfferAlliance, shared_phrase_buf);
+ }
+ if (PHRASE_ENABLED (what_about_you))
+ {
+ Response (what_about_you, AboutPkunk);
+ }
+ if (PHRASE_ENABLED (what_about_ilwrath))
+ {
+ if (!GET_GAME_STATE (ILWRATH_DECEIVED))
+ {
+ Response (what_about_ilwrath, AboutIlwrath);
+ }
+ else
+ {
+ Response (what_about_ilwrath, PkunkHome);
+ }
+ }
+ Response (bye_friendly, ExitConversation);
+}
+
+static void
+PkunkFriendlySpace (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_space))
+ {
+ if (ShipsReady ())
+ NPCPhrase (SHIPS_AT_HOME);
+ else
+ {
+ NumVisits = GET_GAME_STATE (PKUNK_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_SPACE_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_SPACE_4);
+ break;
+ case 4:
+ NPCPhrase (GENERAL_INFO_SPACE_5);
+ break;
+ case 5:
+ NPCPhrase (GENERAL_INFO_SPACE_6);
+ break;
+ case 6:
+ NPCPhrase (GENERAL_INFO_SPACE_7);
+ break;
+ case 7:
+ NPCPhrase (GENERAL_INFO_SPACE_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_INFO, NumVisits);
+ }
+
+ DISABLE_PHRASE (whats_up_space);
+ }
+ else if (PLAYER_SAID (R, how_goes_war))
+ {
+ NumVisits = GET_GAME_STATE (PKUNK_WAR);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (WAR_GOES_1);
+ SET_GAME_STATE (KNOW_URQUAN_STORY, 1);
+ SET_GAME_STATE (KNOW_KOHR_AH_STORY, 1);
+ break;
+ case 1:
+ NPCPhrase (WAR_GOES_2);
+ break;
+ case 2:
+ NPCPhrase (WAR_GOES_3);
+ break;
+ case 3:
+ NPCPhrase (WAR_GOES_4);
+ SET_GAME_STATE (PKUNK_DONE_WAR, 1);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_WAR, NumVisits);
+
+ DISABLE_PHRASE (how_goes_war);
+ }
+ else if (PLAYER_SAID (R, tell_my_fortune))
+ {
+ NumVisits = GET_GAME_STATE (PKUNK_FORTUNE);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FORTUNE_IS_1);
+ break;
+ case 1:
+ NPCPhrase (FORTUNE_IS_2);
+ break;
+ case 2:
+ NPCPhrase (FORTUNE_IS_3);
+ break;
+ case 3:
+ NPCPhrase (FORTUNE_IS_4);
+ break;
+ case 4:
+ NPCPhrase (FORTUNE_IS_5);
+ break;
+ case 5:
+ NPCPhrase (FORTUNE_IS_6);
+ break;
+ case 6:
+ NPCPhrase (FORTUNE_IS_7);
+ break;
+ case 7:
+ NPCPhrase (FORTUNE_IS_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_FORTUNE, NumVisits);
+
+ DISABLE_PHRASE (tell_my_fortune);
+ }
+
+ if (PHRASE_ENABLED (whats_up_space))
+ Response (whats_up_space, PkunkFriendlySpace);
+ if (!GET_GAME_STATE (PKUNK_DONE_WAR) && PHRASE_ENABLED (how_goes_war))
+ Response (how_goes_war, PkunkFriendlySpace);
+ if (PHRASE_ENABLED (tell_my_fortune))
+ Response (tell_my_fortune, PkunkFriendlySpace);
+ Response (friendly_bye_space, ExitConversation);
+}
+
+static void
+PkunkNeutralSpace (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, form_alliance))
+ {
+ NPCPhrase (GO_TO_HOMEWORLD);
+
+ DISABLE_PHRASE (form_alliance);
+ }
+ else if (PLAYER_SAID (R, can_you_help))
+ {
+ NPCPhrase (GO_TO_HOMEWORLD_AGAIN);
+
+ DISABLE_PHRASE (can_you_help);
+ }
+ else if (PLAYER_SAID (R, hostile_greeting))
+ {
+ NPCPhrase (DONT_BE_HOSTILE);
+
+ DISABLE_PHRASE (hostile_greeting);
+ }
+ else if (PLAYER_SAID (R, whats_up_neutral))
+ {
+ NumVisits = GET_GAME_STATE (PKUNK_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_SPACE_6 /* was 3 */);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_SPACE_7 /* was 4 */);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_neutral);
+ }
+
+ if (PHRASE_ENABLED (form_alliance))
+ Response (form_alliance, PkunkNeutralSpace);
+ else if (PHRASE_ENABLED (can_you_help))
+ Response (can_you_help, PkunkNeutralSpace);
+ if (PHRASE_ENABLED (hostile_greeting))
+ Response (hostile_greeting, PkunkNeutralSpace);
+ else
+ {
+ Response (obey, ExitConversation);
+ }
+ if (PHRASE_ENABLED (whats_up_neutral))
+ Response (whats_up_neutral, PkunkNeutralSpace);
+ Response (neutral_bye_space, ExitConversation);
+}
+
+static void
+PkunkMigrate (RESPONSE_REF R)
+{
+ BYTE ReasonMask;
+ (void) R; // ignored
+
+ ReasonMask = GET_GAME_STATE (PKUNK_REASONS);
+ if (!(ReasonMask & GOOD_REASON_1))
+ Response (good_reason_1, ExitConversation);
+ if (!(ReasonMask & BAD_REASON_1))
+ Response (bad_reason_1, ExitConversation);
+ if (!(ReasonMask & GOOD_REASON_2))
+ Response (good_reason_2, ExitConversation);
+ if (!(ReasonMask & BAD_REASON_2))
+ Response (bad_reason_2, ExitConversation);
+ Response (suit_yourself, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits, Manner;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ Manner = GET_GAME_STATE (PKUNK_MANNER);
+ if (Manner == 2)
+ {
+ // Irreparably Pissed off the Pkunk.
+ NumVisits = GET_GAME_STATE (PKUNK_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HATE_YOU_FOREVER_1);
+ break;
+ case 1:
+ NPCPhrase (HATE_YOU_FOREVER_2);
+ break;
+ case 2:
+ NPCPhrase (HATE_YOU_FOREVER_3);
+ break;
+ case 3:
+ NPCPhrase (HATE_YOU_FOREVER_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_VISITS, NumVisits);
+
+ setSegue (Segue_hostile);
+ }
+ else if (Manner == 1)
+ {
+ // Bad relations with the Pkunk, but not irreparably.
+ NumVisits = GET_GAME_STATE (PKUNK_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (SPIRITUAL_PROBLEMS_1);
+ break;
+ case 1:
+ NPCPhrase (SPIRITUAL_PROBLEMS_2);
+ break;
+ case 2:
+ NPCPhrase (SPIRITUAL_PROBLEMS_3);
+ break;
+ case 3:
+ NPCPhrase (SPIRITUAL_PROBLEMS_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_VISITS, NumVisits);
+
+ PkunkAngry ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ // Encountering the Pkunk at their home world.
+ if (!GET_GAME_STATE (CLEAR_SPINDLE))
+ {
+ NPCPhrase (GIVE_SPINDLE);
+
+ SET_GAME_STATE (CLEAR_SPINDLE, 1);
+ SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 1);
+ }
+ else if (!GET_GAME_STATE (PKUNK_SENSE_VICTOR)
+ && GLOBAL (GameClock.year_index) > START_YEAR
+ && !GET_GAME_STATE (KOHR_AH_FRENZY))
+ {
+ NPCPhrase (SENSE_KOHRAH_VICTORY);
+
+ SET_GAME_STATE (PKUNK_SENSE_VICTOR, 1);
+ }
+
+ NumVisits = GET_GAME_STATE (PKUNK_HOME_VISITS);
+ if (Manner == 0)
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ }
+ else
+ {
+ if (NumVisits && ShipsReady ())
+ {
+ if (EscortFeasibilityStudy (PKUNK_SHIP) == 0)
+ NPCPhrase (NO_ROOM);
+ else
+ {
+ NPCPhrase (SHIP_GIFT);
+ PrepareShip ();
+ }
+ }
+ else switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_4);
+ break;
+ case 4:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_5);
+ break;
+ case 5:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_6);
+ break;
+ case 6:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_7);
+ break;
+ case 7:
+ NPCPhrase (FRIENDLY_HOMEWORLD_HELLO_8);
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (PKUNK_HOME_VISITS, NumVisits);
+
+ PkunkHome ((RESPONSE_REF)0);
+ }
+ else if ((NumVisits = GET_GAME_STATE (PKUNK_MISSION)) == 0
+ || !(NumVisits & 1))
+ {
+ // Encountering a Pkunk ship in space, while they are not
+ // migrating.
+ NumVisits = GET_GAME_STATE (PKUNK_VISITS);
+ if (Manner == 3)
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_4);
+ break;
+ case 4:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_5);
+ break;
+ case 5:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_6);
+ break;
+ case 6:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_7);
+ break;
+ case 7:
+ NPCPhrase (FRIENDLY_SPACE_HELLO_8);
+ --NumVisits;
+ break;
+ }
+
+ PkunkFriendlySpace ((RESPONSE_REF)0);
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+
+ PkunkNeutralSpace ((RESPONSE_REF)0);
+ }
+ SET_GAME_STATE (PKUNK_VISITS, NumVisits);
+
+ }
+ else
+ {
+ // Encountering a Pkunk ship in space, while they are
+ // migrating.
+ NumVisits = GET_GAME_STATE (PKUNK_MIGRATE_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (MIGRATING_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (MIGRATING_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (MIGRATING_SPACE_3);
+ break;
+ case 3:
+ NPCPhrase (MIGRATING_SPACE_4);
+ break;
+ case 4:
+ NPCPhrase (MIGRATING_SPACE_5);
+ break;
+ case 5:
+ NPCPhrase (MIGRATING_SPACE_6);
+ break;
+ case 6:
+ NPCPhrase (MIGRATING_SPACE_7);
+ break;
+ case 7:
+ NPCPhrase (MIGRATING_SPACE_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (PKUNK_MIGRATE_VISITS, NumVisits);
+
+ PkunkMigrate ((RESPONSE_REF)0);
+ }
+}
+
+// Called after combat or communications
+static COUNT
+uninit_pkunk (void)
+{
+ return (0);
+}
+
+static void
+post_pkunk_enc (void)
+{
+ BYTE Manner;
+
+ if (getSegue () == Segue_hostile
+ && (Manner = GET_GAME_STATE (PKUNK_MANNER)) != 2)
+ {
+ SET_GAME_STATE (PKUNK_MANNER, 1);
+ if (Manner != 1)
+ {
+ SET_GAME_STATE (PKUNK_VISITS, 0);
+ SET_GAME_STATE (PKUNK_HOME_VISITS, 0);
+ }
+ }
+}
+
+LOCDATA*
+init_pkunk_comm (void)
+{
+ LOCDATA *retval;
+
+ pkunk_desc.init_encounter_func = Intro;
+ pkunk_desc.post_encounter_func = post_pkunk_enc;
+ pkunk_desc.uninit_encounter_func = uninit_pkunk;
+
+ pkunk_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ pkunk_desc.AlienTextBaseline.y = 0;
+ pkunk_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (GET_GAME_STATE (PKUNK_MANNER) == 3
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ // Enter communications immediately.
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ // Ask the player whether to attack or talk.
+ setSegue (Segue_hostile);
+ }
+ retval = &pkunk_desc;
+
+ return (retval);
+}
+
+
diff --git a/src/uqm/comm/pkunk/resinst.h b/src/uqm/comm/pkunk/resinst.h
new file mode 100644
index 0000000..8f9ab7a
--- /dev/null
+++ b/src/uqm/comm/pkunk/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define PKUNK_COLOR_MAP "comm.pkunk.colortable"
+#define PKUNK_CONVERSATION_PHRASES "comm.pkunk.dialogue"
+#define PKUNK_FONT "comm.pkunk.font"
+#define PKUNK_MUSIC "comm.pkunk.music"
+#define PKUNK_PMAP_ANIM "comm.pkunk.graphics"
diff --git a/src/uqm/comm/pkunk/strings.h b/src/uqm/comm/pkunk/strings.h
new file mode 100644
index 0000000..beb8b86
--- /dev/null
+++ b/src/uqm/comm/pkunk/strings.h
@@ -0,0 +1,214 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PKUNK_STRINGS_H
+#define PKUNK_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ GIVE_SPINDLE,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ NEUTRAL_SPACE_HELLO_1,
+ NEUTRAL_SPACE_HELLO_3,
+ NEUTRAL_SPACE_HELLO_2,
+ NEUTRAL_SPACE_HELLO_4,
+ FRIENDLY_SPACE_HELLO_1,
+ FRIENDLY_SPACE_HELLO_2,
+ FRIENDLY_SPACE_HELLO_3,
+ FRIENDLY_SPACE_HELLO_4,
+ FRIENDLY_SPACE_HELLO_5,
+ FRIENDLY_SPACE_HELLO_6,
+ FRIENDLY_SPACE_HELLO_7,
+ FRIENDLY_SPACE_HELLO_8,
+ NEUTRAL_HOMEWORLD_HELLO_1,
+ NEUTRAL_HOMEWORLD_HELLO_2,
+ NEUTRAL_HOMEWORLD_HELLO_3,
+ NEUTRAL_HOMEWORLD_HELLO_4,
+ FRIENDLY_HOMEWORLD_HELLO_1,
+ FRIENDLY_HOMEWORLD_HELLO_2,
+ FRIENDLY_HOMEWORLD_HELLO_3,
+ FRIENDLY_HOMEWORLD_HELLO_4,
+ FRIENDLY_HOMEWORLD_HELLO_5,
+ FRIENDLY_HOMEWORLD_HELLO_6,
+ FRIENDLY_HOMEWORLD_HELLO_7,
+ FRIENDLY_HOMEWORLD_HELLO_8,
+ whats_up_neutral,
+ GENERAL_INFO_NEUTRAL_1,
+ GENERAL_INFO_NEUTRAL_2,
+ GENERAL_INFO_NEUTRAL_3,
+ GENERAL_INFO_NEUTRAL_4,
+ good_reason_1,
+ WE_GO_HOME_1,
+ good_reason_2,
+ WE_GO_HOME_2,
+ bad_reason_1,
+ NO_GO_HOME_1,
+ bad_reason_2,
+ NO_GO_HOME_2,
+ SENSE_KOHRAH_VICTORY,
+ SPIRITUAL_PROBLEMS_1,
+ SPIRITUAL_PROBLEMS_2,
+ SPIRITUAL_PROBLEMS_3,
+ SPIRITUAL_PROBLEMS_4,
+ HATE_YOU_FOREVER_1,
+ HATE_YOU_FOREVER_2,
+ HATE_YOU_FOREVER_3,
+ HATE_YOU_FOREVER_4,
+ MIGRATING_SPACE_1,
+ MIGRATING_SPACE_2,
+ MIGRATING_SPACE_3,
+ MIGRATING_SPACE_4,
+ MIGRATING_SPACE_5,
+ MIGRATING_SPACE_6,
+ MIGRATING_SPACE_7,
+ MIGRATING_SPACE_8,
+ die_idiot_fools,
+ VERY_WELL,
+ why_insults,
+ RELEASE_TENSION,
+ what_about_you_angry,
+ ABOUT_US_ANGRY,
+ what_about_you,
+ should_be_friends,
+ YES_FRIENDS,
+ try_to_be_nicer,
+ CANT_ASK_FOR_MORE,
+ VISIT_OUR_HOMEWORLD,
+ CAN_BE_FRIENDS,
+ bye_angry,
+ GOODBYE_ANGRY,
+ we_conquer,
+ WHY_CONQUER,
+ conquer_because_1,
+#if 0
+ NOT_CONQUER_10,
+ NOT_CONQUER_11,
+ NOT_CONQUER_12,
+#endif
+ NOT_CONQUER_1,
+ conquer_because_2,
+ NOT_CONQUER_2,
+ must_conquer,
+ BAD_IDEA,
+ no_conquest,
+ GOOD_IDEA,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ we_are_vindicator2,
+ WHY_YOU_HERE,
+ we_here_to_help,
+ NEED_HELP,
+ we_need_help,
+ GIVE_HELP,
+ exploring_universe,
+ SENSE_DEEPER_CONFLICT,
+ fun_cruise,
+ REPRESS,
+ why_ilwrath_fight,
+ ILWRATH_FIGHT_BECAUSE,
+ when_fight_start,
+ FIGHT_START_WHEN,
+ how_goes_fight,
+ FIGHT_GOES,
+ how_goes_war,
+ WAR_GOES_1,
+ WAR_GOES_2,
+ WAR_GOES_3,
+ WAR_GOES_4,
+ how_stop_fight,
+ STOP_FIGHT_LIKE_SO,
+ enough_ilwrath,
+ OK_ENOUGH_ILWRATH,
+ what_about_history,
+ ABOUT_HISTORY,
+ what_about_yehat,
+ ABOUT_YEHAT,
+ what_about_culture,
+ ABOUT_CULTURE,
+ elaborate_culture,
+ OK_ELABORATE_CULTURE,
+ what_about_future,
+ ABOUT_FUTURE,
+ enough_about_you,
+ OK_ENOUGH_ABOUT_US,
+ ABOUT_US,
+ where_fleet_1,
+ where_fleet_2,
+ where_fleet_3,
+ MIGRATING_HOMEWORLD_1,
+ MIGRATING_HOMEWORLD_2,
+ MIGRATING_HOMEWORLD_3,
+ RETURNING_FROM_YEHAT_1,
+ RETURNING_FROM_YEHAT_2,
+ am_worried_1,
+ am_worried_2,
+ am_worried_3,
+ DONT_WORRY_1,
+ DONT_WORRY_2,
+ DONT_WORRY_3,
+ form_alliance,
+ GO_TO_HOMEWORLD,
+ can_you_help,
+ GO_TO_HOMEWORLD_AGAIN,
+ hostile_greeting,
+ DONT_BE_HOSTILE,
+ obey,
+ NO_OBEY,
+ neutral_bye_space,
+ NEUTRAL_GOODBYE_SPACE,
+ SHIP_GIFT,
+ NO_ROOM,
+ friendly_bye_space,
+ FRIENDLY_GOODBYE_SPACE,
+ bye_friendly,
+ GOODBYE_FRIENDLY,
+ ALMOST_ALLIANCE,
+ INIT_NO_ROOM,
+ INIT_SHIP_GIFT,
+ suit_yourself,
+ GOODBYE_MIGRATION,
+ what_about_ilwrath,
+ ABOUT_ILWRATH,
+ whats_up_space,
+ SHIPS_AT_HOME,
+ GENERAL_INFO_SPACE_1,
+ GENERAL_INFO_SPACE_2,
+ GENERAL_INFO_SPACE_3,
+ GENERAL_INFO_SPACE_4,
+ GENERAL_INFO_SPACE_5,
+ GENERAL_INFO_SPACE_6,
+ GENERAL_INFO_SPACE_7,
+ GENERAL_INFO_SPACE_8,
+ tell_my_fortune,
+ FORTUNE_IS_1,
+ FORTUNE_IS_2,
+ FORTUNE_IS_3,
+ FORTUNE_IS_4,
+ FORTUNE_IS_5,
+ FORTUNE_IS_6,
+ FORTUNE_IS_7,
+ FORTUNE_IS_8,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/rebel/Makeinfo b/src/uqm/comm/rebel/Makeinfo
new file mode 100644
index 0000000..4a17467
--- /dev/null
+++ b/src/uqm/comm/rebel/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="rebel.c"
+uqm_HFILES="strings.h"
diff --git a/src/uqm/comm/rebel/rebel.c b/src/uqm/comm/rebel/rebel.c
new file mode 100644
index 0000000..6366f20
--- /dev/null
+++ b/src/uqm/comm/rebel/rebel.c
@@ -0,0 +1,449 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "../yehat/resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+
+
+static LOCDATA yehat_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ YEHAT_PMAP_ANIM, /* AlienFrame */
+ YEHAT_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* (SIS_TEXT_WIDTH - 16) * 2 / 3, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ YEHAT_COLOR_MAP, /* AlienColorMap */
+ YEHAT_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ REBEL_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 15, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ { /* right hand-wing tapping keyboard; front guy */
+ 4, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 10, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 6) | (1 << 7),
+ },
+ { /* left hand-wing tapping keyboard; front guy */
+ 7, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 10, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 6) | (1 << 7),
+ },
+ {
+ 10, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4) | (1 << 14),
+ },
+ {
+ 13, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5),
+ },
+ {
+ 16, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3,/* RestartRate */
+ (1 << 2) | (1 << 14),
+ },
+ {
+ 21, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3,/* RestartRate */
+ (1 << 3),
+ },
+ { /* right arm-wing rising; front guy */
+ 26, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3,/* RestartRate */
+ (1 << 0) | (1 << 1),
+ },
+ { /* left arm-wing rising; front guy */
+ 28, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3,/* RestartRate */
+ (1 << 0) | (1 << 1),
+ },
+ {
+ 30, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 33, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 36, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 39, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 42, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 45, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 48, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 2) | (1 << 4),
+ },
+ },
+ { /* AlienTransitionDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+PrepareShip (void)
+{
+ BYTE mi, di, yi;
+
+ mi = (GLOBAL (GameClock.month_index) % 12) + 1;
+ SET_GAME_STATE (YEHAT_SHIP_MONTH, mi);
+ if ((di = GLOBAL (GameClock.day_index)) > 28)
+ di = 28;
+ SET_GAME_STATE (YEHAT_SHIP_DAY, di);
+ yi = (BYTE)(GLOBAL (GameClock.year_index) - START_YEAR);
+ if (mi == 1)
+ ++yi;
+ SET_GAME_STATE (YEHAT_SHIP_YEAR, yi);
+}
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, bye_rebel))
+ NPCPhrase (GOODBYE_REBEL);
+}
+
+static void Rebels (RESPONSE_REF R);
+
+static void
+RebelInfo (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ InfoLeft = FALSE;
+ if (PLAYER_SAID (R, give_info_rebels))
+ NPCPhrase (WHAT_INFO);
+ else if (PLAYER_SAID (R, what_about_urquan))
+ {
+ NPCPhrase (ABOUT_URQUAN);
+
+ DISABLE_PHRASE (what_about_urquan);
+ }
+ else if (PLAYER_SAID (R, what_about_royalty))
+ {
+ NPCPhrase (ABOUT_ROYALTY);
+
+ DISABLE_PHRASE (what_about_royalty);
+ }
+ else if (PLAYER_SAID (R, what_about_war))
+ {
+ NPCPhrase (ABOUT_WAR);
+
+ DISABLE_PHRASE (what_about_war);
+ }
+ else if (PLAYER_SAID (R, what_about_vux))
+ {
+ NPCPhrase (ABOUT_VUX);
+
+ DISABLE_PHRASE (what_about_vux);
+ }
+ else if (PLAYER_SAID (R, what_about_clue))
+ {
+ NPCPhrase (ABOUT_CLUE);
+
+ DISABLE_PHRASE (what_about_clue);
+ }
+
+ if (PHRASE_ENABLED (what_about_urquan))
+ {
+ Response (what_about_urquan, RebelInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_royalty))
+ {
+ Response (what_about_royalty, RebelInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_war))
+ {
+ Response (what_about_war, RebelInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_vux))
+ {
+ Response (what_about_vux, RebelInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_clue))
+ {
+ Response (what_about_clue, RebelInfo);
+ InfoLeft = TRUE;
+ }
+ Response (enough_info, Rebels);
+
+ if (!InfoLeft)
+ {
+ DISABLE_PHRASE (give_info_rebels);
+ }
+}
+
+static void
+Rebels (RESPONSE_REF R)
+{
+ SBYTE NumVisits;
+
+ if (PLAYER_SAID (R, how_goes_revolution))
+ {
+ NumVisits = GET_GAME_STATE (YEHAT_REBEL_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (REBEL_REVOLUTION_1);
+ break;
+ case 1:
+ NPCPhrase (REBEL_REVOLUTION_2);
+ break;
+ case 2:
+ NPCPhrase (REBEL_REVOLUTION_3);
+ break;
+ case 3:
+ NPCPhrase (REBEL_REVOLUTION_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_REBEL_INFO, NumVisits);
+
+ DISABLE_PHRASE (how_goes_revolution);
+ }
+ else if (PLAYER_SAID (R, any_ships))
+ {
+ if (GET_GAME_STATE (YEHAT_SHIP_MONTH)
+ && ((NumVisits = (GLOBAL (GameClock.year_index) - START_YEAR) - GET_GAME_STATE (YEHAT_SHIP_YEAR)) < 0
+ || ((NumVisits == 0 && (NumVisits = GLOBAL (GameClock.month_index) - GET_GAME_STATE (YEHAT_SHIP_MONTH)) < 0)
+ || (NumVisits == 0 && GLOBAL (GameClock.day_index) < GET_GAME_STATE (YEHAT_SHIP_DAY)))))
+ NPCPhrase (NO_SHIPS_YET);
+ else if ((NumVisits = EscortFeasibilityStudy (YEHAT_SHIP)) == 0)
+ NPCPhrase (NO_ROOM);
+ else
+ {
+#define NUM_YEHAT_SHIPS 4
+ if (NumVisits < NUM_YEHAT_SHIPS)
+ NPCPhrase (HAVE_FEW_SHIPS);
+ else
+ {
+ NumVisits = NUM_YEHAT_SHIPS;
+ NPCPhrase (HAVE_ALL_SHIPS);
+ }
+
+ AlienTalkSegue ((COUNT)~0);
+ AddEscortShips (YEHAT_SHIP, NumVisits);
+ PrepareShip ();
+ }
+
+ DISABLE_PHRASE (any_ships);
+ }
+ else if (PLAYER_SAID (R, what_about_pkunk_rebel))
+ {
+ if (GET_GAME_STATE (YEHAT_ABSORBED_PKUNK))
+ NPCPhrase (PKUNK_ABSORBED_REBEL);
+ else
+ NPCPhrase (HATE_PKUNK_REBEL);
+
+ SET_GAME_STATE (YEHAT_REBEL_TOLD_PKUNK, 1);
+ }
+ else if (PLAYER_SAID (R, enough_info))
+ NPCPhrase (OK_ENOUGH_INFO);
+
+ if (PHRASE_ENABLED (how_goes_revolution))
+ Response (how_goes_revolution, Rebels);
+ if (!GET_GAME_STATE (YEHAT_REBEL_TOLD_PKUNK)
+ && GET_GAME_STATE (PKUNK_VISITS)
+ && GET_GAME_STATE (PKUNK_HOME_VISITS))
+ Response (what_about_pkunk_rebel, Rebels);
+ if (PHRASE_ENABLED (any_ships))
+ Response (any_ships, Rebels);
+ if (PHRASE_ENABLED (give_info_rebels))
+ {
+ Response (give_info_rebels, RebelInfo);
+ }
+ Response (bye_rebel, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ setSegue (Segue_peace);
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ {
+ NPCPhrase (YEHAT_CAVALRY);
+ AlienTalkSegue ((COUNT)~0);
+
+ NumVisits = (BYTE) EscortFeasibilityStudy (YEHAT_REBEL_SHIP);
+ if (NumVisits > 8)
+ NumVisits = 8;
+ AddEscortShips (YEHAT_REBEL_SHIP, NumVisits - (NumVisits >> 1));
+ AddEscortShips (PKUNK_SHIP, NumVisits >> 1);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (YEHAT_REBEL_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (REBEL_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (REBEL_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (REBEL_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (REBEL_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_REBEL_VISITS, NumVisits);
+
+ Rebels ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_yehat (void)
+{
+ return (0);
+}
+
+static void
+post_yehat_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_rebel_yehat_comm (void)
+{
+ LOCDATA *retval;
+
+ yehat_desc.init_encounter_func = Intro;
+ yehat_desc.post_encounter_func = post_yehat_enc;
+ yehat_desc.uninit_encounter_func = uninit_yehat;
+
+ yehat_desc.AlienTextBaseline.x = SIS_SCREEN_WIDTH * 2 / 3;
+ yehat_desc.AlienTextBaseline.y = 60;
+ yehat_desc.AlienTextWidth = (SIS_TEXT_WIDTH - 16) * 2 / 3;
+
+ // use alternate "Rebels" track if available
+ yehat_desc.AlienAltSongRes = REBEL_MUSIC;
+ yehat_desc.AlienSongFlags |= LDASF_USE_ALTERNATE;
+
+ setSegue (Segue_peace);
+ retval = &yehat_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/rebel/strings.h b/src/uqm/comm/rebel/strings.h
new file mode 100644
index 0000000..c7e0b4f
--- /dev/null
+++ b/src/uqm/comm/rebel/strings.h
@@ -0,0 +1,61 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef REBEL_STRINGS_H
+#define REBEL_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ REBEL_HELLO_1,
+ REBEL_HELLO_2,
+ REBEL_HELLO_3,
+ REBEL_HELLO_4,
+ how_goes_revolution,
+ REBEL_REVOLUTION_1,
+ REBEL_REVOLUTION_2,
+ REBEL_REVOLUTION_3,
+ REBEL_REVOLUTION_4,
+ any_ships,
+ NO_ROOM,
+ HAVE_ALL_SHIPS,
+ HAVE_FEW_SHIPS,
+ NO_SHIPS_YET,
+ give_info_rebels,
+ WHAT_INFO,
+ what_about_royalty,
+ ABOUT_ROYALTY,
+ what_about_war,
+ ABOUT_WAR,
+ what_about_urquan,
+ ABOUT_URQUAN,
+ what_about_vux,
+ ABOUT_VUX,
+ what_about_clue,
+ ABOUT_CLUE,
+ enough_info,
+ OK_ENOUGH_INFO,
+ bye_rebel,
+ GOODBYE_REBEL,
+ YEHAT_CAVALRY,
+ what_about_pkunk_rebel,
+ PKUNK_ABSORBED_REBEL,
+ HATE_PKUNK_REBEL,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/shofixt/Makeinfo b/src/uqm/comm/shofixt/Makeinfo
new file mode 100644
index 0000000..3cad2ac
--- /dev/null
+++ b/src/uqm/comm/shofixt/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="shofixt.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/shofixt/resinst.h b/src/uqm/comm/shofixt/resinst.h
new file mode 100644
index 0000000..8fe9a87
--- /dev/null
+++ b/src/uqm/comm/shofixt/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SHOFIXTI_COLOR_MAP "comm.shofixti.colortable"
+#define SHOFIXTI_CONVERSATION_PHRASES "comm.shofixti.dialogue"
+#define SHOFIXTI_FONT "comm.shofixti.font"
+#define SHOFIXTI_MUSIC "comm.shofixti.music"
+#define SHOFIXTI_PMAP_ANIM "comm.shofixti.graphics"
diff --git a/src/uqm/comm/shofixt/shofixt.c b/src/uqm/comm/shofixt/shofixt.c
new file mode 100644
index 0000000..e76d8d0
--- /dev/null
+++ b/src/uqm/comm/shofixt/shofixt.c
@@ -0,0 +1,652 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/gameev.h"
+
+
+static LOCDATA shofixti_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ SHOFIXTI_PMAP_ANIM, /* AlienFrame */
+ SHOFIXTI_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ SHOFIXTI_COLOR_MAP, /* AlienColorMap */
+ SHOFIXTI_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ SHOFIXTI_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 11, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 5, /* StartIndex */
+ 15, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND / 30, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 20, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ (ONE_SECOND >> 1), (ONE_SECOND >> 1) * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 23, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ (ONE_SECOND >> 1), (ONE_SECOND >> 1) * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 26, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 29, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+
+ {
+ 33, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 39, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* RestartRate */
+ (1 << 7), /* BlockMask */
+ },
+ {
+ 46, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* RestartRate */
+ (1 << 6), /* BlockMask */
+ },
+ {
+ 52, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 56, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* RestartRate */
+ (1 << 10), /* BlockMask */
+ },
+ {
+ 63, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 20, ONE_SECOND / 30, /* RestartRate */
+ (1 << 9), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 4, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND / 15, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static RESPONSE_REF shofixti_name;
+
+static void
+GetShofixtiName (void)
+{
+ if (GET_GAME_STATE (SHOFIXTI_KIA))
+ shofixti_name = katana;
+ else
+ shofixti_name = tanaka;
+}
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, bye0))
+ {
+ NPCPhrase (GOODBYE);
+
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, go_ahead))
+ {
+ NPCPhrase (ON_SECOND_THOUGHT);
+
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, need_you_for_duty))
+ {
+ NPCPhrase (OK_WILL_BE_SENTRY);
+
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, females)
+ || PLAYER_SAID (R, nubiles)
+ || PLAYER_SAID (R, rat_babes))
+ {
+ NPCPhrase (LEAPING_HAPPINESS);
+
+ SET_GAME_STATE (SHOFIXTI_RECRUITED, 1);
+ SET_GAME_STATE (MAIDENS_ON_SHIP, 0);
+ setSegue (Segue_peace);
+
+ AddEvent (RELATIVE_EVENT, 2, 0, 0, SHOFIXTI_RETURN_EVENT);
+ }
+ else if (PLAYER_SAID (R, dont_attack))
+ {
+ NPCPhrase (TYPICAL_PLOY);
+
+ SET_GAME_STATE (SHOFIXTI_STACK1, 1);
+ }
+ else if (PLAYER_SAID (R, hey_stop))
+ {
+ NPCPhrase (ONLY_STOP);
+
+ SET_GAME_STATE (SHOFIXTI_STACK1, 2);
+ }
+ else if (PLAYER_SAID (R, look_you_are))
+ {
+ NPCPhrase (TOO_BAD);
+
+ SET_GAME_STATE (SHOFIXTI_STACK1, 3);
+ }
+ else if (PLAYER_SAID (R, no_one_insults))
+ {
+ NPCPhrase (YOU_LIMP);
+
+ SET_GAME_STATE (SHOFIXTI_STACK2, 1);
+ }
+ else if (PLAYER_SAID (R, mighty_words))
+ {
+ NPCPhrase (HANG_YOUR);
+
+ SET_GAME_STATE (SHOFIXTI_STACK2, 2);
+ }
+ else if (PLAYER_SAID (R, dont_know))
+ {
+ NPCPhrase (NEVER);
+
+ SET_GAME_STATE (SHOFIXTI_STACK3, 1);
+ }
+ else if (PLAYER_SAID (R, look0))
+ {
+ NPCPhrase (FOR_YOU);
+
+ SET_GAME_STATE (SHOFIXTI_STACK3, 2);
+ }
+ else if (PLAYER_SAID (R, no_bloodshed))
+ {
+ NPCPhrase (YES_BLOODSHED);
+
+ SET_GAME_STATE (SHOFIXTI_STACK3, 3);
+ }
+ else if (PLAYER_SAID (R, dont_want_to_fight))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SHOFIXTI_STACK4);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (MUST_FIGHT_YOU_URQUAN_1);
+ break;
+ case 1:
+ NPCPhrase (MUST_FIGHT_YOU_URQUAN_2);
+ break;
+ case 2:
+ NPCPhrase (MUST_FIGHT_YOU_URQUAN_3);
+ break;
+ case 3:
+ NPCPhrase (MUST_FIGHT_YOU_URQUAN_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SHOFIXTI_STACK4, NumVisits);
+ }
+}
+
+static void
+GiveMaidens (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, important_duty))
+ {
+ NPCPhrase (WHAT_DUTY);
+
+ Response (procreating_wildly, GiveMaidens);
+ Response (replenishing_your_species, GiveMaidens);
+ Response (hope_you_have, GiveMaidens);
+ }
+ else
+ {
+ NPCPhrase (SOUNDS_GREAT_BUT_HOW);
+
+ Response (females, ExitConversation);
+ Response (nubiles, ExitConversation);
+ Response (rat_babes, ExitConversation);
+ }
+}
+
+static void
+ConsoleShofixti (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, dont_do_it))
+ {
+ NPCPhrase (YES_I_DO_IT);
+ DISABLE_PHRASE (dont_do_it);
+ }
+ else
+ NPCPhrase (VERY_SAD_KILL_SELF);
+
+ if (GET_GAME_STATE (MAIDENS_ON_SHIP))
+ {
+ Response (important_duty, GiveMaidens);
+ }
+ if (PHRASE_ENABLED (dont_do_it))
+ {
+ Response (dont_do_it, ConsoleShofixti);
+ }
+ Response (need_you_for_duty, ExitConversation);
+ Response (go_ahead, ExitConversation);
+}
+
+static void
+ExplainDefeat (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, i_am_nice))
+ NPCPhrase (MUST_UNDERSTAND);
+ else if (PLAYER_SAID (R, i_am_guy))
+ NPCPhrase (NICE_BUT_WHAT_IS_DONKEY);
+ else /* if (PLAYER_SAID (R, i_am_captain0)) */
+ NPCPhrase (SO_SORRY);
+ NPCPhrase (IS_DEFEAT_TRUE);
+
+ Response (yes_and_no, ConsoleShofixti);
+ Response (clobbered, ConsoleShofixti);
+ Response (butt_blasted, ConsoleShofixti);
+}
+
+static void
+RealizeMistake (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ NPCPhrase (DGRUNTI);
+ SET_GAME_STATE (SHOFIXTI_STACK1, 0);
+ SET_GAME_STATE (SHOFIXTI_STACK3, 0);
+ SET_GAME_STATE (SHOFIXTI_STACK2, 3);
+
+ {
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ i_am_captain0,
+ GLOBAL_SIS (CommanderName),
+ i_am_captain1,
+ buf,
+ i_am_captain2,
+ GLOBAL_SIS (ShipName),
+ i_am_captain3,
+ (UNICODE*)NULL);
+ }
+ DoResponsePhrase (i_am_captain0, ExplainDefeat, shared_phrase_buf);
+ Response (i_am_nice, ExplainDefeat);
+ Response (i_am_guy, ExplainDefeat);
+}
+
+static void
+Hostile (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ switch (GET_GAME_STATE (SHOFIXTI_STACK1))
+ {
+ case 0:
+ Response (dont_attack, ExitConversation);
+ break;
+ case 1:
+ Response (hey_stop, ExitConversation);
+ break;
+ case 2:
+ Response (look_you_are, ExitConversation);
+ break;
+ }
+ switch (GET_GAME_STATE (SHOFIXTI_STACK2))
+ {
+ case 0:
+ Response (no_one_insults, ExitConversation);
+ break;
+ case 1:
+ Response (mighty_words, ExitConversation);
+ break;
+ case 2:
+ Response (donkey_breath, RealizeMistake);
+ break;
+ }
+ switch (GET_GAME_STATE (SHOFIXTI_STACK3))
+ {
+ case 0:
+ Response (dont_know, ExitConversation);
+ break;
+ case 1:
+ {
+ construct_response (
+ shared_phrase_buf,
+ look0,
+ "",
+ shofixti_name,
+ "",
+ look1,
+ (UNICODE*)NULL);
+ DoResponsePhrase (look0, ExitConversation, shared_phrase_buf);
+ break;
+ }
+ case 2:
+ Response (look_you_are, ExitConversation);
+ break;
+ }
+ Response (dont_want_to_fight, ExitConversation);
+}
+
+static void
+Friendly (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ struct
+ {
+ RESPONSE_REF pStr;
+ UNICODE *c_buf;
+ } Resp[3];
+ static UNICODE buf0[80], buf1[80];
+
+ LastStack = 0;
+ memset (Resp, 0, sizeof (Resp));
+ if (PLAYER_SAID (R, report0))
+ {
+ NPCPhrase (NOTHING_NEW);
+
+ DISABLE_PHRASE (report0);
+ }
+ else if (PLAYER_SAID (R, why_here0))
+ {
+ NPCPhrase (I_GUARD);
+
+ LastStack = 1;
+ SET_GAME_STATE (SHOFIXTI_STACK1, 1);
+ }
+ else if (PLAYER_SAID (R, what_happened))
+ {
+ NPCPhrase (MET_VUX);
+
+ LastStack = 1;
+ SET_GAME_STATE (SHOFIXTI_STACK1, 2);
+ }
+ else if (PLAYER_SAID (R, glory_device))
+ {
+ NPCPhrase (SWITCH_BROKE);
+
+ SET_GAME_STATE (SHOFIXTI_STACK1, 3);
+ }
+ else if (PLAYER_SAID (R, where_world))
+ {
+ NPCPhrase (BLEW_IT_UP);
+
+ LastStack = 2;
+ SET_GAME_STATE (SHOFIXTI_STACK3, 1);
+ }
+ else if (PLAYER_SAID (R, how_survive))
+ {
+ NPCPhrase (NOT_HERE);
+
+ SET_GAME_STATE (SHOFIXTI_STACK3, 2);
+ }
+
+ if (PHRASE_ENABLED (report0))
+ {
+ construct_response (
+ buf0,
+ report0,
+ "",
+ shofixti_name,
+ "",
+ report1,
+ (UNICODE*)NULL);
+ Resp[0].pStr = report0;
+ Resp[0].c_buf = buf0;
+ }
+
+ switch (GET_GAME_STATE (SHOFIXTI_STACK1))
+ {
+ case 0:
+ construct_response (
+ buf1,
+ why_here0,
+ "",
+ shofixti_name,
+ "",
+ why_here1,
+ (UNICODE*)NULL);
+ Resp[1].pStr = why_here0;
+ Resp[1].c_buf = buf1;
+ break;
+ case 1:
+ Resp[1].pStr = what_happened;
+ break;
+ case 2:
+ Resp[1].pStr = glory_device;
+ break;
+ }
+
+ switch (GET_GAME_STATE (SHOFIXTI_STACK3))
+ {
+ case 0:
+ Resp[2].pStr = where_world;
+ break;
+ case 1:
+ Resp[2].pStr = how_survive;
+ break;
+ }
+
+ if (Resp[LastStack].pStr)
+ DoResponsePhrase (Resp[LastStack].pStr, Friendly, Resp[LastStack].c_buf);
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && Resp[i].pStr)
+ DoResponsePhrase (Resp[i].pStr, Friendly, Resp[i].c_buf);
+ }
+ if (GET_GAME_STATE (MAIDENS_ON_SHIP))
+ {
+ Response (important_duty, GiveMaidens);
+ }
+
+ construct_response (
+ shared_phrase_buf,
+ bye0,
+ "",
+ shofixti_name,
+ "",
+ bye1,
+ (UNICODE*)NULL);
+ DoResponsePhrase (bye0, ExitConversation, shared_phrase_buf);
+}
+
+static void
+Intro (void)
+{
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ GetShofixtiName ();
+
+ if (GET_GAME_STATE (SHOFIXTI_STACK2) > 2)
+ {
+ NPCPhrase (FRIENDLY_HELLO);
+
+ Friendly ((RESPONSE_REF)0);
+ }
+ else
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SHOFIXTI_VISITS);
+ if (GET_GAME_STATE (SHOFIXTI_KIA))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_KATANA_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_KATANA_2);
+ break;
+ case 2:
+ NPCPhrase (HOSTILE_KATANA_3);
+ break;
+ case 3:
+ NPCPhrase (HOSTILE_KATANA_4);
+ --NumVisits;
+ break;
+ }
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_TANAKA_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_TANAKA_2);
+ break;
+ case 2:
+ NPCPhrase (HOSTILE_TANAKA_3);
+ break;
+ case 3:
+ NPCPhrase (HOSTILE_TANAKA_4);
+ break;
+ case 4:
+ NPCPhrase (HOSTILE_TANAKA_5);
+ break;
+ case 5:
+ NPCPhrase (HOSTILE_TANAKA_6);
+ break;
+ case 6:
+ NPCPhrase (HOSTILE_TANAKA_7);
+ break;
+ case 7:
+ NPCPhrase (HOSTILE_TANAKA_8);
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (SHOFIXTI_VISITS, NumVisits);
+
+ Hostile ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_shofixti (void)
+{
+ return(0);
+}
+
+static void
+post_shofixti_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_shofixti_comm (void)
+{
+ LOCDATA *retval;
+
+ shofixti_desc.init_encounter_func = Intro;
+ shofixti_desc.post_encounter_func = post_shofixti_enc;
+ shofixti_desc.uninit_encounter_func = uninit_shofixti;
+
+ shofixti_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ shofixti_desc.AlienTextBaseline.y = 0;
+ shofixti_desc.AlienTextWidth = SIS_TEXT_WIDTH;
+
+ setSegue (Segue_peace);
+
+ retval = &shofixti_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/shofixt/strings.h b/src/uqm/comm/shofixt/strings.h
new file mode 100644
index 0000000..b4f165b
--- /dev/null
+++ b/src/uqm/comm/shofixt/strings.h
@@ -0,0 +1,122 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SHOFIXT_STRINGS_H
+#define SHOFIXT_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ tanaka,
+ katana,
+ HOSTILE_KATANA_1,
+ HOSTILE_KATANA_2,
+ HOSTILE_KATANA_3,
+ HOSTILE_KATANA_4,
+ HOSTILE_TANAKA_1,
+ HOSTILE_TANAKA_2,
+ HOSTILE_TANAKA_3,
+ HOSTILE_TANAKA_4,
+ HOSTILE_TANAKA_5,
+ HOSTILE_TANAKA_6,
+ HOSTILE_TANAKA_7,
+ HOSTILE_TANAKA_8,
+ dont_attack,
+ TYPICAL_PLOY,
+ hey_stop,
+ ONLY_STOP,
+ look_you_are,
+ TOO_BAD,
+ dont_know,
+ NEVER,
+ look0,
+ look1,
+ FOR_YOU,
+ no_bloodshed,
+ YES_BLOODSHED,
+ dont_want_to_fight,
+ MUST_FIGHT_YOU_URQUAN_1,
+ MUST_FIGHT_YOU_URQUAN_2,
+ MUST_FIGHT_YOU_URQUAN_3,
+ MUST_FIGHT_YOU_URQUAN_4,
+ no_one_insults,
+ YOU_LIMP,
+ mighty_words,
+ HANG_YOUR,
+ donkey_breath,
+ DGRUNTI,
+ i_am_captain0,
+ i_am_captain1,
+ i_am_captain2,
+ i_am_captain3,
+ i_am_nice,
+ i_am_guy,
+ SO_SORRY,
+ MUST_UNDERSTAND,
+ NICE_BUT_WHAT_IS_DONKEY,
+ IS_DEFEAT_TRUE,
+ yes_and_no,
+ butt_blasted,
+ clobbered,
+ VERY_SAD_KILL_SELF,
+ important_duty,
+ WHAT_DUTY,
+ need_you_for_duty,
+ OK_WILL_BE_SENTRY,
+ dont_do_it,
+ YES_I_DO_IT,
+ go_ahead,
+ ON_SECOND_THOUGHT,
+ procreating_wildly,
+ replenishing_your_species,
+ hope_you_have,
+ SOUNDS_GREAT_BUT_HOW,
+ females,
+ nubiles,
+ rat_babes,
+ LEAPING_HAPPINESS,
+ bye0,
+ bye1,
+ GOODBYE0,
+ GOODBYE1,
+ why_here0,
+ why_here1,
+ I_GUARD,
+ where_world,
+ BLEW_IT_UP,
+ how_survive,
+ NOT_HERE,
+ what_happened,
+ MET_VUX,
+ glory_device,
+ SWITCH_BROKE,
+ bye,
+ GOODBYE,
+ FRIENDLY_HELLO,
+ report0,
+ report1,
+ NOTHING_NEW,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/slyhome/Makeinfo b/src/uqm/comm/slyhome/Makeinfo
new file mode 100644
index 0000000..a09471e
--- /dev/null
+++ b/src/uqm/comm/slyhome/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="slyhome.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/slyhome/resinst.h b/src/uqm/comm/slyhome/resinst.h
new file mode 100644
index 0000000..1bbf162
--- /dev/null
+++ b/src/uqm/comm/slyhome/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SLYLANDRO_COLOR_MAP "comm.slylandro.colortable"
+#define SLYLANDRO_CONVERSATION_PHRASES "comm.slylandro.dialogue"
+#define SLYLANDRO_FONT "comm.slylandro.font"
+#define SLYLANDRO_MUSIC "comm.slylandro.music"
+#define SLYLANDRO_PMAP_ANIM "comm.slylandro.graphics"
diff --git a/src/uqm/comm/slyhome/slyhome.c b/src/uqm/comm/slyhome/slyhome.c
new file mode 100644
index 0000000..80cb839
--- /dev/null
+++ b/src/uqm/comm/slyhome/slyhome.c
@@ -0,0 +1,921 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/gameev.h"
+
+
+static LOCDATA slylandro_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ SLYLANDRO_PMAP_ANIM, /* AlienFrame */
+ SLYLANDRO_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ SLYLANDRO_COLOR_MAP, /* AlienColorMap */
+ SLYLANDRO_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ SLYLANDRO_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 13, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 0, /* StartIndex */
+ 5, /* NumFrames */
+ RANDOM_ANIM | COLORXFORM_ANIM, /* AnimFlags */
+ ONE_SECOND / 8, ONE_SECOND * 5 / 8, /* FrameRate */
+ ONE_SECOND / 8, ONE_SECOND * 5 / 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 1, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 6, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 11, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 16, /* StartIndex */
+ 6, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 15, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 30, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 8) | (1 << 9), /* BlockMask */
+ },
+ {
+ 39, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 43, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 6), /* BlockMask */
+ },
+ {
+ 48, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 6), /* BlockMask */
+ },
+ {
+ 54, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 12), /* BlockMask */
+ },
+ {
+ 60, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 12), /* BlockMask */
+ },
+ {
+ 67, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 10) | (1 << 11), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ setSegue (Segue_peace);
+
+ switch (GET_GAME_STATE (SLYLANDRO_HOME_VISITS))
+ {
+ case 1:
+ NPCPhrase (GOODBYE_1);
+ break;
+ default:
+ NPCPhrase (GOODBYE_2);
+ break;
+ }
+}
+
+static void HomeWorld (RESPONSE_REF R);
+
+static void
+HumanInfo (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ if (PLAYER_SAID (R, happy_to_tell_more))
+ {
+ NPCPhrase (TELL_MORE);
+
+ SET_GAME_STATE (SLYLANDRO_STACK4, 1);
+ }
+ else if (PLAYER_SAID (R, would_you_like_to_know_more))
+ {
+ NPCPhrase (YES_TELL_MORE);
+ }
+ else if (PLAYER_SAID (R, we_come_from_earth))
+ {
+ NPCPhrase (OK_EARTH);
+
+ SET_GAME_STATE (SLYLANDRO_KNOW_EARTH, 1);
+ }
+ else if (PLAYER_SAID (R, we_explore))
+ {
+ NPCPhrase (OK_EXPLORE);
+
+ SET_GAME_STATE (SLYLANDRO_KNOW_EXPLORE, 1);
+ }
+ else if (PLAYER_SAID (R, we_fight_urquan))
+ {
+ NPCPhrase (URQUAN_NICE_GUYS);
+
+ SET_GAME_STATE (SLYLANDRO_KNOW_URQUAN, 1);
+ }
+ else if (PLAYER_SAID (R, not_same_urquan))
+ {
+ NPCPhrase (PERSONALITY_CHANGE);
+
+ SET_GAME_STATE (SLYLANDRO_KNOW_URQUAN, 2);
+ }
+ else if (PLAYER_SAID (R, we_gather))
+ {
+ NPCPhrase (MAYBE_INTERESTED);
+
+ SET_GAME_STATE (SLYLANDRO_KNOW_GATHER, 1);
+ }
+
+ InfoLeft = FALSE;
+ if (GET_GAME_STATE (SLYLANDRO_KNOW_URQUAN) == 1)
+ {
+ InfoLeft = TRUE;
+ Response (not_same_urquan, HumanInfo);
+ }
+ if (!GET_GAME_STATE (SLYLANDRO_KNOW_EARTH))
+ {
+ InfoLeft = TRUE;
+ Response (we_come_from_earth, HumanInfo);
+ }
+ if (!GET_GAME_STATE (SLYLANDRO_KNOW_EXPLORE))
+ {
+ InfoLeft = TRUE;
+ Response (we_explore, HumanInfo);
+ }
+ if (!GET_GAME_STATE (SLYLANDRO_KNOW_URQUAN))
+ {
+ InfoLeft = TRUE;
+ Response (we_fight_urquan, HumanInfo);
+ }
+ if (!GET_GAME_STATE (SLYLANDRO_KNOW_GATHER))
+ {
+ InfoLeft = TRUE;
+ Response (we_gather, HumanInfo);
+ }
+
+ Response (enough_about_me, HomeWorld);
+ if (!InfoLeft)
+ {
+ SET_GAME_STATE (SLYLANDRO_STACK4, 2);
+ }
+}
+
+static void
+SlylandroInfo (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ if (PLAYER_SAID (R, like_more_about_you))
+ {
+ NPCPhrase (SURE_KNOW_WHAT);
+ }
+ else if (PLAYER_SAID (R, what_about_home))
+ {
+ NPCPhrase (ABOUT_HOME);
+
+ DISABLE_PHRASE (what_about_home);
+ }
+ else if (PLAYER_SAID (R, what_about_culture))
+ {
+ NPCPhrase (ABOUT_CULTURE);
+
+ DISABLE_PHRASE (what_about_culture);
+ }
+ else if (PLAYER_SAID (R, what_about_history))
+ {
+ NPCPhrase (ABOUT_HISTORY);
+
+ DISABLE_PHRASE (what_about_history);
+ }
+ else if (PLAYER_SAID (R, what_about_biology))
+ {
+ NPCPhrase (ABOUT_BIOLOGY);
+
+ DISABLE_PHRASE (what_about_biology);
+ }
+
+ InfoLeft = FALSE;
+ if (PHRASE_ENABLED (what_about_home))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_home, SlylandroInfo);
+ }
+ if (PHRASE_ENABLED (what_about_culture))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_culture, SlylandroInfo);
+ }
+ if (PHRASE_ENABLED (what_about_history))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_history, SlylandroInfo);
+ }
+ if (PHRASE_ENABLED (what_about_biology))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_biology, SlylandroInfo);
+ }
+
+ Response (enough_info, HomeWorld);
+ if (!InfoLeft)
+ {
+ DISABLE_PHRASE (like_more_about_you);
+ }
+}
+
+static void
+FixBug (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, think_about_rep_priorities))
+ NPCPhrase (UH_OH);
+ else if (PLAYER_SAID (R, hunt_them_down))
+ {
+ NPCPhrase (GROW_TOO_FAST);
+
+ DISABLE_PHRASE (hunt_them_down);
+ }
+ else if (PLAYER_SAID (R, sue_melnorme))
+ {
+ NPCPhrase (SIGNED_WAIVER);
+
+ DISABLE_PHRASE (sue_melnorme);
+ }
+ else if (PLAYER_SAID (R, recall_signal))
+ {
+ NPCPhrase (NOT_THIS_MODEL);
+
+ DISABLE_PHRASE (recall_signal);
+ }
+
+ if (PHRASE_ENABLED (hunt_them_down))
+ Response (hunt_them_down, FixBug);
+ if (PHRASE_ENABLED (sue_melnorme))
+ Response (sue_melnorme, FixBug);
+ if (PHRASE_ENABLED (recall_signal))
+ Response (recall_signal, FixBug);
+ Response (mega_self_destruct, HomeWorld);
+}
+
+static void
+ProbeBug (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, probe_has_bug))
+ NPCPhrase (NO_IT_DOESNT);
+ else if (PLAYER_SAID (R, tell_me_about_rep_2))
+ {
+ NPCPhrase (REP_NO_PROBLEM);
+
+ DISABLE_PHRASE (tell_me_about_rep_2);
+ }
+ else if (PLAYER_SAID (R, what_about_rep_priorities))
+ {
+ NPCPhrase (MAXIMUM_SO_WHAT);
+
+ DISABLE_PHRASE (what_about_rep_priorities);
+ }
+ else if (PLAYER_SAID (R, tell_me_about_attack))
+ {
+ NPCPhrase (ATTACK_NO_PROBLEM);
+
+ DISABLE_PHRASE (tell_me_about_attack);
+ }
+
+ if (PHRASE_ENABLED (tell_me_about_rep_2))
+ Response (tell_me_about_rep_2, ProbeBug);
+ else if (PHRASE_ENABLED (what_about_rep_priorities))
+ Response (what_about_rep_priorities, ProbeBug);
+ else
+ {
+ Response (think_about_rep_priorities, FixBug);
+ }
+ if (PHRASE_ENABLED (tell_me_about_attack))
+ Response (tell_me_about_attack, ProbeBug);
+}
+
+static void ProbeInfo (RESPONSE_REF R);
+
+static void
+ProbeFunction (RESPONSE_REF R)
+{
+ BYTE LastStack;
+ RESPONSE_REF pStr[2];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = 0;
+ if (PLAYER_SAID (R, talk_more_probe_attack))
+ {
+ NPCPhrase (NO_PROBLEM_BUT_SURE);
+ }
+ else if (PLAYER_SAID (R, tell_me_about_basics))
+ {
+ NPCPhrase (BASIC_COMMANDS);
+
+ SET_GAME_STATE (PLAYER_KNOWS_PROGRAM, 1);
+ DISABLE_PHRASE (tell_basics_again);
+ }
+ else if (PLAYER_SAID (R, tell_basics_again))
+ {
+ NPCPhrase (OK_BASICS_AGAIN);
+
+ DISABLE_PHRASE (tell_basics_again);
+ }
+ else if (PLAYER_SAID (R, what_effect))
+ {
+ NPCPhrase (AFFECTS_BEHAVIOR);
+
+ SET_GAME_STATE (PLAYER_KNOWS_EFFECTS, 1);
+ DISABLE_PHRASE (what_effect);
+ }
+ else if (PLAYER_SAID (R, tell_me_about_rep_1))
+ {
+ NPCPhrase (ABOUT_REP);
+
+ LastStack = 2;
+ SET_GAME_STATE (SLYLANDRO_STACK8, 3);
+ }
+ else if (PLAYER_SAID (R, what_set_priority))
+ {
+ NPCPhrase (MAXIMUM);
+
+ SET_GAME_STATE (PLAYER_KNOWS_PRIORITY, 1);
+ DISABLE_PHRASE (what_set_priority);
+ }
+ else if (PLAYER_SAID (R, how_does_probe_defend))
+ {
+ NPCPhrase (ONLY_SELF_DEFENSE);
+
+ LastStack = 1;
+ SET_GAME_STATE (SLYLANDRO_STACK9, 1);
+ }
+ else if (PLAYER_SAID (R, combat_behavior))
+ {
+ NPCPhrase (MISSILE_BATTERIES);
+
+ LastStack = 1;
+ SET_GAME_STATE (SLYLANDRO_STACK9, 2);
+ }
+ else if (PLAYER_SAID (R, what_missile_batteries))
+ {
+ NPCPhrase (LIGHTNING_ONLY_FOR_HARVESTING);
+
+ SET_GAME_STATE (SLYLANDRO_STACK9, 3);
+ }
+
+ switch (GET_GAME_STATE (SLYLANDRO_STACK9))
+ {
+ case 0:
+ pStr[0] = how_does_probe_defend;
+ break;
+ case 1:
+ pStr[0] = combat_behavior;
+ break;
+ case 2:
+ pStr[0] = what_missile_batteries;
+ break;
+ }
+ switch (GET_GAME_STATE (SLYLANDRO_STACK8))
+ {
+ case 2:
+ pStr[1] = tell_me_about_rep_1;
+ break;
+ case 3:
+ if (PHRASE_ENABLED (what_set_priority))
+ pStr[1] = what_set_priority;
+ break;
+ }
+
+ if (LastStack && pStr[LastStack - 1])
+ Response (pStr[LastStack - 1], ProbeFunction);
+ if (!GET_GAME_STATE (PLAYER_KNOWS_PROGRAM))
+ Response (tell_me_about_basics, ProbeFunction);
+ else
+ {
+ if (GET_GAME_STATE (PLAYER_KNOWS_PRIORITY))
+ {
+ if (GET_GAME_STATE (PLAYER_KNOWS_EFFECTS))
+ {
+ Response (probe_has_bug, ProbeBug);
+ }
+ if (PHRASE_ENABLED (what_effect))
+ Response (what_effect, ProbeFunction);
+ }
+ if (PHRASE_ENABLED (tell_basics_again))
+ Response (tell_basics_again, ProbeFunction);
+ }
+ if (LastStack == 0)
+ {
+ do
+ {
+ if (pStr[LastStack])
+ Response (pStr[LastStack], ProbeFunction);
+ } while (++LastStack < 2);
+ }
+ else
+ {
+ LastStack = (LastStack - 1) ^ 1;
+ if (pStr[LastStack])
+ Response (pStr[LastStack], ProbeFunction);
+ }
+
+ Response (enough_problem, ProbeInfo);
+}
+
+static void
+ProbeInfo (RESPONSE_REF R)
+{
+ BYTE i, LastStack, InfoLeft;
+ RESPONSE_REF pStr[3];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = 0;
+ if (PLAYER_SAID (R, what_are_probes))
+ {
+ NPCPhrase (PROBES_ARE);
+
+ SET_GAME_STATE (SLYLANDRO_STACK5, 1);
+ }
+ else if (PLAYER_SAID (R, know_more_probe))
+ NPCPhrase (OK_WHAT_MORE_PROBE);
+ else if (PLAYER_SAID (R, why_probe_always_attack))
+ {
+ NPCPhrase (ONLY_DEFEND);
+
+ SET_GAME_STATE (SLYLANDRO_STACK6, 1);
+ }
+ else if (PLAYER_SAID (R, talk_more_probe_attack))
+ {
+ ProbeFunction (R);
+ return;
+ }
+ else if (PLAYER_SAID (R, where_probes_from))
+ {
+ NPCPhrase (PROBES_FROM_MELNORME);
+
+ LastStack = 1;
+ SET_GAME_STATE (SLYLANDRO_STACK7, 1);
+ }
+ else if (PLAYER_SAID (R, why_sell))
+ {
+ NPCPhrase (SELL_FOR_INFO);
+
+ LastStack = 1;
+ SET_GAME_STATE (SLYLANDRO_STACK7, 2);
+ }
+ else if (PLAYER_SAID (R, how_long_ago))
+ {
+ NPCPhrase (FIFTY_THOUSAND_ROTATIONS);
+
+ SET_GAME_STATE (SLYLANDRO_STACK7, 3);
+ }
+ else if (PLAYER_SAID (R, whats_probes_mission))
+ {
+ NPCPhrase (SEEK_OUT_NEW_LIFE);
+
+ LastStack = 2;
+ SET_GAME_STATE (SLYLANDRO_STACK8, 1);
+ }
+ else if (PLAYER_SAID (R, if_only_one))
+ {
+ NPCPhrase (THEY_REPLICATE);
+
+ SET_GAME_STATE (SLYLANDRO_STACK8, 2);
+ }
+ else if (PLAYER_SAID (R, enough_problem))
+ NPCPhrase (OK_ENOUGH_PROBLEM);
+
+ if (!GET_GAME_STATE (SLYLANDRO_KNOW_BROKEN)
+ && GET_GAME_STATE (PROBE_EXHIBITED_BUG))
+ {
+ switch (GET_GAME_STATE (SLYLANDRO_STACK6))
+ {
+ case 0:
+ pStr[0] = why_probe_always_attack;
+ break;
+ case 1:
+ pStr[0] = talk_more_probe_attack;
+ break;
+ }
+ }
+ switch (GET_GAME_STATE (SLYLANDRO_STACK7))
+ {
+ case 0:
+ pStr[1] = where_probes_from;
+ break;
+ case 1:
+ pStr[1] = why_sell;
+ break;
+ case 2:
+ pStr[1] = how_long_ago;
+ break;
+ }
+ switch (GET_GAME_STATE (SLYLANDRO_STACK8))
+ {
+ case 0:
+ pStr[2] = whats_probes_mission;
+ break;
+ case 1:
+ pStr[2] = if_only_one;
+ break;
+ }
+
+ InfoLeft = FALSE;
+ if (pStr[LastStack])
+ {
+ InfoLeft = TRUE;
+ Response (pStr[LastStack], ProbeInfo);
+ }
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ InfoLeft = TRUE;
+ Response (pStr[i], ProbeInfo);
+ }
+ }
+
+ Response (enough_probe, HomeWorld);
+ if (!InfoLeft)
+ {
+ DISABLE_PHRASE (know_more_probe);
+ }
+}
+
+static void
+HomeWorld (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[3];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = 0;
+ if (PLAYER_SAID (R, we_are_us0))
+ {
+ NPCPhrase (TERRIBLY_EXCITING);
+
+ SET_GAME_STATE (SLYLANDRO_STACK1, 1);
+ DISABLE_PHRASE (we_are_us0);
+ }
+ else if (PLAYER_SAID (R, what_other_visitors))
+ {
+ NPCPhrase (VISITORS);
+
+ SET_GAME_STATE (PLAYER_KNOWS_PROBE, 1);
+ SET_GAME_STATE (SLYLANDRO_STACK1, 2);
+ }
+ else if (PLAYER_SAID (R, any_other_visitors))
+ {
+ NPCPhrase (LONG_AGO);
+
+ SET_GAME_STATE (SLYLANDRO_STACK1, 3);
+ }
+ else if (PLAYER_SAID (R, what_about_sentient_milieu))
+ {
+ NPCPhrase (MET_TAALO_THEY_ARE_FROM);
+
+ SET_GAME_STATE (SLYLANDRO_STACK1, 4);
+ }
+ else if (PLAYER_SAID (R, who_else))
+ {
+ NPCPhrase (PRECURSORS);
+
+ SET_GAME_STATE (SLYLANDRO_STACK1, 5);
+ }
+ else if (PLAYER_SAID (R, precursors_yow))
+ {
+ NPCPhrase (ABOUT_PRECURSORS);
+
+ SET_GAME_STATE (SLYLANDRO_STACK1, 6);
+ }
+ else if (PLAYER_SAID (R, must_know_more))
+ {
+ NPCPhrase (ALL_WE_KNOW);
+
+ SET_GAME_STATE (SLYLANDRO_STACK1, 7);
+ }
+ else if (PLAYER_SAID (R, who_are_you))
+ {
+ NPCPhrase (WE_ARE_SLY);
+
+ LastStack = 1;
+ SET_GAME_STATE (SLYLANDRO_STACK2, 1);
+ }
+ else if (PLAYER_SAID (R, where_are_you))
+ {
+ NPCPhrase (DOWN_HERE);
+
+ LastStack = 2;
+ SET_GAME_STATE (SLYLANDRO_STACK3, 1);
+ }
+ else if (PLAYER_SAID (R, thats_impossible_1))
+ {
+ NPCPhrase (NO_ITS_NOT_1);
+
+ LastStack = 2;
+ SET_GAME_STATE (SLYLANDRO_STACK3, 2);
+ }
+ else if (PLAYER_SAID (R, thats_impossible_2))
+ {
+ NPCPhrase (NO_ITS_NOT_2);
+
+ LastStack = 2;
+ SET_GAME_STATE (SLYLANDRO_STACK3, 3);
+ }
+ else if (PLAYER_SAID (R, like_more_about_you))
+ {
+ SlylandroInfo (R);
+ return;
+ }
+ else if (PLAYER_SAID (R, enough_about_me))
+ NPCPhrase (OK_ENOUGH_YOU);
+ else if (PLAYER_SAID (R, enough_info))
+ NPCPhrase (OK_ENOUGH_INFO);
+ else if (PLAYER_SAID (R, enough_probe))
+ NPCPhrase (OK_ENOUGH_PROBE);
+ else if (PLAYER_SAID (R, mega_self_destruct))
+ {
+ NPCPhrase (WHY_YES_THERE_IS);
+
+ SET_GAME_STATE (SLYLANDRO_KNOW_BROKEN, 1);
+ SET_GAME_STATE (DESTRUCT_CODE_ON_SHIP, 1);
+ i = GET_GAME_STATE (SLYLANDRO_MULTIPLIER) + 1;
+ SET_GAME_STATE (SLYLANDRO_MULTIPLIER, i);
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, SLYLANDRO_RAMP_DOWN);
+ }
+
+ switch (GET_GAME_STATE (SLYLANDRO_STACK1))
+ {
+ case 0:
+ construct_response (shared_phrase_buf,
+ we_are_us0,
+ GLOBAL_SIS (CommanderName),
+ we_are_us1,
+ GLOBAL_SIS (ShipName),
+ we_are_us2,
+ (UNICODE*)NULL);
+ pStr[0] = we_are_us0;
+ break;
+ case 1:
+ pStr[0] = what_other_visitors;
+ break;
+ case 2:
+ pStr[0] = any_other_visitors;
+ break;
+ case 3:
+ pStr[0] = what_about_sentient_milieu;
+ break;
+ case 4:
+ pStr[0] = who_else;
+ break;
+ case 5:
+ pStr[0] = precursors_yow;
+ break;
+ case 6:
+ pStr[0] = must_know_more;
+ break;
+ }
+ switch (GET_GAME_STATE (SLYLANDRO_STACK2))
+ {
+ case 0:
+ pStr[1] = who_are_you;
+ break;
+ case 1:
+ if (PHRASE_ENABLED (like_more_about_you))
+ pStr[1] = like_more_about_you;
+ break;
+ }
+ switch (GET_GAME_STATE (SLYLANDRO_STACK3))
+ {
+ case 0:
+ pStr[2] = where_are_you;
+ break;
+ case 1:
+ pStr[2] = thats_impossible_1;
+ break;
+ case 2:
+ pStr[2] = thats_impossible_2;
+ break;
+ }
+
+ if (pStr[LastStack])
+ {
+ if (pStr[LastStack] != we_are_us0)
+ Response (pStr[LastStack], HomeWorld);
+ else
+ DoResponsePhrase (pStr[LastStack], HomeWorld, shared_phrase_buf);
+ }
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ if (pStr[i] != we_are_us0)
+ Response (pStr[i], HomeWorld);
+ else
+ DoResponsePhrase (pStr[i], HomeWorld, shared_phrase_buf);
+ }
+ }
+ if (GET_GAME_STATE (SLYLANDRO_STACK1))
+ {
+ switch (GET_GAME_STATE (SLYLANDRO_STACK4))
+ {
+ case 0:
+ Response (happy_to_tell_more, HumanInfo);
+ break;
+ case 1:
+ Response (would_you_like_to_know_more, HumanInfo);
+ break;
+ }
+ }
+ if (GET_GAME_STATE (PLAYER_KNOWS_PROBE)
+ && !GET_GAME_STATE (SLYLANDRO_KNOW_BROKEN))
+ {
+ switch (GET_GAME_STATE (SLYLANDRO_STACK5))
+ {
+ case 0:
+ Response (what_are_probes, ProbeInfo);
+ break;
+ case 1:
+ if (PHRASE_ENABLED (know_more_probe))
+ Response (know_more_probe, ProbeInfo);
+ break;
+ }
+ }
+ Response (bye, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (GET_GAME_STATE (SLYLANDRO_KNOW_BROKEN)
+ && (NumVisits = GET_GAME_STATE (RECALL_VISITS)) == 0)
+ {
+ NPCPhrase (RECALL_PROGRAM_1);
+ ++NumVisits;
+ SET_GAME_STATE (RECALL_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (SLYLANDRO_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SLYLANDRO_HOME_VISITS, NumVisits);
+ }
+
+ HomeWorld ((RESPONSE_REF)0);
+}
+
+static COUNT
+uninit_slylandro (void)
+{
+ return (0);
+}
+
+static void
+post_slylandro_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_slylandro_comm (void)
+{
+ LOCDATA *retval;
+
+ slylandro_desc.init_encounter_func = Intro;
+ slylandro_desc.post_encounter_func = post_slylandro_enc;
+ slylandro_desc.uninit_encounter_func = uninit_slylandro;
+
+ slylandro_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ slylandro_desc.AlienTextBaseline.y = 0;
+ slylandro_desc.AlienTextWidth = SIS_TEXT_WIDTH;
+
+ setSegue (Segue_peace);
+ retval = &slylandro_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/slyhome/strings.h b/src/uqm/comm/slyhome/strings.h
new file mode 100644
index 0000000..bedac27
--- /dev/null
+++ b/src/uqm/comm/slyhome/strings.h
@@ -0,0 +1,143 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SLYHOME_STRINGS_H
+#define SLYHOME_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HELLO_1,
+ HELLO_2,
+ HELLO_3,
+ HELLO_4,
+ RECALL_PROGRAM_1,
+ we_are_us0,
+ we_are_us1,
+ we_are_us2,
+ TERRIBLY_EXCITING,
+ happy_to_tell_more,
+ TELL_MORE,
+ would_you_like_to_know_more,
+ YES_TELL_MORE,
+ we_come_from_earth,
+ OK_EARTH,
+ we_explore,
+ OK_EXPLORE,
+ we_fight_urquan,
+ URQUAN_NICE_GUYS,
+ not_same_urquan,
+ PERSONALITY_CHANGE,
+ we_gather,
+ MAYBE_INTERESTED,
+ enough_about_me,
+ OK_ENOUGH_YOU,
+ what_other_visitors,
+ VISITORS,
+ any_other_visitors,
+ LONG_AGO,
+ what_about_sentient_milieu,
+ MET_TAALO_THEY_ARE_FROM,
+ who_else,
+ PRECURSORS,
+ precursors_yow,
+ ABOUT_PRECURSORS,
+ must_know_more,
+ ALL_WE_KNOW,
+ who_are_you,
+ WE_ARE_SLY,
+ like_more_about_you,
+ SURE_KNOW_WHAT,
+ what_about_home,
+ ABOUT_HOME,
+ what_about_culture,
+ ABOUT_CULTURE,
+ what_about_history,
+ ABOUT_HISTORY,
+ what_about_biology,
+ ABOUT_BIOLOGY,
+ enough_info,
+ OK_ENOUGH_INFO,
+ where_are_you,
+ DOWN_HERE,
+ thats_impossible_1,
+ NO_ITS_NOT_1,
+ thats_impossible_2,
+ NO_ITS_NOT_2,
+ bye,
+ GOODBYE_1,
+ GOODBYE_2,
+ what_are_probes,
+ PROBES_ARE,
+ know_more_probe,
+ OK_WHAT_MORE_PROBE,
+ where_probes_from,
+ PROBES_FROM_MELNORME,
+ why_sell,
+ SELL_FOR_INFO,
+ how_long_ago,
+ FIFTY_THOUSAND_ROTATIONS,
+ whats_probes_mission,
+ SEEK_OUT_NEW_LIFE,
+ if_only_one,
+ THEY_REPLICATE,
+ enough_probe,
+ OK_ENOUGH_PROBE,
+ why_probe_always_attack,
+ ONLY_DEFEND,
+ talk_more_probe_attack,
+ NO_PROBLEM_BUT_SURE,
+ tell_me_about_basics,
+ BASIC_COMMANDS,
+ tell_basics_again,
+ OK_BASICS_AGAIN,
+ what_effect,
+ AFFECTS_BEHAVIOR,
+ how_does_probe_defend,
+ ONLY_SELF_DEFENSE,
+ combat_behavior,
+ MISSILE_BATTERIES,
+ what_missile_batteries,
+ LIGHTNING_ONLY_FOR_HARVESTING,
+ tell_me_about_rep_1,
+ ABOUT_REP,
+ what_set_priority,
+ MAXIMUM,
+ enough_problem,
+ OK_ENOUGH_PROBLEM,
+ probe_has_bug,
+ NO_IT_DOESNT,
+ tell_me_about_attack,
+ ATTACK_NO_PROBLEM,
+ tell_me_about_rep_2,
+ REP_NO_PROBLEM,
+ what_about_rep_priorities,
+ MAXIMUM_SO_WHAT,
+ think_about_rep_priorities,
+ UH_OH,
+ hunt_them_down,
+ GROW_TOO_FAST,
+ sue_melnorme,
+ SIGNED_WAIVER,
+ recall_signal,
+ NOT_THIS_MODEL,
+ mega_self_destruct,
+ WHY_YES_THERE_IS,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/slyland/Makeinfo b/src/uqm/comm/slyland/Makeinfo
new file mode 100644
index 0000000..10d3a53
--- /dev/null
+++ b/src/uqm/comm/slyland/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="slyland.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/slyland/resinst.h b/src/uqm/comm/slyland/resinst.h
new file mode 100644
index 0000000..d0b9a36
--- /dev/null
+++ b/src/uqm/comm/slyland/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SLYLAND_COLOR_MAP "comm.probe.colortable"
+#define SLYLAND_CONVERSATION_PHRASES "comm.probe.dialogue"
+#define SLYLAND_FONT "comm.probe.font"
+#define SLYLAND_MUSIC "comm.probe.music"
+#define SLYLAND_PMAP_ANIM "comm.probe.graphics"
diff --git a/src/uqm/comm/slyland/slyland.c b/src/uqm/comm/slyland/slyland.c
new file mode 100644
index 0000000..ae7cb44
--- /dev/null
+++ b/src/uqm/comm/slyland/slyland.c
@@ -0,0 +1,518 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include <stdlib.h>
+#include "resinst.h"
+#include "strings.h"
+
+#include "options.h"
+#include "uqm/battle.h"
+#include "uqm/setup.h"
+
+
+static const NUMBER_SPEECH_DESC probe_numbers_english;
+
+static LOCDATA slylandro_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ SLYLAND_PMAP_ANIM, /* AlienFrame */
+ SLYLAND_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ SLYLAND_COLOR_MAP, /* AlienColorMap */
+ SLYLAND_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ SLYLAND_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 6, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 1, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 3, /* RestartRate */
+ (1 << 3) /* BlockMask */
+ },
+ {
+ 10, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4) /* BlockMask */
+ },
+ {
+ 18, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5) /* BlockMask */
+ },
+ {
+ 26, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 3, /* RestartRate */
+ (1 << 0) /* BlockMask */
+ },
+ {
+ 34, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 3, /* RestartRate */
+ (1 << 1) /* BlockMask */
+ },
+ {
+ 42, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 3, /* RestartRate */
+ (1 << 2) /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ &probe_numbers_english, /* AlienNumberSpeech - default */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static COUNT probe_digit_names[] =
+{
+ ENUMERATE_ZERO,
+ ENUMERATE_ONE,
+ ENUMERATE_TWO,
+ ENUMERATE_THREE,
+ ENUMERATE_FOUR,
+ ENUMERATE_FIVE,
+ ENUMERATE_SIX,
+ ENUMERATE_SEVEN,
+ ENUMERATE_EIGHT,
+ ENUMERATE_NINE,
+};
+
+static COUNT probe_teen_names[] =
+{
+ ENUMERATE_TEN,
+ ENUMERATE_ELEVEN,
+ ENUMERATE_TWELVE,
+ ENUMERATE_THIRTEEN,
+ ENUMERATE_FOURTEEN,
+ ENUMERATE_FIFTEEN,
+ ENUMERATE_SIXTEEN,
+ ENUMERATE_SEVENTEEN,
+ ENUMERATE_EIGHTEEN,
+ ENUMERATE_NINETEEN,
+};
+
+static COUNT probe_tens_names[] =
+{
+ 0, /* invalid */
+ 0, /* skip digit */
+ ENUMERATE_TWENTY,
+ ENUMERATE_THIRTY,
+ ENUMERATE_FOURTY,
+ ENUMERATE_FIFTY,
+ ENUMERATE_SIXTY,
+ ENUMERATE_SEVENTY,
+ ENUMERATE_EIGHTY,
+ ENUMERATE_NINETY,
+};
+
+static const NUMBER_SPEECH_DESC probe_numbers_english =
+{
+ 5, /* NumDigits */
+ {
+ { /* 1000-999999 */
+ 1000, /* Divider */
+ 0, /* Subtrahend */
+ NULL, /* StrDigits - recurse */
+ NULL, /* Names - not used */
+ ENUMERATE_THOUSAND /* CommonIndex */
+ },
+ { /* 100-999 */
+ 100, /* Divider */
+ 0, /* Subtrahend */
+ probe_digit_names, /* StrDigits */
+ NULL, /* Names - not used */
+ ENUMERATE_HUNDRED /* CommonIndex */
+ },
+ { /* 20-99 */
+ 10, /* Divider */
+ 0, /* Subtrahend */
+ probe_tens_names, /* StrDigits */
+ NULL, /* Names - not used */
+ 0 /* CommonIndex - not used */
+ },
+ { /* 10-19 */
+ 1, /* Divider */
+ 10, /* Subtrahend */
+ probe_teen_names, /* StrDigits */
+ NULL, /* Names - not used */
+ 0 /* CommonIndex - not used */
+ },
+ { /* 0-9 */
+ 1, /* Divider */
+ 0, /* Subtrahend */
+ probe_digit_names, /* StrDigits */
+ NULL, /* Names - not used */
+ 0 /* CommonIndex - not used */
+ }
+ }
+};
+
+static RESPONSE_REF threat,
+ something_wrong,
+ we_are_us,
+ why_attack,
+ bye;
+
+static void
+sayCoord (int coord)
+{
+ int ac;
+
+ ac = abs (coord);
+
+ NPCPhrase_splice (coord < 0 ? COORD_MINUS : COORD_PLUS);
+ NPCNumber (ac / 10, "%03d");
+ NPCPhrase_splice (COORD_POINT);
+ NPCNumber (ac % 10, NULL);
+}
+
+static void
+CombatIsInevitable (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ if (GET_GAME_STATE (DESTRUCT_CODE_ON_SHIP))
+ Response (destruct_code, CombatIsInevitable);
+ switch (GET_GAME_STATE (SLYLANDRO_PROBE_THREAT))
+ {
+ case 0:
+ threat = threat_1;
+ break;
+ case 1:
+ threat = threat_2;
+ break;
+ case 2:
+ threat = threat_3;
+ break;
+ default:
+ threat = threat_4;
+ break;
+ }
+ Response (threat, CombatIsInevitable);
+ switch (GET_GAME_STATE (SLYLANDRO_PROBE_WRONG))
+ {
+ case 0:
+ something_wrong = something_wrong_1;
+ break;
+ case 1:
+ something_wrong = something_wrong_2;
+ break;
+ case 2:
+ something_wrong = something_wrong_3;
+ break;
+ default:
+ something_wrong = something_wrong_4;
+ break;
+ }
+ Response (something_wrong, CombatIsInevitable);
+ switch (GET_GAME_STATE (SLYLANDRO_PROBE_ID))
+ {
+ case 0:
+ we_are_us = we_are_us_1;
+ break;
+ case 1:
+ we_are_us = we_are_us_2;
+ break;
+ case 2:
+ we_are_us = we_are_us_3;
+ break;
+ default:
+ we_are_us = we_are_us_4;
+ break;
+ }
+ Response (we_are_us, CombatIsInevitable);
+ switch (GET_GAME_STATE (SLYLANDRO_PROBE_INFO))
+ {
+ case 0:
+ why_attack = why_attack_1;
+ break;
+ case 1:
+ why_attack = why_attack_2;
+ break;
+ case 2:
+ why_attack = why_attack_3;
+ break;
+ default:
+ why_attack = why_attack_4;
+ break;
+ }
+ Response (why_attack, CombatIsInevitable);
+ switch (GET_GAME_STATE (SLYLANDRO_PROBE_EXIT))
+ {
+ case 0:
+ bye = bye_1;
+ break;
+ case 1:
+ bye = bye_2;
+ break;
+ case 2:
+ bye = bye_3;
+ break;
+ default:
+ bye = bye_4;
+ break;
+ }
+ Response (bye, CombatIsInevitable);
+ }
+ else if (PLAYER_SAID (R, destruct_code))
+ {
+ NPCPhrase (DESTRUCT_SEQUENCE);
+ setSegue (Segue_victory);
+ }
+ else
+ {
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, threat))
+ {
+ NumVisits = GET_GAME_STATE (SLYLANDRO_PROBE_THREAT);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (PROGRAMMED_TO_DEFEND_1);
+ break;
+ case 1:
+ NPCPhrase (PROGRAMMED_TO_DEFEND_2);
+ break;
+ case 2:
+ NPCPhrase (PROGRAMMED_TO_DEFEND_3);
+ break;
+ case 3:
+ NPCPhrase (PROGRAMMED_TO_DEFEND_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SLYLANDRO_PROBE_THREAT, NumVisits);
+ }
+ else if (PLAYER_SAID (R, something_wrong))
+ {
+ NumVisits = GET_GAME_STATE (SLYLANDRO_PROBE_WRONG);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NOMINAL_FUNCTION_1);
+ break;
+ case 1:
+ NPCPhrase (NOMINAL_FUNCTION_2);
+ break;
+ case 2:
+ NPCPhrase (NOMINAL_FUNCTION_3);
+ break;
+ case 3:
+ NPCPhrase (NOMINAL_FUNCTION_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SLYLANDRO_PROBE_WRONG, NumVisits);
+ }
+ else if (PLAYER_SAID (R, we_are_us))
+ {
+ NumVisits = GET_GAME_STATE (SLYLANDRO_PROBE_ID);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (THIS_IS_PROBE_1);
+ break;
+ case 1:
+ NPCPhrase (THIS_IS_PROBE_2);
+ break;
+ case 2:
+ NPCPhrase (THIS_IS_PROBE_3);
+ break;
+ case 3:
+ {
+ SIZE dx, dy;
+
+ // Probe's coordinate system is {-Y,X} relative to B Corvi
+ dx = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x)) - 333;
+ dy = 9812 - LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+
+ NPCPhrase (THIS_IS_PROBE_40);
+ sayCoord (dy);
+ NPCPhrase_splice (THIS_IS_PROBE_41);
+ sayCoord (dx);
+ NPCPhrase (THIS_IS_PROBE_42);
+
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (SLYLANDRO_PROBE_ID, NumVisits);
+ }
+ else if (PLAYER_SAID (R, why_attack))
+ {
+ NumVisits = GET_GAME_STATE (SLYLANDRO_PROBE_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (PEACEFUL_MISSION_1);
+ break;
+ case 1:
+ NPCPhrase (PEACEFUL_MISSION_2);
+ break;
+ case 2:
+ NPCPhrase (PEACEFUL_MISSION_3);
+ break;
+ case 3:
+ NPCPhrase (PEACEFUL_MISSION_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SLYLANDRO_PROBE_INFO, NumVisits);
+ }
+ else if (PLAYER_SAID (R, bye))
+ {
+ NumVisits = GET_GAME_STATE (SLYLANDRO_PROBE_EXIT);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GOODBYE_1);
+ break;
+ case 1:
+ NPCPhrase (GOODBYE_2);
+ break;
+ case 2:
+ NPCPhrase (GOODBYE_3);
+ break;
+ case 3:
+ NPCPhrase (GOODBYE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SLYLANDRO_PROBE_EXIT, NumVisits);
+ }
+
+ NPCPhrase (HOSTILE);
+
+ SET_GAME_STATE (PROBE_EXHIBITED_BUG, 1);
+ setSegue (Segue_hostile);
+ }
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SLYLANDRO_PROBE_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (WE_COME_IN_PEACE_1);
+ break;
+ case 1:
+ NPCPhrase (WE_COME_IN_PEACE_2);
+ break;
+ case 2:
+ NPCPhrase (WE_COME_IN_PEACE_3);
+ break;
+ case 3:
+ NPCPhrase (WE_COME_IN_PEACE_4);
+ break;
+ case 4:
+ NPCPhrase (WE_COME_IN_PEACE_5);
+ break;
+ case 5:
+ NPCPhrase (WE_COME_IN_PEACE_6);
+ break;
+ case 6:
+ NPCPhrase (WE_COME_IN_PEACE_7);
+ break;
+ case 7:
+ NPCPhrase (WE_COME_IN_PEACE_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SLYLANDRO_PROBE_VISITS, NumVisits);
+
+ CombatIsInevitable ((RESPONSE_REF)0);
+}
+
+static COUNT
+uninit_slyland (void)
+{
+ return (0);
+}
+
+static void
+post_slyland_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_slyland_comm (void)
+{
+ LOCDATA *retval;
+
+ slylandro_desc.init_encounter_func = Intro;
+ slylandro_desc.post_encounter_func = post_slyland_enc;
+ slylandro_desc.uninit_encounter_func = uninit_slyland;
+
+ slylandro_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ slylandro_desc.AlienTextBaseline.y = 0;
+ slylandro_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ setSegue (Segue_hostile);
+ retval = &slylandro_desc;
+
+ return (retval);
+}
+
diff --git a/src/uqm/comm/slyland/strings.h b/src/uqm/comm/slyland/strings.h
new file mode 100644
index 0000000..4a1ef17
--- /dev/null
+++ b/src/uqm/comm/slyland/strings.h
@@ -0,0 +1,113 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SLYLAND_STRINGS_H
+#define SLYLAND_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ WE_COME_IN_PEACE_1,
+ WE_COME_IN_PEACE_2,
+ WE_COME_IN_PEACE_3,
+ WE_COME_IN_PEACE_4,
+ WE_COME_IN_PEACE_5,
+ WE_COME_IN_PEACE_6,
+ WE_COME_IN_PEACE_7,
+ WE_COME_IN_PEACE_8,
+ threat_1,
+ threat_2,
+ threat_3,
+ threat_4,
+ PROGRAMMED_TO_DEFEND_1,
+ PROGRAMMED_TO_DEFEND_2,
+ PROGRAMMED_TO_DEFEND_3,
+ PROGRAMMED_TO_DEFEND_4,
+ something_wrong_1,
+ something_wrong_2,
+ something_wrong_3,
+ something_wrong_4,
+ NOMINAL_FUNCTION_1,
+ NOMINAL_FUNCTION_2,
+ NOMINAL_FUNCTION_3,
+ NOMINAL_FUNCTION_4,
+ we_are_us_1,
+ we_are_us_2,
+ we_are_us_3,
+ we_are_us_4,
+ THIS_IS_PROBE_1,
+ THIS_IS_PROBE_2,
+ THIS_IS_PROBE_3,
+ THIS_IS_PROBE_40,
+ THIS_IS_PROBE_41,
+ THIS_IS_PROBE_42,
+ why_attack_1,
+ why_attack_2,
+ why_attack_3,
+ why_attack_4,
+ PEACEFUL_MISSION_1,
+ PEACEFUL_MISSION_2,
+ PEACEFUL_MISSION_3,
+ PEACEFUL_MISSION_4,
+ bye_1,
+ bye_2,
+ bye_3,
+ bye_4,
+ GOODBYE_1,
+ GOODBYE_2,
+ GOODBYE_3,
+ GOODBYE_4,
+ HOSTILE,
+ DESTRUCT_SEQUENCE,
+ destruct_code,
+ COORD_PLUS,
+ COORD_MINUS,
+ COORD_POINT,
+ ENUMERATE_HUNDRED,
+ ENUMERATE_THOUSAND,
+ ENUMERATE_ZERO,
+ ENUMERATE_ONE,
+ ENUMERATE_TWO,
+ ENUMERATE_THREE,
+ ENUMERATE_FOUR,
+ ENUMERATE_FIVE,
+ ENUMERATE_SIX,
+ ENUMERATE_SEVEN,
+ ENUMERATE_EIGHT,
+ ENUMERATE_NINE,
+ ENUMERATE_TEN,
+ ENUMERATE_ELEVEN,
+ ENUMERATE_TWELVE,
+ ENUMERATE_THIRTEEN,
+ ENUMERATE_FOURTEEN,
+ ENUMERATE_FIFTEEN,
+ ENUMERATE_SIXTEEN,
+ ENUMERATE_SEVENTEEN,
+ ENUMERATE_EIGHTEEN,
+ ENUMERATE_NINETEEN,
+ ENUMERATE_TWENTY,
+ ENUMERATE_THIRTY,
+ ENUMERATE_FOURTY,
+ ENUMERATE_FIFTY,
+ ENUMERATE_SIXTY,
+ ENUMERATE_SEVENTY,
+ ENUMERATE_EIGHTY,
+ ENUMERATE_NINETY,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/spahome/Makeinfo b/src/uqm/comm/spahome/Makeinfo
new file mode 100644
index 0000000..6972013
--- /dev/null
+++ b/src/uqm/comm/spahome/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="spahome.c"
+uqm_HFILES="strings.h"
diff --git a/src/uqm/comm/spahome/spahome.c b/src/uqm/comm/spahome/spahome.c
new file mode 100644
index 0000000..190e3bb
--- /dev/null
+++ b/src/uqm/comm/spahome/spahome.c
@@ -0,0 +1,1018 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "../spathi/resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/gameev.h"
+
+
+static LOCDATA spahome_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ SPATHI_HOME_PMAP_ANIM, /* AlienFrame */
+ SPATHI_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ SPATHI_HOME_COLOR_MAP, /* AlienColorMap */
+ SPATHI_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ SPATHI_HOME_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 14, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 4, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 9, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 10) | (1 << 11), /* BlockMask */
+ },
+ {
+ 13, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND / 20, 0, /* RestartRate */
+ (1 << 4) | (1 << 5) /* BlockMask */
+ },
+ {
+ 19, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 3) | (1 << 5), /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* RestartRate */
+ (1 << 3) | (1 << 4)
+ | (1 << 10), /* BlockMask */
+ },
+ {
+ 26, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 10), /* BlockMask */
+ },
+ {
+ 29, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 32, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND / 20, 0, /* RestartRate */
+ (1 << 9) | (1 << 10), /* BlockMask */
+ },
+ {
+ 39, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 8) | (1 << 10), /* BlockMask */
+ },
+ {
+ 42, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, 0, /* RestartRate */
+ (1 << 8) | (1 << 9)
+ | (1 << 6) | (1 << 2)
+ | (1 << 11) | (1 << 5), /* BlockMask */
+ },
+ {
+ 46, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* RestartRate */
+ (1 << 2) | (1 << 10), /* BlockMask */
+ },
+ {
+ 50, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND / 20, 0, /* RestartRate */
+ (1 << 13), /* BlockMask */
+ },
+ {
+ 56, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 12), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+ if (PLAYER_SAID (R, we_attack_again))
+ {
+ NPCPhrase (WE_FIGHT_AGAIN);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, surrender_or_die))
+ {
+ NPCPhrase (DEFEND_OURSELVES);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, we_are_vindicator0))
+ {
+ NPCPhrase (NO_PASSWORD);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, gort_merenga)
+ || PLAYER_SAID (R, guph_florp)
+ || PLAYER_SAID (R, wagngl_fthagn)
+ || PLAYER_SAID (R, pleeese))
+ {
+ NPCPhrase (WRONG_PASSWORD);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, screw_password))
+ {
+ NPCPhrase (NO_PASSWORD);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, bye_no_ally_offer))
+ NPCPhrase (GOODBYE_NO_ALLY_OFFER);
+ else if (PLAYER_SAID (R, bye_angry_spathi))
+ NPCPhrase (GOODBYE_ANGRY_SPATHI);
+ else if (PLAYER_SAID (R, bye_ally))
+ NPCPhrase (GOODBYE_ALLY);
+ else if (PLAYER_SAID (R, already_got_them))
+ {
+ NPCPhrase (EARLY_BIRD_CHECK);
+
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ SET_GAME_STATE (SPATHI_PARTY, 1);
+ SET_GAME_STATE (SPATHI_MANNER, 3);
+ }
+ else if (PLAYER_SAID (R, too_dangerous))
+ NPCPhrase (WE_AGREE);
+ else if (PLAYER_SAID (R, think_more))
+ NPCPhrase (COWARD);
+ else if (PLAYER_SAID (R, i_accept))
+ {
+ NPCPhrase (AWAIT_RETURN);
+
+ SET_GAME_STATE (SPATHI_QUEST, 1);
+ SET_GAME_STATE (SPATHI_MANNER, 3);
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, do_as_we_say))
+ {
+ NPCPhrase (DEPART_FOR_EARTH);
+
+ SetRaceAllied (SPATHI_SHIP, TRUE);
+ AddEvent (RELATIVE_EVENT, 6, 0, 0, SPATHI_SHIELD_EVENT);
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, killed_them_all_1))
+ {
+ NPCPhrase (WILL_CHECK_1);
+
+ if (!GET_GAME_STATE (SPATHI_CREATURES_ELIMINATED))
+ {
+ SET_GAME_STATE (LIED_ABOUT_CREATURES, 1);
+ }
+ else
+ {
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ SET_GAME_STATE (SPATHI_PARTY, 1);
+ SET_GAME_STATE (SPATHI_MANNER, 3);
+ }
+ }
+ else if (PLAYER_SAID (R, killed_them_all_2))
+ {
+ NPCPhrase (WILL_CHECK_2);
+
+ if (!GET_GAME_STATE (SPATHI_CREATURES_ELIMINATED))
+ {
+ SET_GAME_STATE (LIED_ABOUT_CREATURES, 2);
+ }
+ else
+ {
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ SET_GAME_STATE (SPATHI_PARTY, 1);
+ SET_GAME_STATE (SPATHI_MANNER, 3);
+ }
+ }
+ else if (PLAYER_SAID (R, bye_before_party))
+ {
+ NPCPhrase (GOODBYE_BEFORE_PARTY);
+ }
+ else if (PLAYER_SAID (R, bye_from_party_1)
+ || PLAYER_SAID (R, bye_from_party_2)
+ || PLAYER_SAID (R, bye_from_party_3))
+ {
+ NPCPhrase (GOODBYE_FROM_PARTY);
+ }
+}
+
+static void SpathiAllies (RESPONSE_REF R);
+
+static void
+SpathiInfo (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ InfoLeft = FALSE;
+ if (PLAYER_SAID (R, like_some_info))
+ NPCPhrase (WHAT_ABOUT);
+ else if (PLAYER_SAID (R, what_about_hierarchy))
+ {
+ NPCPhrase (ABOUT_HIERARCHY);
+
+ DISABLE_PHRASE (what_about_hierarchy);
+ }
+ else if (PLAYER_SAID (R, what_about_history))
+ {
+ NPCPhrase (ABOUT_HISTORY);
+
+ DISABLE_PHRASE (what_about_history);
+ }
+ else if (PLAYER_SAID (R, what_about_alliance))
+ {
+ NPCPhrase (ABOUT_ALLIANCE);
+
+ DISABLE_PHRASE (what_about_alliance);
+ }
+ else if (PLAYER_SAID (R, what_about_other))
+ {
+ NPCPhrase (ABOUT_OTHER);
+
+ DISABLE_PHRASE (what_about_other);
+ }
+ else if (PLAYER_SAID (R, what_about_precursors))
+ {
+ NPCPhrase (ABOUT_PRECURSORS);
+
+ DISABLE_PHRASE (what_about_precursors);
+ }
+
+ if (PHRASE_ENABLED (what_about_hierarchy))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_hierarchy, SpathiInfo);
+ }
+ if (PHRASE_ENABLED (what_about_history))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_history, SpathiInfo);
+ }
+ if (PHRASE_ENABLED (what_about_alliance))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_alliance, SpathiInfo);
+ }
+ if (PHRASE_ENABLED (what_about_other))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_other, SpathiInfo);
+ }
+ if (PHRASE_ENABLED (what_about_precursors))
+ {
+ InfoLeft = TRUE;
+ Response (what_about_precursors, SpathiInfo);
+ }
+ Response (enough_info, SpathiAllies);
+
+ if (!InfoLeft)
+ {
+ DISABLE_PHRASE (like_some_info);
+ }
+}
+
+static void
+SpathiAllies (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (R == 0)
+ {
+ NumVisits = GET_GAME_STATE (SPATHI_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_ALLIES_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_ALLIES_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_ALLIES_3);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_HOME_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, whats_up))
+ {
+ NumVisits = GET_GAME_STATE (SPATHI_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_3);
+ SET_GAME_STATE (KNOW_URQUAN_STORY, 1);
+ SET_GAME_STATE (KNOW_KOHR_AH_STORY, 1);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_4);
+ break;
+ case 4:
+ NPCPhrase (GENERAL_INFO_5);
+ --NumVisits;
+ break;
+ case 5:
+ NPCPhrase (GENERAL_INFO_5);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up);
+ }
+ else if (PLAYER_SAID (R, resources_please))
+ {
+ NPCPhrase (SORRY_NO_RESOURCES);
+
+ DISABLE_PHRASE (resources_please);
+ }
+ else if (PLAYER_SAID (R, something_fishy))
+ {
+ NPCPhrase (NOTHING_FISHY);
+
+ SET_GAME_STATE (SPATHI_INFO, 5);
+ }
+ else if (PLAYER_SAID (R, enough_info))
+ NPCPhrase (OK_ENOUGH_INFO);
+
+ if (GET_GAME_STATE (SPATHI_INFO) == 4)
+ Response (something_fishy, SpathiAllies);
+ if (PHRASE_ENABLED (whats_up))
+ Response (whats_up, SpathiAllies);
+ if (PHRASE_ENABLED (resources_please))
+ Response (resources_please, SpathiAllies);
+ if (PHRASE_ENABLED (like_some_info))
+ Response (like_some_info, SpathiInfo);
+ Response (bye_ally, ExitConversation);
+}
+
+static void
+SpathiQuest (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ if (!GET_GAME_STATE (LIED_ABOUT_CREATURES))
+ NPCPhrase (HOW_GO_EFFORTS);
+ else
+ NPCPhrase (YOU_LIED_1);
+ }
+ else if (PLAYER_SAID (R, little_mistake))
+ {
+ NPCPhrase (BIG_MISTAKE);
+
+ DISABLE_PHRASE (little_mistake);
+ }
+ else if (PLAYER_SAID (R, talk_test))
+ {
+ NPCPhrase (TEST_AGAIN);
+
+ DISABLE_PHRASE (talk_test);
+ }
+ else if (PLAYER_SAID (R, zapped_a_few))
+ {
+ NPCPhrase (YOU_FORTUNATE);
+
+ DISABLE_PHRASE (zapped_a_few);
+ }
+
+ if (!GET_GAME_STATE (LIED_ABOUT_CREATURES))
+ Response (killed_them_all_1, ExitConversation);
+ else
+ {
+ if (PHRASE_ENABLED (little_mistake))
+ {
+ Response (little_mistake, SpathiQuest);
+ }
+ Response (killed_them_all_2, ExitConversation);
+ }
+ if (!GET_GAME_STATE (SPATHI_CREATURES_ELIMINATED))
+ {
+ if (PHRASE_ENABLED (talk_test))
+ {
+ Response (talk_test, SpathiQuest);
+ }
+ if (PHRASE_ENABLED (zapped_a_few)
+ && GET_GAME_STATE (SPATHI_CREATURES_EXAMINED))
+ {
+ Response (zapped_a_few, SpathiQuest);
+ }
+ Response (bye_before_party, ExitConversation);
+ }
+}
+
+static void
+LearnQuest (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ NPCPhrase (QUEST_AGAIN);
+
+ DISABLE_PHRASE (what_test);
+ if (GET_GAME_STATE (KNOW_SPATHI_EVIL))
+ {
+ DISABLE_PHRASE (tell_evil);
+ }
+ }
+ else if (PLAYER_SAID (R, how_prove))
+ NPCPhrase (BETTER_IDEA);
+ else if (PLAYER_SAID (R, what_test))
+ {
+ NPCPhrase (WIPE_EVIL);
+
+ SET_GAME_STATE (KNOW_SPATHI_QUEST, 1);
+ DISABLE_PHRASE (what_test);
+ }
+ else if (PLAYER_SAID (R, tell_evil))
+ {
+ NPCPhrase (BEFORE_ACCEPT);
+
+ SET_GAME_STATE (KNOW_SPATHI_EVIL, 1);
+ DISABLE_PHRASE (tell_evil);
+ DISABLE_PHRASE (prove_strength);
+ }
+ else if (PLAYER_SAID (R, prove_strength))
+ {
+ NPCPhrase (YOUR_BEHAVIOR);
+
+ DISABLE_PHRASE (prove_strength);
+ }
+ else if (PLAYER_SAID (R, why_dont_you_do_it))
+ {
+ NPCPhrase (WE_WONT_BECAUSE);
+
+ DISABLE_PHRASE (why_dont_you_do_it);
+ }
+
+ if (PHRASE_ENABLED (what_test))
+ Response (what_test, LearnQuest);
+ else if (GET_GAME_STATE (SPATHI_CREATURES_ELIMINATED))
+ {
+ Response (already_got_them, ExitConversation);
+ }
+ else if (PHRASE_ENABLED (tell_evil))
+ {
+ Response (too_dangerous, ExitConversation);
+ Response (tell_evil, LearnQuest);
+ }
+ else
+ {
+ Response (too_dangerous, ExitConversation);
+ Response (think_more, ExitConversation);
+ Response (i_accept, ExitConversation);
+ }
+ if (PHRASE_ENABLED (prove_strength) && !GET_GAME_STATE (KNOW_SPATHI_QUEST))
+ Response (prove_strength, LearnQuest);
+ if (PHRASE_ENABLED (why_dont_you_do_it))
+ Response (why_dont_you_do_it, LearnQuest);
+}
+
+static void
+AllianceOffer (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, misunderstanding))
+ {
+ NPCPhrase (JUST_MISUNDERSTANDING);
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1)
+ ), ONE_SECOND / 4);
+
+ SET_GAME_STATE (SPATHI_MANNER, 3);
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, we_come_in_peace))
+ NPCPhrase (OF_COURSE);
+ else if (PLAYER_SAID (R, hand_in_friendship))
+ {
+ NPCPhrase (TOO_AFRAID);
+
+ DISABLE_PHRASE (hand_in_friendship);
+ }
+ else if (PLAYER_SAID (R, stronger))
+ {
+ NPCPhrase (YOURE_NOT);
+
+ DISABLE_PHRASE (stronger);
+ }
+ else if (PLAYER_SAID (R, yes_we_are))
+ {
+ NPCPhrase (NO_YOURE_NOT);
+
+ DISABLE_PHRASE (yes_we_are);
+ }
+ else if (PLAYER_SAID (R, share_info))
+ {
+ NPCPhrase (NO_INFO);
+
+ DISABLE_PHRASE (share_info);
+ }
+ else if (PLAYER_SAID (R, give_us_resources))
+ {
+ NPCPhrase (NO_RESOURCES);
+
+ DISABLE_PHRASE (give_us_resources);
+ }
+
+ if (PHRASE_ENABLED (hand_in_friendship))
+ Response (hand_in_friendship, AllianceOffer);
+ else if (PHRASE_ENABLED (stronger))
+ Response (stronger, AllianceOffer);
+ else if (PHRASE_ENABLED (yes_we_are))
+ Response (yes_we_are, AllianceOffer);
+ else
+ {
+ Response (how_prove, LearnQuest);
+ }
+ if (PHRASE_ENABLED (share_info))
+ Response (share_info, AllianceOffer);
+ if (PHRASE_ENABLED (give_us_resources))
+ Response (give_us_resources, AllianceOffer);
+}
+
+static void
+SpathiAngry (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ NPCPhrase (MEAN_GUYS_RETURN);
+
+ Response (we_apologize, SpathiAngry);
+ }
+ else /* if (R == we_apologize) */
+ {
+ NPCPhrase (DONT_BELIEVE);
+
+ Response (misunderstanding, AllianceOffer);
+ }
+
+ Response (we_attack_again, ExitConversation);
+ Response (bye_angry_spathi, ExitConversation);
+}
+
+static void
+SpathiParty (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SPATHI_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (MUST_PARTY_1);
+ break;
+ case 1:
+ NPCPhrase (MUST_PARTY_2);
+ break;
+ case 2:
+ NPCPhrase (MUST_PARTY_3);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_HOME_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, deals_a_deal))
+ {
+ NPCPhrase (WAIT_A_WHILE);
+
+ DISABLE_PHRASE (deals_a_deal);
+ }
+ else if (PLAYER_SAID (R, how_long))
+ {
+ NPCPhrase (TEN_YEARS);
+
+ DISABLE_PHRASE (how_long);
+ }
+ else if (PLAYER_SAID (R, reneging))
+ {
+ NPCPhrase (ADULT_VIEW);
+
+ DISABLE_PHRASE (reneging);
+ }
+ else if (PLAYER_SAID (R, return_beasts))
+ {
+ NPCPhrase (WHAT_RELATIONSHIP);
+
+ DISABLE_PHRASE (return_beasts);
+ }
+ else if (PLAYER_SAID (R, minds_and_might))
+ {
+ NPCPhrase (HUH);
+
+ DISABLE_PHRASE (minds_and_might);
+ }
+ else if (PLAYER_SAID (R, fellowship))
+ {
+ NPCPhrase (WHAT);
+
+ DISABLE_PHRASE (fellowship);
+ }
+
+ if (PHRASE_ENABLED (deals_a_deal))
+ Response (deals_a_deal, SpathiParty);
+ else if (PHRASE_ENABLED (how_long))
+ Response (how_long, SpathiParty);
+ else if (PHRASE_ENABLED (reneging))
+ Response (reneging, SpathiParty);
+ else if (PHRASE_ENABLED (return_beasts))
+ Response (return_beasts, SpathiParty);
+ else
+ {
+ if (PHRASE_ENABLED (minds_and_might))
+ Response (minds_and_might, SpathiParty);
+ if (PHRASE_ENABLED (fellowship))
+ Response (fellowship, SpathiParty);
+ Response (do_as_we_say, ExitConversation);
+
+ return;
+ }
+ switch (GET_GAME_STATE (SPATHI_HOME_VISITS) - 1)
+ {
+ case 0:
+ Response (bye_from_party_1, ExitConversation);
+ break;
+ case 1:
+ Response (bye_from_party_2, ExitConversation);
+ break;
+ default:
+ Response (bye_from_party_3, ExitConversation);
+ break;
+ }
+}
+
+static void
+SpathiCouncil (RESPONSE_REF R)
+{
+ if (R == 0)
+ NPCPhrase (HELLO_AGAIN);
+ else if (PLAYER_SAID (R, good_password))
+ {
+ NPCPhrase (YES_GOOD_PASSWORD);
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1)
+ ), ONE_SECOND / 4);
+
+ SET_GAME_STATE (KNOW_SPATHI_PASSWORD, 1);
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, we_come_in_peace))
+ {
+ NPCPhrase (KILLED_SPATHI);
+
+ DISABLE_PHRASE (we_come_in_peace);
+ }
+ else if (PLAYER_SAID (R, spathi_on_pluto))
+ {
+ NPCPhrase (WHERE_SPATHI);
+
+ DISABLE_PHRASE (spathi_on_pluto);
+ }
+ else if (PLAYER_SAID (R, hostage))
+ {
+ NPCPhrase (GUN_TO_HEAD);
+
+ SET_GAME_STATE (FOUND_PLUTO_SPATHI, 3);
+ DISABLE_PHRASE (hostage);
+ }
+ else if (PLAYER_SAID (R, killed_fwiffo))
+ {
+ NPCPhrase (POOR_FWIFFO);
+
+ SET_GAME_STATE (FOUND_PLUTO_SPATHI, 3);
+ DISABLE_PHRASE (killed_fwiffo);
+ }
+ else if (PLAYER_SAID (R, fwiffo_fine))
+ {
+ NPCPhrase (NOT_LIKELY);
+
+ R = killed_fwiffo;
+ DISABLE_PHRASE (fwiffo_fine);
+ }
+ else if (PLAYER_SAID (R, surrender))
+ {
+ NPCPhrase (NO_SURRENDER);
+
+ DISABLE_PHRASE (surrender);
+ }
+
+ if (GET_GAME_STATE (SPATHI_MANNER) == 0)
+ {
+ Response (we_come_in_peace, AllianceOffer);
+ }
+ else if (PHRASE_ENABLED (we_come_in_peace))
+ {
+ Response (we_come_in_peace, SpathiCouncil);
+ }
+ else
+ {
+ Response (misunderstanding, AllianceOffer);
+ }
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI)
+ && GET_GAME_STATE (FOUND_PLUTO_SPATHI) < 3)
+ {
+ if (PHRASE_ENABLED (spathi_on_pluto))
+ Response (spathi_on_pluto, SpathiCouncil);
+ else if (HaveEscortShip (SPATHI_SHIP))
+ {
+ if (PHRASE_ENABLED (hostage))
+ Response (hostage, SpathiCouncil);
+ }
+ else if (PHRASE_ENABLED (killed_fwiffo))
+ {
+ Response (killed_fwiffo, SpathiCouncil);
+ if (PHRASE_ENABLED (fwiffo_fine))
+ Response (fwiffo_fine, SpathiCouncil);
+ }
+ }
+ if (PHRASE_ENABLED (surrender))
+ Response (surrender, SpathiCouncil);
+ else
+ {
+ Response (surrender_or_die, ExitConversation);
+ }
+ Response (bye_no_ally_offer, ExitConversation);
+}
+
+static void
+SpathiPassword (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SPATHI_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ default:
+ NPCPhrase (WHAT_IS_PASSWORD);
+ NumVisits = 1;
+ break;
+ case 1:
+ NPCPhrase (WHAT_IS_PASSWORD_AGAIN);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_HOME_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, what_do_i_get))
+ {
+ NPCPhrase (YOU_GET_TO_LIVE);
+
+ DISABLE_PHRASE (what_do_i_get);
+ }
+
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI)
+ || GET_GAME_STATE (KNOW_SPATHI_PASSWORD))
+ {
+ Response (good_password, SpathiCouncil);
+ if (PHRASE_ENABLED (what_do_i_get))
+ {
+ Response (what_do_i_get, SpathiPassword);
+ }
+ }
+ else
+ {
+ construct_response (shared_phrase_buf,
+ we_are_vindicator0,
+ GLOBAL_SIS (CommanderName),
+ we_are_vindicator1,
+ GLOBAL_SIS (ShipName),
+ we_are_vindicator2,
+ (UNICODE*)NULL);
+ DoResponsePhrase (we_are_vindicator0, ExitConversation, shared_phrase_buf);
+ Response (gort_merenga, ExitConversation);
+ Response (guph_florp, ExitConversation);
+ Response (wagngl_fthagn, ExitConversation);
+ Response (pleeese, ExitConversation);
+ Response (screw_password, ExitConversation);
+ }
+}
+
+static void
+Intro (void)
+{
+ BYTE Manner;
+
+ Manner = GET_GAME_STATE (SPATHI_MANNER);
+ if (Manner == 2)
+ {
+ NPCPhrase (HATE_YOU_FOREVER);
+ setSegue (Segue_hostile);
+ }
+ else if (Manner == 1
+ && GET_GAME_STATE (KNOW_SPATHI_PASSWORD)
+ && (GET_GAME_STATE (FOUND_PLUTO_SPATHI)
+ || GET_GAME_STATE (SPATHI_HOME_VISITS) != 7))
+ {
+ SpathiAngry ((RESPONSE_REF)0);
+ }
+ else if (CheckAlliance (SPATHI_SHIP) == GOOD_GUY)
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+ SpathiAllies ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (SPATHI_PARTY))
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+ SpathiParty ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (SPATHI_QUEST))
+ {
+ if (GET_GAME_STATE (LIED_ABOUT_CREATURES) < 2)
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+ SpathiQuest ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NPCPhrase (YOU_LIED_2);
+
+ SET_GAME_STATE (SPATHI_MANNER, 2);
+ setSegue (Segue_hostile);
+ }
+ }
+ else if (GET_GAME_STATE (KNOW_SPATHI_QUEST))
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+ LearnQuest ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (KNOW_SPATHI_PASSWORD)
+ && (GET_GAME_STATE (FOUND_PLUTO_SPATHI)
+ || GET_GAME_STATE (SPATHI_HOME_VISITS) != 7))
+ {
+ CommData.AlienColorMap =
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1);
+ SpathiCouncil ((RESPONSE_REF)0);
+ }
+ else
+ {
+ SpathiPassword ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_spahome (void)
+{
+ return (0);
+}
+
+static void
+post_spahome_enc (void)
+{
+ BYTE Manner;
+
+ if (getSegue () == Segue_hostile
+ && (Manner = GET_GAME_STATE (SPATHI_MANNER)) != 2)
+ {
+ SET_GAME_STATE (SPATHI_MANNER, 1);
+ if (Manner != 1)
+ {
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ }
+ }
+}
+
+LOCDATA*
+init_spahome_comm ()
+{
+ LOCDATA *retval;
+
+ spahome_desc.init_encounter_func = Intro;
+ spahome_desc.post_encounter_func = post_spahome_enc;
+ spahome_desc.uninit_encounter_func = uninit_spahome;
+
+ spahome_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ spahome_desc.AlienTextBaseline.y = 0;
+ spahome_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ // use alternate "Safe Ones" track if available
+ spahome_desc.AlienAltSongRes = SPAHOME_MUSIC;
+ spahome_desc.AlienSongFlags |= LDASF_USE_ALTERNATE;
+
+ if (GET_GAME_STATE (SPATHI_MANNER) == 3)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+
+ retval = &spahome_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/spahome/strings.h b/src/uqm/comm/spahome/strings.h
new file mode 100644
index 0000000..6df9560
--- /dev/null
+++ b/src/uqm/comm/spahome/strings.h
@@ -0,0 +1,174 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SPAHOME_STRINGS_H
+#define SPAHOME_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ killed_fwiffo,
+ POOR_FWIFFO,
+ fwiffo_fine,
+ NOT_LIKELY,
+ we_attack_again,
+ WE_FIGHT_AGAIN,
+ bye_no_ally_offer,
+ GOODBYE_NO_ALLY_OFFER,
+ bye_angry_spathi,
+ GOODBYE_ANGRY_SPATHI,
+ why_dont_you_do_it,
+ WE_WONT_BECAUSE,
+ MEAN_GUYS_RETURN,
+ we_apologize,
+ DONT_BELIEVE,
+ HELLO_AGAIN,
+ HATE_YOU_FOREVER,
+ WHAT_IS_PASSWORD,
+ WHAT_IS_PASSWORD_AGAIN,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ we_are_vindicator2,
+ gort_merenga,
+ guph_florp,
+ pleeese,
+ wagngl_fthagn,
+ screw_password,
+ good_password,
+ WRONG_PASSWORD,
+ NO_PASSWORD,
+ what_do_i_get,
+ YOU_GET_TO_LIVE,
+ YES_GOOD_PASSWORD,
+ spathi_on_pluto,
+ WHERE_SPATHI,
+ hostage,
+ GUN_TO_HEAD,
+ we_come_in_peace,
+ OF_COURSE,
+ KILLED_SPATHI,
+ misunderstanding,
+ JUST_MISUNDERSTANDING,
+// JUST_MISUNDERSTANDING0,
+// JUST_MISUNDERSTANDING1,
+ give_us_resources,
+ NO_RESOURCES,
+ resources_please,
+ SORRY_NO_RESOURCES,
+ bye_ally,
+ GOODBYE_ALLY,
+ what_about_hierarchy,
+ what_about_history,
+ what_about_alliance,
+ what_about_other,
+ what_about_precursors,
+ enough_info,
+ OK_ENOUGH_INFO,
+ ABOUT_HIERARCHY,
+ ABOUT_HISTORY,
+ ABOUT_ALLIANCE,
+ ABOUT_OTHER,
+ ABOUT_PRECURSORS,
+ little_mistake,
+ BIG_MISTAKE,
+ bye_before_party,
+ QUEST_AGAIN,
+ GOODBYE_BEFORE_PARTY,
+ GOOD_START,
+ something_fishy,
+ NOTHING_FISHY,
+ surrender,
+ NO_SURRENDER,
+ surrender_or_die,
+ DEFEND_OURSELVES,
+ hand_in_friendship,
+ TOO_AFRAID,
+ stronger,
+ YOURE_NOT,
+ yes_we_are,
+ NO_YOURE_NOT,
+ how_prove,
+ BETTER_IDEA,
+ share_info,
+ NO_INFO,
+ WE_UNDERSTAND,
+ prove_strength,
+ YOUR_BEHAVIOR,
+ what_test,
+ BEFORE_ACCEPT,
+ WIPE_EVIL,
+ think_more,
+ COWARD,
+ tell_evil,
+ i_accept,
+ AWAIT_RETURN,
+ talk_test,
+ already_got_them,
+ EARLY_BIRD_CHECK,
+ NOT_SURPRISED,
+ TEST_AGAIN,
+ too_dangerous,
+ WE_AGREE,
+ HOW_GO_EFFORTS,
+ killed_them_all_1,
+ killed_them_all_2,
+ WILL_CHECK_1,
+ WILL_CHECK_2,
+ zapped_a_few,
+ RETURN_COMPLETE,
+ MUST_DESTROY_ALL,
+ no_landing,
+ saw_creatures,
+ YOU_FORTUNATE,
+ YOU_LIED_1,
+ YOU_LIED_2,
+ bye_from_party_1,
+ bye_from_party_2,
+ bye_from_party_3,
+ GOODBYE_FROM_PARTY,
+ MUST_PARTY_1,
+ MUST_PARTY_2,
+ MUST_PARTY_3,
+ deals_a_deal,
+ WAIT_A_WHILE,
+ how_long,
+ TEN_YEARS,
+ reneging,
+ ADULT_VIEW,
+ return_beasts,
+ WHAT_RELATIONSHIP,
+ minds_and_might,
+ HUH,
+ fellowship,
+ WHAT,
+ do_as_we_say,
+ DEPART_FOR_EARTH,
+ HELLO_ALLIES_1,
+ HELLO_ALLIES_2,
+ HELLO_ALLIES_3,
+ whats_up,
+ GENERAL_INFO_1,
+ GENERAL_INFO_2,
+ GENERAL_INFO_3,
+ GENERAL_INFO_4,
+ GENERAL_INFO_5,
+ like_some_info,
+ WHAT_ABOUT
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/spathi/Makeinfo b/src/uqm/comm/spathi/Makeinfo
new file mode 100644
index 0000000..09e8874
--- /dev/null
+++ b/src/uqm/comm/spathi/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="spathic.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/spathi/resinst.h b/src/uqm/comm/spathi/resinst.h
new file mode 100644
index 0000000..f83962b
--- /dev/null
+++ b/src/uqm/comm/spathi/resinst.h
@@ -0,0 +1,14 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define FWIFFO_MUSIC "comm.fwiffo.music"
+#define SPAHOME_MUSIC "comm.safeones.music"
+#define SPATHI_COLOR_MAP "comm.spathi.colortable"
+#define SPATHI_CONVERSATION_PHRASES "comm.spathi.dialogue"
+#define SPATHI_FONT "comm.spathi.font"
+#define SPATHI_HOME_COLOR_MAP "comm.safeones.colortable"
+#define SPATHI_HOME_CONVERSATION_PHRASES "comm.safeones.dialogue"
+#define SPATHI_HOME_PMAP_ANIM "comm.safeones.graphics"
+#define SPATHI_MUSIC "comm.spathi.music"
+#define SPATHI_PMAP_ANIM "comm.spathi.graphics"
diff --git a/src/uqm/comm/spathi/spathic.c b/src/uqm/comm/spathi/spathic.c
new file mode 100644
index 0000000..29288dc
--- /dev/null
+++ b/src/uqm/comm/spathi/spathic.c
@@ -0,0 +1,834 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+
+
+static LOCDATA spathi_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ SPATHI_PMAP_ANIM, /* AlienFrame */
+ SPATHI_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ SPATHI_COLOR_MAP, /* AlienColorMap */
+ SPATHI_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ SPATHI_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 8, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 1, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ 0, ONE_SECOND, /* RestartRate */
+ (1 << 1), /* BlockMask */
+ },
+ {
+ 7, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND / 20, 0, /* RestartRate */
+ (1 << 0), /* BlockMask */
+ },
+ {
+ 16, /* StartIndex */
+ 4, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 10, ONE_SECOND / 15, /* RestartRate */
+ (1 << 4), /* BlockMask */
+ },
+ {
+ 20, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ (1 << 5)
+ },
+ {
+ 24, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 2), /* BlockMask */
+ },
+
+
+ {
+ 34, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 3), /* BlockMask */
+ },
+ {
+ 38, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 41, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+#ifdef NEVER
+ { /* AlienTalkDesc */
+ 29, /* StartIndex */
+ 5, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+#else
+ { /* AlienTalkDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+#endif /* NEVER */
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF Response)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (Response, bye_ally_space))
+ NPCPhrase (GOODBYE_ALLY_SPACE);
+ else if (PLAYER_SAID (Response, bye_friendly_space))
+ NPCPhrase (GOODBYE_FRIENDLY_SPACE);
+ else if (PLAYER_SAID (Response, part_in_peace))
+ NPCPhrase (KEEP_IT_SECRET);
+ else if (PLAYER_SAID (Response, we_sorry_space))
+ NPCPhrase (APOLOGIZE_AT_HOMEWORLD);
+ else if (PLAYER_SAID (Response, give_info_space))
+ NPCPhrase (HERES_SOME_INFO);
+ else if (PLAYER_SAID (Response, bye_angry_space))
+ NPCPhrase (GOODBYE_ANGRY_SPACE);
+ else if (PLAYER_SAID (Response, attack_you_now))
+ {
+ NPCPhrase (YIPES);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (Response, we_fight_again_space))
+ {
+ NPCPhrase (OK_FIGHT_AGAIN_SPACE);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (Response, die_slugboy)
+ || PLAYER_SAID (Response, we_fight_1)
+ || PLAYER_SAID (Response, we_fight_2)
+ || PLAYER_SAID (Response, pay_for_crimes)
+ || PLAYER_SAID (Response, tell_me_coordinates)
+ || PLAYER_SAID (Response, changed_mind))
+ {
+ if (PLAYER_SAID (Response, tell_me_coordinates))
+ NPCPhrase (FAKE_COORDINATES);
+ NPCPhrase (OK_WE_FIGHT_AT_PLUTO);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (Response, join_us))
+ {
+ if (EscortFeasibilityStudy (SPATHI_SHIP) == 0)
+ NPCPhrase (TOO_SCARY);
+ else
+ {
+ NPCPhrase (WILL_JOIN);
+
+ AlienTalkSegue ((COUNT)~0);
+ AddEscortShips (SPATHI_SHIP, 1);
+ /* Make the Eluder escort captained by Fwiffo alone */
+ SetEscortCrewComplement (SPATHI_SHIP, 1,
+ NAME_OFFSET + NUM_CAPTAINS_NAMES);
+ }
+ }
+}
+
+static BYTE join_us_refusals;
+
+static void
+SpathiOnPluto (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, hi_there))
+ NPCPhrase (ARE_YOU_SURE);
+ else if (PLAYER_SAID (R, dont_kill))
+ NPCPhrase (OK_WONT);
+ else if (PLAYER_SAID (R, youre_forgiven))
+ NPCPhrase (THANKS_FOR_FORGIVENESS);
+ else if (PLAYER_SAID (R, you_wont_die_yet))
+ NPCPhrase (ETERNAL_GRATITUDE);
+ else if (PLAYER_SAID (R, you_may_live))
+ NPCPhrase (GEE_THANKS);
+ else if (PLAYER_SAID (R, youve_got_me_all_wrong))
+ NPCPhrase (SORRY_NO_COORDS);
+ else if (PLAYER_SAID (R, what_doing_on_pluto_1))
+ {
+ NPCPhrase (ABOUT_20_YEARS_AGO);
+
+ DISABLE_PHRASE (what_doing_on_pluto_1);
+ }
+ else if (PLAYER_SAID (R, what_doing_on_pluto_2))
+ {
+ NPCPhrase (WHEN_URQUAN_ARRIVED);
+
+ DISABLE_PHRASE (what_doing_on_pluto_2);
+ }
+ else if (PLAYER_SAID (R, what_doing_on_pluto_3))
+ {
+ NPCPhrase (STATIONED_ON_EARTH_MOON);
+
+ DISABLE_PHRASE (what_doing_on_pluto_3);
+ }
+ else if (PLAYER_SAID (R, what_about_ilwrath))
+ {
+ NPCPhrase (ABOUT_ILWRATH);
+
+ DISABLE_PHRASE (what_about_ilwrath);
+ }
+ else if (PLAYER_SAID (R, when_ilwrath))
+ {
+ NPCPhrase (THEN_ILWRATH);
+
+ DISABLE_PHRASE (when_ilwrath);
+ }
+ else if (PLAYER_SAID (R, what_about_moonbase))
+ {
+ NPCPhrase (SET_UP_BASE);
+
+ DISABLE_PHRASE (what_about_moonbase);
+ }
+ else if (PLAYER_SAID (R, what_about_other_spathi))
+ {
+ NPCPhrase (SPATHI_ARE);
+
+ DISABLE_PHRASE (what_about_other_spathi);
+ }
+ else if (PLAYER_SAID (R, what_about_other_spathi))
+ {
+ NPCPhrase (THEN_ILWRATH);
+
+ DISABLE_PHRASE (what_about_other_spathi);
+ }
+ else if (PLAYER_SAID (R, how_many_crew))
+ {
+ NPCPhrase (THOUSANDS);
+
+ DISABLE_PHRASE (how_many_crew);
+ }
+ else if (PLAYER_SAID (R, really_thousands))
+ {
+ NPCPhrase (JUST_ME);
+
+ DISABLE_PHRASE (really_thousands);
+ }
+ else if (PLAYER_SAID (R, full_of_monsters))
+ {
+ NPCPhrase (HOW_TRUE);
+
+ DISABLE_PHRASE (full_of_monsters);
+ }
+ else if (PLAYER_SAID (R, what_enemy))
+ {
+ NPCPhrase (ENEMY_IS);
+
+ DISABLE_PHRASE (what_enemy);
+ }
+ else if (PLAYER_SAID (R, why_you_here))
+ {
+ NPCPhrase (DREW_SHORT_STRAW);
+
+ DISABLE_PHRASE (why_you_here);
+ }
+ else if (PLAYER_SAID (R, where_are_urquan))
+ {
+ NPCPhrase (URQUAN_LEFT);
+
+ DISABLE_PHRASE (where_are_urquan);
+ }
+ else if (PLAYER_SAID (R, what_about_other_races))
+ {
+ NPCPhrase (ABOUT_OTHER_RACES);
+
+ DISABLE_PHRASE (what_about_other_races);
+ }
+ else if (PLAYER_SAID (R, what_blaze_of_glory))
+ {
+ NPCPhrase (BLAZE_IS);
+
+ DISABLE_PHRASE (what_blaze_of_glory);
+ }
+ else if (PLAYER_SAID (R, what_about_yourself))
+ {
+ NPCPhrase (ABOUT_MYSELF);
+
+ DISABLE_PHRASE (what_about_yourself);
+ }
+ else if (PLAYER_SAID (R, join_us))
+ {
+ if (join_us_refusals == 0)
+ {
+ NPCPhrase (WONT_JOIN_1);
+ ++join_us_refusals;
+ }
+ else if (join_us_refusals == 1)
+ {
+ NPCPhrase (WONT_JOIN_2);
+ ++join_us_refusals;
+ }
+ else
+ NPCPhrase (WONT_JOIN_3);
+ }
+
+ if (PHRASE_ENABLED (what_doing_on_pluto_1))
+ Response (what_doing_on_pluto_1, SpathiOnPluto);
+ else if (PHRASE_ENABLED (what_doing_on_pluto_2))
+ Response (what_doing_on_pluto_2, SpathiOnPluto);
+ else if (PHRASE_ENABLED (what_doing_on_pluto_3))
+ Response (what_doing_on_pluto_3, SpathiOnPluto);
+ else
+ {
+ if (PHRASE_ENABLED (what_about_ilwrath))
+ Response (what_about_ilwrath, SpathiOnPluto);
+ else if (PHRASE_ENABLED (when_ilwrath))
+ Response (when_ilwrath, SpathiOnPluto);
+
+ if (PHRASE_ENABLED (what_about_moonbase))
+ Response (what_about_moonbase, SpathiOnPluto);
+ else if (PHRASE_ENABLED (what_about_other_spathi))
+ Response (what_about_other_spathi, SpathiOnPluto);
+ else
+ {
+ if (PHRASE_ENABLED (how_many_crew))
+ Response (how_many_crew, SpathiOnPluto);
+ else if (PHRASE_ENABLED (really_thousands))
+ Response (really_thousands, SpathiOnPluto);
+ else if (PHRASE_ENABLED (full_of_monsters))
+ Response (full_of_monsters, SpathiOnPluto);
+
+ if (PHRASE_ENABLED (what_enemy))
+ Response (what_enemy, SpathiOnPluto);
+ else if (PHRASE_ENABLED (why_you_here))
+ Response (why_you_here, SpathiOnPluto);
+ }
+ }
+ if (PHRASE_ENABLED (where_are_urquan))
+ Response (where_are_urquan, SpathiOnPluto);
+ else if (PHRASE_ENABLED (what_about_other_races))
+ Response (what_about_other_races, SpathiOnPluto);
+ else if (PHRASE_ENABLED (what_blaze_of_glory))
+ Response (what_blaze_of_glory, SpathiOnPluto);
+ else if (PHRASE_ENABLED (what_about_yourself))
+ Response (what_about_yourself, SpathiOnPluto);
+
+ if (!PHRASE_ENABLED (full_of_monsters))
+ Response (join_us, ExitConversation);
+ else
+ Response (join_us, SpathiOnPluto);
+ Response (changed_mind, ExitConversation);
+}
+
+static void
+SpathiMustGrovel (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, identify))
+ {
+ NPCPhrase (I_FWIFFO);
+
+ Response (do_cultural, SpathiMustGrovel);
+ Response (youre_forgiven, SpathiOnPluto);
+ Response (die_slugboy, ExitConversation);
+ }
+ else if (PLAYER_SAID (R, do_cultural))
+ {
+ NPCPhrase (WEZZY_WEZZAH);
+
+ Response (begin_ritual, SpathiMustGrovel);
+ Response (you_wont_die_yet, SpathiOnPluto);
+ Response (we_fight_2, ExitConversation);
+ }
+ else if (PLAYER_SAID (R, begin_ritual))
+ {
+ NPCPhrase (MUST_DO_RITUAL_AT_HOME);
+
+ Response (you_may_live, SpathiOnPluto);
+ Response (pay_for_crimes, ExitConversation);
+ Response (what_are_coordinates, SpathiMustGrovel);
+ }
+ else /* if (R == what_are_coordinates) */
+ {
+ NPCPhrase (COORDINATES_ARE);
+
+ Response (youve_got_me_all_wrong, SpathiOnPluto);
+ Response (tell_me_coordinates, ExitConversation);
+ }
+}
+
+static void
+SpathiAllies (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SPATHI_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_ALLIED_HELLO_SPACE);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_ALLIED_HELLO_SPACE);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, whats_up_space_2))
+ {
+ NPCPhrase (GENERAL_INFO_SPACE_2);
+
+ DISABLE_PHRASE (whats_up_space_2);
+ }
+ else if (PLAYER_SAID (R, give_us_info_from_space))
+ {
+ NPCPhrase (GET_INFO_FROM_SPATHIWA);
+
+ DISABLE_PHRASE (give_us_info_from_space);
+ }
+ else if (PLAYER_SAID (R, give_us_resources_space))
+ {
+ NPCPhrase (GET_RESOURCES_FROM_SPATHIWA);
+
+ DISABLE_PHRASE (give_us_resources_space);
+ }
+ else if (PLAYER_SAID (R, what_do_for_fun))
+ {
+ NPCPhrase (DO_THIS_FOR_FUN);
+
+ DISABLE_PHRASE (what_do_for_fun);
+ }
+
+ if (PHRASE_ENABLED (whats_up_space_2))
+ Response (whats_up_space_2, SpathiAllies);
+ if (PHRASE_ENABLED (give_us_info_from_space))
+ Response (give_us_info_from_space, SpathiAllies);
+ if (PHRASE_ENABLED (give_us_resources_space))
+ Response (give_us_resources_space, SpathiAllies);
+ if (PHRASE_ENABLED (what_do_for_fun))
+ Response (what_do_for_fun, SpathiAllies);
+ Response (bye_ally_space, ExitConversation);
+}
+
+static void
+SpathiFriendly (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SPATHI_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_FRIENDLY_HELLO_SPACE);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_FRIENDLY_HELLO_SPACE);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, since_friendly_give_stuff))
+ {
+ NPCPhrase (GIVE_ADVICE);
+
+ DISABLE_PHRASE (since_friendly_give_stuff);
+ }
+ else if (PLAYER_SAID (R, whats_up_space_1))
+ {
+ NPCPhrase (GENERAL_INFO_SPACE_1);
+
+ DISABLE_PHRASE (whats_up_space_1);
+ }
+
+ if (PHRASE_ENABLED (whats_up_space_1))
+ Response (whats_up_space_1, SpathiFriendly);
+ if (PHRASE_ENABLED (since_friendly_give_stuff))
+ Response (since_friendly_give_stuff, SpathiFriendly);
+ Response (bye_friendly_space, ExitConversation);
+}
+
+static void SpathiNeutral (RESPONSE_REF R);
+
+static void
+SpathiBefriend (RESPONSE_REF R)
+{
+ BYTE InfoLeft, LastStack;
+ RESPONSE_REF pStr[2];
+
+ InfoLeft = FALSE;
+ LastStack = 0;
+ pStr[0] = pStr[1] = 0;
+ if (PLAYER_SAID (R, come_in_peace))
+ NPCPhrase (AGAINST_NATURE);
+ else if (PLAYER_SAID (R, looking_for_a_few_good_squids))
+ {
+ NPCPhrase (URQUAN_SLAVES);
+
+ DISABLE_PHRASE (looking_for_a_few_good_squids);
+ }
+ else if (PLAYER_SAID (R, why_slaves))
+ {
+ NPCPhrase (UMGAH_TRICK);
+
+ DISABLE_PHRASE (why_slaves);
+ }
+ else if (PLAYER_SAID (R, tell_us_about_you))
+ {
+ NPCPhrase (ABOUT_US);
+
+ DISABLE_PHRASE (tell_us_about_you);
+ LastStack = 1;
+ }
+ else if (PLAYER_SAID (R, what_you_really_want))
+ {
+ NPCPhrase (WANT_THIS);
+
+ DISABLE_PHRASE (what_you_really_want);
+ }
+ else if (PLAYER_SAID (R, how_about_alliance))
+ {
+ NPCPhrase (SURE);
+
+ DISABLE_PHRASE (how_about_alliance);
+ }
+
+ if (PHRASE_ENABLED (looking_for_a_few_good_squids))
+ pStr[0] = looking_for_a_few_good_squids;
+ else if (PHRASE_ENABLED (why_slaves))
+ pStr[0] = why_slaves;
+ if (PHRASE_ENABLED (tell_us_about_you))
+ pStr[1] = tell_us_about_you;
+ else if (PHRASE_ENABLED (what_you_really_want))
+ pStr[1] = what_you_really_want;
+ if (pStr[LastStack])
+ {
+ InfoLeft = TRUE;
+ Response (pStr[LastStack], SpathiBefriend);
+ }
+ LastStack ^= 1;
+ if (pStr[LastStack])
+ {
+ InfoLeft = TRUE;
+ Response (pStr[LastStack], SpathiBefriend);
+ }
+ if (PHRASE_ENABLED (how_about_alliance))
+ {
+ InfoLeft = TRUE;
+ Response (how_about_alliance, SpathiBefriend);
+ }
+
+ if (!InfoLeft)
+ {
+ SET_GAME_STATE (SPATHI_STACK1, 1);
+ SpathiNeutral (R);
+ }
+}
+
+static void
+SpathiAntagonize (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, prepare_to_die))
+ {
+ NPCPhrase (ALWAYS_PREPARED);
+
+ SET_GAME_STATE (SPATHI_STACK2, 1);
+ }
+ else if (PLAYER_SAID (R, heard_youre_cowards))
+ {
+ NPCPhrase (DARN_TOOTIN);
+
+ DISABLE_PHRASE (heard_youre_cowards);
+ }
+ else if (PLAYER_SAID (R, wanna_fight))
+ {
+ NPCPhrase (YES_WE_DO);
+
+ DISABLE_PHRASE (wanna_fight);
+ }
+ else if (PLAYER_SAID (R, so_lets_fight))
+ {
+ NPCPhrase (OK_LETS_FIGHT);
+
+ DISABLE_PHRASE (so_lets_fight);
+ }
+ else if (PLAYER_SAID (R, so_lets_fight_already))
+ {
+ NPCPhrase (DONT_REALLY_WANT_TO_FIGHT);
+
+ DISABLE_PHRASE (so_lets_fight_already);
+ }
+
+ if (PHRASE_ENABLED (wanna_fight))
+ Response (wanna_fight, SpathiAntagonize);
+ else if (PHRASE_ENABLED (so_lets_fight))
+ Response (so_lets_fight, SpathiAntagonize);
+ else if (PHRASE_ENABLED (so_lets_fight_already))
+ Response (so_lets_fight_already, SpathiAntagonize);
+ if (PHRASE_ENABLED (heard_youre_cowards))
+ Response (heard_youre_cowards, SpathiAntagonize);
+ Response (attack_you_now, ExitConversation);
+}
+
+static void
+SpathiNeutral (RESPONSE_REF R)
+{
+ if (R == 0)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SPATHI_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_NEUTRAL_HELLO_SPACE);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_NEUTRAL_HELLO_SPACE);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, look_weird))
+ {
+ NPCPhrase (YOU_LOOK_WEIRD);
+
+ SET_GAME_STATE (SPATHI_STACK0, 1);
+ }
+ else if (PLAYER_SAID (R, no_look_really_weird))
+ {
+ NPCPhrase (NO_YOU_LOOK_REALLY_WEIRD);
+
+ SET_GAME_STATE (SPATHI_STACK0, 2);
+ }
+
+ switch (GET_GAME_STATE (SPATHI_STACK0))
+ {
+ case 0:
+ Response (look_weird, SpathiNeutral);
+ break;
+ case 1:
+ Response (no_look_really_weird, SpathiNeutral);
+ break;
+ }
+ if (GET_GAME_STATE (SPATHI_STACK1) == 0)
+ {
+ Response (come_in_peace, SpathiBefriend);
+ }
+ if (GET_GAME_STATE (SPATHI_STACK2) == 0)
+ {
+ Response (prepare_to_die, SpathiAntagonize);
+ }
+ else
+ {
+ Response (attack_you_now, ExitConversation);
+ }
+ Response (part_in_peace, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE Manner;
+
+ Manner = GET_GAME_STATE (SPATHI_MANNER);
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI) == 1)
+ {
+ join_us_refusals = 0;
+
+ NPCPhrase (SORRY_ABOUT_THAT);
+
+ /* if already know password from Melnorme,
+ * but haven't visited Spathiwa yet . . .
+ */
+ if (GET_GAME_STATE (SPATHI_HOME_VISITS) == 7)
+ {
+ SET_GAME_STATE (KNOW_SPATHI_PASSWORD, 0);
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ }
+
+ Response (identify, SpathiMustGrovel);
+ Response (hi_there, SpathiOnPluto);
+ Response (dont_kill, SpathiOnPluto);
+ Response (we_fight_1, ExitConversation);
+ }
+ else if (Manner == 2)
+ {
+ NPCPhrase (HATE_YOU_FOREVER_SPACE);
+ setSegue (Segue_hostile);
+ }
+ else if (Manner == 1)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SPATHI_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_ANGRY_HELLO_SPACE);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_ANGRY_HELLO_SPACE);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SPATHI_VISITS, NumVisits);
+
+ Response (give_info_space, ExitConversation);
+ Response (we_sorry_space,ExitConversation);
+ Response (we_fight_again_space, ExitConversation);
+ Response (bye_angry_space, ExitConversation);
+ }
+ else if (CheckAlliance (SPATHI_SHIP) == GOOD_GUY)
+ {
+ SpathiAllies ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (SPATHI_QUEST))
+ {
+ SpathiFriendly ((RESPONSE_REF)0);
+ }
+ else
+ {
+ SpathiNeutral ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_spathi (void)
+{
+ return (0);
+}
+
+static void
+post_spathi_enc (void)
+{
+ BYTE Manner;
+
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI) == 1)
+ {
+ SET_GAME_STATE (FOUND_PLUTO_SPATHI, 2);
+ }
+ else if (getSegue () == Segue_hostile
+ && (Manner = GET_GAME_STATE (SPATHI_MANNER)) != 2)
+ {
+ SET_GAME_STATE (SPATHI_MANNER, 1);
+ if (Manner != 1)
+ {
+ SET_GAME_STATE (SPATHI_VISITS, 0);
+ /* if don't know about Spathi via Melnorme . . . */
+ if (GET_GAME_STATE (SPATHI_HOME_VISITS) != 7)
+ {
+ SET_GAME_STATE (SPATHI_HOME_VISITS, 0);
+ }
+ }
+ }
+}
+
+LOCDATA*
+init_spathi_comm (void)
+{
+ LOCDATA *retval;
+
+ spathi_desc.init_encounter_func = Intro;
+ spathi_desc.post_encounter_func = post_spathi_enc;
+ spathi_desc.uninit_encounter_func = uninit_spathi;
+
+ spathi_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ spathi_desc.AlienTextBaseline.y = 0;
+ spathi_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI) == 1)
+ { // use alternate Fwiffo track if available
+ spathi_desc.AlienAltSongRes = FWIFFO_MUSIC;
+ spathi_desc.AlienSongFlags |= LDASF_USE_ALTERNATE;
+ }
+ else
+ { // regular track -- let's make sure
+ spathi_desc.AlienSongFlags &= ~LDASF_USE_ALTERNATE;
+ }
+
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI) == 1
+ || GET_GAME_STATE (SPATHI_MANNER) == 3
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &spathi_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/spathi/strings.h b/src/uqm/comm/spathi/strings.h
new file mode 100644
index 0000000..a09ea87
--- /dev/null
+++ b/src/uqm/comm/spathi/strings.h
@@ -0,0 +1,160 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SPATHI_STRINGS_H
+#define SPATHI_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ SORRY_ABOUT_THAT,
+ identify,
+ I_FWIFFO,
+ hi_there,
+ ARE_YOU_SURE,
+ dont_kill,
+ we_fight_1,
+ we_fight_2,
+ OK_WONT,
+ do_cultural,
+ WEZZY_WEZZAH,
+ die_slugboy,
+ begin_ritual,
+ MUST_DO_RITUAL_AT_HOME,
+ you_wont_die_yet,
+ ETERNAL_GRATITUDE,
+ we_fight,
+ pay_for_crimes,
+ CLUTCH_MAVEN,
+ you_may_live,
+ HONEST_AND_FRIENDLY,
+ what_are_coordinates,
+ COORDINATES_ARE,
+ tell_me_coordinates,
+ FAKE_COORDINATES,
+ TOO_SCARY,
+ youve_got_me_all_wrong,
+ SORRY_NO_COORDS,
+ what_doing_on_pluto_1,
+ ABOUT_20_YEARS_AGO,
+ what_doing_on_pluto_2,
+ WHEN_URQUAN_ARRIVED,
+ where_are_urquan,
+ URQUAN_LEFT,
+ what_about_other_races,
+ ABOUT_OTHER_RACES,
+ what_doing_on_pluto_3,
+ what_about_yourself,
+ ABOUT_MYSELF,
+ STATIONED_ON_EARTH_MOON,
+ what_blaze_of_glory,
+ BLAZE_IS,
+ what_about_moonbase,
+ SET_UP_BASE,
+ what_about_ilwrath,
+ ABOUT_ILWRATH,
+ what_about_other_spathi,
+ really_thousands,
+ SPATHI_ARE,
+ what_enemy,
+ ENEMY_IS,
+ when_ilwrath,
+ THEN_ILWRATH,
+ why_you_here,
+ DREW_SHORT_STRAW,
+ how_many_crew,
+ JUST_ME,
+ THOUSANDS,
+ full_of_monsters,
+ HOW_TRUE,
+ join_us,
+ WILL_JOIN,
+ WONT_JOIN_1,
+ give_ship_or_die,
+ WONT_JOIN_2,
+ WONT_JOIN_3,
+ GEE_THANKS,
+ changed_mind,
+ youre_forgiven,
+ THANKS_FOR_FORGIVENESS,
+ HATE_YOU_FOREVER_SPACE,
+ INIT_ANGRY_HELLO_SPACE,
+ SUBSEQUENT_ANGRY_HELLO_SPACE,
+ INIT_NEUTRAL_HELLO_SPACE,
+ SUBSEQUENT_NEUTRAL_HELLO_SPACE,
+ INIT_FRIENDLY_HELLO_SPACE,
+ SUBSEQUENT_FRIENDLY_HELLO_SPACE,
+ INIT_ALLIED_HELLO_SPACE,
+ SUBSEQUENT_ALLIED_HELLO_SPACE,
+ give_info_space,
+ HERES_SOME_INFO,
+ we_sorry_space,
+ APOLOGIZE_AT_HOMEWORLD,
+ we_fight_again_space,
+ OK_FIGHT_AGAIN_SPACE,
+ bye_angry_space,
+ GOODBYE_ANGRY_SPACE,
+ look_weird,
+ YOU_LOOK_WEIRD,
+ no_look_really_weird,
+ NO_YOU_LOOK_REALLY_WEIRD,
+ come_in_peace,
+ AGAINST_NATURE,
+ prepare_to_die,
+ ALWAYS_PREPARED,
+ since_friendly_give_stuff,
+ GIVE_ADVICE,
+ whats_up_space_1,
+ GENERAL_INFO_SPACE_1,
+ bye_friendly_space,
+ GOODBYE_FRIENDLY_SPACE,
+ looking_for_a_few_good_squids,
+ URQUAN_SLAVES,
+ why_slaves,
+ UMGAH_TRICK,
+ tell_us_about_you,
+ ABOUT_US,
+ what_you_really_want,
+ WANT_THIS,
+ how_about_alliance,
+ SURE,
+ part_in_peace,
+ KEEP_IT_SECRET,
+ heard_youre_cowards,
+ DARN_TOOTIN,
+ wanna_fight,
+ YES_WE_DO,
+ so_lets_fight,
+ OK_LETS_FIGHT,
+ so_lets_fight_already,
+ DONT_REALLY_WANT_TO_FIGHT,
+ attack_you_now,
+ YIPES,
+ whats_up_space_2,
+ GENERAL_INFO_SPACE_2,
+ give_us_info_from_space,
+ GET_INFO_FROM_SPATHIWA,
+ give_us_resources_space,
+ GET_RESOURCES_FROM_SPATHIWA,
+ what_do_for_fun,
+ DO_THIS_FOR_FUN,
+ bye_ally_space,
+ GOODBYE_ALLY_SPACE,
+ OK_WE_FIGHT_AT_PLUTO,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/starbas/Makeinfo b/src/uqm/comm/starbas/Makeinfo
new file mode 100644
index 0000000..7f1eab8
--- /dev/null
+++ b/src/uqm/comm/starbas/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="starbas.c"
+uqm_HFILES="strings.h"
diff --git a/src/uqm/comm/starbas/starbas.c b/src/uqm/comm/starbas/starbas.c
new file mode 100644
index 0000000..5f25a2d
--- /dev/null
+++ b/src/uqm/comm/starbas/starbas.c
@@ -0,0 +1,1961 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "../comandr/resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/setup.h"
+#include "uqm/shipcont.h"
+#include "uqm/sis.h"
+ // for DeltaSISGauges()
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+#include "libs/inplib.h"
+
+
+static void TellMission (RESPONSE_REF R);
+static void SellMinerals (RESPONSE_REF R);
+
+
+static LOCDATA commander_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ COMMANDER_PMAP_ANIM, /* AlienFrame */
+ COMMANDER_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ COMMANDER_COLOR_MAP, /* AlienColorMap */
+ COMMANDER_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ STARBASE_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 10, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ { /* Blink */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Running light */
+ 10, /* StartIndex */
+ 30, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ ONE_SECOND * 2, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Arc welder 0 */
+ 40, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Arc welder 1 */
+ 47, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Arc welder 2 */
+ 55, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Arc welder 3 */
+ 61, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Arc welder 4 */
+ 67, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Arc welder 5 */
+ 74, /* StartIndex */
+ 11, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Arc welder 6 */
+ 85, /* StartIndex */
+ 10, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 40, 0, /* FrameRate */
+ 0, ONE_SECOND * 8, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Flagship picture */
+ 95, /* StartIndex */
+ 1, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 4, /* StartIndex */
+ 6, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 7 / 60, ONE_SECOND / 12, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static DWORD CurBulletinMask;
+
+static void
+ByeBye (RESPONSE_REF R)
+{
+ (void) R; // ignored
+
+ CurBulletinMask |= GET_GAME_STATE_32 (STARBASE_BULLETS0);
+ SET_GAME_STATE_32 (STARBASE_BULLETS0, CurBulletinMask);
+
+ /* if (R == goodbye_starbase_commander) */
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) >= 2)
+ NPCPhrase (GOOD_LUCK_AGAIN);
+ else
+ {
+ RESPONSE_REF pStr0 = 0;
+ RESPONSE_REF pStr1 = 0;
+
+ switch ((BYTE)TFB_Random () & 7)
+ {
+ case 0:
+ pStr0 = NORMAL_GOODBYE_A0;
+ pStr1 = NORMAL_GOODBYE_A1;
+ break;
+ case 1:
+ pStr0 = NORMAL_GOODBYE_B0;
+ pStr1 = NORMAL_GOODBYE_B1;
+ break;
+ case 2:
+ pStr0 = NORMAL_GOODBYE_C0;
+ pStr1 = NORMAL_GOODBYE_C1;
+ break;
+ case 3:
+ pStr0 = NORMAL_GOODBYE_D0;
+ pStr1 = NORMAL_GOODBYE_D1;
+ break;
+ case 4:
+ pStr0 = NORMAL_GOODBYE_E0;
+ pStr1 = NORMAL_GOODBYE_E1;
+ break;
+ case 5:
+ pStr0 = NORMAL_GOODBYE_F0;
+ pStr1 = NORMAL_GOODBYE_F1;
+ break;
+ case 6:
+ pStr0 = NORMAL_GOODBYE_G0;
+ pStr1 = NORMAL_GOODBYE_G1;
+ break;
+ case 7:
+ pStr0 = NORMAL_GOODBYE_H0;
+ pStr1 = NORMAL_GOODBYE_H1;
+ break;
+ }
+
+ NPCPhrase (pStr0);
+ if (!usingSpeech)
+ {
+ NPCPhrase (SPACE);
+ NPCPhrase (GLOBAL_PLAYER_NAME);
+ }
+ NPCPhrase (pStr1);
+ }
+}
+
+static void NeedInfo (RESPONSE_REF R);
+static void TellHistory (RESPONSE_REF R);
+static void AlienRaces (RESPONSE_REF R);
+
+static BYTE stack0;
+static BYTE stack1;
+static BYTE stack2;
+static BYTE stack3;
+
+static void
+AllianceInfo (RESPONSE_REF R)
+{
+#define ALLIANCE_SHOFIXTI (1 << 0)
+#define ALLIANCE_YEHAT (1 << 1)
+#define ALLIANCE_ARILOU (1 << 2)
+#define ALLIANCE_CHENJESU (1 << 3)
+#define ALLIANCE_MMRNMHRM (1 << 4)
+#define ALLIANCE_SYREEN (1 << 5)
+ static BYTE AllianceMask = 0;
+
+ if (PLAYER_SAID (R, what_about_alliance))
+ {
+ NPCPhrase (WHICH_ALLIANCE);
+ AllianceMask = 0;
+ }
+ else if (PLAYER_SAID (R, shofixti))
+ {
+ NPCPhrase (ABOUT_SHOFIXTI);
+ AllianceMask |= ALLIANCE_SHOFIXTI;
+ }
+ else if (PLAYER_SAID (R, yehat))
+ {
+ NPCPhrase (ABOUT_YEHAT);
+ AllianceMask |= ALLIANCE_YEHAT;
+ }
+ else if (PLAYER_SAID (R, arilou))
+ {
+ NPCPhrase (ABOUT_ARILOU);
+ AllianceMask |= ALLIANCE_ARILOU;
+ }
+ else if (PLAYER_SAID (R, chenjesu))
+ {
+ NPCPhrase (ABOUT_CHENJESU);
+ AllianceMask |= ALLIANCE_CHENJESU;
+ }
+ else if (PLAYER_SAID (R, mmrnmhrm))
+ {
+ NPCPhrase (ABOUT_MMRNMHRM);
+ AllianceMask |= ALLIANCE_MMRNMHRM;
+ }
+ else if (PLAYER_SAID (R, syreen))
+ {
+ NPCPhrase (ABOUT_SYREEN);
+ AllianceMask |= ALLIANCE_SYREEN;
+ }
+
+ if (!(AllianceMask & ALLIANCE_SHOFIXTI))
+ Response (shofixti, AllianceInfo);
+ if (!(AllianceMask & ALLIANCE_YEHAT))
+ Response (yehat, AllianceInfo);
+ if (!(AllianceMask & ALLIANCE_ARILOU))
+ Response (arilou, AllianceInfo);
+ if (!(AllianceMask & ALLIANCE_CHENJESU))
+ Response (chenjesu, AllianceInfo);
+ if (!(AllianceMask & ALLIANCE_MMRNMHRM))
+ Response (mmrnmhrm, AllianceInfo);
+ if (!(AllianceMask & ALLIANCE_SYREEN))
+ Response (syreen, AllianceInfo);
+ Response (enough_alliance, AlienRaces);
+}
+
+static void
+HierarchyInfo (RESPONSE_REF R)
+{
+#define HIERARCHY_MYCON (1 << 0)
+#define HIERARCHY_SPATHI (1 << 1)
+#define HIERARCHY_UMGAH (1 << 2)
+#define HIERARCHY_ANDROSYNTH (1 << 3)
+#define HIERARCHY_ILWRATH (1 << 4)
+#define HIERARCHY_VUX (1 << 5)
+#define HIERARCHY_URQUAN (1 << 6)
+ static BYTE HierarchyMask = 0;
+
+ if (PLAYER_SAID (R, what_about_hierarchy))
+ {
+ NPCPhrase (WHICH_HIERARCHY);
+ HierarchyMask = 0;
+ }
+ else if (PLAYER_SAID (R, urquan))
+ {
+ NPCPhrase (ABOUT_URQUAN);
+ HierarchyMask |= HIERARCHY_URQUAN;
+ }
+ else if (PLAYER_SAID (R, mycon))
+ {
+ NPCPhrase (ABOUT_MYCON);
+ HierarchyMask |= HIERARCHY_MYCON;
+ }
+ else if (PLAYER_SAID (R, spathi))
+ {
+ NPCPhrase (ABOUT_SPATHI);
+ HierarchyMask |= HIERARCHY_SPATHI;
+ }
+ else if (PLAYER_SAID (R, umgah))
+ {
+ NPCPhrase (ABOUT_UMGAH);
+ HierarchyMask |= HIERARCHY_UMGAH;
+ }
+ else if (PLAYER_SAID (R, androsynth))
+ {
+ NPCPhrase (ABOUT_ANDROSYNTH);
+ HierarchyMask |= HIERARCHY_ANDROSYNTH;
+ }
+ else if (PLAYER_SAID (R, ilwrath))
+ {
+ NPCPhrase (ABOUT_ILWRATH);
+ HierarchyMask |= HIERARCHY_ILWRATH;
+ }
+ else if (PLAYER_SAID (R, vux))
+ {
+ NPCPhrase (ABOUT_VUX);
+ HierarchyMask |= HIERARCHY_VUX;
+ }
+
+ if (!(HierarchyMask & HIERARCHY_URQUAN))
+ Response (urquan, HierarchyInfo);
+ if (!(HierarchyMask & HIERARCHY_MYCON))
+ Response (mycon, HierarchyInfo);
+ if (!(HierarchyMask & HIERARCHY_SPATHI))
+ Response (spathi, HierarchyInfo);
+ if (!(HierarchyMask & HIERARCHY_UMGAH))
+ Response (umgah, HierarchyInfo);
+ if (!(HierarchyMask & HIERARCHY_ANDROSYNTH))
+ Response (androsynth, HierarchyInfo);
+ if (!(HierarchyMask & HIERARCHY_ILWRATH))
+ Response (ilwrath, HierarchyInfo);
+ if (!(HierarchyMask & HIERARCHY_VUX))
+ Response (vux, HierarchyInfo);
+ Response (enough_hierarchy, AlienRaces);
+}
+
+static void
+AlienRaces (RESPONSE_REF R)
+{
+#define RACES_ALLIANCE (1 << 0)
+#define RACES_HIERARCHY (1 << 1)
+#define RACES_OTHER (1 << 2)
+ static BYTE RacesMask = 0;
+
+ if (PLAYER_SAID (R, alien_races))
+ {
+ NPCPhrase (WHICH_ALIEN);
+ RacesMask = 0;
+ }
+ else if (PLAYER_SAID (R, enough_alliance))
+ {
+ NPCPhrase (OK_ENOUGH_ALLIANCE);
+ RacesMask |= RACES_ALLIANCE;
+ }
+ else if (PLAYER_SAID (R, enough_hierarchy))
+ {
+ NPCPhrase (OK_ENOUGH_HIERARCHY);
+ RacesMask |= RACES_HIERARCHY;
+ }
+ else if (PLAYER_SAID (R, what_about_other))
+ {
+ NPCPhrase (ABOUT_OTHER);
+ RacesMask |= RACES_OTHER;
+ }
+
+ if (!(RacesMask & RACES_ALLIANCE))
+ {
+ Response (what_about_alliance, AllianceInfo);
+ }
+ if (!(RacesMask & RACES_HIERARCHY))
+ {
+ Response (what_about_hierarchy, HierarchyInfo);
+ }
+ if (!(RacesMask & RACES_OTHER))
+ {
+ Response (what_about_other, AlienRaces);
+ }
+ Response (enough_aliens, TellHistory);
+}
+
+static void
+WarInfo (RESPONSE_REF R)
+{
+#define WAR_STARTED (1 << 0)
+#define WAR_WAS_LIKE (1 << 1)
+#define WAR_LOST (1 << 2)
+#define WAR_AFTERMATH (1 << 3)
+ static BYTE WarMask = 0;
+
+ if (PLAYER_SAID (R, the_war))
+ {
+ NPCPhrase (WHICH_WAR);
+ WarMask = 0;
+ }
+ else if (PLAYER_SAID (R, what_started_war))
+ {
+ NPCPhrase (URQUAN_STARTED_WAR);
+ WarMask |= WAR_STARTED;
+ }
+ else if (PLAYER_SAID (R, what_was_war_like))
+ {
+ NPCPhrase (WAR_WAS_LIKE_SO);
+ WarMask |= WAR_WAS_LIKE;
+ }
+ else if (PLAYER_SAID (R, why_lose_war))
+ {
+ NPCPhrase (LOST_WAR_BECAUSE);
+ WarMask |= WAR_LOST;
+ }
+ else if (PLAYER_SAID (R, what_after_war))
+ {
+ NPCPhrase (AFTER_WAR);
+ WarMask |= WAR_AFTERMATH;
+ }
+
+ if (!(WarMask & WAR_STARTED))
+ Response (what_started_war, WarInfo);
+ if (!(WarMask & WAR_WAS_LIKE))
+ Response (what_was_war_like, WarInfo);
+ if (!(WarMask & WAR_LOST))
+ Response (why_lose_war, WarInfo);
+ if (!(WarMask & WAR_AFTERMATH))
+ Response (what_after_war, WarInfo);
+ Response (enough_war, TellHistory);
+}
+
+static void
+AncientHistory (RESPONSE_REF R)
+{
+#define ANCIENT_PRECURSORS (1 << 0)
+#define ANCIENT_RACES (1 << 1)
+#define ANCIENT_EARTH (1 << 2)
+ static BYTE AncientMask = 0;
+
+ if (PLAYER_SAID (R, ancient_history))
+ {
+ NPCPhrase (WHICH_ANCIENT);
+ AncientMask = 0;
+ }
+ else if (PLAYER_SAID (R, precursors))
+ {
+ NPCPhrase (ABOUT_PRECURSORS);
+ AncientMask |= ANCIENT_PRECURSORS;
+ }
+ else if (PLAYER_SAID (R, old_races))
+ {
+ NPCPhrase (ABOUT_OLD_RACES);
+ AncientMask |= ANCIENT_RACES;
+ }
+ else if (PLAYER_SAID (R, aliens_on_earth))
+ {
+ NPCPhrase (ABOUT_ALIENS_ON_EARTH);
+ AncientMask |= ANCIENT_EARTH;
+ }
+
+ if (!(AncientMask & ANCIENT_PRECURSORS))
+ Response (precursors, AncientHistory);
+ if (!(AncientMask & ANCIENT_RACES))
+ Response (old_races, AncientHistory);
+ if (!(AncientMask & ANCIENT_EARTH))
+ Response (aliens_on_earth, AncientHistory);
+ Response (enough_ancient, TellHistory);
+}
+
+static void
+TellHistory (RESPONSE_REF R)
+{
+ RESPONSE_REF pstack[3];
+
+ if (PLAYER_SAID (R, history))
+ {
+ NPCPhrase (WHICH_HISTORY);
+ stack0 = 0;
+ stack1 = 0;
+ stack2 = 0;
+ }
+ else if (PLAYER_SAID (R, enough_aliens))
+ {
+ NPCPhrase (OK_ENOUGH_ALIENS);
+
+ stack0 = 1;
+ }
+ else if (PLAYER_SAID (R, enough_war))
+ {
+ NPCPhrase (OK_ENOUGH_WAR);
+
+ stack1 = 1;
+ }
+ else if (PLAYER_SAID (R, enough_ancient))
+ {
+ NPCPhrase (OK_ENOUGH_ANCIENT);
+
+ stack2 = 1;
+ }
+
+ switch (stack0)
+ {
+ case 0:
+ pstack[0] = alien_races;
+ break;
+ default:
+ pstack[0] = 0;
+ break;
+ }
+ switch (stack1)
+ {
+ case 0:
+ pstack[1] = the_war;
+ break;
+ default:
+ pstack[1] = 0;
+ break;
+ }
+ switch (stack2)
+ {
+ case 0:
+ pstack[2] = ancient_history;
+ break;
+ default:
+ pstack[2] = 0;
+ break;
+ }
+
+ if (pstack[0])
+ {
+ Response (pstack[0], AlienRaces);
+ }
+ if (pstack[1])
+ {
+ Response (pstack[1], WarInfo);
+ }
+ if (pstack[2])
+ {
+ Response (pstack[2], AncientHistory);
+ }
+ Response (enough_history, NeedInfo);
+}
+
+static void
+DefeatUrquan (RESPONSE_REF R)
+{
+#define HOW_FIND_URQUAN (1 << 0)
+#define HOW_FIGHT_URQUAN (1 << 1)
+#define HOW_ALLY_AGAINST_URQUAN (1 << 2)
+#define HOW_STRONG_AGAINST_URQUAN (1 << 3)
+ static BYTE DefeatMask = 0;
+
+ if (PLAYER_SAID (R, how_defeat))
+ {
+ NPCPhrase (DEFEAT_LIKE_SO);
+ DefeatMask = 0;
+ }
+ else if (PLAYER_SAID (R, how_find_urquan))
+ {
+ NPCPhrase (FIND_URQUAN);
+ DefeatMask |= HOW_FIND_URQUAN;
+ }
+ else if (PLAYER_SAID (R, how_fight_urquan))
+ {
+ NPCPhrase (FIGHT_URQUAN);
+ DefeatMask |= HOW_FIGHT_URQUAN;
+ }
+ else if (PLAYER_SAID (R, how_ally))
+ {
+ NPCPhrase (ALLY_LIKE_SO);
+ DefeatMask |= HOW_ALLY_AGAINST_URQUAN;
+ }
+ else if (PLAYER_SAID (R, how_get_strong))
+ {
+ NPCPhrase (STRONG_LIKE_SO);
+ DefeatMask |= HOW_STRONG_AGAINST_URQUAN;
+ }
+
+ if (!(DefeatMask & HOW_FIND_URQUAN))
+ Response (how_find_urquan, DefeatUrquan);
+ if (!(DefeatMask & HOW_FIGHT_URQUAN))
+ Response (how_fight_urquan, DefeatUrquan);
+ if (!(DefeatMask & HOW_ALLY_AGAINST_URQUAN))
+ Response (how_ally, DefeatUrquan);
+ if (!(DefeatMask & HOW_STRONG_AGAINST_URQUAN))
+ Response (how_get_strong, DefeatUrquan);
+ Response (enough_defeat, TellMission);
+}
+
+static void
+AnalyzeCondition (void)
+{
+ BYTE i;
+ BYTE num_thrusters = 0,
+ num_jets = 0,
+ num_guns = 0,
+ num_bays = 0,
+ num_batts = 0,
+ num_track = 0,
+ num_defense = 0;
+ BOOLEAN HasMinimum;
+
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (DriveSlots[i]) < EMPTY_SLOT)
+ ++num_thrusters;
+ }
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (JetSlots[i]) < EMPTY_SLOT)
+ ++num_jets;
+ }
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ BYTE which_piece;
+
+ switch (which_piece = GLOBAL_SIS (ModuleSlots[i]))
+ {
+ case STORAGE_BAY:
+ ++num_bays;
+ break;
+ case DYNAMO_UNIT:
+ case SHIVA_FURNACE:
+ num_batts += 1 + (which_piece - DYNAMO_UNIT);
+ break;
+ case GUN_WEAPON:
+ case BLASTER_WEAPON:
+ case CANNON_WEAPON:
+ num_guns += 1 + (which_piece - GUN_WEAPON);
+ break;
+ case TRACKING_SYSTEM:
+ ++num_track;
+ break;
+ case ANTIMISSILE_DEFENSE:
+ ++num_defense;
+ break;
+ }
+ }
+ if (num_track && num_guns)
+ num_guns += 2;
+
+ HasMinimum = (num_thrusters >= 7 && num_jets >= 5
+ && GLOBAL_SIS (CrewEnlisted) >= CREW_POD_CAPACITY
+ && GLOBAL_SIS (FuelOnBoard) >= FUEL_TANK_CAPACITY
+ && num_bays >= 1 && GLOBAL_SIS (NumLanders)
+ && num_batts >= 1 && num_guns >= 2);
+ NPCPhrase (LETS_SEE);
+ if (!HasMinimum && GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
+ {
+ NPCPhrase (IMPROVE_1);
+ if (num_thrusters < 7)
+ NPCPhrase (NEED_THRUSTERS_1);
+ if (num_jets < 5)
+ NPCPhrase (NEED_TURN_1);
+ if (num_guns < 2)
+ NPCPhrase (NEED_GUNS_1);
+ if (GLOBAL_SIS (CrewEnlisted) < CREW_POD_CAPACITY)
+ NPCPhrase (NEED_CREW_1);
+ if (GLOBAL_SIS (FuelOnBoard) < FUEL_TANK_CAPACITY)
+ NPCPhrase (NEED_FUEL_1);
+ if (num_bays < 1)
+ NPCPhrase (NEED_STORAGE_1);
+ if (GLOBAL_SIS (NumLanders) == 0)
+ NPCPhrase (NEED_LANDERS_2);
+ if (num_batts < 1)
+ NPCPhrase (NEED_DYNAMOS_1);
+
+ if (GLOBAL_SIS (ResUnits) >= 3000)
+ NPCPhrase (IMPROVE_FLAGSHIP_WITH_RU);
+ else
+ NPCPhrase (GO_GET_MINERALS);
+ }
+ else
+ {
+ BYTE num_aliens = 0;
+ COUNT FleetStrength;
+ BOOLEAN HasMaximum;
+
+ FleetStrength = CalculateEscortsWorth ();
+ for (i = 0; i < NUM_AVAILABLE_RACES; ++i)
+ {
+ if (i != HUMAN_SHIP && CheckAlliance (i) == GOOD_GUY)
+ ++num_aliens;
+ }
+
+ HasMaximum = (num_thrusters == NUM_DRIVE_SLOTS
+ && num_jets == NUM_JET_SLOTS
+ && GLOBAL_SIS (CrewEnlisted) >= CREW_POD_CAPACITY * 3
+ && GLOBAL_SIS (FuelOnBoard) >= FUEL_TANK_CAPACITY * 3
+ && GLOBAL_SIS (NumLanders) >= 3
+ && num_batts >= 4 && num_guns >= 7 && num_defense >= 2);
+ if (!HasMaximum && GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
+ NPCPhrase (GOT_OK_FLAGSHIP);
+ else
+ NPCPhrase (GOT_AWESOME_FLAGSHIP);
+
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) >= 2)
+ {
+ NPCPhrase (CHMMR_IMPROVED_BOMB);
+ if (FleetStrength < 20000)
+ NPCPhrase (MUST_ACQUIRE_AWESOME_FLEET);
+ else
+ {
+ NPCPhrase (GOT_AWESOME_FLEET);
+ if (!GET_GAME_STATE (TALKING_PET_ON_SHIP))
+ NPCPhrase (MUST_ELIMINATE_URQUAN_GUARDS);
+ else
+ NPCPhrase (GO_DESTROY_SAMATRA);
+ }
+ }
+ else if (num_aliens < 2)
+ NPCPhrase (GO_ALLY_WITH_ALIENS);
+ else
+ {
+ NPCPhrase (MADE_SOME_ALLIES);
+ if (FleetStrength < 6000)
+ {
+ if (GLOBAL_SIS (ResUnits) >= 3000)
+ NPCPhrase (BUY_COMBAT_SHIPS);
+ else
+ NPCPhrase (GET_SHIPS_BY_MINING_OR_ALLIANCE);
+ }
+ else
+ {
+ NPCPhrase (GOT_OK_FLEET);
+ if (!HasMaximum)
+ {
+ NPCPhrase (IMPROVE_2);
+ if (num_thrusters < NUM_DRIVE_SLOTS)
+ NPCPhrase (NEED_THRUSTERS_2);
+ if (num_jets < NUM_JET_SLOTS)
+ NPCPhrase (NEED_TURN_2);
+ if (num_guns < 7)
+ NPCPhrase (NEED_GUNS_2);
+ if (GLOBAL_SIS (CrewEnlisted) < CREW_POD_CAPACITY * 3)
+ NPCPhrase (NEED_CREW_2);
+ if (GLOBAL_SIS (FuelOnBoard) < FUEL_TANK_CAPACITY * 3)
+ NPCPhrase (NEED_FUEL_2);
+ if (GLOBAL_SIS (NumLanders) < 3)
+ NPCPhrase (NEED_LANDERS_1);
+ if (num_batts < 4)
+ NPCPhrase (NEED_DYNAMOS_2);
+ if (num_defense < 2)
+ NPCPhrase (NEED_POINT);
+ }
+ else if (!GET_GAME_STATE (AWARE_OF_SAMATRA))
+ NPCPhrase (GO_LEARN_ABOUT_URQUAN);
+ else
+ {
+ NPCPhrase (KNOW_ABOUT_SAMATRA);
+ if (!GET_GAME_STATE (UTWIG_BOMB))
+ NPCPhrase (FIND_WAY_TO_DESTROY_SAMATRA);
+ else if (GET_GAME_STATE (UTWIG_BOMB_ON_SHIP))
+ NPCPhrase (MUST_INCREASE_BOMB_STRENGTH);
+ }
+ }
+ }
+ }
+}
+
+static void
+TellMission (RESPONSE_REF R)
+{
+ RESPONSE_REF pstack[4];
+
+ if (PLAYER_SAID (R, our_mission))
+ {
+ NPCPhrase (WHICH_MISSION);
+ stack0 = 0;
+ stack1 = 0;
+ stack2 = 0;
+ stack3 = 0;
+ }
+ else if (PLAYER_SAID (R, where_get_minerals))
+ {
+ NPCPhrase (GET_MINERALS);
+
+ stack0 = 1;
+ }
+ else if (PLAYER_SAID (R, what_about_aliens))
+ {
+ NPCPhrase (ABOUT_ALIENS);
+
+ stack1 = 1;
+ }
+ else if (PLAYER_SAID (R, what_do_now))
+ {
+ AnalyzeCondition ();
+
+ stack2 = 1;
+ }
+ else if (PLAYER_SAID (R, what_about_urquan))
+ {
+ NPCPhrase (MUST_DEFEAT);
+
+ stack3 = 1;
+ }
+ else if (PLAYER_SAID (R, enough_defeat))
+ {
+ NPCPhrase (OK_ENOUGH_DEFEAT);
+
+ stack3 = 2;
+ }
+
+ switch (stack0)
+ {
+ case 0:
+ pstack[0] = where_get_minerals;
+ break;
+ default:
+ pstack[0] = 0;
+ break;
+ }
+ switch (stack1)
+ {
+ case 0:
+ pstack[1] = what_about_aliens;
+ break;
+ default:
+ pstack[1] = 0;
+ break;
+ }
+ switch (stack2)
+ {
+ case 0:
+ pstack[2] = what_do_now;
+ break;
+ default:
+ pstack[2] = 0;
+ break;
+ }
+ switch (stack3)
+ {
+ case 0:
+ pstack[3] = what_about_urquan;
+ break;
+ case 1:
+ pstack[3] = how_defeat;
+ break;
+ default:
+ pstack[3] = 0;
+ break;
+ }
+
+ if (pstack[0])
+ Response (pstack[0], TellMission);
+ if (pstack[1])
+ Response (pstack[1], TellMission);
+ if (pstack[2])
+ Response (pstack[2], TellMission);
+ if (pstack[3])
+ {
+ if (stack3 == 1)
+ Response (pstack[3], DefeatUrquan);
+ else
+ Response (pstack[3], TellMission);
+ }
+
+ Response (enough_mission, NeedInfo);
+}
+
+static void
+TellStarBase (RESPONSE_REF R)
+{
+ RESPONSE_REF pstack[4];
+ static UNICODE buf0[80];
+
+ if (PLAYER_SAID (R, starbase_functions))
+ {
+ NPCPhrase (WHICH_FUNCTION);
+ stack0 = 0;
+ stack1 = 0;
+ stack2 = 0;
+ stack3 = 0;
+ }
+ else if (PLAYER_SAID (R, tell_me_about_fuel0))
+ {
+ NPCPhrase (ABOUT_FUEL);
+
+ stack1 = 1;
+ }
+ else if (PLAYER_SAID (R, tell_me_about_crew))
+ {
+ NPCPhrase (ABOUT_CREW0);
+ if (usingSpeech)
+ NPCPhrase (YOUR_FLAGSHIP_3DO2);
+ else {
+ NPCPhrase (YOUR_FLAGSHIP_PC);
+ NPCPhrase (GLOBAL_SHIP_NAME);
+ }
+ NPCPhrase (ABOUT_CREW1);
+
+ stack2 = 2;
+ }
+ else if (PLAYER_SAID (R, tell_me_about_modules0))
+ {
+ NPCPhrase (ABOUT_MODULES);
+
+ stack0 = 1;
+ }
+ else if (PLAYER_SAID (R, tell_me_about_ships))
+ {
+ NPCPhrase (ABOUT_SHIPS);
+
+ stack2 = 1;
+ }
+ else if (PLAYER_SAID (R, tell_me_about_ru))
+ {
+ NPCPhrase (ABOUT_RU);
+
+ stack3 = 1;
+ }
+ else if (PLAYER_SAID (R, tell_me_about_minerals))
+ {
+ NPCPhrase (ABOUT_MINERALS);
+
+ stack3 = 2;
+ }
+ else if (PLAYER_SAID (R, tell_me_about_life))
+ {
+ NPCPhrase (ABOUT_LIFE);
+
+ stack3 = 3;
+ }
+
+ switch (stack0)
+ {
+ case 0:
+ construct_response (
+ buf0,
+ tell_me_about_modules0,
+ GLOBAL_SIS (ShipName),
+ tell_me_about_modules1,
+ (UNICODE*)NULL);
+ pstack[0] = tell_me_about_modules0;
+ break;
+ default:
+ pstack[0] = 0;
+ break;
+ }
+ switch (stack1)
+ {
+ case 0:
+ construct_response (
+ shared_phrase_buf,
+ tell_me_about_fuel0,
+ GLOBAL_SIS (ShipName),
+ tell_me_about_fuel1,
+ (UNICODE*)NULL);
+ pstack[1] = tell_me_about_fuel0;
+ break;
+ default:
+ pstack[1] = 0;
+ break;
+ }
+ switch (stack2)
+ {
+ case 0:
+ pstack[2] = tell_me_about_ships;
+ break;
+ case 1:
+ pstack[2] = tell_me_about_crew;
+ break;
+ default:
+ pstack[2] = 0;
+ break;
+ }
+ switch (stack3)
+ {
+ case 0:
+ pstack[3] = tell_me_about_ru;
+ break;
+ case 1:
+ pstack[3] = tell_me_about_minerals;
+ break;
+ case 2:
+ pstack[3] = tell_me_about_life;
+ break;
+ default:
+ pstack[3] = 0;
+ break;
+ }
+
+ if (pstack[0])
+ DoResponsePhrase (pstack[0], TellStarBase, buf0);
+ if (pstack[1])
+ DoResponsePhrase (pstack[1], TellStarBase, shared_phrase_buf);
+ if (pstack[2])
+ Response (pstack[2], TellStarBase);
+ if (pstack[3])
+ Response (pstack[3], TellStarBase);
+
+ Response (enough_starbase, NeedInfo);
+}
+
+static void NormalStarbase (RESPONSE_REF R);
+
+static void
+NeedInfo (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, need_info))
+ NPCPhrase (WHAT_KIND_OF_INFO);
+ else if (PLAYER_SAID (R, enough_starbase))
+ NPCPhrase (OK_ENOUGH_STARBASE);
+ else if (PLAYER_SAID (R, enough_history))
+ NPCPhrase (OK_ENOUGH_HISTORY);
+ else if (PLAYER_SAID (R, enough_mission))
+ NPCPhrase (OK_ENOUGH_MISSION);
+
+ Response (starbase_functions, TellStarBase);
+ Response (history, TellHistory);
+ Response (our_mission, TellMission);
+ Response (no_need_info, NormalStarbase);
+}
+
+static BOOLEAN
+DiscussDevices (BOOLEAN TalkAbout)
+{
+ COUNT i, VuxBeastIndex, PhraseIndex;
+ BOOLEAN Undiscussed;
+
+ if (TalkAbout)
+ NPCPhrase (DEVICE_HEAD);
+ PhraseIndex = 2;
+
+ VuxBeastIndex = 0;
+ Undiscussed = FALSE;
+ for (i = 0; i < NUM_DEVICES; ++i)
+ {
+ RESPONSE_REF pStr;
+
+ pStr = 0;
+ switch (i)
+ {
+ case ROSY_SPHERE_DEVICE:
+ if (GET_GAME_STATE (ROSY_SPHERE_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_ROSY_SPHERE))
+ {
+ pStr = ABOUT_SPHERE;
+ SET_GAME_STATE (DISCUSSED_ROSY_SPHERE, TalkAbout);
+ }
+ break;
+ case ARTIFACT_2_DEVICE:
+ if (GET_GAME_STATE (ARTIFACT_2_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_ARTIFACT_2))
+ {
+ pStr = ABOUT_ARTIFACT_2;
+ SET_GAME_STATE (DISCUSSED_ARTIFACT_2, TalkAbout);
+ }
+ break;
+ case ARTIFACT_3_DEVICE:
+ if (GET_GAME_STATE (ARTIFACT_3_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_ARTIFACT_3))
+ {
+ pStr = ABOUT_ARTIFACT_3;
+ SET_GAME_STATE (DISCUSSED_ARTIFACT_3, TalkAbout);
+ }
+ break;
+ case SUN_EFFICIENCY_DEVICE:
+ if (GET_GAME_STATE (SUN_DEVICE_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_SUN_EFFICIENCY))
+ {
+ pStr = ABOUT_SUN;
+ SET_GAME_STATE (DISCUSSED_SUN_EFFICIENCY, TalkAbout);
+ }
+ break;
+ case UTWIG_BOMB_DEVICE:
+ if (GET_GAME_STATE (UTWIG_BOMB_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_UTWIG_BOMB))
+ {
+ pStr = ABOUT_BOMB;
+ SET_GAME_STATE (DISCUSSED_UTWIG_BOMB, TalkAbout);
+ }
+ break;
+ case ULTRON_0_DEVICE:
+ if (GET_GAME_STATE (ULTRON_CONDITION) == 1
+ && !GET_GAME_STATE (DISCUSSED_ULTRON))
+ {
+ pStr = ABOUT_ULTRON_0;
+ SET_GAME_STATE (DISCUSSED_ULTRON, TalkAbout);
+ }
+ break;
+ case ULTRON_1_DEVICE:
+ if (GET_GAME_STATE (ULTRON_CONDITION) == 2
+ && !GET_GAME_STATE (DISCUSSED_ULTRON))
+ {
+ pStr = ABOUT_ULTRON_1;
+ SET_GAME_STATE (DISCUSSED_ULTRON, TalkAbout);
+ }
+ break;
+ case ULTRON_2_DEVICE:
+ if (GET_GAME_STATE (ULTRON_CONDITION) == 3
+ && !GET_GAME_STATE (DISCUSSED_ULTRON))
+ {
+ pStr = ABOUT_ULTRON_2;
+ SET_GAME_STATE (DISCUSSED_ULTRON, TalkAbout);
+ }
+ break;
+ case ULTRON_3_DEVICE:
+ if (GET_GAME_STATE (ULTRON_CONDITION) == 4
+ && !GET_GAME_STATE (DISCUSSED_ULTRON))
+ {
+ pStr = ABOUT_ULTRON_3;
+ SET_GAME_STATE (DISCUSSED_ULTRON, TalkAbout);
+ }
+ break;
+ case MAIDENS_DEVICE:
+ if (GET_GAME_STATE (MAIDENS_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_MAIDENS))
+ {
+ pStr = ABOUT_MAIDENS;
+ SET_GAME_STATE (DISCUSSED_MAIDENS, TalkAbout);
+ }
+ break;
+ case TALKING_PET_DEVICE:
+ if (GET_GAME_STATE (TALKING_PET_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_TALKING_PET))
+ {
+ pStr = ABOUT_TALKPET;
+ SET_GAME_STATE (DISCUSSED_TALKING_PET, TalkAbout);
+ }
+ break;
+ case AQUA_HELIX_DEVICE:
+ if (GET_GAME_STATE (AQUA_HELIX_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_AQUA_HELIX))
+ {
+ pStr = ABOUT_HELIX;
+ SET_GAME_STATE (DISCUSSED_AQUA_HELIX, TalkAbout);
+ }
+ break;
+ case CLEAR_SPINDLE_DEVICE:
+ if (GET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_CLEAR_SPINDLE))
+ {
+ pStr = ABOUT_SPINDLE;
+ SET_GAME_STATE (DISCUSSED_CLEAR_SPINDLE, TalkAbout);
+ }
+ break;
+ case UMGAH_HYPERWAVE_DEVICE:
+ if (GET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_UMGAH_HYPERWAVE))
+ {
+ pStr = ABOUT_UCASTER;
+ SET_GAME_STATE (DISCUSSED_UMGAH_HYPERWAVE, TalkAbout);
+ }
+ break;
+#ifdef NEVER
+ case DATA_PLATE_1_DEVICE:
+ if (GET_GAME_STATE (DATA_PLATE_1_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_DATA_PLATE_1))
+ {
+ pStr = ABOUT_DATAPLATE_1;
+ SET_GAME_STATE (DISCUSSED_DATA_PLATE_1, TalkAbout);
+ }
+ break;
+ case DATA_PLATE_2_DEVICE:
+ if (GET_GAME_STATE (DATA_PLATE_2_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_DATA_PLATE_2))
+ {
+ pStr = ABOUT_DATAPLATE_2;
+ SET_GAME_STATE (DISCUSSED_DATA_PLATE_2, TalkAbout);
+ }
+ break;
+ case DATA_PLATE_3_DEVICE:
+ if (GET_GAME_STATE (DATA_PLATE_3_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_DATA_PLATE_3))
+ {
+ pStr = ABOUT_DATAPLATE_3;
+ SET_GAME_STATE (DISCUSSED_DATA_PLATE_3, TalkAbout);
+ }
+ break;
+#endif /* NEVER */
+ case TAALO_PROTECTOR_DEVICE:
+ if (GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_TAALO_PROTECTOR))
+ {
+ pStr = ABOUT_SHIELD;
+ SET_GAME_STATE (DISCUSSED_TAALO_PROTECTOR, TalkAbout);
+ }
+ break;
+ case EGG_CASING0_DEVICE:
+ if (GET_GAME_STATE (EGG_CASE0_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_EGG_CASING0))
+ {
+ pStr = ABOUT_EGGCASE_0;
+ SET_GAME_STATE (DISCUSSED_EGG_CASING0, TalkAbout);
+ SET_GAME_STATE (DISCUSSED_EGG_CASING1, TalkAbout);
+ SET_GAME_STATE (DISCUSSED_EGG_CASING2, TalkAbout);
+ }
+ break;
+ case EGG_CASING1_DEVICE:
+ if (GET_GAME_STATE (EGG_CASE1_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_EGG_CASING1))
+ {
+ pStr = ABOUT_EGGCASE_0;
+ SET_GAME_STATE (DISCUSSED_EGG_CASING0, TalkAbout);
+ SET_GAME_STATE (DISCUSSED_EGG_CASING1, TalkAbout);
+ SET_GAME_STATE (DISCUSSED_EGG_CASING2, TalkAbout);
+ }
+ break;
+ case EGG_CASING2_DEVICE:
+ if (GET_GAME_STATE (EGG_CASE2_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_EGG_CASING2))
+ {
+ pStr = ABOUT_EGGCASE_0;
+ SET_GAME_STATE (DISCUSSED_EGG_CASING0, TalkAbout);
+ SET_GAME_STATE (DISCUSSED_EGG_CASING1, TalkAbout);
+ SET_GAME_STATE (DISCUSSED_EGG_CASING2, TalkAbout);
+ }
+ break;
+ case SYREEN_SHUTTLE_DEVICE:
+ if (GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_SYREEN_SHUTTLE))
+ {
+ pStr = ABOUT_SHUTTLE;
+ SET_GAME_STATE (DISCUSSED_SYREEN_SHUTTLE, TalkAbout);
+ }
+ break;
+ case VUX_BEAST_DEVICE:
+ if (GET_GAME_STATE (VUX_BEAST_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_VUX_BEAST))
+ {
+ pStr = ABOUT_VUXBEAST0;
+ SET_GAME_STATE (DISCUSSED_VUX_BEAST, TalkAbout);
+ }
+ break;
+ case DESTRUCT_CODE_DEVICE:
+ if (GET_GAME_STATE (DESTRUCT_CODE_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_DESTRUCT_CODE))
+ {
+ pStr = ABOUT_DESTRUCT;
+ SET_GAME_STATE (DISCUSSED_DESTRUCT_CODE, TalkAbout);
+ }
+ break;
+ case PORTAL_SPAWNER_DEVICE:
+ if (GET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_PORTAL_SPAWNER))
+ {
+ pStr = ABOUT_PORTAL;
+ SET_GAME_STATE (DISCUSSED_PORTAL_SPAWNER, TalkAbout);
+ }
+ break;
+ case URQUAN_WARP_DEVICE:
+ if (GET_GAME_STATE (PORTAL_KEY_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_URQUAN_WARP))
+ {
+ pStr = ABOUT_WARPPOD;
+ SET_GAME_STATE (DISCUSSED_URQUAN_WARP, TalkAbout);
+ }
+ break;
+ case BURVIX_HYPERWAVE_DEVICE:
+ if (GET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP)
+ && !GET_GAME_STATE (DISCUSSED_BURVIX_HYPERWAVE))
+ {
+ pStr = ABOUT_BCASTER;
+ SET_GAME_STATE (DISCUSSED_BURVIX_HYPERWAVE, TalkAbout);
+ }
+ break;
+ }
+
+ if (pStr)
+ {
+ if (TalkAbout)
+ {
+ if (PhraseIndex > 2)
+ NPCPhrase (BETWEEN_DEVICES);
+ NPCPhrase (pStr);
+ if (pStr == ABOUT_VUXBEAST0)
+ {
+ VuxBeastIndex = ++PhraseIndex;
+ NPCPhrase (ABOUT_VUXBEAST1);
+ }
+ }
+ PhraseIndex += 2;
+ }
+ }
+
+ if (TalkAbout)
+ {
+ NPCPhrase (DEVICE_TAIL);
+
+ if (VuxBeastIndex)
+ {
+ // Run all tracks upto the Vux Beast scientist's report
+ AlienTalkSegue (VuxBeastIndex - 1);
+ // Disable Commander's speech animation and run the report
+ EnableTalkingAnim (FALSE);
+ AlienTalkSegue (VuxBeastIndex);
+ // Enable Commander's speech animation and run the rest
+ EnableTalkingAnim (TRUE);
+ AlienTalkSegue ((COUNT)~0);
+ }
+ }
+
+ return (PhraseIndex > 2);
+}
+
+static BOOLEAN
+CheckTiming (COUNT month_index, COUNT day_index)
+{
+ COUNT mi, year_index;
+ BYTE days_in_month[12] =
+ {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+ };
+
+ mi = GET_GAME_STATE (STARBASE_MONTH);
+ year_index = START_YEAR;
+
+ day_index += GET_GAME_STATE (STARBASE_DAY);
+ while (day_index > days_in_month[mi - 1])
+ {
+ day_index -= days_in_month[mi - 1];
+ if (++mi > 12)
+ {
+ mi = 1;
+ ++year_index;
+ }
+ }
+
+ month_index += mi;
+ year_index += (month_index - 1) / 12;
+ month_index = ((month_index - 1) % 12) + 1;
+
+ return (year_index < GLOBAL (GameClock.year_index)
+ || (year_index == GLOBAL (GameClock.year_index)
+ && (month_index < GLOBAL (GameClock.month_index)
+ || (month_index == GLOBAL (GameClock.month_index)
+ && day_index < GLOBAL (GameClock.day_index)))));
+}
+
+static void
+CheckBulletins (BOOLEAN Repeat)
+{
+ RESPONSE_REF pIntro;
+ BYTE b0;
+ DWORD BulletinMask;
+
+ if (Repeat)
+ BulletinMask = CurBulletinMask ^ 0xFFFFFFFFL;
+ else
+ BulletinMask = GET_GAME_STATE_32 (STARBASE_BULLETS0);
+
+ pIntro = 0;
+ for (b0 = 0; b0 < 32; ++b0)
+ {
+ if (!(BulletinMask & (1L << b0)))
+ {
+ RESPONSE_REF pStr;
+
+ pStr = 0;
+ switch (b0)
+ {
+ case 0:
+ if (CheckAlliance (SPATHI_SHIP) == GOOD_GUY)
+ {
+ pStr = STARBASE_BULLETIN_1;
+ }
+ break;
+ case 1:
+ if (CheckAlliance (ZOQFOTPIK_SHIP) == GOOD_GUY)
+ {
+ pStr = STARBASE_BULLETIN_2;
+ }
+ break;
+ case 2:
+ if (CheckAlliance (SUPOX_SHIP) == GOOD_GUY)
+ {
+ pStr = STARBASE_BULLETIN_3;
+ }
+ break;
+ case 3:
+ if (CheckAlliance (UTWIG_SHIP) == GOOD_GUY)
+ {
+ pStr = STARBASE_BULLETIN_4;
+ }
+ break;
+ case 4:
+ if (CheckAlliance (ORZ_SHIP) == GOOD_GUY)
+ {
+ pStr = STARBASE_BULLETIN_5;
+ }
+ break;
+ case 5:
+ if (GET_GAME_STATE (ARILOU_MANNER) == 2)
+ BulletinMask |= 1L << b0;
+ else if (GET_GAME_STATE (PORTAL_SPAWNER)
+ && (Repeat || EscortFeasibilityStudy (
+ ARILOU_SHIP)))
+ {
+#define NUM_GIFT_ARILOUS 3
+ pStr = STARBASE_BULLETIN_6;
+ if (!Repeat)
+ AddEscortShips (ARILOU_SHIP, NUM_GIFT_ARILOUS);
+ }
+ break;
+ case 6:
+ if (GET_GAME_STATE (ZOQFOT_DISTRESS) == 1)
+ {
+ pStr = STARBASE_BULLETIN_7;
+ }
+ break;
+ case 7:
+ if (GET_GAME_STATE (MET_MELNORME))
+ BulletinMask |= 1L << b0;
+ else if (CheckTiming (3, 0))
+ {
+ pStr = STARBASE_BULLETIN_8;
+ }
+ break;
+ case 8:
+ if (GET_GAME_STATE (MET_MELNORME))
+ BulletinMask |= 1L << b0;
+ else if (CheckTiming (6, 0))
+ {
+ pStr = STARBASE_BULLETIN_9;
+ }
+ break;
+ case 9:
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI))
+ BulletinMask |= 1L << b0;
+ else if (CheckTiming (0, 7))
+ {
+ pStr = STARBASE_BULLETIN_10;
+ }
+ break;
+ case 10:
+ if (GET_GAME_STATE (SPATHI_SHIELDED_SELVES))
+ {
+ pStr = STARBASE_BULLETIN_11;
+ }
+ break;
+ case 11:
+ if (GET_GAME_STATE (ZOQFOT_HOME_VISITS)
+ || GET_GAME_STATE_32 (ZOQFOT_GRPOFFS0))
+ BulletinMask |= 1L << b0;
+ else if (CheckTiming (0, 42))
+ {
+ pStr = STARBASE_BULLETIN_12;
+ }
+ break;
+ case 12:
+ if (CheckAlliance (CHMMR_SHIP) == GOOD_GUY)
+ {
+ pStr = STARBASE_BULLETIN_13;
+ }
+ break;
+ case 13:
+ if (CheckAlliance (SHOFIXTI_SHIP) == GOOD_GUY)
+ {
+ pStr = STARBASE_BULLETIN_14;
+ }
+ break;
+ case 14:
+ if (GET_GAME_STATE (PKUNK_MISSION))
+ {
+ pStr = STARBASE_BULLETIN_15;
+ }
+ break;
+ case 15:
+ if (GET_GAME_STATE (DESTRUCT_CODE_ON_SHIP))
+ BulletinMask |= 1L << b0;
+ else if (CheckTiming (7, 0))
+ {
+ pStr = STARBASE_BULLETIN_16;
+ }
+ break;
+ case 16:
+ break;
+ case 17:
+ if (GET_GAME_STATE (YEHAT_ABSORBED_PKUNK))
+ {
+ pStr = STARBASE_BULLETIN_18;
+ }
+ break;
+ case 18:
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ {
+ pStr = STARBASE_BULLETIN_19;
+ }
+ break;
+ case 19:
+ break;
+ case 20:
+ break;
+ case 21:
+ if (GET_GAME_STATE (ZOQFOT_DISTRESS) == 2)
+ {
+ pStr = STARBASE_BULLETIN_22;
+ }
+ break;
+ case 22:
+ break;
+ case 23:
+ break;
+ case 24:
+ break;
+ case 25:
+ break;
+ case 26:
+ {
+ COUNT crew_sold;
+
+ crew_sold = MAKE_WORD (
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE0),
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE1)
+ );
+ if (crew_sold > 100)
+ BulletinMask |= 1L << b0;
+ else if (crew_sold)
+ {
+ pStr = STARBASE_BULLETIN_27;
+ }
+ break;
+ }
+ case 27:
+ {
+ COUNT crew_sold;
+
+ crew_sold = MAKE_WORD (
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE0),
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE1)
+ );
+ if (crew_sold > 250)
+ BulletinMask |= 1L << b0;
+ else if (crew_sold > 100)
+ {
+ pStr = STARBASE_BULLETIN_28;
+ }
+ break;
+ }
+ case 28:
+ {
+ COUNT crew_bought;
+
+ crew_bought = MAKE_WORD (
+ GET_GAME_STATE (CREW_PURCHASED0),
+ GET_GAME_STATE (CREW_PURCHASED1)
+ );
+ if (crew_bought >= CREW_EXPENSE_THRESHOLD)
+ {
+ pStr = STARBASE_BULLETIN_29;
+ }
+ break;
+ }
+ case 29:
+ if (MAKE_WORD (
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE0),
+ GET_GAME_STATE (CREW_SOLD_TO_DRUUGE1)
+ ) > 250)
+ {
+ pStr = STARBASE_BULLETIN_30;
+ }
+ break;
+ case 30:
+ break;
+ case 31:
+ break;
+ }
+
+ if (pStr)
+ {
+ if (pIntro)
+ NPCPhrase (BETWEEN_BULLETINS);
+ else if (Repeat)
+ pIntro = BEFORE_WE_GO_ON_1;
+ else
+ {
+ switch ((BYTE)TFB_Random () % 7)
+ {
+ case 0:
+ pIntro = BEFORE_WE_GO_ON_1;
+ break;
+ case 1:
+ pIntro = BEFORE_WE_GO_ON_2;
+ break;
+ case 2:
+ pIntro = BEFORE_WE_GO_ON_3;
+ break;
+ case 3:
+ pIntro = BEFORE_WE_GO_ON_4;
+ break;
+ case 4:
+ pIntro = BEFORE_WE_GO_ON_5;
+ break;
+ case 5:
+ pIntro = BEFORE_WE_GO_ON_6;
+ break;
+ default:
+ pIntro = BEFORE_WE_GO_ON_7;
+ break;
+ }
+
+ NPCPhrase (pIntro);
+ }
+
+ NPCPhrase (pStr);
+ CurBulletinMask |= 1L << b0;
+ }
+ }
+ }
+
+ if (pIntro == 0 && GET_GAME_STATE (STARBASE_VISITED))
+ NPCPhrase (RETURN_HELLO);
+ else if (!Repeat)
+ SET_GAME_STATE_32 (STARBASE_BULLETS0, BulletinMask);
+}
+
+static void
+NormalStarbase (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, no_need_info))
+ NPCPhrase (OK_NO_NEED_INFO);
+ else if (PLAYER_SAID (R, new_devices))
+ DiscussDevices (TRUE);
+ else if (PLAYER_SAID (R, repeat_bulletins))
+ CheckBulletins (TRUE);
+ else if (R == 0)
+ {
+ if (GET_GAME_STATE (MOONBASE_ON_SHIP))
+ {
+ NPCPhrase (STARBASE_IS_READY_A);
+ if (usingSpeech)
+ NPCPhrase (YOUR_FLAGSHIP_3DO1);
+ else {
+ NPCPhrase (YOUR_FLAGSHIP_PC);
+ NPCPhrase (GLOBAL_SHIP_NAME);
+ }
+ NPCPhrase (STARBASE_IS_READY_B);
+ if (usingSpeech)
+ NPCPhrase (YOUR_FLAGSHIP_3DO0);
+ else
+ NPCPhrase (GLOBAL_SHIP_NAME);
+ NPCPhrase (STARBASE_IS_READY_C);
+ DeltaSISGauges (0, 0, 2500);
+ SET_GAME_STATE (STARBASE_MONTH,
+ GLOBAL (GameClock.month_index));
+ SET_GAME_STATE (STARBASE_DAY,
+ GLOBAL (GameClock.day_index));
+ }
+ else if (GET_GAME_STATE (STARBASE_VISITED))
+ {
+ CheckBulletins (FALSE);
+ }
+ else
+ {
+ RESPONSE_REF pStr0 = 0;
+ RESPONSE_REF pStr1 = 0;
+
+ switch ((BYTE)TFB_Random () & 7)
+ {
+ case 0:
+ pStr0 = NORMAL_HELLO_A0;
+ pStr1 = NORMAL_HELLO_A1;
+ break;
+ case 1:
+ pStr0 = NORMAL_HELLO_B0;
+ pStr1 = NORMAL_HELLO_B1;
+ break;
+ case 2:
+ pStr0 = NORMAL_HELLO_C0;
+ pStr1 = NORMAL_HELLO_C1;
+ break;
+ case 3:
+ pStr0 = NORMAL_HELLO_D0;
+ pStr1 = NORMAL_HELLO_D1;
+ break;
+ case 4:
+ pStr0 = NORMAL_HELLO_E0;
+ pStr1 = NORMAL_HELLO_E1;
+ break;
+ case 5:
+ pStr0 = NORMAL_HELLO_F0;
+ pStr1 = NORMAL_HELLO_F1;
+ break;
+ case 6:
+ pStr0 = NORMAL_HELLO_G0;
+ pStr1 = NORMAL_HELLO_G1;
+ break;
+ case 7:
+ pStr0 = NORMAL_HELLO_H0;
+ pStr1 = NORMAL_HELLO_H1;
+ break;
+ }
+ NPCPhrase (pStr0);
+ if (!usingSpeech)
+ {
+ NPCPhrase (SPACE);
+ NPCPhrase (GLOBAL_PLAYER_NAME);
+ }
+ NPCPhrase (pStr1);
+ CheckBulletins (FALSE);
+ }
+
+ SET_GAME_STATE (STARBASE_VISITED, 1);
+ }
+
+ if (GLOBAL_SIS (TotalElementMass))
+ Response (have_minerals, SellMinerals);
+ if (DiscussDevices (FALSE))
+ Response (new_devices, NormalStarbase);
+ if (CurBulletinMask)
+ Response (repeat_bulletins, NormalStarbase);
+ Response (need_info, NeedInfo);
+ Response (goodbye_commander, ByeBye);
+}
+
+static void
+SellMinerals (RESPONSE_REF R)
+{
+ COUNT i, total;
+ BOOLEAN Sleepy;
+ RESPONSE_REF pStr1 = 0;
+ RESPONSE_REF pStr2 = 0;
+
+ total = 0;
+ Sleepy = TRUE;
+ for (i = 0; i < NUM_ELEMENT_CATEGORIES; ++i)
+ {
+ COUNT amount;
+ DWORD TimeIn = 0;
+
+ if (i == 0)
+ {
+ DrawCargoStrings ((BYTE)~0, (BYTE)~0);
+ SleepThread (ONE_SECOND / 2);
+ TimeIn = GetTimeCounter ();
+ DrawCargoStrings ((BYTE)0, (BYTE)0);
+ }
+ else if (Sleepy)
+ {
+ DrawCargoStrings ((BYTE)(i - 1), (BYTE)i);
+ TimeIn = GetTimeCounter ();
+ }
+
+ if ((amount = GLOBAL_SIS (ElementAmounts[i])) != 0)
+ {
+ total += amount * GLOBAL (ElementWorth[i]);
+ do
+ {
+ if (!Sleepy || AnyButtonPress (TRUE) ||
+ (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ Sleepy = FALSE;
+ GLOBAL_SIS (ElementAmounts[i]) = 0;
+ GLOBAL_SIS (TotalElementMass) -= amount;
+ DeltaSISGauges (0, 0, amount * GLOBAL (ElementWorth[i]));
+ break;
+ }
+
+ --GLOBAL_SIS (ElementAmounts[i]);
+ --GLOBAL_SIS (TotalElementMass);
+ TaskSwitch ();
+ TimeIn = GetTimeCounter ();
+ DrawCargoStrings ((BYTE)i, (BYTE)i);
+ ShowRemainingCapacity ();
+ DeltaSISGauges (0, 0, GLOBAL (ElementWorth[i]));
+ } while (--amount);
+ }
+ if (Sleepy) {
+ SleepThreadUntil (TimeIn + (ONE_SECOND / 4));
+ TimeIn = GetTimeCounter ();
+ }
+ }
+ SleepThread (ONE_SECOND / 2);
+
+ ClearSISRect (DRAW_SIS_DISPLAY);
+// DrawStorageBays (FALSE);
+
+ if (total < 1000)
+ {
+ total = GET_GAME_STATE (LIGHT_MINERAL_LOAD);
+ switch (total++)
+ {
+ case 0:
+ pStr1 = LIGHT_LOAD_A0;
+ pStr2 = LIGHT_LOAD_A1;
+ break;
+ case 1:
+ pStr1 = LIGHT_LOAD_B0;
+ pStr2 = LIGHT_LOAD_B1;
+ break;
+ case 2:
+ pStr1 = LIGHT_LOAD_C0;
+ pStr2 = LIGHT_LOAD_C1;
+ break;
+ case 3:
+ pStr1 = LIGHT_LOAD_D0;
+ pStr2 = LIGHT_LOAD_D1;
+ break;
+ case 4:
+ pStr1 = LIGHT_LOAD_E0;
+ pStr2 = LIGHT_LOAD_E1;
+ break;
+ case 5:
+ pStr1 = LIGHT_LOAD_F0;
+ pStr2 = LIGHT_LOAD_F1;
+ break;
+ case 6:
+ --total;
+ pStr1 = LIGHT_LOAD_G0;
+ pStr2 = LIGHT_LOAD_G1;
+ break;
+ }
+ SET_GAME_STATE (LIGHT_MINERAL_LOAD, total);
+ }
+ else if (total < 2500)
+ {
+ total = GET_GAME_STATE (MEDIUM_MINERAL_LOAD);
+ switch (total++)
+ {
+ case 0:
+ pStr1 = MEDIUM_LOAD_A0;
+ pStr2 = MEDIUM_LOAD_A1;
+ break;
+ case 1:
+ pStr1 = MEDIUM_LOAD_B0;
+ pStr2 = MEDIUM_LOAD_B1;
+ break;
+ case 2:
+ pStr1 = MEDIUM_LOAD_C0;
+ pStr2 = MEDIUM_LOAD_C1;
+ break;
+ case 3:
+ pStr1 = MEDIUM_LOAD_D0;
+ pStr2 = MEDIUM_LOAD_D1;
+ break;
+ case 4:
+ pStr1 = MEDIUM_LOAD_E0;
+ pStr2 = MEDIUM_LOAD_E1;
+ break;
+ case 5:
+ pStr1 = MEDIUM_LOAD_F0;
+ pStr2 = MEDIUM_LOAD_F1;
+ break;
+ case 6:
+ --total;
+ pStr1 = MEDIUM_LOAD_G0;
+ pStr2 = MEDIUM_LOAD_G1;
+ break;
+ }
+ SET_GAME_STATE (MEDIUM_MINERAL_LOAD, total);
+ }
+ else
+ {
+ total = GET_GAME_STATE (HEAVY_MINERAL_LOAD);
+ switch (total++)
+ {
+ case 0:
+ pStr1 = HEAVY_LOAD_A0;
+ pStr2 = HEAVY_LOAD_A1;
+ break;
+ case 1:
+ pStr1 = HEAVY_LOAD_B0;
+ pStr2 = HEAVY_LOAD_B1;
+ break;
+ case 2:
+ pStr1 = HEAVY_LOAD_C0;
+ pStr2 = HEAVY_LOAD_C1;
+ break;
+ case 3:
+ pStr1 = HEAVY_LOAD_D0;
+ pStr2 = HEAVY_LOAD_D1;
+ break;
+ case 4:
+ pStr1 = HEAVY_LOAD_E0;
+ pStr2 = HEAVY_LOAD_E1;
+ break;
+ case 5:
+ pStr1 = HEAVY_LOAD_F0;
+ pStr2 = HEAVY_LOAD_F1;
+ break;
+ case 6:
+ --total;
+ pStr1 = HEAVY_LOAD_G0;
+ pStr2 = HEAVY_LOAD_G1;
+ break;
+ }
+ SET_GAME_STATE (HEAVY_MINERAL_LOAD, total);
+ }
+
+ NPCPhrase (pStr1);
+ if (!usingSpeech)
+ {
+ NPCPhrase (SPACE);
+ NPCPhrase (GLOBAL_PLAYER_NAME);
+ }
+ NPCPhrase (pStr2);
+
+ NormalStarbase (R);
+}
+
+static void
+Intro (void)
+{
+ NormalStarbase (0);
+}
+
+static COUNT
+uninit_starbase (void)
+{
+ return (0);
+}
+
+static void
+post_starbase_enc (void)
+{
+ SET_GAME_STATE (MOONBASE_ON_SHIP, 0);
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ {
+ SET_GAME_STATE (CHMMR_BOMB_STATE, 3);
+ }
+}
+
+LOCDATA*
+init_starbase_comm ()
+{
+ LOCDATA *retval;
+
+ commander_desc.init_encounter_func = Intro;
+ commander_desc.post_encounter_func = post_starbase_enc;
+ commander_desc.uninit_encounter_func = uninit_starbase;
+
+ commander_desc.AlienTextWidth = 143;
+ commander_desc.AlienTextBaseline.x = 164;
+ commander_desc.AlienTextBaseline.y = 20;
+
+ // use alternate Starbase track if available
+ commander_desc.AlienAltSongRes = STARBASE_ALT_MUSIC;
+ commander_desc.AlienSongFlags |= LDASF_USE_ALTERNATE;
+
+ CurBulletinMask = 0;
+ setSegue (Segue_peace);
+ retval = &commander_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/starbas/strings.h b/src/uqm/comm/starbas/strings.h
new file mode 100644
index 0000000..df123f3
--- /dev/null
+++ b/src/uqm/comm/starbas/strings.h
@@ -0,0 +1,327 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef STARBAS_STRINGS_H
+#define STARBAS_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ BEFORE_WE_GO_ON_1,
+ BEFORE_WE_GO_ON_2,
+ BEFORE_WE_GO_ON_3,
+ BEFORE_WE_GO_ON_4,
+ BEFORE_WE_GO_ON_5,
+ BEFORE_WE_GO_ON_6,
+ BEFORE_WE_GO_ON_7,
+ NORMAL_HELLO_A0,
+ NORMAL_HELLO_A1,
+ NORMAL_HELLO_B0,
+ NORMAL_HELLO_B1,
+ NORMAL_HELLO_C0,
+ NORMAL_HELLO_C1,
+ NORMAL_HELLO_D0,
+ NORMAL_HELLO_D1,
+ NORMAL_HELLO_E0,
+ NORMAL_HELLO_E1,
+ NORMAL_HELLO_F0,
+ NORMAL_HELLO_F1,
+ NORMAL_HELLO_G0,
+ NORMAL_HELLO_G1,
+ NORMAL_HELLO_H0,
+ NORMAL_HELLO_H1,
+ RETURN_HELLO,
+ NORMAL_HELLO_TAIL,
+ NORMAL_GOODBYE_A0,
+ NORMAL_GOODBYE_A1,
+ NORMAL_GOODBYE_B0,
+ NORMAL_GOODBYE_B1,
+ NORMAL_GOODBYE_C0,
+ NORMAL_GOODBYE_C1,
+ NORMAL_GOODBYE_D0,
+ NORMAL_GOODBYE_D1,
+ NORMAL_GOODBYE_E0,
+ NORMAL_GOODBYE_E1,
+ NORMAL_GOODBYE_F0,
+ NORMAL_GOODBYE_F1,
+ NORMAL_GOODBYE_G0,
+ NORMAL_GOODBYE_G1,
+ NORMAL_GOODBYE_H0,
+ NORMAL_GOODBYE_H1,
+ LIGHT_LOAD_A0,
+ LIGHT_LOAD_A1,
+ LIGHT_LOAD_B0,
+ LIGHT_LOAD_B1,
+ LIGHT_LOAD_C0,
+ LIGHT_LOAD_C1,
+ LIGHT_LOAD_D0,
+ LIGHT_LOAD_D1,
+ LIGHT_LOAD_E0,
+ LIGHT_LOAD_E1,
+ LIGHT_LOAD_F0,
+ LIGHT_LOAD_F1,
+ LIGHT_LOAD_G0,
+ LIGHT_LOAD_G1,
+ MEDIUM_LOAD_A0,
+ MEDIUM_LOAD_A1,
+ MEDIUM_LOAD_B0,
+ MEDIUM_LOAD_B1,
+ MEDIUM_LOAD_C0,
+ MEDIUM_LOAD_C1,
+ MEDIUM_LOAD_D0,
+ MEDIUM_LOAD_D1,
+ MEDIUM_LOAD_E0,
+ MEDIUM_LOAD_E1,
+ MEDIUM_LOAD_F0,
+ MEDIUM_LOAD_F1,
+ MEDIUM_LOAD_G0,
+ MEDIUM_LOAD_G1,
+ HEAVY_LOAD_A0,
+ HEAVY_LOAD_A1,
+ HEAVY_LOAD_B0,
+ HEAVY_LOAD_B1,
+ HEAVY_LOAD_C0,
+ HEAVY_LOAD_C1,
+ HEAVY_LOAD_D0,
+ HEAVY_LOAD_D1,
+ HEAVY_LOAD_E0,
+ HEAVY_LOAD_E1,
+ HEAVY_LOAD_F0,
+ HEAVY_LOAD_F1,
+ HEAVY_LOAD_G0 ,
+ HEAVY_LOAD_G1,
+ STARBASE_IS_READY_A,
+ STARBASE_IS_READY_B,
+ STARBASE_IS_READY_C,
+ WHAT_KIND_OF_INFO,
+ WHICH_FUNCTION,
+ WHICH_HISTORY,
+ WHICH_MISSION,
+ OK_NO_NEED_INFO,
+ ABOUT_FUEL,
+ ABOUT_MODULES,
+ ABOUT_CREW0,
+ ABOUT_CREW1,
+ ABOUT_SHIPS,
+ ABOUT_RU,
+ ABOUT_MINERALS,
+ ABOUT_LIFE,
+ OK_ENOUGH_STARBASE,
+ OK_ENOUGH_MISSION,
+ GET_MINERALS,
+ ABOUT_ALIENS,
+ MUST_DEFEAT,
+ DEFEAT_LIKE_SO,
+ FIND_URQUAN,
+ FIGHT_URQUAN,
+ ALLY_LIKE_SO,
+ STRONG_LIKE_SO,
+ OK_ENOUGH_DEFEAT,
+ WHICH_ALIEN,
+ WHICH_WAR,
+ WHICH_ANCIENT,
+ OK_ENOUGH_HISTORY,
+ WHICH_ALLIANCE,
+ WHICH_HIERARCHY,
+ ABOUT_OTHER,
+ OK_ENOUGH_ALIENS,
+ ABOUT_SHOFIXTI,
+ ABOUT_YEHAT,
+ ABOUT_ARILOU,
+ ABOUT_CHENJESU,
+ ABOUT_MMRNMHRM,
+ ABOUT_SYREEN,
+ OK_ENOUGH_ALLIANCE,
+ ABOUT_URQUAN,
+ ABOUT_MYCON,
+ ABOUT_SPATHI,
+ ABOUT_UMGAH,
+ ABOUT_ANDROSYNTH,
+ ABOUT_VUX,
+ ABOUT_ILWRATH,
+ OK_ENOUGH_HIERARCHY,
+ ABOUT_PRECURSORS,
+ ABOUT_OLD_RACES,
+ ABOUT_ALIENS_ON_EARTH,
+ OK_ENOUGH_ANCIENT,
+ URQUAN_STARTED_WAR,
+ WAR_WAS_LIKE_SO,
+ LOST_WAR_BECAUSE,
+ AFTER_WAR,
+ OK_ENOUGH_WAR,
+ STARBASE_BULLETIN_TAIL,
+ BETWEEN_BULLETINS,
+ STARBASE_BULLETIN_1,
+ STARBASE_BULLETIN_2,
+ STARBASE_BULLETIN_3,
+ STARBASE_BULLETIN_4,
+ STARBASE_BULLETIN_5,
+ STARBASE_BULLETIN_6,
+ STARBASE_BULLETIN_7,
+ STARBASE_BULLETIN_8,
+ STARBASE_BULLETIN_9,
+ STARBASE_BULLETIN_10,
+ STARBASE_BULLETIN_11,
+ STARBASE_BULLETIN_12,
+ STARBASE_BULLETIN_13,
+ STARBASE_BULLETIN_14,
+ STARBASE_BULLETIN_15,
+ STARBASE_BULLETIN_16,
+ STARBASE_BULLETIN_18,
+ STARBASE_BULLETIN_19,
+ STARBASE_BULLETIN_22,
+ STARBASE_BULLETIN_27,
+ STARBASE_BULLETIN_28,
+ STARBASE_BULLETIN_29,
+ STARBASE_BULLETIN_30,
+ DEVICE_HEAD,
+ BETWEEN_DEVICES,
+ DEVICE_TAIL,
+ ABOUT_PORTAL,
+ ABOUT_TALKPET,
+ ABOUT_BOMB,
+ ABOUT_SUN,
+ ABOUT_MAIDENS,
+ ABOUT_SPHERE,
+ ABOUT_HELIX,
+ ABOUT_SPINDLE,
+ ABOUT_ULTRON_0,
+ ABOUT_ULTRON_1,
+ ABOUT_ULTRON_2,
+ ABOUT_ULTRON_3,
+ ABOUT_UCASTER,
+ ABOUT_BCASTER,
+ ABOUT_SHIELD,
+ ABOUT_EGGCASE_0,
+ ABOUT_SHUTTLE,
+ ABOUT_VUXBEAST0,
+ ABOUT_VUXBEAST1,
+ ABOUT_DESTRUCT,
+ ABOUT_WARPPOD,
+ ABOUT_ARTIFACT_2,
+ ABOUT_ARTIFACT_3,
+ LETS_SEE,
+ GO_GET_MINERALS,
+ IMPROVE_FLAGSHIP_WITH_RU,
+ GOT_OK_FLAGSHIP,
+ GO_ALLY_WITH_ALIENS,
+ MADE_SOME_ALLIES,
+ GET_SHIPS_BY_MINING_OR_ALLIANCE,
+ GOT_OK_FLEET,
+ BUY_COMBAT_SHIPS,
+ GO_LEARN_ABOUT_URQUAN,
+ MAKE_FLAGSHIP_AWESOME,
+ KNOW_ABOUT_SAMATRA,
+ GOT_AWESOME_FLAGSHIP,
+ GOT_BOMB,
+ FIND_WAY_TO_DESTROY_SAMATRA,
+ MUST_INCREASE_BOMB_STRENGTH,
+ MUST_ACQUIRE_AWESOME_FLEET,
+ MUST_ELIMINATE_URQUAN_GUARDS,
+ CHMMR_IMPROVED_BOMB,
+ GOT_AWESOME_FLEET,
+ GO_DESTROY_SAMATRA,
+ GOOD_LUCK_AGAIN,
+ IMPROVE_1,
+ IMPROVE_2,
+ NEED_THRUSTERS_1,
+ NEED_THRUSTERS_2,
+ NEED_TURN_1,
+ NEED_TURN_2,
+ NEED_GUNS_1,
+ NEED_GUNS_2,
+ NEED_CREW_1,
+ NEED_CREW_2,
+ NEED_FUEL_1,
+ NEED_FUEL_2,
+ NEED_STORAGE_1,
+ NEED_LANDERS_2,
+ NEED_LANDERS_1,
+ NEED_DYNAMOS_1,
+ NEED_DYNAMOS_2,
+ NEED_POINT,
+
+ have_minerals,
+ goodbye_commander,
+ repeat_bulletins,
+ need_info,
+ starbase_functions,
+ history,
+ our_mission,
+ no_need_info,
+ enough_starbase,
+ enough_mission,
+ tell_me_about_fuel0,
+ tell_me_about_fuel1,
+ tell_me_about_modules0,
+ tell_me_about_modules1,
+ tell_me_about_crew,
+ tell_me_about_ships,
+ tell_me_about_ru,
+ tell_me_about_minerals,
+ tell_me_about_life,
+ where_get_minerals,
+ what_about_aliens,
+ what_about_urquan,
+ how_defeat,
+ how_find_urquan,
+ how_fight_urquan,
+ how_ally,
+ enough_defeat,
+ alien_races,
+ the_war,
+ ancient_history,
+ enough_history,
+ what_about_alliance,
+ what_about_hierarchy,
+ what_about_other,
+ enough_aliens,
+ shofixti,
+ yehat,
+ arilou,
+ chenjesu,
+ mmrnmhrm,
+ syreen,
+ enough_alliance,
+ urquan,
+ mycon,
+ spathi,
+ umgah,
+ androsynth,
+ vux,
+ ilwrath,
+ enough_hierarchy,
+ precursors,
+ old_races,
+ aliens_on_earth,
+ enough_ancient,
+ what_started_war,
+ what_was_war_like,
+ why_lose_war,
+ what_after_war,
+ enough_war,
+ new_devices,
+ how_get_strong,
+ what_do_now,
+ YOUR_FLAGSHIP_PC,
+ YOUR_FLAGSHIP_3DO0,
+ YOUR_FLAGSHIP_3DO1,
+ YOUR_FLAGSHIP_3DO2,
+ SPACE,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/supox/Makeinfo b/src/uqm/comm/supox/Makeinfo
new file mode 100644
index 0000000..8745013
--- /dev/null
+++ b/src/uqm/comm/supox/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="supoxc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/supox/resinst.h b/src/uqm/comm/supox/resinst.h
new file mode 100644
index 0000000..03459ea
--- /dev/null
+++ b/src/uqm/comm/supox/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SUPOX_COLOR_MAP "comm.supox.colortable"
+#define SUPOX_CONVERSATION_PHRASES "comm.supox.dialogue"
+#define SUPOX_FONT "comm.supox.font"
+#define SUPOX_MUSIC "comm.supox.music"
+#define SUPOX_PMAP_ANIM "comm.supox.graphics"
diff --git a/src/uqm/comm/supox/strings.h b/src/uqm/comm/supox/strings.h
new file mode 100644
index 0000000..b3312f7
--- /dev/null
+++ b/src/uqm/comm/supox/strings.h
@@ -0,0 +1,124 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SUPOX_STRINGS_H
+#define SUPOX_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ NEUTRAL_SPACE_HELLO_1,
+ NEUTRAL_SPACE_HELLO_2,
+ NEUTRAL_HOMEWORLD_HELLO_1,
+ NEUTRAL_HOMEWORLD_HELLO_2,
+ HOSTILE_SPACE_HELLO_1,
+ HOSTILE_SPACE_HELLO_2,
+ ALLIED_HOMEWORLD_HELLO_1,
+ ALLIED_HOMEWORLD_HELLO_2,
+ ALLIED_HOMEWORLD_HELLO_3,
+ ALLIED_HOMEWORLD_HELLO_4,
+ i_am0,
+ i_am1,
+ WE_ARE_SUPOX,
+ my_ship0,
+ my_ship1,
+ OUR_SHIP,
+ from_alliance0,
+ from_alliance1,
+ FROM_SUPOX,
+ are_you_copying,
+ YEAH_SORRY,
+ why_copy,
+ SYMBIOTS,
+ tell_us_of_your_species,
+ OUR_SPECIES,
+ plants_arent_intelligent,
+ PROVES_WERE_SPECIAL,
+ anyone_around_here,
+ UTWIG_NEARBY,
+ what_relation_to_utwig,
+ UTWIG_ALLIES,
+ whats_wrong_with_utwig,
+ BROKE_ULTRON,
+ whats_ultron,
+ TAKE_ULTRON,
+ what_do_i_do_now,
+ FIX_IT,
+ thanks_now_we_eat_you,
+ HIDEOUS_MONSTERS,
+ got_fixed_ultron,
+ GOOD_GIVE_TO_UTWIG,
+ look_i_repaired_lots,
+ ALMOST_THERE,
+ look_i_slightly_repaired,
+ GREAT_DO_MORE,
+ where_get_repairs,
+ ANCIENT_RHYME,
+ bye_neutral,
+ GOODBYE_NEUTRAL,
+ ABOUT_BATTLE,
+ HELLO_BEFORE_KOHRAH_SPACE_1,
+ HELLO_BEFORE_KOHRAH_SPACE_2,
+ HELLO_DURING_KOHRAH_SPACE_1,
+ HELLO_DURING_KOHRAH_SPACE_2,
+ HELLO_AFTER_KOHRAH_SPACE_1,
+ HELLO_AFTER_KOHRAH_SPACE_2,
+ whats_up_after_space,
+ GENERAL_INFO_AFTER_SPACE_1,
+ GENERAL_INFO_AFTER_SPACE_2,
+ what_now_after_space,
+ DO_THIS_AFTER_SPACE,
+ bye_after_space,
+ GOODBYE_AFTER_SPACE,
+ whats_up_before_space,
+ GENERAL_INFO_BEFORE_SPACE_1,
+ GENERAL_INFO_BEFORE_SPACE_2,
+ what_now_before_space,
+ DO_THIS_BEFORE_SPACE,
+ bye_before_space,
+ GOODBYE_BEFORE_SPACE,
+ how_went_war,
+ how_goes_war,
+ BATTLE_HAPPENS_1,
+ BATTLE_HAPPENS_2,
+ FLEET_ON_WAY,
+ learn_new_info,
+ NO_NEW_INFO,
+ SAMATRA,
+ what_now_homeworld,
+ HOPE_KILL_EACH_OTHER,
+ UP_TO_YOU,
+ can_you_help,
+ HOW_HELP,
+ DONT_NEED,
+ HAVE_4_SHIPS,
+ give_info,
+ GOOD_HINTS,
+ how_is_ultron,
+ ULTRON_IS_GREAT,
+ bye_allied_homeworld,
+ GOODBYE_ALLIED_HOMEWORLD,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/supox/supoxc.c b/src/uqm/comm/supox/supoxc.c
new file mode 100644
index 0000000..e169cba
--- /dev/null
+++ b/src/uqm/comm/supox/supoxc.c
@@ -0,0 +1,708 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+
+
+static LOCDATA supox_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ SUPOX_PMAP_ANIM, /* AlienFrame */
+ SUPOX_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ SUPOX_COLOR_MAP, /* AlienColorMap */
+ SUPOX_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ SUPOX_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 4, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 4, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 10, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 9, /* StartIndex */
+ 10, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 19, /* StartIndex */
+ 10, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 29, /* StartIndex */
+ 13, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, bye_neutral))
+ NPCPhrase (GOODBYE_NEUTRAL);
+ else if (PLAYER_SAID (R, what_do_i_do_now))
+ NPCPhrase (FIX_IT);
+ else if (PLAYER_SAID (R, thanks_now_we_eat_you))
+ {
+ NPCPhrase (HIDEOUS_MONSTERS);
+
+ SET_GAME_STATE (SUPOX_HOSTILE, 1);
+ SET_GAME_STATE (SUPOX_HOME_VISITS, 0);
+ SET_GAME_STATE (SUPOX_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, bye_after_space))
+ NPCPhrase (GOODBYE_AFTER_SPACE);
+ else if (PLAYER_SAID (R, bye_before_space))
+ NPCPhrase (GOODBYE_BEFORE_SPACE);
+ else if (PLAYER_SAID (R, bye_allied_homeworld))
+ NPCPhrase (GOODBYE_ALLIED_HOMEWORLD);
+ else if (PLAYER_SAID (R, can_you_help))
+ {
+ NPCPhrase (HOW_HELP);
+ if (EscortFeasibilityStudy (SUPOX_SHIP) == 0)
+ NPCPhrase (DONT_NEED);
+ else
+ {
+ NPCPhrase (HAVE_4_SHIPS);
+
+ AlienTalkSegue ((COUNT)~0);
+ AddEscortShips (SUPOX_SHIP, 4);
+ }
+ }
+}
+
+static void AlliedHome (RESPONSE_REF R);
+
+static void
+AlliedHome (RESPONSE_REF R)
+{
+ BYTE NumVisits, News;
+
+ News = GET_GAME_STATE (SUPOX_WAR_NEWS);
+ NumVisits = GET_GAME_STATE (UTWIG_SUPOX_MISSION);
+ if (PLAYER_SAID (R, how_went_war))
+ {
+ NPCPhrase (ABOUT_BATTLE);
+
+ News |= (1 << 0);
+ }
+ else if (PLAYER_SAID (R, how_goes_war))
+ {
+ if (NumVisits == 1)
+ {
+ NPCPhrase (FLEET_ON_WAY);
+
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 1);
+ }
+ else switch (GET_GAME_STATE (SUPOX_WAR_NEWS))
+ {
+ case 0:
+ NPCPhrase (BATTLE_HAPPENS_1);
+ News = 1;
+ break;
+ case 1:
+ NPCPhrase (BATTLE_HAPPENS_2);
+ News = 2;
+ break;
+ }
+
+ DISABLE_PHRASE (how_goes_war);
+ }
+ else if (PLAYER_SAID (R, learn_new_info))
+ {
+ if (NumVisits < 5)
+ NPCPhrase (NO_NEW_INFO);
+ else
+ {
+ NPCPhrase (SAMATRA);
+
+ News |= (1 << 1);
+ }
+
+ DISABLE_PHRASE (learn_new_info);
+ }
+ else if (PLAYER_SAID (R, what_now_homeworld))
+ {
+ if (NumVisits < 5)
+ NPCPhrase (UP_TO_YOU);
+ else
+ NPCPhrase (HOPE_KILL_EACH_OTHER);
+
+ DISABLE_PHRASE (what_now_homeworld);
+ }
+ else if (PLAYER_SAID (R, how_is_ultron))
+ {
+ NPCPhrase (ULTRON_IS_GREAT);
+
+ DISABLE_PHRASE (how_is_ultron);
+ }
+ SET_GAME_STATE (SUPOX_WAR_NEWS, News);
+
+ if (NumVisits >= 5)
+ {
+ if (!(News & (1 << 0)))
+ Response (how_went_war, AlliedHome);
+ }
+ else if (PHRASE_ENABLED (how_goes_war)
+ && ((NumVisits == 1 && News == 0)
+ || (NumVisits && News < 2)))
+ Response (how_goes_war, AlliedHome);
+ if (PHRASE_ENABLED (learn_new_info))
+ Response (learn_new_info, AlliedHome);
+ if (PHRASE_ENABLED (what_now_homeworld))
+ Response (what_now_homeworld, AlliedHome);
+ if (PHRASE_ENABLED (how_is_ultron))
+ Response (how_is_ultron, AlliedHome);
+ if (NumVisits == 0)
+ Response (can_you_help, ExitConversation);
+ Response (bye_allied_homeworld, ExitConversation);
+}
+
+static void
+BeforeKohrAh (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_before_space))
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_BEFORE_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_BEFORE_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_before_space);
+ }
+ else if (PLAYER_SAID (R, what_now_before_space))
+ {
+ NPCPhrase (DO_THIS_BEFORE_SPACE);
+
+ DISABLE_PHRASE (what_now_before_space);
+ }
+
+ if (PHRASE_ENABLED (whats_up_before_space))
+ Response (whats_up_before_space, BeforeKohrAh);
+ if (PHRASE_ENABLED (what_now_before_space))
+ Response (what_now_before_space, BeforeKohrAh);
+ Response (bye_before_space, ExitConversation);
+}
+
+static void
+AfterKohrAh (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_after_space))
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_AFTER_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_AFTER_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_after_space);
+ }
+ else if (PLAYER_SAID (R, what_now_after_space))
+ {
+ NPCPhrase (DO_THIS_AFTER_SPACE);
+
+ DISABLE_PHRASE (what_now_after_space);
+ }
+
+ if (PHRASE_ENABLED (whats_up_after_space))
+ Response (whats_up_after_space, AfterKohrAh);
+ if (PHRASE_ENABLED (what_now_after_space))
+ Response (what_now_after_space, AfterKohrAh);
+ Response (bye_after_space, ExitConversation);
+}
+
+static void
+NeutralSupox (RESPONSE_REF R)
+{
+ BYTE i, LastStack, NumVisits;
+ RESPONSE_REF pStr[3];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = 0;
+ if (PLAYER_SAID (R, i_am0))
+ {
+ NPCPhrase (WE_ARE_SUPOX);
+
+ SET_GAME_STATE (SUPOX_STACK1, 1);
+ DISABLE_PHRASE (i_am0);
+ }
+ else if (PLAYER_SAID (R, my_ship0))
+ {
+ NPCPhrase (OUR_SHIP);
+
+ SET_GAME_STATE (SUPOX_STACK1, 2);
+ DISABLE_PHRASE (my_ship0);
+ }
+ else if (PLAYER_SAID (R, from_alliance0))
+ {
+ NPCPhrase (FROM_SUPOX);
+
+ SET_GAME_STATE (SUPOX_STACK1, 3);
+ DISABLE_PHRASE (from_alliance0);
+ }
+ else if (PLAYER_SAID (R, are_you_copying))
+ {
+ NPCPhrase (YEAH_SORRY);
+
+ SET_GAME_STATE (SUPOX_STACK1, 4);
+ }
+ else if (PLAYER_SAID (R, why_copy))
+ {
+ NPCPhrase (SYMBIOTS);
+
+ SET_GAME_STATE (SUPOX_STACK1, 5);
+ }
+ else if (PLAYER_SAID (R, tell_us_of_your_species))
+ {
+ NPCPhrase (OUR_SPECIES);
+
+ LastStack = 1;
+ SET_GAME_STATE (SUPOX_STACK2, 1);
+ }
+ else if (PLAYER_SAID (R, plants_arent_intelligent))
+ {
+ NPCPhrase (PROVES_WERE_SPECIAL);
+
+ SET_GAME_STATE (SUPOX_STACK2, 2);
+ }
+ else if (PLAYER_SAID (R, anyone_around_here))
+ {
+ NPCPhrase (UTWIG_NEARBY);
+
+ LastStack = 2;
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 1);
+ StartSphereTracking (UTWIG_SHIP);
+ }
+ else if (PLAYER_SAID (R, what_relation_to_utwig))
+ {
+ NPCPhrase (UTWIG_ALLIES);
+
+ LastStack = 2;
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 1);
+ }
+ else if (PLAYER_SAID (R, whats_wrong_with_utwig))
+ {
+ NPCPhrase (BROKE_ULTRON);
+
+ LastStack = 2;
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 2);
+ }
+ else if (PLAYER_SAID (R, whats_ultron))
+ {
+ NPCPhrase (TAKE_ULTRON);
+
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 0);
+ SET_GAME_STATE (ULTRON_CONDITION, 1);
+
+ Response (what_do_i_do_now, ExitConversation);
+ Response (thanks_now_we_eat_you, ExitConversation);
+
+ return;
+ }
+ else if (PLAYER_SAID (R, got_fixed_ultron))
+ {
+ NPCPhrase (GOOD_GIVE_TO_UTWIG);
+
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 1);
+ }
+ else if (PLAYER_SAID (R, look_i_repaired_lots))
+ {
+ NPCPhrase (ALMOST_THERE);
+
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 1);
+ }
+ else if (PLAYER_SAID (R, look_i_slightly_repaired))
+ {
+ NPCPhrase (GREAT_DO_MORE);
+
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 1);
+ }
+ else if (PLAYER_SAID (R, where_get_repairs))
+ {
+ NPCPhrase (ANCIENT_RHYME);
+
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 1);
+ }
+
+ switch (GET_GAME_STATE (SUPOX_STACK2))
+ {
+ case 0:
+ pStr[1] = tell_us_of_your_species;
+ break;
+ case 1:
+ pStr[1] = plants_arent_intelligent;
+ break;
+ }
+ switch (GET_GAME_STATE (SUPOX_STACK1))
+ {
+ case 0:
+ construct_response (shared_phrase_buf,
+ i_am0,
+ GLOBAL_SIS (CommanderName),
+ i_am1,
+ (UNICODE*)NULL);
+ pStr[0] = i_am0;
+ pStr[1] = 0;
+ break;
+ case 1:
+ construct_response (shared_phrase_buf,
+ my_ship0,
+ GLOBAL_SIS (ShipName),
+ my_ship1,
+ (UNICODE*)NULL);
+ pStr[0] = my_ship0;
+ pStr[1] = 0;
+ break;
+ case 2:
+ {
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ from_alliance0,
+ buf,
+ from_alliance1,
+ (UNICODE*)NULL);
+ }
+ pStr[0] = from_alliance0;
+ pStr[1] = 0;
+ break;
+ case 3:
+ pStr[0] = are_you_copying;
+ pStr[1] = 0;
+ break;
+ case 4:
+ pStr[0] = why_copy;
+ pStr[1] = 0;
+ break;
+ }
+ NumVisits = GET_GAME_STATE (ULTRON_CONDITION);
+ if (NumVisits == 0)
+ {
+ switch (GET_GAME_STATE (SUPOX_WAR_NEWS))
+ {
+ case 0:
+ if (GET_GAME_STATE (UTWIG_VISITS)
+ || GET_GAME_STATE (UTWIG_HOME_VISITS)
+ || GET_GAME_STATE (BOMB_VISITS))
+ pStr[2] = what_relation_to_utwig;
+ else
+ pStr[2] = anyone_around_here;
+ break;
+ case 1:
+ pStr[2] = whats_wrong_with_utwig;
+ break;
+ case 2:
+ pStr[2] = whats_ultron;
+ break;
+ }
+ }
+ if (pStr[LastStack])
+ {
+ if (LastStack != 0 || GET_GAME_STATE (SUPOX_STACK1) > 2)
+ Response (pStr[LastStack], NeutralSupox);
+ else
+ DoResponsePhrase (pStr[LastStack], NeutralSupox, shared_phrase_buf);
+ }
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ if (i != 0 || GET_GAME_STATE (SUPOX_STACK1) > 2)
+ Response (pStr[i], NeutralSupox);
+ else
+ DoResponsePhrase (pStr[i], NeutralSupox, shared_phrase_buf);
+ }
+ }
+ if (!GET_GAME_STATE (SUPOX_ULTRON_HELP))
+ {
+ switch (NumVisits)
+ {
+ case 1:
+ Response (where_get_repairs, NeutralSupox);
+ break;
+ case 2:
+ Response (look_i_slightly_repaired, NeutralSupox);
+ break;
+ case 3:
+ Response (look_i_repaired_lots, NeutralSupox);
+ break;
+ case 4:
+ Response (got_fixed_ultron, NeutralSupox);
+ break;
+ }
+ }
+ Response (bye_neutral, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (SUPOX_HOSTILE))
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_VISITS, NumVisits);
+
+ setSegue (Segue_peace);
+ }
+ else if (CheckAlliance (SUPOX_SHIP) == GOOD_GUY)
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_HOME_VISITS, NumVisits);
+
+ AlliedHome ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_SUPOX_MISSION);
+ if (NumVisits == 1)
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_BEFORE_KOHRAH_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_BEFORE_KOHRAH_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_VISITS, NumVisits);
+
+ BeforeKohrAh ((RESPONSE_REF)0);
+ }
+ else if (NumVisits < 5)
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_DURING_KOHRAH_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_DURING_KOHRAH_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_AFTER_KOHRAH_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_AFTER_KOHRAH_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_VISITS, NumVisits);
+
+ AfterKohrAh ((RESPONSE_REF)0);
+ }
+ }
+ }
+ else
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (SUPOX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SUPOX_VISITS, NumVisits);
+ }
+
+ NeutralSupox ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_supox (void)
+{
+ return (0);
+}
+
+static void
+post_supox_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_supox_comm (void)
+{
+ LOCDATA *retval;
+
+ supox_desc.init_encounter_func = Intro;
+ supox_desc.post_encounter_func = post_supox_enc;
+ supox_desc.uninit_encounter_func = uninit_supox;
+
+ supox_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ supox_desc.AlienTextBaseline.y = 0;
+ supox_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (!GET_GAME_STATE (SUPOX_HOSTILE)
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &supox_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/syreen/Makeinfo b/src/uqm/comm/syreen/Makeinfo
new file mode 100644
index 0000000..e2a265e
--- /dev/null
+++ b/src/uqm/comm/syreen/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="syreenc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/syreen/resinst.h b/src/uqm/comm/syreen/resinst.h
new file mode 100644
index 0000000..16f7b0b
--- /dev/null
+++ b/src/uqm/comm/syreen/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SYREEN_COLOR_MAP "comm.syreen.colortable"
+#define SYREEN_CONVERSATION_PHRASES "comm.syreen.dialogue"
+#define SYREEN_FONT "comm.syreen.font"
+#define SYREEN_MUSIC "comm.syreen.music"
+#define SYREEN_PMAP_ANIM "comm.syreen.graphics"
diff --git a/src/uqm/comm/syreen/strings.h b/src/uqm/comm/syreen/strings.h
new file mode 100644
index 0000000..b796e3d
--- /dev/null
+++ b/src/uqm/comm/syreen/strings.h
@@ -0,0 +1,158 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SYREEN_STRINGS_H
+#define SYREEN_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HELLO_BEFORE_AMBUSH_1,
+ HELLO_BEFORE_AMBUSH_2,
+ HELLO_BEFORE_AMBUSH_3,
+ HELLO_BEFORE_AMBUSH_4,
+ we_are_vice_squad,
+ OK_VICE,
+ we_are_the_one_for_you_baby,
+ MAYBE_CAPTAIN,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ we_are_vindicator2,
+ WELCOME_VINDICATOR0,
+ WELCOME_VINDICATOR1,
+ WELCOME_VINDICATOR2,
+ we_are_impressed,
+ SO_AM_I_CAPTAIN,
+ HOW_CAN_YOU_BE_HERE,
+ we_here_to_help,
+ NO_NEED_HELP,
+ we_need_help,
+ CANT_GIVE_HELP,
+ i_need_you,
+ OK_NEED,
+ i_need_touch_o_vision,
+ TOUCH_O_VISION,
+ know_about_deep_children,
+ WHAT_ABOUT_DEEP_CHILDREN,
+ mycons_involved,
+ WHAT_PROOF,
+ have_no_proof,
+ NEED_PROOF,
+ have_proof,
+ SEE_PROOF,
+ look_at_egg_sacks,
+ HORRIBLE_TRUTH,
+ what_doing_here,
+ OUR_NEW_WORLD,
+ what_about_war,
+ ABOUT_WAR,
+ help_us,
+ WONT_HELP,
+ what_about_history,
+ BEFORE_WAR,
+ what_about_homeworld,
+ ABOUT_HOMEWORLD,
+ what_happened,
+ DONT_KNOW_HOW,
+ what_about_outfit,
+ HOPE_YOU_LIKE_IT,
+ where_mates,
+ MATES_KILLED,
+ get_lonely,
+ MAKE_OUT_ALL_RIGHT,
+ bye,
+ GOODBYE,
+ MUST_ACT,
+ whats_next_step,
+ OPEN_VAULT,
+ where_is_it,
+ DONT_KNOW_WHERE,
+ been_there,
+ GREAT,
+ GIVE_SHUTTLE,
+ im_on_my_way,
+ doing_this_for_you,
+ if_i_die,
+ GOOD_LUCK,
+ OK_FOUND_VAULT,
+ what_now,
+ HERES_THE_PLAN,
+ whats_my_reward,
+ HERES_REWARD,
+ bye_after_vault,
+ GOODBYE_AFTER_VAULT,
+ HELLO_AFTER_AMBUSH_1,
+ HELLO_AFTER_AMBUSH_2,
+ HELLO_AFTER_AMBUSH_3,
+ HELLO_AFTER_AMBUSH_4,
+ what_now_after_ambush,
+ DO_THIS_AFTER_AMBUSH,
+ what_about_you,
+ ABOUT_ME,
+ whats_up_after_ambush,
+ GENERAL_INFO_AFTER_AMBUSH_1,
+ GENERAL_INFO_AFTER_AMBUSH_2,
+ GENERAL_INFO_AFTER_AMBUSH_3,
+ GENERAL_INFO_AFTER_AMBUSH_4,
+ bye_after_ambush,
+ GOODBYE_AFTER_AMBUSH,
+ FOUND_VAULT_YET_1,
+ FOUND_VAULT_YET_2,
+ vault_hint,
+ OK_HINT,
+ found_vault,
+ bye_before_vault,
+ GOODBYE_BEFORE_VAULT,
+ what_do_i_get_for_this,
+ GRATITUDE,
+ not_sure,
+ PLEASE,
+ READY_FOR_AMBUSH,
+ repeat_plan,
+ OK_REPEAT_PLAN,
+ bye_before_ambush,
+ GOODBYE_BEFORE_AMBUSH,
+ what_about_us,
+ ABOUT_US,
+ MORE_COMFORTABLE,
+ in_the_spirit,
+ OK_SPIRIT,
+ what_in_mind,
+ SOMETHING_LIKE_THIS,
+ hands_off,
+ OK_WONT_USE_HANDS,
+ why_lights_off,
+ LIGHTS_OFF_BECAUSE,
+ evil_monster,
+ NOT_EVIL_MONSTER,
+ disease,
+ JUST_RELAX,
+ what_happens_if_i_touch_this,
+ THIS_HAPPENS,
+ are_you_sure_this_is_ok,
+ YES_SURE,
+ boy_they_never_taught,
+ THEN_LET_ME_TEACH,
+ not_much_more_to_say,
+ THEN_STOP_TALKING,
+ LATER,
+ SEX_GOODBYE,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/syreen/syreenc.c b/src/uqm/comm/syreen/syreenc.c
new file mode 100644
index 0000000..8884ad6
--- /dev/null
+++ b/src/uqm/comm/syreen/syreenc.c
@@ -0,0 +1,878 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/setup.h"
+
+
+static LOCDATA syreen_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ SYREEN_PMAP_ANIM, /* AlienFrame */
+ SYREEN_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ SYREEN_COLOR_MAP, /* AlienColorMap */
+ SYREEN_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ SYREEN_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 15, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 5, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 7, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 9, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 11, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 13, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 15, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 12), /* BlockMask */
+ },
+ {
+ 17, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 19, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 13),
+ },
+ {
+ 21, /* StartIndex */
+ 6, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 27, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 14), /* BlockMask */
+ },
+ {
+ 31, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 37, /* StartIndex */
+ 4, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 41, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5), /* BlockMask */
+ },
+ {
+ 44, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 6, 0, /* FrameRate */
+ ONE_SECOND * 3, ONE_SECOND, /* RestartRate */
+ (1 << 7) | (1 << 14), /* BlockMask */
+ },
+ {
+ 48, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND,/* RestartRate */
+ (1 << 9) | (1 << 13), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 4, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+FriendlyExit (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, bye))
+ NPCPhrase (GOODBYE);
+ else if (PLAYER_SAID (R, im_on_my_way)
+ || PLAYER_SAID (R, doing_this_for_you)
+ || PLAYER_SAID (R, if_i_die))
+ NPCPhrase (GOOD_LUCK);
+ else if (PLAYER_SAID (R, bye_before_vault))
+ NPCPhrase (GOODBYE_BEFORE_VAULT);
+ else if (PLAYER_SAID (R, bye_after_vault))
+ NPCPhrase (GOODBYE_AFTER_VAULT);
+ else if (PLAYER_SAID (R, bye_before_ambush))
+ NPCPhrase (GOODBYE_BEFORE_AMBUSH);
+ else if (PLAYER_SAID (R, bye_after_ambush))
+ NPCPhrase (GOODBYE_AFTER_AMBUSH);
+ else
+ {
+ if (PLAYER_SAID (R, hands_off))
+ NPCPhrase (OK_WONT_USE_HANDS);
+ else if (PLAYER_SAID (R, not_much_more_to_say))
+ NPCPhrase (THEN_STOP_TALKING);
+ NPCPhrase (LATER);
+ NPCPhrase (SEX_GOODBYE);
+
+ AlienTalkSegue (2);
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 0)
+ ), ONE_SECOND / 2);
+ AlienTalkSegue ((COUNT)~0);
+
+ SET_GAME_STATE (PLAYER_HAD_SEX, 1);
+ SET_GAME_STATE (PLAYER_HAVING_SEX, 0);
+ }
+}
+
+static void
+Sex (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, in_the_spirit))
+ NPCPhrase (OK_SPIRIT);
+ else if (PLAYER_SAID (R, what_in_mind))
+ NPCPhrase (SOMETHING_LIKE_THIS);
+ else if (PLAYER_SAID (R, disease))
+ NPCPhrase (JUST_RELAX);
+ else if (PLAYER_SAID (R, what_happens_if_i_touch_this))
+ {
+ NPCPhrase (THIS_HAPPENS);
+
+ DISABLE_PHRASE (what_happens_if_i_touch_this);
+ }
+ else if (PLAYER_SAID (R, are_you_sure_this_is_ok))
+ {
+ NPCPhrase (YES_SURE);
+
+ DISABLE_PHRASE (are_you_sure_this_is_ok);
+ }
+ else if (PLAYER_SAID (R, boy_they_never_taught))
+ {
+ NPCPhrase (THEN_LET_ME_TEACH);
+
+ DISABLE_PHRASE (boy_they_never_taught);
+ }
+
+ if (!PHRASE_ENABLED (what_happens_if_i_touch_this)
+ && !PHRASE_ENABLED (are_you_sure_this_is_ok)
+ && !PHRASE_ENABLED (boy_they_never_taught))
+ Response (not_much_more_to_say, FriendlyExit);
+ else
+ {
+ if (PHRASE_ENABLED (what_happens_if_i_touch_this))
+ Response (what_happens_if_i_touch_this, Sex);
+ if (PHRASE_ENABLED (are_you_sure_this_is_ok))
+ Response (are_you_sure_this_is_ok, Sex);
+ if (PHRASE_ENABLED (boy_they_never_taught))
+ Response (boy_they_never_taught, Sex);
+ }
+}
+
+static void
+Foreplay (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_my_reward)
+ || PLAYER_SAID (R, what_about_us))
+ {
+ if (PLAYER_SAID (R, whats_my_reward))
+ NPCPhrase (HERES_REWARD);
+ else
+ NPCPhrase (ABOUT_US);
+ NPCPhrase (MORE_COMFORTABLE);
+ AlienTalkSegue (1);
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1)
+ ), ONE_SECOND);
+ AlienTalkSegue ((COUNT)~0);
+
+ SET_GAME_STATE (PLAYER_HAVING_SEX, 1);
+ }
+ else if (PLAYER_SAID (R, why_lights_off))
+ {
+ NPCPhrase (LIGHTS_OFF_BECAUSE);
+
+ DISABLE_PHRASE (why_lights_off);
+ }
+ else if (PLAYER_SAID (R, evil_monster))
+ {
+ NPCPhrase (NOT_EVIL_MONSTER);
+
+ DISABLE_PHRASE (evil_monster);
+ }
+
+ if (PHRASE_ENABLED (why_lights_off))
+ Response (why_lights_off, Foreplay);
+ else if (PHRASE_ENABLED (evil_monster))
+ Response (evil_monster, Foreplay);
+ else
+ Response (disease, Sex);
+ Response (in_the_spirit, Sex);
+ Response (what_in_mind, Sex);
+ Response (hands_off, FriendlyExit);
+}
+
+static void
+AfterAmbush (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, what_now_after_ambush))
+ {
+ NPCPhrase (DO_THIS_AFTER_AMBUSH);
+
+ DISABLE_PHRASE (what_now_after_ambush);
+ }
+ else if (PLAYER_SAID (R, what_about_you))
+ {
+ NPCPhrase (ABOUT_ME);
+
+ DISABLE_PHRASE (what_about_you);
+ }
+ else if (PLAYER_SAID (R, whats_up_after_ambush))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (SYREEN_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_AFTER_AMBUSH_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_AFTER_AMBUSH_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_AFTER_AMBUSH_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_AFTER_AMBUSH_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (SYREEN_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_after_ambush);
+ }
+
+ if (PHRASE_ENABLED (what_about_you))
+ Response (what_about_you, AfterAmbush);
+ else if (!GET_GAME_STATE (PLAYER_HAD_SEX))
+ {
+ Response (what_about_us, Foreplay);
+ }
+ if (PHRASE_ENABLED (what_now_after_ambush))
+ Response (what_now_after_ambush, AfterAmbush);
+ if (PHRASE_ENABLED (whats_up_after_ambush))
+ Response (whats_up_after_ambush, AfterAmbush);
+ Response (bye_after_ambush, FriendlyExit);
+}
+
+static void
+AmbushReady (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, repeat_plan))
+ {
+ NPCPhrase (OK_REPEAT_PLAN);
+
+ DISABLE_PHRASE (repeat_plan);
+ }
+
+ if (PHRASE_ENABLED (repeat_plan))
+ Response (repeat_plan, AmbushReady);
+ Response (bye_before_ambush, FriendlyExit);
+}
+
+static void
+SyreenShuttle (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_next_step))
+ {
+ NPCPhrase (OPEN_VAULT);
+
+ DISABLE_PHRASE (whats_next_step);
+ }
+ else if (PLAYER_SAID (R, what_do_i_get_for_this))
+ {
+ NPCPhrase (GRATITUDE);
+
+ DISABLE_PHRASE (what_do_i_get_for_this);
+ }
+ else if (PLAYER_SAID (R, not_sure))
+ {
+ NPCPhrase (PLEASE);
+
+ DISABLE_PHRASE (not_sure);
+ }
+ else if (PLAYER_SAID (R, where_is_it))
+ {
+ NPCPhrase (DONT_KNOW_WHERE);
+ NPCPhrase (GIVE_SHUTTLE);
+
+ SET_GAME_STATE (SYREEN_SHUTTLE, 1);
+ SET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP, 1);
+
+ DISABLE_PHRASE (where_is_it);
+ }
+ else if (PLAYER_SAID (R, been_there))
+ {
+ NPCPhrase (GREAT);
+ NPCPhrase (GIVE_SHUTTLE);
+
+ SET_GAME_STATE (SYREEN_SHUTTLE, 1);
+ SET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP, 1);
+
+ DISABLE_PHRASE (been_there);
+ }
+
+ if (PHRASE_ENABLED (whats_next_step))
+ Response (whats_next_step, SyreenShuttle);
+ else
+ {
+ if (!GET_GAME_STATE (KNOW_SYREEN_VAULT))
+ {
+ if (PHRASE_ENABLED (where_is_it))
+ Response (where_is_it, SyreenShuttle);
+ }
+ else
+ {
+ if (PHRASE_ENABLED (been_there))
+ Response (been_there, SyreenShuttle);
+ }
+ if (!PHRASE_ENABLED (where_is_it)
+ || !PHRASE_ENABLED (been_there))
+ {
+ Response (im_on_my_way, FriendlyExit);
+ Response (doing_this_for_you, FriendlyExit);
+ Response (if_i_die, FriendlyExit);
+ }
+ }
+ if (PHRASE_ENABLED (what_do_i_get_for_this))
+ Response (what_do_i_get_for_this, SyreenShuttle);
+ if (PHRASE_ENABLED (not_sure))
+ Response (not_sure, SyreenShuttle);
+}
+
+static void
+NormalSyreen (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[4];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = pStr[3] = 0;
+ if (PLAYER_SAID (R, we_here_to_help))
+ NPCPhrase (NO_NEED_HELP);
+ else if (PLAYER_SAID (R, we_need_help))
+ NPCPhrase (CANT_GIVE_HELP);
+ else if (PLAYER_SAID (R, know_about_deep_children))
+ {
+ NPCPhrase (WHAT_ABOUT_DEEP_CHILDREN);
+
+ DISABLE_PHRASE (know_about_deep_children);
+ }
+ else if (PLAYER_SAID (R, mycons_involved))
+ {
+ NPCPhrase (WHAT_PROOF);
+
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 3);
+ }
+ else if (PLAYER_SAID (R, have_no_proof))
+ {
+ NPCPhrase (NEED_PROOF);
+
+ SET_GAME_STATE (SYREEN_WANT_PROOF, 1);
+ }
+ else if (PLAYER_SAID (R, have_proof))
+ {
+ NPCPhrase (SEE_PROOF);
+
+ DISABLE_PHRASE (have_proof);
+ }
+ else if (PLAYER_SAID (R, what_doing_here))
+ {
+ NPCPhrase (OUR_NEW_WORLD);
+
+ SET_GAME_STATE (SYREEN_STACK0, 1);
+ LastStack = 1;
+ }
+ else if (PLAYER_SAID (R, what_about_war))
+ {
+ NPCPhrase (ABOUT_WAR);
+
+ SET_GAME_STATE (SYREEN_STACK0, 2);
+ LastStack = 1;
+ }
+ else if (PLAYER_SAID (R, help_us))
+ {
+ NPCPhrase (WONT_HELP);
+
+ SET_GAME_STATE (SYREEN_STACK0, 3);
+ }
+ else if (PLAYER_SAID (R, what_about_history))
+ {
+ NPCPhrase (BEFORE_WAR);
+
+ SET_GAME_STATE (SYREEN_STACK1, 1);
+ LastStack = 2;
+ }
+ else if (PLAYER_SAID (R, what_about_homeworld))
+ {
+ NPCPhrase (ABOUT_HOMEWORLD);
+
+ SET_GAME_STATE (SYREEN_STACK1, 2);
+ LastStack = 2;
+ }
+ else if (PLAYER_SAID (R, what_happened))
+ {
+ NPCPhrase (DONT_KNOW_HOW);
+
+ SET_GAME_STATE (KNOW_SYREEN_WORLD_SHATTERED, 1);
+ SET_GAME_STATE (SYREEN_STACK1, 3);
+ }
+ else if (PLAYER_SAID (R, what_about_outfit))
+ {
+ NPCPhrase (HOPE_YOU_LIKE_IT);
+
+ SET_GAME_STATE (SYREEN_STACK2, 1);
+ LastStack = 3;
+ }
+ else if (PLAYER_SAID (R, where_mates))
+ {
+ NPCPhrase (MATES_KILLED);
+
+ SET_GAME_STATE (SYREEN_STACK2, 2);
+ LastStack = 3;
+ }
+ else if (PLAYER_SAID (R, get_lonely))
+ {
+ NPCPhrase (MAKE_OUT_ALL_RIGHT);
+
+ SET_GAME_STATE (SYREEN_STACK2, 3);
+ }
+ else if (PLAYER_SAID (R, look_at_egg_sacks))
+ {
+ NPCPhrase (HORRIBLE_TRUTH);
+
+ setSegue (Segue_peace);
+ SET_GAME_STATE (SYREEN_HOME_VISITS, 0);
+ SET_GAME_STATE (SYREEN_KNOW_ABOUT_MYCON, 1);
+
+ SyreenShuttle ((RESPONSE_REF)0);
+ return;
+ }
+
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) < 3)
+ {
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) == 2
+ && GET_GAME_STATE (KNOW_SYREEN_WORLD_SHATTERED))
+ {
+ if (PHRASE_ENABLED (know_about_deep_children))
+ pStr[0] = know_about_deep_children;
+ else
+ pStr[0] = mycons_involved;
+ }
+ }
+ else
+ {
+ if (GET_GAME_STATE (EGG_CASE0_ON_SHIP)
+ || GET_GAME_STATE (EGG_CASE1_ON_SHIP)
+ || GET_GAME_STATE (EGG_CASE2_ON_SHIP))
+ {
+ if (PHRASE_ENABLED (have_proof))
+ pStr[0] = have_proof;
+ else
+ pStr[0] = look_at_egg_sacks;
+ }
+ else if (!GET_GAME_STATE (SYREEN_WANT_PROOF))
+ {
+ pStr[0] = have_no_proof;
+ }
+ }
+ switch (GET_GAME_STATE (SYREEN_STACK0))
+ {
+ case 0:
+ pStr[1] = what_doing_here;
+ break;
+ case 1:
+ pStr[1] = what_about_war;
+ break;
+ case 2:
+ pStr[1] = help_us;
+ break;
+ }
+ switch (GET_GAME_STATE (SYREEN_STACK1))
+ {
+ case 0:
+ pStr[2] = what_about_history;
+ break;
+ case 1:
+ pStr[2] = what_about_homeworld;
+ break;
+ case 2:
+ pStr[2] = what_happened;
+ break;
+ }
+ switch (GET_GAME_STATE (SYREEN_STACK2))
+ {
+ case 0:
+ pStr[3] = what_about_outfit;
+ break;
+ case 1:
+ pStr[3] = where_mates;
+ break;
+ case 2:
+ pStr[3] = get_lonely;
+ break;
+ }
+ if (pStr[LastStack])
+ Response (pStr[LastStack], NormalSyreen);
+ for (i = 0; i < 4; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ Response (pStr[i], NormalSyreen);
+ }
+ Response (bye, FriendlyExit);
+}
+
+static void
+InitialSyreen (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, we_are_vice_squad))
+ {
+ NPCPhrase (OK_VICE);
+ NPCPhrase (HOW_CAN_YOU_BE_HERE);
+ }
+ else if (PLAYER_SAID (R, we_are_the_one_for_you_baby))
+ {
+ NPCPhrase (MAYBE_CAPTAIN);
+ NPCPhrase (HOW_CAN_YOU_BE_HERE);
+ }
+ else if (PLAYER_SAID (R, we_are_vindicator0))
+ {
+ NPCPhrase (WELCOME_VINDICATOR0);
+ if (!usingSpeech)
+ {
+ NPCPhrase (GLOBAL_PLAYER_NAME);
+ NPCPhrase (WELCOME_VINDICATOR1);
+ NPCPhrase (GLOBAL_SHIP_NAME);
+ NPCPhrase (WELCOME_VINDICATOR2);
+ }
+ NPCPhrase (HOW_CAN_YOU_BE_HERE);
+ }
+ else if (PLAYER_SAID (R, we_are_impressed))
+ {
+ NPCPhrase (SO_AM_I_CAPTAIN);
+ NPCPhrase (HOW_CAN_YOU_BE_HERE);
+ }
+ else if (PLAYER_SAID (R, i_need_you))
+ {
+ NPCPhrase (OK_NEED);
+
+ DISABLE_PHRASE (i_need_you);
+ DISABLE_PHRASE (i_need_touch_o_vision);
+ }
+ else if (PLAYER_SAID (R, i_need_touch_o_vision))
+ {
+ NPCPhrase (TOUCH_O_VISION);
+
+ DISABLE_PHRASE (i_need_you);
+ DISABLE_PHRASE (i_need_touch_o_vision);
+ }
+
+ Response (we_here_to_help, NormalSyreen);
+ Response (we_need_help, NormalSyreen);
+ if (PHRASE_ENABLED (i_need_you))
+ Response (i_need_you, InitialSyreen);
+ if (PHRASE_ENABLED (i_need_touch_o_vision))
+ Response (i_need_touch_o_vision, InitialSyreen);
+}
+
+static void
+PlanAmbush (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ NPCPhrase (OK_FOUND_VAULT);
+
+ SET_GAME_STATE (MYCON_AMBUSH, 1);
+ // This is redundant but left here for clarity
+ SET_GAME_STATE (SYREEN_HOME_VISITS, 0);
+
+ Response (whats_my_reward, Foreplay);
+ Response (bye_after_vault, FriendlyExit);
+}
+
+static void
+SyreenVault (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, vault_hint))
+ {
+ NPCPhrase (OK_HINT);
+
+ DISABLE_PHRASE (vault_hint);
+ }
+
+ if (PHRASE_ENABLED (vault_hint))
+ {
+ Response (vault_hint, SyreenVault);
+ }
+ Response (bye_before_vault, FriendlyExit);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ NumVisits = GET_GAME_STATE (SYREEN_HOME_VISITS);
+ if (GET_GAME_STATE (MYCON_KNOW_AMBUSH))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_AFTER_AMBUSH_1);
+ SetRaceAllied (SYREEN_SHIP, TRUE);
+ break;
+ case 1:
+ NPCPhrase (HELLO_AFTER_AMBUSH_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_AFTER_AMBUSH_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_AFTER_AMBUSH_3);
+ --NumVisits;
+ break;
+ }
+
+ AfterAmbush ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (MYCON_AMBUSH))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (READY_FOR_AMBUSH);
+ --NumVisits;
+ break;
+ }
+
+ AmbushReady ((RESPONSE_REF)0);
+ }
+ else if (!GET_GAME_STATE (SYREEN_KNOW_ABOUT_MYCON))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_BEFORE_AMBUSH_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_BEFORE_AMBUSH_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_BEFORE_AMBUSH_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_BEFORE_AMBUSH_4);
+ --NumVisits;
+ break;
+ }
+
+ if (NumVisits > 1)
+ NormalSyreen ((RESPONSE_REF)0);
+ else
+ {
+ construct_response (shared_phrase_buf,
+ we_are_vindicator0,
+ GLOBAL_SIS (CommanderName),
+ we_are_vindicator1,
+ GLOBAL_SIS (ShipName),
+ we_are_vindicator2,
+ (UNICODE*)NULL);
+ Response (we_are_vice_squad, InitialSyreen);
+ Response (we_are_the_one_for_you_baby, InitialSyreen);
+ DoResponsePhrase (we_are_vindicator0, InitialSyreen, shared_phrase_buf);
+ Response (we_are_impressed, InitialSyreen);
+ }
+ }
+#ifdef NEVER
+ else if (!GET_GAME_STATE (SYREEN_SHUTTLE))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (MUST_ACT);
+ --NumVisits;
+ break;
+ }
+
+ SyreenShuttle ((RESPONSE_REF)0);
+ }
+#endif /* NEVER */
+ else if (GET_GAME_STATE (SHIP_VAULT_UNLOCKED))
+ {
+ PlanAmbush ((RESPONSE_REF)0);
+ // XXX: PlanAmbush() sets SYREEN_HOME_VISITS=0, but then this value
+ // is immediately reset to NumVisits just below. The intent was to
+ // reset the HELLO stack so that is what we will do here as well.
+ // Note that it is completely redundant because genvault.c resets
+ // SYREEN_HOME_VISITS when it sets SHIP_VAULT_UNLOCKED=1.
+ NumVisits = 0;
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FOUND_VAULT_YET_1);
+ break;
+ case 1:
+ NPCPhrase (FOUND_VAULT_YET_2);
+ --NumVisits;
+ break;
+ }
+
+ SyreenVault ((RESPONSE_REF)0);
+ }
+ SET_GAME_STATE (SYREEN_HOME_VISITS, NumVisits);
+}
+
+static COUNT
+uninit_syreen (void)
+{
+ return (0);
+}
+
+static void
+post_syreen_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_syreen_comm (void)
+{
+ LOCDATA *retval;
+
+ syreen_desc.init_encounter_func = Intro;
+ syreen_desc.post_encounter_func = post_syreen_enc;
+ syreen_desc.uninit_encounter_func = uninit_syreen;
+
+ syreen_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ syreen_desc.AlienTextBaseline.y = 0;
+ syreen_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ setSegue (Segue_peace);
+ retval = &syreen_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/talkpet/Makeinfo b/src/uqm/comm/talkpet/Makeinfo
new file mode 100644
index 0000000..59f7d27
--- /dev/null
+++ b/src/uqm/comm/talkpet/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="talkpet.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/talkpet/resinst.h b/src/uqm/comm/talkpet/resinst.h
new file mode 100644
index 0000000..20398f9
--- /dev/null
+++ b/src/uqm/comm/talkpet/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define TALKING_PET_COLOR_MAP "comm.talkingpet.colortable"
+#define TALKING_PET_CONVERSATION_PHRASES "comm.talkingpet.dialogue"
+#define TALKING_PET_FONT "comm.talkingpet.font"
+#define TALKING_PET_MUSIC "comm.talkingpet.music"
+#define TALKING_PET_PMAP_ANIM "comm.talkingpet.graphics"
diff --git a/src/uqm/comm/talkpet/strings.h b/src/uqm/comm/talkpet/strings.h
new file mode 100644
index 0000000..01b959a
--- /dev/null
+++ b/src/uqm/comm/talkpet/strings.h
@@ -0,0 +1,140 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TALKPET_STRINGS_H
+#define TALKPET_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HELLO_AT_UMGAH,
+ what_are_you,
+ JUST_TALKING_PET,
+ talking_pets_dumb,
+ OH_NO_YOU_DONT,
+ what_do_to_umgah,
+ DID_NOTHING,
+ umgah_zombies,
+ WORKS_LIKE_THIS,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ GOOD_FOR_YOU,
+ must_explain_presence,
+ EXPLAIN_NOTHING_MONKEY_BOY,
+ bye_at_umgah,
+ GOODBYE_AT_UMGAH,
+ HYPNOTIZE_AGAIN_1,
+ HYPNOTIZE_AGAIN_2,
+ HYPNOTIZE_AGAIN_3,
+ HYPNOTIZE_AGAIN_4,
+ HYPNO_TAIL,
+ CANT_COMPEL,
+ LETS_MAKE_A_DEAL,
+ what_kind_of_deal,
+ HELP_DEFEAT_URQUAN,
+ ok_lets_do_it,
+ COMING_ABOARD,
+ how_trust,
+ TRUST,
+ boneless_dweeb,
+ YOUR_BONELESS_DWEEB,
+ what_are_you_really,
+ POOR_DNYARRI,
+ hard_to_believe,
+ ITS_TRUE,
+ bullshit,
+ WORTH_A_TRY,
+ kill_you,
+ PLEASE_DONT,
+ must_kill,
+ DONT_KILL,
+ want_kill_1,
+ want_kill_2,
+ want_kill_3,
+ GLAD_YOU_WONT_KILL,
+ whats_up_onboard,
+ GENERAL_INFO_ONBOARD_1,
+ GENERAL_INFO_ONBOARD_2,
+ GENERAL_INFO_ONBOARD_3,
+ GENERAL_INFO_ONBOARD_4,
+ GENERAL_INFO_ONBOARD_5,
+ GENERAL_INFO_ONBOARD_6,
+ GENERAL_INFO_ONBOARD_7,
+ GENERAL_INFO_ONBOARD_8,
+ HELLO_AS_DEVICE_1,
+ HELLO_AS_DEVICE_2,
+ HELLO_AS_DEVICE_3,
+ HELLO_AS_DEVICE_4,
+ HELLO_AS_DEVICE_5,
+ HELLO_AS_DEVICE_6,
+ HELLO_AS_DEVICE_7,
+ HELLO_AS_DEVICE_8,
+ CYBORG_PEP_TALK,
+ HUMAN_PEP_TALK,
+ I_SENSE_MY_SLAVES,
+ HAVENT_GOT_EVERYTHING,
+ NEED_BOMB,
+ SOUP_UP_BOMB,
+ SOUP_UP_FLEET,
+ SOUP_UP_FLAGSHIP,
+ COMEBACK_WHEN_READY,
+ what_now,
+ DO_THIS,
+ compel_urquan,
+ HERE_WE_GO,
+ im_scared,
+ STUPID_FOP,
+ compel_that_ship,
+ SAVING_MY_POWER,
+ any_suggestions,
+ SUGGESTION_1,
+ SUGGESTION_2,
+ SUGGESTION_3,
+ SUGGESTION_4,
+ SUGGESTION_5,
+ SUGGESTION_6,
+ SUGGESTION_7,
+ SUGGESTION_8,
+ about_your_race,
+ WHAT_ABOUT_RACE,
+ you_lied,
+ SO_WHAT,
+ bye_onboard,
+ GOODBYE_ONBOARD,
+ what_about_physiology,
+ NO_TALK_ABOUT_SELF,
+ what_about_powers,
+ NOT_POWERS_BUT_FLOWERS,
+ yes_flowers,
+ GOOD_HUMAN,
+ wish_to_go_now,
+ EXCELLENT_IDEA,
+ what_about_your_history,
+ ABOUT_HISTORY,
+ sentient_milieu,
+ ABOUT_SENTIENT_MILIEU,
+ what_about_war,
+ ABOUT_WAR,
+ enough_info,
+ OK_ENOUGH_INFO,
+ UMGAH_ALL_GONE,
+ HELLO_AFTER_COMPEL_URQUAN,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/talkpet/talkpet.c b/src/uqm/comm/talkpet/talkpet.c
new file mode 100644
index 0000000..ce59011
--- /dev/null
+++ b/src/uqm/comm/talkpet/talkpet.c
@@ -0,0 +1,841 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+
+#define STROBE_RATE 10
+#define STROBE_LENGTH (ONE_SECOND * 3 / 2)
+#define NUM_STROBES (STROBE_LENGTH * STROBE_RATE / ONE_SECOND)
+
+static LOCDATA talkpet_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ TALKING_PET_PMAP_ANIM, /* AlienFrame */
+ TALKING_PET_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ TALKING_PET_COLOR_MAP, /* AlienColorMap */
+ TALKING_PET_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ TALKING_PET_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 17, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 7, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 10, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 13, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 16, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Blink right eye */
+ 18, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 13) /* BlockMask */
+ },
+ { /* Blink left eye */
+ 21, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 12) | (1 << 14), /* BlockMask */
+ },
+ {
+ 24, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 26, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 28, /* StartIndex */
+ 4, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 32, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 35, /* StartIndex */
+ 5, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 40, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 42, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5), /* BlockMask */
+ },
+ { /* Right eyebrow */
+ 48, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4), /* BlockMask */
+ },
+ { /* Left eyebrow */
+ 50, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5), /* BlockMask */
+ },
+ {
+ 52, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Mind control strobe (on-demand) */
+ 1, /* StartIndex */
+ NUM_STROBES * 2, /* NumFrames */
+ CIRCULAR_ANIM | COLORXFORM_ANIM | ONE_SHOT_ANIM
+ | ANIM_DISABLED, /* AnimFlags */
+ ONE_SECOND / (STROBE_RATE * 2), 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 6, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+ SET_GAME_STATE (SHIP_TO_COMPEL, 0);
+
+ if (PLAYER_SAID (R, compel_urquan))
+ {
+ NPCPhrase (HERE_WE_GO);
+
+ SET_GAME_STATE (URQUAN_MESSED_UP, 1);
+ }
+ else if (PLAYER_SAID (R, wish_to_go_now))
+ NPCPhrase (EXCELLENT_IDEA);
+ else if (PLAYER_SAID (R, bye_onboard))
+ NPCPhrase (GOODBYE_ONBOARD);
+ else if (PLAYER_SAID (R, compel_that_ship))
+ NPCPhrase (SAVING_MY_POWER);
+ else if (PLAYER_SAID (R, ok_lets_do_it)
+ || PLAYER_SAID (R, want_kill_1)
+ || PLAYER_SAID (R, want_kill_2)
+ || PLAYER_SAID (R, want_kill_3))
+ {
+ if (PLAYER_SAID (R, ok_lets_do_it))
+ NPCPhrase (COMING_ABOARD);
+ else
+ NPCPhrase (GLAD_YOU_WONT_KILL);
+
+ SET_GAME_STATE (TALKING_PET, 1);
+ SET_GAME_STATE (TALKING_PET_ON_SHIP, 1);
+ SET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES, 0);
+ SET_GAME_STATE (UMGAH_VISITS, 0);
+ SET_GAME_STATE (UMGAH_HOME_VISITS, 0);
+ SET_GAME_STATE (ARILOU_STACK_2, 0);
+ }
+}
+
+static void
+MindFuckUrquan (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, what_now))
+ {
+ NPCPhrase (DO_THIS);
+
+ DISABLE_PHRASE (what_now);
+ }
+ else if (PLAYER_SAID (R, im_scared))
+ {
+ NPCPhrase (STUPID_FOP);
+
+ DISABLE_PHRASE (im_scared);
+ }
+
+ if (PHRASE_ENABLED (what_now))
+ Response (what_now, MindFuckUrquan);
+ if (PHRASE_ENABLED (im_scared))
+ Response (im_scared, MindFuckUrquan);
+ Response (compel_urquan, ExitConversation);
+}
+
+static void PetDevice (RESPONSE_REF R);
+
+static void
+MindControlStrobe (void)
+{
+ // Enable the one-shot strobe animation
+ CommData.AlienAmbientArray[16].AnimFlags &= ~ANIM_DISABLED;
+}
+
+static void
+MindControl (RESPONSE_REF R)
+{
+ RESPONSE_FUNC RespFunc;
+
+ if (PLAYER_SAID (R, what_about_powers))
+ {
+ NPCPhrase (NOT_POWERS_BUT_FLOWERS);
+
+ RespFunc = (RESPONSE_FUNC)MindControl;
+ R = yes_flowers;
+ }
+ else /* if (R == yes_flowers) */
+ {
+ NPCPhrase (GOOD_HUMAN);
+
+ RespFunc = (RESPONSE_FUNC)ExitConversation;
+ R = wish_to_go_now;
+ }
+
+ AlienTalkSegue ((COUNT)~0);
+ MindControlStrobe ();
+
+ Response (R, RespFunc);
+}
+
+static void
+PetInfo (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, about_your_race))
+ NPCPhrase (WHAT_ABOUT_RACE);
+ else if (PLAYER_SAID (R, what_about_physiology))
+ {
+ NPCPhrase (NO_TALK_ABOUT_SELF);
+
+ DISABLE_PHRASE (what_about_physiology);
+ }
+ else if (PLAYER_SAID (R, what_about_your_history))
+ {
+ NPCPhrase (ABOUT_HISTORY);
+
+ DISABLE_PHRASE (what_about_your_history);
+ }
+ else if (PLAYER_SAID (R, sentient_milieu))
+ {
+ NPCPhrase (ABOUT_SENTIENT_MILIEU);
+
+ DISABLE_PHRASE (sentient_milieu);
+ }
+ else if (PLAYER_SAID (R, what_about_war))
+ {
+ NPCPhrase (ABOUT_WAR);
+
+ DISABLE_PHRASE (what_about_war);
+ }
+
+ if (PHRASE_ENABLED (what_about_physiology))
+ {
+ Response (what_about_physiology, PetInfo);
+ }
+ else
+ {
+ Response (what_about_powers, MindControl);
+ }
+ if (PHRASE_ENABLED (what_about_your_history))
+ Response (what_about_your_history, PetInfo);
+ else if (PHRASE_ENABLED (sentient_milieu))
+ Response (sentient_milieu, PetInfo);
+ else if (PHRASE_ENABLED (what_about_war))
+ Response (what_about_war, PetInfo);
+ Response (enough_info, PetDevice);
+}
+
+static void
+PetDevice (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_onboard))
+ {
+ NumVisits = GET_GAME_STATE (TALKING_PET_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_ONBOARD_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_ONBOARD_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_ONBOARD_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_ONBOARD_4);
+ break;
+ case 4:
+ NPCPhrase (GENERAL_INFO_ONBOARD_5);
+ break;
+ case 5:
+ NPCPhrase (GENERAL_INFO_ONBOARD_6);
+ break;
+ case 6:
+ NPCPhrase (GENERAL_INFO_ONBOARD_7);
+ break;
+ case 7:
+ NPCPhrase (GENERAL_INFO_ONBOARD_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (TALKING_PET_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_onboard);
+ }
+ else if (PLAYER_SAID (R, any_suggestions))
+ {
+ NumVisits = GET_GAME_STATE (TALKING_PET_SUGGESTIONS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (SUGGESTION_1);
+ break;
+ case 1:
+ NPCPhrase (SUGGESTION_2);
+ break;
+ case 2:
+ NPCPhrase (SUGGESTION_3);
+ break;
+ case 3:
+ NPCPhrase (SUGGESTION_4);
+ break;
+ case 4:
+ NPCPhrase (SUGGESTION_5);
+ break;
+ case 5:
+ NPCPhrase (SUGGESTION_6);
+ break;
+ case 6:
+ NPCPhrase (SUGGESTION_7);
+ break;
+ case 7:
+ NPCPhrase (SUGGESTION_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (TALKING_PET_SUGGESTIONS, NumVisits);
+
+ DISABLE_PHRASE (any_suggestions);
+ }
+ else if (PLAYER_SAID (R, enough_info))
+ NPCPhrase (OK_ENOUGH_INFO);
+ else if (PLAYER_SAID (R, you_lied))
+ {
+ NPCPhrase (SO_WHAT);
+
+ SET_GAME_STATE (DNYARRI_LIED, 0);
+ }
+
+ if (GET_GAME_STATE (SHIP_TO_COMPEL))
+ {
+ Response (compel_that_ship, ExitConversation);
+ }
+ if (PHRASE_ENABLED (whats_up_onboard))
+ Response (whats_up_onboard, PetDevice);
+ if (PHRASE_ENABLED (any_suggestions))
+ Response (any_suggestions, PetDevice);
+ Response (about_your_race, PetInfo);
+ if (GET_GAME_STATE (DNYARRI_LIED) && GET_GAME_STATE (LEARNED_TALKING_PET))
+ Response (you_lied, PetDevice);
+ Response (bye_onboard, ExitConversation);
+}
+
+static void
+CompelPlayer (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[3];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = 0;
+ if (PLAYER_SAID (R, what_are_you))
+ {
+ NPCPhrase (JUST_TALKING_PET);
+
+ DISABLE_PHRASE (what_are_you);
+ }
+ else if (PLAYER_SAID (R, what_do_to_umgah))
+ {
+ NPCPhrase (DID_NOTHING);
+
+ DISABLE_PHRASE (what_do_to_umgah);
+ LastStack = 1;
+ }
+ else if (PLAYER_SAID (R, we_are_vindicator0))
+ {
+ NPCPhrase (GOOD_FOR_YOU);
+
+ DISABLE_PHRASE (we_are_vindicator0);
+ LastStack = 2;
+ }
+ else if (R != 0)
+ {
+ if (PLAYER_SAID (R, bye_at_umgah))
+ NPCPhrase (GOODBYE_AT_UMGAH);
+ else if (PLAYER_SAID (R, must_explain_presence))
+ NPCPhrase (EXPLAIN_NOTHING_MONKEY_BOY);
+ else if (PLAYER_SAID (R, umgah_zombies))
+ NPCPhrase (WORKS_LIKE_THIS);
+ else if (PLAYER_SAID (R, talking_pets_dumb))
+ NPCPhrase (OH_NO_YOU_DONT);
+
+ SET_GAME_STATE (KNOW_UMGAH_ZOMBIES, 1);
+ if (!GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP))
+ {
+ SET_GAME_STATE (PLAYER_HYPNOTIZED, 1);
+ }
+ else
+ {
+ NPCPhrase (CANT_COMPEL);
+
+ setSegue (Segue_hostile);
+ }
+
+ return;
+ }
+
+ if (PHRASE_ENABLED (what_are_you))
+ pStr[0] = what_are_you;
+ else
+ pStr[0] = talking_pets_dumb;
+ if (GET_GAME_STATE (KNOW_UMGAH_ZOMBIES))
+ {
+ if (PHRASE_ENABLED (what_do_to_umgah))
+ pStr[1] = what_do_to_umgah;
+ else
+ pStr[1] = umgah_zombies;
+ }
+ if (PHRASE_ENABLED (we_are_vindicator0))
+ {
+ construct_response (
+ shared_phrase_buf,
+ we_are_vindicator0,
+ GLOBAL_SIS (ShipName),
+ we_are_vindicator1,
+ (UNICODE*)NULL);
+ pStr[2] = we_are_vindicator0;
+ }
+ else
+ pStr[2] = must_explain_presence;
+
+ if (pStr[LastStack])
+ {
+ if (pStr[LastStack] != we_are_vindicator0)
+ Response (pStr[LastStack], CompelPlayer);
+ else
+ DoResponsePhrase (pStr[LastStack], CompelPlayer, shared_phrase_buf);
+ }
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ if (pStr[i] != we_are_vindicator0)
+ Response (pStr[i], CompelPlayer);
+ else
+ DoResponsePhrase (pStr[i], CompelPlayer, shared_phrase_buf);
+ }
+ }
+ Response (bye_at_umgah, CompelPlayer);
+}
+
+static void PetDeal (RESPONSE_REF R);
+
+static void
+KillPet (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, must_kill))
+ {
+ NPCPhrase (DONT_KILL);
+ AlienTalkSegue ((COUNT)~0);
+
+ MindControlStrobe ();
+ }
+
+ Response (want_kill_1, ExitConversation);
+ Response (want_kill_2, ExitConversation);
+ Response (want_kill_3, ExitConversation);
+}
+
+static void
+PetDeal (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, what_kind_of_deal))
+ {
+ NPCPhrase (HELP_DEFEAT_URQUAN);
+
+ DISABLE_PHRASE (what_kind_of_deal);
+ }
+ else if (PLAYER_SAID (R, how_trust))
+ {
+ NPCPhrase (TRUST);
+
+ DISABLE_PHRASE (how_trust);
+ }
+ else if (PLAYER_SAID (R, boneless_dweeb))
+ {
+ NPCPhrase (YOUR_BONELESS_DWEEB);
+
+ DISABLE_PHRASE (boneless_dweeb);
+ }
+ else if (PLAYER_SAID (R, what_are_you_really))
+ {
+ NPCPhrase (POOR_DNYARRI);
+
+ DISABLE_PHRASE (what_are_you_really);
+ }
+ else if (PLAYER_SAID (R, hard_to_believe))
+ {
+ NPCPhrase (ITS_TRUE);
+
+ SET_GAME_STATE (DNYARRI_LIED, 1);
+ DISABLE_PHRASE (hard_to_believe);
+ }
+ else if (PLAYER_SAID (R, bullshit))
+ {
+ NPCPhrase (WORTH_A_TRY);
+
+ DISABLE_PHRASE (bullshit);
+ }
+ else if (PLAYER_SAID (R, kill_you))
+ {
+ NPCPhrase (PLEASE_DONT);
+
+ DISABLE_PHRASE (kill_you);
+ }
+
+ if (PHRASE_ENABLED (what_kind_of_deal))
+ Response (what_kind_of_deal, PetDeal);
+ else
+ {
+ if (PHRASE_ENABLED (how_trust))
+ Response (how_trust, PetDeal);
+ else if (PHRASE_ENABLED (boneless_dweeb))
+ Response (boneless_dweeb, PetDeal);
+ Response (ok_lets_do_it, ExitConversation);
+ }
+ if (PHRASE_ENABLED (what_are_you_really))
+ Response (what_are_you_really, PetDeal);
+ else
+ {
+ if (PHRASE_ENABLED (hard_to_believe) && !GET_GAME_STATE (LEARNED_TALKING_PET))
+ Response (hard_to_believe, PetDeal);
+ else if (PHRASE_ENABLED (bullshit) && GET_GAME_STATE (LEARNED_TALKING_PET))
+ Response (bullshit, PetDeal);
+ }
+ if (PHRASE_ENABLED (kill_you))
+ Response (kill_you, PetDeal);
+ else if (PHRASE_ENABLED (must_kill))
+ {
+ Response (must_kill, KillPet);
+ }
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ {
+ SET_GAME_STATE (SHIP_TO_COMPEL, 0);
+ setSegue (Segue_hostile);
+ if (!(GLOBAL (glob_flags) & CYBORG_ENABLED))
+ {
+ NPCPhrase (HUMAN_PEP_TALK);
+ }
+ else
+ {
+ NPCPhrase (CYBORG_PEP_TALK);
+ }
+ }
+ else if (GET_GAME_STATE (READY_TO_CONFUSE_URQUAN))
+ {
+ SET_GAME_STATE (SHIP_TO_COMPEL, 0);
+ SET_GAME_STATE (READY_TO_CONFUSE_URQUAN, 0);
+ SET_GAME_STATE (AWARE_OF_SAMATRA, 1);
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) != 3)
+ {
+ NPCPhrase (HAVENT_GOT_EVERYTHING);
+ if (!GET_GAME_STATE (UTWIG_BOMB_ON_SHIP))
+ NPCPhrase (NEED_BOMB);
+ else
+ NPCPhrase (SOUP_UP_BOMB);
+
+ setSegue (Segue_peace);
+ }
+ else if (GET_GAME_STATE (URQUAN_MESSED_UP))
+ {
+ NPCPhrase (HELLO_AFTER_COMPEL_URQUAN);
+
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ NPCPhrase (I_SENSE_MY_SLAVES);
+
+ MindFuckUrquan ((RESPONSE_REF)0);
+ }
+ }
+ else if (GET_GAME_STATE (TALKING_PET_ON_SHIP))
+ {
+ NumVisits = GET_GAME_STATE (TALKING_PET_VISITS);
+
+ // You can acquire the Talking Pet without having Taalo Shield after
+ // the Kohr-Ah wipe out the Umgah. In that case, the Pet will join
+ // you willingly, but his complaints about the Taalo shield as in
+ // HELLO_AS_DEVICE_1 do not make any sense.
+ if (!GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP) && NumVisits == 0)
+ ++NumVisits; // skip HELLO_AS_DEVICE_1
+
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_AS_DEVICE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_AS_DEVICE_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_AS_DEVICE_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_AS_DEVICE_4);
+ break;
+ case 4:
+ NPCPhrase (HELLO_AS_DEVICE_5);
+ break;
+ case 5:
+ NPCPhrase (HELLO_AS_DEVICE_6);
+ break;
+ case 6:
+ NPCPhrase (HELLO_AS_DEVICE_7);
+ break;
+ case 7:
+ NPCPhrase (HELLO_AS_DEVICE_8);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (TALKING_PET_VISITS, NumVisits);
+
+ PetDevice ((RESPONSE_REF)0);
+ }
+ else if (GetHeadLink (&GLOBAL (npc_built_ship_q)))
+ {
+ NumVisits = GET_GAME_STATE (TALKING_PET_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ SET_GAME_STATE (UMGAH_VISITS, 0);
+ NPCPhrase (HELLO_AT_UMGAH);
+ break;
+ case 1:
+ NPCPhrase (HYPNOTIZE_AGAIN_1);
+ break;
+ case 2:
+ NPCPhrase (HYPNOTIZE_AGAIN_2);
+ break;
+ case 3:
+ NPCPhrase (HYPNOTIZE_AGAIN_3);
+ break;
+ case 4:
+ NPCPhrase (HYPNOTIZE_AGAIN_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (TALKING_PET_HOME_VISITS, NumVisits);
+
+ if (NumVisits == 1)
+ {
+ CompelPlayer ((RESPONSE_REF)0);
+ }
+ else if (!GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP))
+ {
+ SET_GAME_STATE (PLAYER_HYPNOTIZED, 1);
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ NPCPhrase (CANT_COMPEL);
+
+ setSegue (Segue_hostile);
+ }
+ }
+ else
+ {
+ if (StartSphereTracking (UMGAH_SHIP))
+ {
+ NPCPhrase (LETS_MAKE_A_DEAL);
+ }
+ else
+ {
+ NPCPhrase (UMGAH_ALL_GONE);
+
+ if (GET_GAME_STATE (TALKING_PET_HOME_VISITS) == 0
+ || !GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP))
+ { // The how_trust-TRUST exchange only makes sense when the
+ // player visited the Talking Pet before so he tried to
+ // kill the player *and* the player has the Taalo shield.
+ DISABLE_PHRASE (how_trust);
+ }
+ }
+
+ PetDeal ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_talkpet (void)
+{
+ return (0);
+}
+
+static void
+post_talkpet_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_talkpet_comm (void)
+{
+ LOCDATA *retval;
+
+ talkpet_desc.init_encounter_func = Intro;
+ talkpet_desc.post_encounter_func = post_talkpet_enc;
+ talkpet_desc.uninit_encounter_func = uninit_talkpet;
+
+ talkpet_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ talkpet_desc.AlienTextBaseline.y = 0;
+ talkpet_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != IN_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+
+ retval = &talkpet_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/thradd/Makeinfo b/src/uqm/comm/thradd/Makeinfo
new file mode 100644
index 0000000..d492800
--- /dev/null
+++ b/src/uqm/comm/thradd/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="thraddc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/thradd/resinst.h b/src/uqm/comm/thradd/resinst.h
new file mode 100644
index 0000000..977f175
--- /dev/null
+++ b/src/uqm/comm/thradd/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define THRADD_COLOR_MAP "comm.thraddash.colortable"
+#define THRADD_CONVERSATION_PHRASES "comm.thraddash.dialogue"
+#define THRADD_FONT "comm.thraddash.font"
+#define THRADD_MUSIC "comm.thraddash.music"
+#define THRADD_PMAP_ANIM "comm.thraddash.graphics"
diff --git a/src/uqm/comm/thradd/strings.h b/src/uqm/comm/thradd/strings.h
new file mode 100644
index 0000000..20e4701
--- /dev/null
+++ b/src/uqm/comm/thradd/strings.h
@@ -0,0 +1,181 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef THRADD_STRINGS_H
+#define THRADD_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HOSTILE_SPACE_HELLO_1,
+ HOSTILE_SPACE_HELLO_2,
+ HOSTILE_SPACE_HELLO_3,
+ HOSTILE_SPACE_HELLO_4,
+ HOSTILE_HOMEWORLD_HELLO_1,
+ HOSTILE_HOMEWORLD_HELLO_2,
+ HOSTILE_HOMEWORLD_HELLO_3,
+ HOSTILE_HOMEWORLD_HELLO_4,
+ whats_up_hostile_1,
+ whats_up_hostile_2,
+ GENERAL_INFO_HOSTILE_1,
+ GENERAL_INFO_HOSTILE_2,
+ GENERAL_INFO_HOSTILE_3,
+ GENERAL_INFO_HOSTILE_4,
+ what_about_you_1,
+ ABOUT_US_1,
+ what_about_you_2,
+ ABOUT_US_2,
+ what_about_urquan_1,
+ ABOUT_URQUAN_1,
+ what_about_urquan_2,
+ ABOUT_URQUAN_2,
+ got_idea,
+ GOOD_IDEA,
+ WE_GO_TO_IMPRESS_URQUAN_1,
+ WE_GO_TO_IMPRESS_URQUAN_2,
+ WE_IMPRESSING_URQUAN_1,
+ WE_IMPRESSING_URQUAN_2,
+ WE_IMPRESSED_URQUAN_1,
+ WE_IMPRESSED_URQUAN_2,
+ HOSTILE_HELIX_HELLO_1,
+ HOSTILE_HELIX_HELLO_2,
+ submit_1,
+ NO_SUBMIT_1,
+ submit_2,
+ NO_SUBMIT_2,
+ be_friends_1,
+ NO_FRIENDS_1,
+ be_friends_2,
+ NO_FRIENDS_2,
+ how_impressed_urquan_1,
+ IMPRESSED_LIKE_SO_1,
+ how_impressed_urquan_2,
+ IMPRESSED_LIKE_SO_2,
+ bye_hostile_1,
+ GOODBYE_HOSTILE_1,
+ bye_hostile_2,
+ GOODBYE_HOSTILE_2,
+ why_you_here_hostile,
+ NONE_OF_YOUR_CONCERN,
+ demand_to_land,
+ NO_DEMAND,
+ what_about_this_world,
+ BLUE_HELIX,
+ whats_helix_hostile,
+ HELIX_IS_HOSTILE,
+ i_need_to_land_lie,
+ CAUGHT_LIE,
+ bye_hostile_helix,
+ GOODBYE_HOSTILE_HELIX,
+ DIE_THIEF_1,
+ DIE_THIEF_2,
+ AMAZING_PERFORMANCE,
+ IMPRESSIVE_PERFORMANCE,
+ ADEQUATE_PERFORMANCE,
+ HELLO_POLITE_1,
+ HELLO_POLITE_2,
+ HELLO_POLITE_3,
+ HELLO_POLITE_4,
+ HELLO_RHYME_1,
+ HELLO_RHYME_2,
+ HELLO_RHYME_3,
+ HELLO_RHYME_4,
+ HELLO_PIG_LATIN_1,
+ HELLO_PIG_LATIN_2,
+ HELLO_PIG_LATIN_3,
+ HELLO_PIG_LATIN_4,
+ HELLO_LIKE_YOU_1,
+ HELLO_LIKE_YOU_2,
+ HELLO_LIKE_YOU_3,
+ HELLO_LIKE_YOU_4,
+ WELCOME_SPACE0,
+ WELCOME_SPACE1,
+ WELCOME_HOMEWORLD0,
+ WELCOME_HOMEWORLD1,
+ WELCOME_HELIX0,
+ WELCOME_HELIX1,
+ why_you_here_ally,
+ GUARDING_HELIX_ALLY,
+ whats_helix_ally,
+ HELIX_IS_ALLY,
+ may_i_land,
+ SURE_LAND,
+ whats_up_ally,
+ GENERAL_INFO_ALLY_1,
+ GENERAL_INFO_ALLY_2,
+ GENERAL_INFO_ALLY_3,
+ GENERAL_INFO_ALLY_4,
+ HOW_SHOULD_WE_ACT,
+ friendly,
+ OK_FRIENDLY,
+ wacky,
+ OK_WACKY,
+ just_like_us,
+ OK_JUST_LIKE_YOU,
+ WORK_TO_DO,
+ contemplative,
+ OK_CONTEMPLATIVE,
+ how_goes_culture,
+ CONTEMP_GOES_1,
+ CONTEMP_GOES_2,
+ FRIENDLY_GOES_1,
+ FRIENDLY_GOES_2,
+ WACKY_GOES_1,
+ WACKY_GOES_2,
+ LIKE_YOU_GOES_1,
+ LIKE_YOU_GOES_2,
+ bye_ally,
+ GOODBYE_ALLY_1,
+ GOODBYE_ALLY_2,
+ GOODBYE_ALLY_3,
+ GOODBYE_ALLY_4,
+ be_polite,
+ OK_POLITE,
+ speak_pig_latin,
+ OK_PIG_LATIN,
+ use_rhymes,
+ OK_RHYMES,
+ just_the_way_we_do,
+ OK_WAY_YOU_DO,
+ WHAT_NAME_FOR_CULTURE,
+ alliance_name,
+ OK_ALLIANCE_NAME,
+ NAME_TAIL,
+ you_decide,
+ OK_CULTURE_20,
+ fat,
+ OK_FAT,
+ the_slave_empire0,
+ the_slave_empire1,
+ OK_SLAVE,
+ FAT_JERKS,
+ CULTURE,
+ SLAVE_EMPIRE,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ HAVING_FUN_WITH_ILWRATH_1,
+ HAVING_FUN_WITH_ILWRATH_2,
+ GO_AWAY_FIGHTING_ILWRATH_1,
+ GO_AWAY_FIGHTING_ILWRATH_2,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/thradd/thraddc.c b/src/uqm/comm/thradd/thraddc.c
new file mode 100644
index 0000000..1be8a1e
--- /dev/null
+++ b/src/uqm/comm/thradd/thraddc.c
@@ -0,0 +1,954 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/gameev.h"
+
+
+static LOCDATA thradd_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ THRADD_PMAP_ANIM, /* AlienFrame */
+ THRADD_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ THRADD_COLOR_MAP, /* AlienColorMap */
+ THRADD_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ THRADD_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 8, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 8, /* StartIndex */
+ 4, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* RestartRate */
+ (1 << 4), /* BlockMask */
+ },
+ {
+ 12, /* StartIndex */
+ 9, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 21, /* StartIndex */
+ 6, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 27, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4), /* BlockMask */
+ },
+ {
+ 30, /* StartIndex */
+ 12, /* NumFrames */
+ CIRCULAR_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND, /* RestartRate */
+ (1 << 0) | (1 << 3) | (1 << 5),
+ },
+ {
+ 42, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4) | (1 << 6), /* BlockMask */
+ },
+ {
+ 47, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5), /* BlockMask */
+ },
+ {
+ 52, /* StartIndex */
+ 4, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND / 20, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 7, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static int
+GetCultureName (void)
+{
+ int culture = 0;
+
+ switch (GET_GAME_STATE (THRADD_CULTURE))
+ {
+ case 1:
+ culture = CULTURE;
+ break;
+ case 2:
+ culture = FAT_JERKS;
+ break;
+ case 3:
+ culture = SLAVE_EMPIRE;
+ break;
+ default:
+ assert (0 && "Unknown culture");
+ }
+
+ return (culture);
+}
+
+static void
+PolitePhrase (BYTE which_phrase)
+{
+ switch (which_phrase)
+ {
+ case 0:
+ NPCPhrase (HELLO_POLITE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_POLITE_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_POLITE_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_POLITE_4);
+ break;
+ }
+}
+
+static void
+RhymePhrase (BYTE which_phrase)
+{
+ switch (which_phrase)
+ {
+ case 0:
+ NPCPhrase (HELLO_RHYME_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_RHYME_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_RHYME_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_RHYME_4);
+ break;
+ }
+}
+
+static void
+PigLatinPhrase (BYTE which_phrase)
+{
+ switch (which_phrase)
+ {
+ case 0:
+ NPCPhrase (HELLO_PIG_LATIN_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_PIG_LATIN_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_PIG_LATIN_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_PIG_LATIN_4);
+ break;
+ }
+}
+
+static void
+LikeYouPhrase (BYTE which_phrase)
+{
+ switch (which_phrase)
+ {
+ case 0:
+ NPCPhrase (HELLO_LIKE_YOU_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_LIKE_YOU_2);
+ break;
+ case 2:
+ NPCPhrase (HELLO_LIKE_YOU_3);
+ break;
+ case 3:
+ NPCPhrase (HELLO_LIKE_YOU_4);
+ break;
+ }
+}
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, bye_hostile_2))
+ NPCPhrase (GOODBYE_HOSTILE_2);
+ else if (PLAYER_SAID (R, bye_hostile_1))
+ {
+ NPCPhrase (GOODBYE_HOSTILE_1);
+
+ SET_GAME_STATE (THRADD_HOSTILE_STACK_5, 1);
+ }
+ else if (PLAYER_SAID (R, submit_1))
+ {
+ NPCPhrase (NO_SUBMIT_1);
+
+ SET_GAME_STATE (THRADD_HOSTILE_STACK_2, 1);
+ }
+ else if (PLAYER_SAID (R, submit_2))
+ NPCPhrase (NO_SUBMIT_2);
+ else if (PLAYER_SAID (R, got_idea))
+ {
+ NPCPhrase (GOOD_IDEA);
+
+ setSegue (Segue_peace);
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, ADVANCE_THRADD_MISSION);
+ SET_GAME_STATE (THRADD_STACK_1, 5);
+ }
+ else if (PLAYER_SAID (R, bye_hostile_helix))
+ NPCPhrase (GOODBYE_HOSTILE_HELIX);
+ else if (PLAYER_SAID (R, bye_ally))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (THRADD_STACK_1);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GOODBYE_ALLY_1);
+ break;
+ case 1:
+ NPCPhrase (GOODBYE_ALLY_2);
+ break;
+ case 2:
+ NPCPhrase (GOODBYE_ALLY_3);
+ break;
+ case 3:
+ NPCPhrase (GOODBYE_ALLY_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (THRADD_STACK_1, NumVisits);
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, may_i_land))
+ {
+ NPCPhrase (SURE_LAND);
+
+ SET_GAME_STATE (HELIX_UNPROTECTED, 1);
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, demand_to_land))
+ NPCPhrase (NO_DEMAND);
+ else if (PLAYER_SAID (R, i_need_to_land_lie))
+ NPCPhrase (CAUGHT_LIE);
+ else
+ {
+ if (PLAYER_SAID (R, contemplative))
+ {
+ NPCPhrase (OK_CONTEMPLATIVE);
+
+ SET_GAME_STATE (THRADD_DEMEANOR, 0);
+ }
+ else if (PLAYER_SAID (R, friendly))
+ {
+ NPCPhrase (OK_FRIENDLY);
+
+ SET_GAME_STATE (THRADD_DEMEANOR, 1);
+ }
+ else if (PLAYER_SAID (R, wacky))
+ {
+ NPCPhrase (OK_WACKY);
+
+ SET_GAME_STATE (THRADD_DEMEANOR, 2);
+ }
+ else if (PLAYER_SAID (R, just_like_us))
+ {
+ NPCPhrase (OK_JUST_LIKE_YOU);
+
+ SET_GAME_STATE (THRADD_DEMEANOR, 3);
+ }
+ NPCPhrase (WORK_TO_DO);
+
+ setSegue (Segue_peace);
+ }
+}
+
+static void
+ThraddAllies (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, why_you_here_ally))
+ {
+ NPCPhrase (GUARDING_HELIX_ALLY);
+
+ DISABLE_PHRASE (why_you_here_ally);
+ }
+ else if (PLAYER_SAID (R, whats_helix_ally))
+ {
+ NPCPhrase (HELIX_IS_ALLY);
+
+ DISABLE_PHRASE (whats_helix_ally);
+ }
+ else if (PLAYER_SAID (R, whats_up_ally))
+ {
+ NumVisits = GET_GAME_STATE (THRADD_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_ALLY_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_ALLY_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_ALLY_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_ALLY_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (THRADD_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_ally);
+ }
+ else if (PLAYER_SAID (R, how_goes_culture))
+ {
+ NumVisits = GET_GAME_STATE (THRADD_DEMEANOR);
+ switch (NumVisits & ((1 << 2) - 1))
+ {
+ case 0:
+ if (!(NumVisits & ~((1 << 2) - 1)))
+ NPCPhrase (CONTEMP_GOES_1);
+ else
+ NPCPhrase (CONTEMP_GOES_2);
+ break;
+ case 1:
+ if (!(NumVisits & ~((1 << 2) - 1)))
+ NPCPhrase (FRIENDLY_GOES_1);
+ else
+ NPCPhrase (FRIENDLY_GOES_2);
+ break;
+ case 2:
+ if (!(NumVisits & ~((1 << 2) - 1)))
+ NPCPhrase (WACKY_GOES_1);
+ else
+ NPCPhrase (WACKY_GOES_2);
+ break;
+ case 3:
+ if (!(NumVisits & ~((1 << 2) - 1)))
+ NPCPhrase (LIKE_YOU_GOES_1);
+ else
+ NPCPhrase (LIKE_YOU_GOES_2);
+ break;
+ }
+ NumVisits |= 1 << 2;
+ SET_GAME_STATE (THRADD_DEMEANOR, NumVisits);
+
+ DISABLE_PHRASE (how_goes_culture);
+ }
+
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ if (PHRASE_ENABLED (why_you_here_ally))
+ Response (why_you_here_ally, ThraddAllies);
+ else
+ {
+ if (PHRASE_ENABLED (whats_helix_ally))
+ Response (whats_helix_ally, ThraddAllies);
+ Response (may_i_land, ExitConversation);
+ }
+ }
+ if (PHRASE_ENABLED (whats_up_ally))
+ Response (whats_up_ally, ThraddAllies);
+ if (PHRASE_ENABLED (how_goes_culture))
+ Response (how_goes_culture, ThraddAllies);
+ Response (bye_ally, ExitConversation);
+}
+
+static void
+ThraddDemeanor (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, you_decide))
+ {
+ NPCPhrase (OK_CULTURE_20);
+
+ SET_GAME_STATE (THRADD_CULTURE, 1);
+ }
+ else if (PLAYER_SAID (R, fat))
+ {
+ NPCPhrase (OK_FAT);
+
+ SET_GAME_STATE (THRADD_CULTURE, 2);
+ }
+ else if (PLAYER_SAID (R, the_slave_empire0))
+ {
+ SET_GAME_STATE (THRADD_CULTURE, 3);
+
+ NPCPhrase (OK_SLAVE);
+ }
+
+ NPCPhrase (HOW_SHOULD_WE_ACT);
+ Response (contemplative, ExitConversation);
+ Response (friendly, ExitConversation);
+ Response (wacky, ExitConversation);
+ Response (just_like_us, ExitConversation);
+}
+
+static void
+ThraddCulture (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, be_polite))
+ {
+ NPCPhrase (OK_POLITE);
+
+ SET_GAME_STATE (THRADD_INTRO, 0);
+ }
+ else if (PLAYER_SAID (R, use_rhymes))
+ {
+ NPCPhrase (OK_RHYMES);
+
+ SET_GAME_STATE (THRADD_INTRO, 1);
+ }
+ else if (PLAYER_SAID (R, speak_pig_latin))
+ {
+ NPCPhrase (OK_PIG_LATIN);
+
+ SET_GAME_STATE (THRADD_INTRO, 2);
+ }
+ else if (PLAYER_SAID (R, just_the_way_we_do))
+ {
+ NPCPhrase (OK_WAY_YOU_DO);
+
+ SET_GAME_STATE (THRADD_INTRO, 3);
+ }
+ NPCPhrase (WHAT_NAME_FOR_CULTURE);
+
+ construct_response (
+ shared_phrase_buf,
+ the_slave_empire0,
+ GLOBAL_SIS (CommanderName),
+ the_slave_empire1,
+ (UNICODE*)NULL);
+
+ Response (you_decide, ThraddDemeanor);
+ Response (fat, ThraddDemeanor);
+ DoResponsePhrase (the_slave_empire0, ThraddDemeanor, shared_phrase_buf);
+}
+
+static void
+ThraddWorship (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ SET_GAME_STATE (THRADD_VISITS, 0);
+ SET_GAME_STATE (THRADD_MANNER, 1);
+ SET_GAME_STATE (THRADD_STACK_1, 0);
+ SetRaceAllied (THRADDASH_SHIP, TRUE);
+
+ Response (be_polite, ThraddCulture);
+ Response (speak_pig_latin, ThraddCulture);
+ Response (use_rhymes, ThraddCulture);
+ Response (just_the_way_we_do, ThraddCulture);
+}
+
+static void
+HelixWorld (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, why_you_here_hostile))
+ {
+ NPCPhrase (NONE_OF_YOUR_CONCERN);
+
+ SET_GAME_STATE (THRADD_CULTURE, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_this_world))
+ {
+ NPCPhrase (BLUE_HELIX);
+
+ SET_GAME_STATE (THRADD_INTRO, 1);
+ }
+ else if (PLAYER_SAID (R, whats_helix_hostile))
+ {
+ NPCPhrase (HELIX_IS_HOSTILE);
+
+ SET_GAME_STATE (THRADD_INTRO, 2);
+ }
+ else if (PLAYER_SAID (R, i_need_to_land_lie))
+ {
+ NPCPhrase (CAUGHT_LIE);
+
+ SET_GAME_STATE (THRADD_DEMEANOR, 1);
+ }
+
+ if (!GET_GAME_STATE (THRADD_CULTURE))
+ Response (why_you_here_hostile, HelixWorld);
+ else
+ {
+ Response (demand_to_land, ExitConversation);
+ }
+ switch (GET_GAME_STATE (THRADD_INTRO))
+ {
+ case 0:
+ Response (what_about_this_world, HelixWorld);
+ break;
+ case 1:
+ Response (whats_helix_hostile, HelixWorld);
+ break;
+ }
+ if (!GET_GAME_STATE (THRADD_DEMEANOR))
+ {
+ Response (i_need_to_land_lie, ExitConversation);
+ }
+ Response (bye_hostile_helix, ExitConversation);
+}
+
+static void
+ThraddHostile (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_hostile_1))
+ {
+ NPCPhrase (GENERAL_INFO_HOSTILE_1);
+
+ SET_GAME_STATE (THRADD_INFO, 1);
+ DISABLE_PHRASE (whats_up_hostile_2);
+ }
+ else if (PLAYER_SAID (R, whats_up_hostile_2))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (THRADD_INFO);
+ switch (NumVisits++)
+ {
+ case 1:
+ NPCPhrase (GENERAL_INFO_HOSTILE_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_HOSTILE_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_HOSTILE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (THRADD_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_hostile_2);
+ }
+ else if (PLAYER_SAID (R, what_about_you_1))
+ {
+ NPCPhrase (ABOUT_US_1);
+
+ SET_GAME_STATE (THRADD_STACK_1, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_you_2))
+ {
+ NPCPhrase (ABOUT_US_2);
+
+ SET_GAME_STATE (THRADD_STACK_1, 2);
+ }
+ else if (PLAYER_SAID (R, what_about_urquan_1))
+ {
+ NPCPhrase (ABOUT_URQUAN_1);
+
+ SET_GAME_STATE (THRADD_STACK_1, 3);
+ }
+ else if (PLAYER_SAID (R, what_about_urquan_2))
+ {
+ NPCPhrase (ABOUT_URQUAN_2);
+
+ SET_GAME_STATE (THRADD_STACK_1, 4);
+ }
+ else if (PLAYER_SAID (R, be_friends_1))
+ {
+ NPCPhrase (NO_FRIENDS_1);
+
+ SET_GAME_STATE (THRADD_HOSTILE_STACK_3, 1);
+ }
+ else if (PLAYER_SAID (R, be_friends_2))
+ {
+ NPCPhrase (NO_FRIENDS_2);
+ DISABLE_PHRASE (be_friends_2);
+ }
+ else if (PLAYER_SAID (R, how_impressed_urquan_1))
+ {
+ NPCPhrase (IMPRESSED_LIKE_SO_1);
+
+ SET_GAME_STATE (THRADD_HOSTILE_STACK_4, 1);
+ }
+ else if (PLAYER_SAID (R, how_impressed_urquan_2))
+ {
+ NPCPhrase (IMPRESSED_LIKE_SO_2);
+
+ SET_GAME_STATE (THRADD_MISSION, 5);
+ }
+
+ if (GET_GAME_STATE (THRADD_INFO) == 0)
+ Response (whats_up_hostile_1, ThraddHostile);
+ else if (PHRASE_ENABLED (whats_up_hostile_2))
+ Response (whats_up_hostile_2, ThraddHostile);
+ switch (GET_GAME_STATE (THRADD_STACK_1))
+ {
+ case 0:
+ Response (what_about_you_1, ThraddHostile);
+ break;
+ case 1:
+ Response (what_about_you_2, ThraddHostile);
+ break;
+ case 2:
+ Response (what_about_urquan_1, ThraddHostile);
+ break;
+ case 3:
+ Response (what_about_urquan_2, ThraddHostile);
+ break;
+ case 4:
+ if (!GET_GAME_STATE (KOHR_AH_FRENZY))
+ Response (got_idea, ExitConversation);
+ else
+ {
+ SET_GAME_STATE (THRADD_STACK_1, 5);
+ }
+ break;
+ }
+ if (GET_GAME_STATE (THRADD_HOSTILE_STACK_2) == 0)
+ Response (submit_1, ExitConversation);
+ else
+ Response (submit_2, ExitConversation);
+ if (GET_GAME_STATE (THRADD_HOSTILE_STACK_3) == 0)
+ Response (be_friends_1, ThraddHostile);
+ else if (PHRASE_ENABLED (be_friends_2))
+ Response (be_friends_2, ThraddHostile);
+ if (GET_GAME_STATE (THRADD_MISSION) == 4)
+ {
+ if (GET_GAME_STATE (THRADD_HOSTILE_STACK_4) == 0)
+ Response (how_impressed_urquan_1, ThraddHostile);
+ else
+ Response (how_impressed_urquan_2, ThraddHostile);
+ }
+ if (GET_GAME_STATE (THRADD_HOSTILE_STACK_5) == 0)
+ Response (bye_hostile_1, ExitConversation);
+ else
+ Response (bye_hostile_2, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (AQUA_HELIX))
+ {
+ NumVisits = GET_GAME_STATE (HELIX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (DIE_THIEF_1);
+ break;
+ case 1:
+ NPCPhrase (DIE_THIEF_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (HELIX_VISITS, NumVisits);
+
+ setSegue (Segue_hostile);
+ }
+ else if (GET_GAME_STATE (ILWRATH_FIGHT_THRADDASH))
+ {
+ NumVisits = GET_GAME_STATE (THRADD_VISITS);
+ if (GET_GAME_STATE (THRADD_MANNER))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HAVING_FUN_WITH_ILWRATH_1);
+ break;
+ case 1:
+ NPCPhrase (HAVING_FUN_WITH_ILWRATH_2);
+ --NumVisits;
+ break;
+ }
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GO_AWAY_FIGHTING_ILWRATH_1);
+ break;
+ case 1:
+ NPCPhrase (GO_AWAY_FIGHTING_ILWRATH_2);
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (THRADD_VISITS, NumVisits);
+
+ setSegue (Segue_peace);
+ }
+ else if (GET_GAME_STATE (THRADD_MANNER))
+ {
+ RESPONSE_REF pStr0, pStr1;
+
+ NumVisits = GET_GAME_STATE (THRADD_VISITS);
+ switch (GET_GAME_STATE (THRADD_INTRO))
+ {
+ case 0:
+ PolitePhrase (NumVisits);
+ break;
+ case 1:
+ RhymePhrase (NumVisits);
+ break;
+ case 2:
+ PigLatinPhrase (NumVisits);
+ break;
+ case 3:
+ LikeYouPhrase (NumVisits);
+ break;
+ }
+ if (++NumVisits < 4)
+ {
+ SET_GAME_STATE (THRADD_VISITS, NumVisits);
+ }
+
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ pStr0 = WELCOME_HELIX0;
+ pStr1 = WELCOME_HELIX1;
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ pStr0 = WELCOME_HOMEWORLD0;
+ pStr1 = WELCOME_HOMEWORLD1;
+ }
+ else
+ {
+ pStr0 = WELCOME_SPACE0;
+ pStr1 = WELCOME_SPACE1;
+ }
+ NPCPhrase (pStr0);
+ NPCPhrase (GetCultureName ());
+ NPCPhrase (pStr1);
+
+ ThraddAllies ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ NumVisits = GET_GAME_STATE (HELIX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_HELIX_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_HELIX_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (HELIX_VISITS, NumVisits);
+
+ HelixWorld ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (THRADDASH_BODY_COUNT) >= THRADDASH_BODY_THRESHOLD)
+ {
+ NPCPhrase (AMAZING_PERFORMANCE);
+
+ ThraddWorship ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (THRADDASH_BODY_COUNT);
+ if (NumVisits >= 16
+ && GET_GAME_STATE (THRADD_BODY_LEVEL) == 1)
+ {
+ SET_GAME_STATE (THRADD_BODY_LEVEL, 2);
+ NPCPhrase (IMPRESSIVE_PERFORMANCE);
+ }
+ else if (NumVisits >= 8
+ && GET_GAME_STATE (THRADD_BODY_LEVEL) == 0)
+ {
+ SET_GAME_STATE (THRADD_BODY_LEVEL, 1);
+ NPCPhrase (ADEQUATE_PERFORMANCE);
+ }
+
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (THRADD_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (HOSTILE_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (HOSTILE_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (THRADD_HOME_VISITS, NumVisits);
+ }
+ else if ((NumVisits = GET_GAME_STATE (THRADD_MISSION)) == 0
+ || NumVisits > 3)
+ {
+ NumVisits = GET_GAME_STATE (THRADD_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (HOSTILE_SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (HOSTILE_SPACE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (THRADD_VISITS, NumVisits);
+ }
+ else
+ {
+ switch (NumVisits)
+ {
+ case 1:
+ if (GET_GAME_STATE (THRADD_MISSION_VISITS) == 0)
+ NPCPhrase (WE_GO_TO_IMPRESS_URQUAN_1);
+ else
+ NPCPhrase (WE_GO_TO_IMPRESS_URQUAN_2);
+ break;
+ case 2:
+ if (GET_GAME_STATE (THRADD_MISSION_VISITS) == 0)
+ NPCPhrase (WE_IMPRESSING_URQUAN_1);
+ else
+ NPCPhrase (WE_IMPRESSING_URQUAN_2);
+ break;
+ case 3:
+ if (GET_GAME_STATE (THRADD_MISSION_VISITS) == 0)
+ NPCPhrase (WE_IMPRESSED_URQUAN_1);
+ else
+ NPCPhrase (WE_IMPRESSED_URQUAN_2);
+ break;
+ }
+ SET_GAME_STATE (THRADD_MISSION_VISITS, 1);
+ }
+
+ ThraddHostile ((RESPONSE_REF)0);
+ }
+ }
+}
+
+static COUNT
+uninit_thradd (void)
+{
+ return (0);
+}
+
+static void
+post_thradd_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_thradd_comm (void)
+{
+ LOCDATA *retval;
+
+ thradd_desc.init_encounter_func = Intro;
+ thradd_desc.post_encounter_func = post_thradd_enc;
+ thradd_desc.uninit_encounter_func = uninit_thradd;
+
+ thradd_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ thradd_desc.AlienTextBaseline.y = 0;
+ thradd_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (GET_GAME_STATE (THRADD_MANNER)
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &thradd_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/umgah/Makeinfo b/src/uqm/comm/umgah/Makeinfo
new file mode 100644
index 0000000..9cad008
--- /dev/null
+++ b/src/uqm/comm/umgah/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="umgahc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/umgah/resinst.h b/src/uqm/comm/umgah/resinst.h
new file mode 100644
index 0000000..7b7479d
--- /dev/null
+++ b/src/uqm/comm/umgah/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define UMGAH_COLOR_MAP "comm.umgah.colortable"
+#define UMGAH_CONVERSATION_PHRASES "comm.umgah.dialogue"
+#define UMGAH_FONT "comm.umgah.font"
+#define UMGAH_MUSIC "comm.umgah.music"
+#define UMGAH_PMAP_ANIM "comm.umgah.graphics"
diff --git a/src/uqm/comm/umgah/strings.h b/src/uqm/comm/umgah/strings.h
new file mode 100644
index 0000000..f7dcfed
--- /dev/null
+++ b/src/uqm/comm/umgah/strings.h
@@ -0,0 +1,114 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UMGAH_STRINGS_H
+#define UMGAH_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HWLD_PRE_ZOMBIE_HELLO_1,
+ HWLD_PRE_ZOMBIE_HELLO_2,
+ HWLD_PRE_ZOMBIE_HELLO_3,
+ HWLD_PRE_ZOMBIE_HELLO_4,
+ SPACE_PRE_ZOMBIE_HELLO_1,
+ SPACE_PRE_ZOMBIE_HELLO_2,
+ SPACE_PRE_ZOMBIE_HELLO_3,
+ SPACE_PRE_ZOMBIE_HELLO_4,
+ UNKNOWN_ZOMBIE_HELLO_1,
+ UNKNOWN_ZOMBIE_HELLO_2,
+ UNKNOWN_ZOMBIE_HELLO_3,
+ UNKNOWN_ZOMBIE_HELLO_4,
+ DESTROY_INTERFERER_1,
+ DESTROY_INTERFERER_2,
+ DESTROY_INTERFERER_3,
+ DESTROY_INTERFERER_4,
+ REVEALED_ZOMBIE_HELLO_1,
+ REVEALED_ZOMBIE_HELLO_2,
+ REVEALED_ZOMBIE_HELLO_3,
+ REVEALED_ZOMBIE_HELLO_4,
+ HOSTILE_HELLO_1,
+ HOSTILE_HELLO_2,
+ HOSTILE_HELLO_3,
+ HOSTILE_HELLO_4,
+ REWARD_AT_HOMEWORLD_1,
+ REWARD_AT_HOMEWORLD_2,
+ POST_ZOMBIE_HWLD_HELLO,
+ owe_me_big_time,
+ our_largesse,
+ GIVE_LIFEDATA,
+ THANKS,
+ what_do_with_tpet,
+ TRICK_URQUAN,
+ any_jokes,
+ SURE,
+ what_before_tpet,
+ TRKD_SPATHI_AND_ILWRATH,
+ where_caster,
+ SPATHI_TOOK_THEM,
+ so_what_for_now,
+ DO_THIS_NOW,
+ bye_post_zombie,
+ FUNNY_IDEA,
+ whats_up_pre_zombie,
+ GENERAL_INFO_PRE_ZOMBIE,
+ evil_blobbies_give_up,
+ NOT_EVIL_BLOBBIES,
+ evil_blobbies_must_die,
+ OH_NO_WE_WONT,
+ can_we_be_friends,
+ SURE_FRIENDS,
+ want_to_defeat_urquan,
+ FINE_BY_US,
+ bye_pre_zombie,
+ GOODBYE_PRE_ZOMBIE,
+ threat,
+ NO_THREAT,
+ whats_up_zombies,
+ GENERAL_INFO_ZOMBIE,
+ how_goes_tpet,
+ WHAT_TPET,
+ you_told_us,
+ SADLY_IT_DIED,
+ dont_believe,
+ THEN_DIE,
+ bye_unknown,
+ GOODBYE_UNKNOWN,
+ evil_blobbies,
+ YES_VERY_EVIL,
+ give_up_or_die,
+ NOT_GIVE_UP,
+ we_vindicator0,
+ we_vindicator1,
+ we_vindicator2,
+ GOOD_FOR_YOU_1,
+ come_in_peace,
+ GOOD_FOR_YOU_2,
+ know_any_jokes,
+ JOKE_1,
+ better_joke,
+ JOKE_2,
+ not_very_funny,
+ YES_WE_ARE,
+ what_about_tpet,
+ arilou_told_us,
+ bye_zombie,
+ GOODBYE_ZOMBIE,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/umgah/umgahc.c b/src/uqm/comm/umgah/umgahc.c
new file mode 100644
index 0000000..d9debd5
--- /dev/null
+++ b/src/uqm/comm/umgah/umgahc.c
@@ -0,0 +1,729 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+
+
+static LOCDATA umgah_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ UMGAH_PMAP_ANIM, /* AlienFrame */
+ UMGAH_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ UMGAH_COLOR_MAP, /* AlienColorMap */
+ UMGAH_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ UMGAH_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 16, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 5, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5),
+ },
+ {
+ 8, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 6),
+ },
+ {
+ 11, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 7),
+ },
+ {
+ 13, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 8),
+ },
+ {
+ 15, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 9),
+ },
+ {
+ 17, /* StartIndex */
+ 3, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 7 / 60, 0, /* FrameRate */
+ ONE_SECOND * 3, ONE_SECOND * 3, /* RestartRate */
+ (1 << 0),
+ },
+ {
+ 20, /* StartIndex */
+ 3, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 7 / 60, 0, /* FrameRate */
+ ONE_SECOND * 3, ONE_SECOND * 3, /* RestartRate */
+ (1 << 1),
+ },
+ {
+ 23, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 7 / 60, 0, /* FrameRate */
+ ONE_SECOND * 3, ONE_SECOND * 3, /* RestartRate */
+ (1 << 2),
+ },
+ {
+ 25, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 7 / 60, 0, /* FrameRate */
+ ONE_SECOND * 3, ONE_SECOND * 3, /* RestartRate */
+ (1 << 3),
+ },
+ {
+ 27, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 7 / 60, 0, /* FrameRate */
+ ONE_SECOND * 3, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4),
+ },
+ {
+ 29, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 32, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 35, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 40, /* StartIndex */
+ 6, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND / 10, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 46, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 5, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 15), /* BlockMask */
+ },
+ {
+ 48, /* StartIndex */
+ 2, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 5, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 14), /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 4, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+CombatIsInevitable (RESPONSE_REF R)
+{
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, bye_zombie))
+ {
+ NPCPhrase (GOODBYE_ZOMBIE);
+
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, bye_pre_zombie))
+ NPCPhrase (GOODBYE_PRE_ZOMBIE);
+ else if (PLAYER_SAID (R, can_we_be_friends))
+ {
+ NPCPhrase (SURE_FRIENDS);
+
+ SET_GAME_STATE (UMGAH_MENTIONED_TRICKS, 1);
+ }
+ else if (PLAYER_SAID (R, evil_blobbies_give_up))
+ {
+ NPCPhrase (NOT_EVIL_BLOBBIES);
+
+ SET_GAME_STATE (UMGAH_EVIL_BLOBBIES, 1);
+ }
+ else if (PLAYER_SAID (R, evil_blobbies_must_die))
+ NPCPhrase (OH_NO_WE_WONT);
+ else if (PLAYER_SAID (R, threat))
+ NPCPhrase (NO_THREAT);
+ else if (PLAYER_SAID (R, dont_believe))
+ {
+ NPCPhrase (THEN_DIE);
+
+ SET_GAME_STATE (KNOW_UMGAH_ZOMBIES, 1);
+ SET_GAME_STATE (UMGAH_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, bye_unknown))
+ {
+ NPCPhrase (GOODBYE_UNKNOWN);
+
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, bye_post_zombie))
+ {
+ NPCPhrase (FUNNY_IDEA);
+
+ AlienTalkSegue ((COUNT)~0);
+ AddEscortShips (UMGAH_SHIP, 4);
+ SET_GAME_STATE (UMGAH_HOSTILE, 1);
+ }
+}
+
+static void
+Zombies (RESPONSE_REF R)
+{
+ if (GET_GAME_STATE (MET_NORMAL_UMGAH))
+ {
+ if (PLAYER_SAID (R, whats_up_zombies))
+ {
+ NPCPhrase (GENERAL_INFO_ZOMBIE);
+
+ DISABLE_PHRASE (whats_up_zombies);
+ }
+ else if (PLAYER_SAID (R, how_goes_tpet))
+ {
+ NPCPhrase (WHAT_TPET);
+
+ DISABLE_PHRASE (how_goes_tpet);
+ }
+ else if (PLAYER_SAID (R, you_told_us))
+ {
+ NPCPhrase (SADLY_IT_DIED);
+
+ DISABLE_PHRASE (you_told_us);
+ }
+
+ if (PHRASE_ENABLED (whats_up_zombies) && PHRASE_ENABLED (how_goes_tpet))
+ Response (whats_up_zombies, Zombies);
+ if (PHRASE_ENABLED (how_goes_tpet))
+ Response (how_goes_tpet, Zombies);
+ else if (PHRASE_ENABLED (you_told_us))
+ Response (you_told_us, Zombies);
+ else
+ {
+ Response (dont_believe, CombatIsInevitable);
+ }
+ if (PHRASE_ENABLED (whats_up_zombies) && !PHRASE_ENABLED (how_goes_tpet))
+ Response (whats_up_zombies, Zombies);
+ Response (threat, CombatIsInevitable);
+ Response (bye_unknown, CombatIsInevitable);
+ }
+ else
+ {
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[4];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = pStr[3] = 0;
+ if (PLAYER_SAID (R, evil_blobbies))
+ {
+ NPCPhrase (YES_VERY_EVIL);
+
+ DISABLE_PHRASE (evil_blobbies);
+ LastStack = 0;
+ }
+ else if (PLAYER_SAID (R, we_vindicator0))
+ {
+ NPCPhrase (GOOD_FOR_YOU_1);
+
+ DISABLE_PHRASE (we_vindicator0);
+ LastStack = 1;
+ }
+ else if (PLAYER_SAID (R, come_in_peace))
+ {
+ NPCPhrase (GOOD_FOR_YOU_2);
+
+ DISABLE_PHRASE (come_in_peace);
+ LastStack = 1;
+ }
+ else if (PLAYER_SAID (R, know_any_jokes))
+ {
+ NPCPhrase (JOKE_1);
+
+ DISABLE_PHRASE (know_any_jokes);
+ LastStack = 2;
+ }
+ else if (PLAYER_SAID (R, better_joke))
+ {
+ NPCPhrase (JOKE_2);
+
+ DISABLE_PHRASE (better_joke);
+ LastStack = 2;
+ }
+ else if (PLAYER_SAID (R, not_very_funny))
+ {
+ NPCPhrase (YES_WE_ARE);
+
+ DISABLE_PHRASE (not_very_funny);
+ LastStack = 2;
+ }
+ else if (PLAYER_SAID (R, what_about_tpet))
+ {
+ NPCPhrase (WHAT_TPET);
+
+ DISABLE_PHRASE (what_about_tpet);
+ LastStack = 3;
+ }
+ else if (PLAYER_SAID (R, give_up_or_die))
+ {
+ NPCPhrase (NOT_GIVE_UP);
+
+ setSegue (Segue_hostile);
+ return;
+ }
+ else if (PLAYER_SAID (R, arilou_told_us))
+ {
+ NPCPhrase (THEN_DIE);
+
+ setSegue (Segue_hostile);
+ SET_GAME_STATE (KNOW_UMGAH_ZOMBIES, 1);
+ SET_GAME_STATE (UMGAH_VISITS, 0);
+ return;
+ }
+
+ if (PHRASE_ENABLED (evil_blobbies))
+ pStr[0] = evil_blobbies;
+ else
+ pStr[0] = give_up_or_die;
+
+ if (PHRASE_ENABLED (we_vindicator0))
+ {
+ construct_response (shared_phrase_buf,
+ we_vindicator0,
+ GLOBAL_SIS (CommanderName),
+ we_vindicator1,
+ GLOBAL_SIS (ShipName),
+ we_vindicator2,
+ (UNICODE*)NULL);
+ pStr[1] = we_vindicator0;
+ }
+ else if (PHRASE_ENABLED (come_in_peace))
+ pStr[1] = come_in_peace;
+
+ if (PHRASE_ENABLED (know_any_jokes))
+ pStr[2] = know_any_jokes;
+ else if (PHRASE_ENABLED (better_joke))
+ pStr[2] = better_joke;
+ else if (PHRASE_ENABLED (not_very_funny))
+ pStr[2] = not_very_funny;
+
+ if (PHRASE_ENABLED (what_about_tpet))
+ pStr[3] = what_about_tpet;
+ else
+ pStr[3] = arilou_told_us;
+
+ if (pStr[LastStack])
+ {
+ if (pStr[LastStack] != we_vindicator0)
+ Response (pStr[LastStack], Zombies);
+ else
+ DoResponsePhrase (pStr[LastStack], Zombies, shared_phrase_buf);
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ if (pStr[i] != we_vindicator0)
+ Response (pStr[i], Zombies);
+ else
+ DoResponsePhrase (pStr[i], Zombies, shared_phrase_buf);
+ }
+ }
+ Response (bye_zombie, CombatIsInevitable);
+ }
+}
+
+static void
+NormalUmgah (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, whats_up_pre_zombie))
+ {
+ NPCPhrase (GENERAL_INFO_PRE_ZOMBIE);
+
+ DISABLE_PHRASE (whats_up_pre_zombie);
+ }
+ else if (PLAYER_SAID (R, want_to_defeat_urquan))
+ {
+ NPCPhrase (FINE_BY_US);
+
+ DISABLE_PHRASE (want_to_defeat_urquan);
+ }
+
+ if (!GET_GAME_STATE (UMGAH_EVIL_BLOBBIES))
+ Response (evil_blobbies_give_up, CombatIsInevitable);
+ else
+ Response (evil_blobbies_must_die, CombatIsInevitable);
+ if (PHRASE_ENABLED (whats_up_pre_zombie))
+ Response (whats_up_pre_zombie, NormalUmgah);
+ if (PHRASE_ENABLED (want_to_defeat_urquan))
+ Response (want_to_defeat_urquan, NormalUmgah);
+ switch (GET_GAME_STATE (UMGAH_MENTIONED_TRICKS))
+ {
+ case 0:
+ Response (can_we_be_friends, CombatIsInevitable);
+ break;
+ }
+ Response (bye_pre_zombie, CombatIsInevitable);
+}
+
+static void
+UmgahReward (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, what_before_tpet))
+ {
+ NPCPhrase (TRKD_SPATHI_AND_ILWRATH);
+
+ DISABLE_PHRASE (what_before_tpet);
+ }
+ else if (PLAYER_SAID (R, where_caster))
+ {
+ NPCPhrase (SPATHI_TOOK_THEM);
+
+ DISABLE_PHRASE (where_caster);
+ }
+ else if (PLAYER_SAID (R, owe_me_big_time))
+ {
+ NPCPhrase (THANKS);
+
+ GLOBAL_SIS (TotalBioMass) += 1000 / BIO_CREDIT_VALUE;
+ DISABLE_PHRASE (owe_me_big_time);
+ DISABLE_PHRASE (our_largesse);
+ }
+ else if (PLAYER_SAID (R, our_largesse))
+ {
+ NPCPhrase (GIVE_LIFEDATA);
+
+ GLOBAL_SIS (TotalBioMass) += 1000 / BIO_CREDIT_VALUE;
+ DISABLE_PHRASE (our_largesse);
+ DISABLE_PHRASE (owe_me_big_time);
+ }
+ else if (PLAYER_SAID (R, what_do_with_tpet))
+ {
+ NPCPhrase (TRICK_URQUAN);
+
+ DISABLE_PHRASE (what_do_with_tpet);
+ }
+ else if (PLAYER_SAID (R, any_jokes))
+ {
+ NPCPhrase (SURE);
+
+ DISABLE_PHRASE (any_jokes);
+ }
+ else if (PLAYER_SAID (R, so_what_for_now))
+ {
+ NPCPhrase (DO_THIS_NOW);
+
+ DISABLE_PHRASE (so_what_for_now);
+ }
+
+ if (!GET_GAME_STATE (MET_NORMAL_UMGAH))
+ {
+ if (PHRASE_ENABLED (what_before_tpet))
+ Response (what_before_tpet, UmgahReward);
+ else if (PHRASE_ENABLED (where_caster))
+ Response (where_caster, UmgahReward);
+ }
+ if (PHRASE_ENABLED (owe_me_big_time))
+ {
+ Response (owe_me_big_time, UmgahReward);
+ Response (our_largesse, UmgahReward);
+ }
+ if (PHRASE_ENABLED (what_do_with_tpet))
+ Response (what_do_with_tpet, UmgahReward);
+ else if (PHRASE_ENABLED (any_jokes) && GET_GAME_STATE (UMGAH_MENTIONED_TRICKS) < 2)
+ Response (any_jokes, UmgahReward);
+ if (PHRASE_ENABLED (so_what_for_now))
+ Response (so_what_for_now, UmgahReward);
+ Response (bye_post_zombie, CombatIsInevitable);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (GET_GAME_STATE (UMGAH_HOSTILE))
+ {
+ NumVisits = GET_GAME_STATE (UMGAH_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (HOSTILE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (HOSTILE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UMGAH_VISITS, NumVisits);
+
+ setSegue (Segue_hostile);
+ }
+ else if (GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES))
+ {
+ NumVisits = GET_GAME_STATE (UMGAH_VISITS);
+ if (GET_GAME_STATE (TALKING_PET_VISITS))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (DESTROY_INTERFERER_1);
+ break;
+ case 1:
+ NPCPhrase (DESTROY_INTERFERER_2);
+ break;
+ case 2:
+ NPCPhrase (DESTROY_INTERFERER_3);
+ break;
+ case 3:
+ NPCPhrase (DESTROY_INTERFERER_4);
+ --NumVisits;
+ break;
+ }
+
+ setSegue (Segue_hostile);
+ }
+ else if (GET_GAME_STATE (KNOW_UMGAH_ZOMBIES))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (REVEALED_ZOMBIE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (REVEALED_ZOMBIE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (REVEALED_ZOMBIE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (REVEALED_ZOMBIE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (UNKNOWN_ZOMBIE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (UNKNOWN_ZOMBIE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (UNKNOWN_ZOMBIE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (UNKNOWN_ZOMBIE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+
+ Zombies ((RESPONSE_REF)0);
+ }
+ SET_GAME_STATE (UMGAH_VISITS, NumVisits);
+ }
+ else if (!GET_GAME_STATE (TALKING_PET))
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (UMGAH_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HWLD_PRE_ZOMBIE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HWLD_PRE_ZOMBIE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (HWLD_PRE_ZOMBIE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (HWLD_PRE_ZOMBIE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UMGAH_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (UMGAH_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (SPACE_PRE_ZOMBIE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (SPACE_PRE_ZOMBIE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (SPACE_PRE_ZOMBIE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (SPACE_PRE_ZOMBIE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UMGAH_VISITS, NumVisits);
+ }
+
+ NormalUmgah ((RESPONSE_REF)0);
+ }
+ else
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NPCPhrase (POST_ZOMBIE_HWLD_HELLO);
+
+ UmgahReward ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (UMGAH_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (REWARD_AT_HOMEWORLD_1);
+ break;
+ case 1:
+ NPCPhrase (REWARD_AT_HOMEWORLD_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UMGAH_VISITS, NumVisits);
+
+ setSegue (Segue_peace);
+ }
+ }
+}
+
+static COUNT
+uninit_umgah (void)
+{
+ return (0);
+}
+
+static void
+post_umgah_enc (void)
+{
+ if (!GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES))
+ {
+ SET_GAME_STATE (MET_NORMAL_UMGAH, 1);
+ }
+}
+
+LOCDATA*
+init_umgah_comm (void)
+{
+ LOCDATA *retval;
+
+ umgah_desc.init_encounter_func = Intro;
+ umgah_desc.post_encounter_func = post_umgah_enc;
+ umgah_desc.uninit_encounter_func = uninit_umgah;
+
+ umgah_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ umgah_desc.AlienTextBaseline.y = 0;
+ umgah_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if ((GET_GAME_STATE (TALKING_PET) && !GET_GAME_STATE (UMGAH_HOSTILE))
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &umgah_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/urquan/Makeinfo b/src/uqm/comm/urquan/Makeinfo
new file mode 100644
index 0000000..7b756fa
--- /dev/null
+++ b/src/uqm/comm/urquan/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="urquanc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/urquan/resinst.h b/src/uqm/comm/urquan/resinst.h
new file mode 100644
index 0000000..5b69860
--- /dev/null
+++ b/src/uqm/comm/urquan/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define URQUAN_COLOR_MAP "comm.urquan.colortable"
+#define URQUAN_CONVERSATION_PHRASES "comm.urquan.dialogue"
+#define URQUAN_FONT "comm.urquan.font"
+#define URQUAN_MUSIC "comm.urquan.music"
+#define URQUAN_PMAP_ANIM "comm.urquan.graphics"
diff --git a/src/uqm/comm/urquan/strings.h b/src/uqm/comm/urquan/strings.h
new file mode 100644
index 0000000..18ca86c
--- /dev/null
+++ b/src/uqm/comm/urquan/strings.h
@@ -0,0 +1,101 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef URQUAN_STRINGS_H
+#define URQUAN_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HELLO_SAMATRA,
+ SENSE_EVIL,
+ INIT_URQUAN_WAKE_UP,
+ where_am_i,
+ YOU_ARE_HERE,
+ why_does_my_head_hurt,
+ HURTS_BECAUSE,
+ what_about_2_weeks,
+ ABOUT_2_WEEKS,
+ compulsion,
+ WHAT_COMPULSION,
+ wascally_little_guy,
+ WHAT_IT_LOOK_LIKE,
+ terran_amphibian,
+ talking_pet_on_steroids,
+ BAD_NEWS,
+ turd_and_toad,
+ WHAT_IS_TURD_AND_TOAD,
+ alien_mind_control,
+ WHAT_FELT_LIKE,
+ possessed_by_devil,
+ STUPID_DEVIL,
+ falling_asleep,
+ someone_else_controlled,
+ SOUNDS_FAMILIAR,
+ before_coffee,
+ EXPLAIN,
+ why_explain,
+ MUST_EXPLAIN,
+ bye_init_hypno,
+ GOODBYE_AND_DIE_INIT_HYPNO,
+ SUBSEQUENT_URQUAN_WAKE_UP,
+ uh_oh,
+ NO_UH_OH,
+ stop_meeting,
+ NO_STOP_MEETING,
+ bye_sub_hypno,
+ GOODBYE_AND_DIE_SUB_HYPNO,
+ CAUGHT_YA,
+ INIT_FLEE_HUMAN,
+ SUBSEQUENT_FLEE_HUMAN,
+ why_flee,
+ FLEE_BECAUSE,
+ what_happens_now,
+ HAPPENS_NOW,
+ what_about_you,
+ ABOUT_US,
+ bye_wars_over,
+ GOODBYE_WARS_OVER,
+ SEND_MESSAGE,
+ INIT_HELLO,
+ SUBSEQUENT_HELLO_1,
+ SUBSEQUENT_HELLO_2,
+ SUBSEQUENT_HELLO_3,
+ SUBSEQUENT_HELLO_4,
+ you_must_surrender,
+ NOPE,
+ i_surrender,
+ DISOBEDIENCE_PUNISHED,
+ i_wont_surrender,
+ BAD_CHOICE,
+ i_will_surrender,
+ GOOD_CHOICE,
+ key_phrase,
+ URQUAN_STORY,
+ like_to_leave,
+ INDEPENDENCE_IS_BAD,
+ whats_up_1,
+ GENERAL_INFO_1,
+ whats_up_2,
+ GENERAL_INFO_2,
+ whats_up_3,
+ GENERAL_INFO_3,
+ whats_up_4,
+ GENERAL_INFO_4,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/urquan/urquanc.c b/src/uqm/comm/urquan/urquanc.c
new file mode 100644
index 0000000..efdf710
--- /dev/null
+++ b/src/uqm/comm/urquan/urquanc.c
@@ -0,0 +1,555 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+static LOCDATA urquan_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ URQUAN_PMAP_ANIM, /* AlienFrame */
+ URQUAN_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ URQUAN_COLOR_MAP, /* AlienColorMap */
+ URQUAN_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ URQUAN_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 7, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 7, /* StartIndex */
+ 6, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 10, 0, /* FrameRate */
+ ONE_SECOND / 10, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 13, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 16, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 19, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 29, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 36, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 1, /* StartIndex */
+ 2, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 6, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 2, /* StartIndex */
+ 5, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+CombatIsInevitable (RESPONSE_REF R)
+{
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, you_must_surrender))
+ NPCPhrase (NOPE);
+ else if (PLAYER_SAID (R, whats_up_1)
+ || PLAYER_SAID (R, whats_up_2)
+ || PLAYER_SAID (R, whats_up_3)
+ || PLAYER_SAID (R, whats_up_4))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (URQUAN_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (URQUAN_INFO, NumVisits);
+ }
+ else if (PLAYER_SAID (R, i_wont_surrender))
+ NPCPhrase (BAD_CHOICE);
+ else if (PLAYER_SAID (R, i_will_surrender))
+ {
+ NPCPhrase (GOOD_CHOICE);
+
+ setSegue (Segue_defeat);
+ }
+ else if (PLAYER_SAID (R, like_to_leave))
+ NPCPhrase (INDEPENDENCE_IS_BAD);
+ else if (PLAYER_SAID (R, bye_wars_over))
+ {
+ NPCPhrase (GOODBYE_WARS_OVER);
+
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, bye_sub_hypno))
+ NPCPhrase (GOODBYE_AND_DIE_SUB_HYPNO);
+ else if (PLAYER_SAID (R, bye_init_hypno))
+ {
+ NPCPhrase (GOODBYE_AND_DIE_INIT_HYPNO);
+
+ SET_GAME_STATE (URQUAN_HYPNO_VISITS, 1);
+ }
+ else if (PLAYER_SAID (R, terran_amphibian)
+ || PLAYER_SAID (R, talking_pet_on_steroids))
+ {
+ NPCPhrase (BAD_NEWS);
+
+ setSegue (Segue_peace);
+ SET_GAME_STATE (URQUAN_HYPNO_VISITS, 1);
+ }
+ else if (PLAYER_SAID (R, falling_asleep)
+ || PLAYER_SAID (R, someone_else_controlled))
+ {
+ NPCPhrase (SOUNDS_FAMILIAR);
+
+ setSegue (Segue_peace);
+ SET_GAME_STATE (URQUAN_HYPNO_VISITS, 1);
+ }
+}
+
+static void
+DescribePet (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, wascally_little_guy))
+ NPCPhrase (WHAT_IT_LOOK_LIKE);
+ else if (PLAYER_SAID (R, turd_and_toad))
+ {
+ NPCPhrase (WHAT_IS_TURD_AND_TOAD);
+
+ DISABLE_PHRASE (turd_and_toad);
+ }
+
+ Response (terran_amphibian, CombatIsInevitable);
+ Response (talking_pet_on_steroids, CombatIsInevitable);
+ if (PHRASE_ENABLED (turd_and_toad))
+ {
+ Response (turd_and_toad, DescribePet);
+ }
+}
+
+static void
+DescribeCompulsion (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, alien_mind_control))
+ NPCPhrase (WHAT_FELT_LIKE);
+ else if (PLAYER_SAID (R, before_coffee))
+ {
+ NPCPhrase (EXPLAIN);
+
+ DISABLE_PHRASE (before_coffee);
+ }
+
+ Response (falling_asleep, CombatIsInevitable);
+ Response (someone_else_controlled, CombatIsInevitable);
+ if (PHRASE_ENABLED (before_coffee))
+ {
+ Response (before_coffee, DescribeCompulsion);
+ }
+}
+
+static void
+MentionCompulsion (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, compulsion))
+ {
+ NPCPhrase (WHAT_COMPULSION);
+
+ SET_GAME_STATE (MENTIONED_PET_COMPULSION, 1);
+ }
+ else if (PLAYER_SAID (R, possessed_by_devil))
+ {
+ NPCPhrase (STUPID_DEVIL);
+
+ DISABLE_PHRASE (possessed_by_devil);
+ }
+ else if (PLAYER_SAID (R, why_explain))
+ {
+ NPCPhrase (MUST_EXPLAIN);
+
+ DISABLE_PHRASE (why_explain);
+ }
+
+ Response (wascally_little_guy, DescribePet);
+ Response (alien_mind_control, DescribeCompulsion);
+ if (PHRASE_ENABLED (possessed_by_devil))
+ Response (possessed_by_devil, MentionCompulsion);
+ if (PHRASE_ENABLED (why_explain))
+ Response (why_explain, MentionCompulsion);
+}
+
+static void
+UrquanHypno (RESPONSE_REF R)
+{
+ if (GET_GAME_STATE (URQUAN_HYPNO_VISITS) == 0)
+ {
+ if (R == 0)
+ NPCPhrase (INIT_URQUAN_WAKE_UP);
+ else if (PLAYER_SAID (R, where_am_i))
+ {
+ NPCPhrase (YOU_ARE_HERE);
+
+ DISABLE_PHRASE (where_am_i);
+ }
+ else if (PLAYER_SAID (R, why_does_my_head_hurt))
+ {
+ NPCPhrase (HURTS_BECAUSE);
+
+ DISABLE_PHRASE (why_does_my_head_hurt);
+ }
+ else if (PLAYER_SAID (R, what_about_2_weeks))
+ {
+ NPCPhrase (ABOUT_2_WEEKS);
+
+ DISABLE_PHRASE (what_about_2_weeks);
+ }
+
+ if (PHRASE_ENABLED (where_am_i))
+ Response (where_am_i, UrquanHypno);
+ if (PHRASE_ENABLED (why_does_my_head_hurt))
+ Response (why_does_my_head_hurt, UrquanHypno);
+ if (PHRASE_ENABLED (what_about_2_weeks))
+ Response (what_about_2_weeks, UrquanHypno);
+ Response (compulsion, MentionCompulsion);
+ Response (bye_init_hypno, CombatIsInevitable);
+ }
+ else
+ {
+ if (R == 0)
+ NPCPhrase (SUBSEQUENT_URQUAN_WAKE_UP);
+ else if (PLAYER_SAID (R, uh_oh))
+ {
+ NPCPhrase (NO_UH_OH);
+
+ DISABLE_PHRASE (uh_oh);
+ }
+ else if (PLAYER_SAID (R, stop_meeting))
+ {
+ NPCPhrase (NO_STOP_MEETING);
+
+ DISABLE_PHRASE (stop_meeting);
+ }
+
+ if (PHRASE_ENABLED (uh_oh))
+ Response (uh_oh, UrquanHypno);
+ if (PHRASE_ENABLED (stop_meeting))
+ Response (stop_meeting, UrquanHypno);
+ if (!GET_GAME_STATE (MENTIONED_PET_COMPULSION))
+ {
+ Response (compulsion, MentionCompulsion);
+ }
+ Response (bye_sub_hypno, CombatIsInevitable);
+ }
+}
+
+static void
+NormalUrquan (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, i_surrender))
+ {
+ NPCPhrase (DISOBEDIENCE_PUNISHED);
+
+ Response (i_wont_surrender, CombatIsInevitable);
+ Response (i_will_surrender, CombatIsInevitable);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, key_phrase))
+ {
+ NPCPhrase (URQUAN_STORY);
+
+ SET_GAME_STATE (KNOW_URQUAN_STORY, 2);
+ }
+
+ Response (you_must_surrender, CombatIsInevitable);
+ if (GET_GAME_STATE (KNOW_URQUAN_STORY) == 1)
+ {
+ Response (key_phrase, NormalUrquan);
+ }
+ switch (GET_GAME_STATE (URQUAN_INFO))
+ {
+ case 0:
+ Response (whats_up_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (whats_up_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (whats_up_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (whats_up_4, CombatIsInevitable);
+ break;
+ }
+ Response (i_surrender, NormalUrquan);
+ Response (like_to_leave, CombatIsInevitable);
+ }
+}
+
+static void
+LoserUrquan (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, why_flee))
+ {
+ NPCPhrase (FLEE_BECAUSE);
+
+ DISABLE_PHRASE (why_flee);
+ }
+ else if (PLAYER_SAID (R, what_happens_now))
+ {
+ NPCPhrase (HAPPENS_NOW);
+
+ DISABLE_PHRASE (what_happens_now);
+ }
+ else if (PLAYER_SAID (R, what_about_you))
+ {
+ NPCPhrase (ABOUT_US);
+
+ DISABLE_PHRASE (what_about_you);
+ }
+
+ if (PHRASE_ENABLED (why_flee))
+ Response (why_flee, LoserUrquan);
+ if (PHRASE_ENABLED (what_happens_now))
+ Response (what_happens_now, LoserUrquan);
+ if (PHRASE_ENABLED (what_about_you))
+ Response (what_about_you, LoserUrquan);
+ Response (bye_wars_over, CombatIsInevitable);
+}
+
+static void
+Intro (void)
+{
+ DWORD GrpOffs;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ GrpOffs = GET_GAME_STATE_32 (URQUAN_PROBE_GRPOFFS0);
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && GLOBAL (BattleGroupRef)
+ && GLOBAL (BattleGroupRef) == GrpOffs)
+ {
+ NPCPhrase (SEND_MESSAGE);
+ SET_GAME_STATE (PROBE_MESSAGE_DELIVERED, 1);
+ }
+ else if (GET_GAME_STATE (PLAYER_HYPNOTIZED))
+ {
+ SetCommIntroMode (CIM_FADE_IN_SCREEN, ONE_SECOND * 5);
+ UrquanHypno ((RESPONSE_REF)0);
+ }
+ else
+ {
+ BYTE NumVisits;
+
+ if (!GET_GAME_STATE (URQUAN_SENSES_EVIL)
+ && GET_GAME_STATE (TALKING_PET_ON_SHIP))
+ {
+ NPCPhrase (SENSE_EVIL);
+ SET_GAME_STATE (URQUAN_SENSES_EVIL, 1);
+ }
+
+ GrpOffs = GET_GAME_STATE_32 (COLONY_GRPOFFS0);
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && GLOBAL (BattleGroupRef)
+ && GLOBAL (BattleGroupRef) == GrpOffs)
+ {
+ NPCPhrase (CAUGHT_YA);
+
+ setSegue (Segue_hostile);
+ return;
+ }
+
+ GrpOffs = GET_GAME_STATE_32 (SAMATRA_GRPOFFS0);
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && GLOBAL (BattleGroupRef)
+ && GLOBAL (BattleGroupRef) == GrpOffs)
+ {
+ NPCPhrase (HELLO_SAMATRA);
+
+ SET_GAME_STATE (AWARE_OF_SAMATRA, 1);
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (URQUAN_VISITS);
+ if (!GET_GAME_STATE (KOHR_AH_FRENZY))
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_HELLO);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_HELLO_1);
+ break;
+ case 2:
+ NPCPhrase (SUBSEQUENT_HELLO_2);
+ break;
+ case 3:
+ NPCPhrase (SUBSEQUENT_HELLO_3);
+ break;
+ case 4:
+ NPCPhrase (SUBSEQUENT_HELLO_4);
+ --NumVisits;
+ break;
+ }
+
+ NormalUrquan ((RESPONSE_REF)0);
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (INIT_FLEE_HUMAN);
+ LoserUrquan ((RESPONSE_REF)0);
+ break;
+ case 1:
+ NPCPhrase (SUBSEQUENT_FLEE_HUMAN);
+ setSegue (Segue_peace);
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (URQUAN_VISITS, NumVisits);
+ }
+ }
+}
+
+static COUNT
+uninit_urquan (void)
+{
+ return (0);
+}
+
+static void
+post_urquan_enc (void)
+{
+ SET_GAME_STATE (PLAYER_HYPNOTIZED, 0);
+}
+
+LOCDATA*
+init_urquan_comm (void)
+{
+ LOCDATA *retval;
+
+ DWORD GrpOffs;
+
+ urquan_desc.init_encounter_func = Intro;
+ urquan_desc.post_encounter_func = post_urquan_enc;
+ urquan_desc.uninit_encounter_func = uninit_urquan;
+
+ urquan_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ urquan_desc.AlienTextBaseline.y = 0;
+ urquan_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ GrpOffs = GET_GAME_STATE_32 (URQUAN_PROBE_GRPOFFS0);
+ if (GET_GAME_STATE (PLAYER_HYPNOTIZED)
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE
+ || (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && GLOBAL (BattleGroupRef)
+ && GLOBAL (BattleGroupRef) == GrpOffs))
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &urquan_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/utwig/Makeinfo b/src/uqm/comm/utwig/Makeinfo
new file mode 100644
index 0000000..432250e
--- /dev/null
+++ b/src/uqm/comm/utwig/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="utwigc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/utwig/resinst.h b/src/uqm/comm/utwig/resinst.h
new file mode 100644
index 0000000..5176ecb
--- /dev/null
+++ b/src/uqm/comm/utwig/resinst.h
@@ -0,0 +1,10 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define UTWIG_COLOR_MAP "comm.utwig.colortable"
+#define UTWIG_CONVERSATION_PHRASES "comm.utwig.dialogue"
+#define UTWIG_FONT "comm.utwig.font"
+#define UTWIG_MUSIC "comm.utwig.music"
+#define UTWIG_PMAP_ANIM "comm.utwig.graphics"
+#define UTWIG_ULTRON_MUSIC "comm.utwig.ultron.music"
diff --git a/src/uqm/comm/utwig/strings.h b/src/uqm/comm/utwig/strings.h
new file mode 100644
index 0000000..bb6f693
--- /dev/null
+++ b/src/uqm/comm/utwig/strings.h
@@ -0,0 +1,144 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UTWIG_STRINGS_H
+#define UTWIG_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ NEUTRAL_SPACE_HELLO_1,
+ NEUTRAL_SPACE_HELLO_2,
+ HOSTILE_SPACE_HELLO_1,
+ HOSTILE_SPACE_HELLO_2,
+ BOMB_WORLD_HELLO_1,
+ BOMB_WORLD_HELLO_2,
+ HOSTILE_BOMB_HELLO_1,
+ HOSTILE_BOMB_HELLO_2,
+ NEUTRAL_HOMEWORLD_HELLO_1,
+ NEUTRAL_HOMEWORLD_HELLO_2,
+ NEUTRAL_HOMEWORLD_HELLO_3,
+ NEUTRAL_HOMEWORLD_HELLO_4,
+ HOSTILE_HOMEWORLD_HELLO_1,
+ HOSTILE_HOMEWORLD_HELLO_2,
+ why_you_here,
+ WE_GUARD_BOMB,
+ what_about_bomb,
+ ABOUT_BOMB,
+ give_us_bomb_or_die,
+ GUARDS_WARN,
+ demand_bomb,
+ GUARDS_FIGHT,
+ may_we_have_bomb,
+ NO_BOMB,
+ please,
+ SORRY_NO_BOMB,
+ whats_up_bomb,
+ GENERAL_INFO_BOMB_1,
+ GENERAL_INFO_BOMB_2,
+ bye_bomb,
+ GOODBYE_BOMB,
+ hey_wait_got_ultron,
+ TAUNT_US_BUT_WE_LOOK,
+ TRICKED_US_1,
+ TRICKED_US_2,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ we_are_vindicator2,
+ WOULD_BE_HAPPY_BUT,
+ why_sad,
+ ULTRON_BROKE,
+ what_ultron,
+ GLORIOUS_ULTRON,
+ dont_be_babies,
+ MOCK_OUR_PAIN,
+ real_sorry_about_ultron,
+ APPRECIATE_SYMPATHY,
+ what_about_you_1,
+ ABOUT_US_1,
+ what_about_you_2,
+ ABOUT_US_2,
+ what_about_you_3,
+ ABOUT_US_3,
+ what_about_urquan_1,
+ ABOUT_URQUAN_1,
+ what_about_urquan_2,
+ ABOUT_URQUAN_2,
+ got_ultron,
+ DONT_WANT_TO_LOOK,
+ SICK_TRICK_1,
+ SICK_TRICK_2,
+ bye_neutral,
+ GOODBYE_NEUTRAL,
+ TOO_LATE,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ HAPPY_DAYS,
+ OK_ATTACK_KOHRAH,
+ whats_up_after_space,
+ GENERAL_INFO_AFTER_SPACE_1,
+ GENERAL_INFO_AFTER_SPACE_2,
+ what_now_after_space,
+ DO_THIS_AFTER_SPACE,
+ bye_after_space,
+ GOODBYE_AFTER_SPACE,
+ whats_up_before_space,
+ GENERAL_INFO_BEFORE_SPACE_1,
+ GENERAL_INFO_BEFORE_SPACE_2,
+ what_now_before_space,
+ DO_THIS_BEFORE_SPACE,
+ bye_before_space,
+ GOODBYE_BEFORE_SPACE,
+ how_went_war,
+ ABOUT_BATTLE,
+ how_goes_war,
+ BATTLE_HAPPENS_1,
+ BATTLE_HAPPENS_2,
+ FLEET_ON_WAY,
+ learn_new_info,
+ NO_NEW_INFO,
+ SAMATRA,
+ what_now_homeworld,
+ HOPE_KILL_EACH_OTHER,
+ how_is_ultron,
+ ULTRON_IS_GREAT,
+ bye_allied_homeworld,
+ GOODBYE_ALLIED_HOMEWORLD,
+ ALLIED_HOMEWORLD_HELLO_1,
+ ALLIED_HOMEWORLD_HELLO_2,
+ ALLIED_HOMEWORLD_HELLO_3,
+ ALLIED_HOMEWORLD_HELLO_4,
+ HELLO_BEFORE_KOHRAH_SPACE_1,
+ HELLO_BEFORE_KOHRAH_SPACE_2,
+ HELLO_DURING_KOHRAH_SPACE_1,
+ HELLO_DURING_KOHRAH_SPACE_2,
+ HELLO_AFTER_KOHRAH_SPACE_1,
+ HELLO_AFTER_KOHRAH_SPACE_2,
+ UP_TO_YOU,
+ can_you_help,
+ HOW_HELP,
+ DONT_NEED,
+ HAVE_4_SHIPS,
+ NO_ULTRON_AT_BOMB,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/utwig/utwigc.c b/src/uqm/comm/utwig/utwigc.c
new file mode 100644
index 0000000..53bacbb
--- /dev/null
+++ b/src/uqm/comm/utwig/utwigc.c
@@ -0,0 +1,996 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/gameev.h"
+
+
+static LOCDATA utwig_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ UTWIG_PMAP_ANIM, /* AlienFrame */
+ UTWIG_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* SIS_TEXT_WIDTH - 16, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ UTWIG_COLOR_MAP, /* AlienColorMap */
+ UTWIG_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ UTWIG_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 16, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 4, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 7, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 2), /* BlockMask */
+ },
+ {
+ 11, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ (1 << 1), /* BlockMask */
+ },
+ {
+ 13, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 12, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 18, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 20, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 22, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 25, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 27, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 30, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 32, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 34, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 36, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 38, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 40, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 42, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND * 2 / 15, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND * 10, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 20, ONE_SECOND / 20, /* FrameRate */
+ ONE_SECOND * 7 / 60, ONE_SECOND / 2, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, bye_neutral))
+ NPCPhrase (GOODBYE_NEUTRAL);
+ else if (PLAYER_SAID (R, bye_after_space))
+ NPCPhrase (GOODBYE_AFTER_SPACE);
+ else if (PLAYER_SAID (R, bye_before_space))
+ NPCPhrase (GOODBYE_BEFORE_SPACE);
+ else if (PLAYER_SAID (R, bye_allied_homeworld))
+ NPCPhrase (GOODBYE_ALLIED_HOMEWORLD);
+ else if (PLAYER_SAID (R, bye_bomb))
+ NPCPhrase (GOODBYE_BOMB);
+ else if (PLAYER_SAID (R, demand_bomb))
+ {
+ NPCPhrase (GUARDS_FIGHT);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, got_ultron)
+ || PLAYER_SAID (R, hey_wait_got_ultron))
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ NPCPhrase (NO_ULTRON_AT_BOMB);
+
+ SET_GAME_STATE (REFUSED_ULTRON_AT_BOMB, 1);
+ }
+ else
+ {
+ if (PLAYER_SAID (R, got_ultron))
+ NPCPhrase (DONT_WANT_TO_LOOK);
+ else
+ NPCPhrase (TAUNT_US_BUT_WE_LOOK);
+ if (GET_GAME_STATE (ULTRON_CONDITION) < 4)
+ {
+ switch (GET_GAME_STATE (UTWIG_INFO))
+ {
+ case 0:
+ if (PLAYER_SAID (R, got_ultron))
+ NPCPhrase (SICK_TRICK_1);
+ else
+ {
+ NPCPhrase (TRICKED_US_1);
+
+ setSegue (Segue_hostile);
+ }
+ break;
+ case 1:
+ if (PLAYER_SAID (R, got_ultron))
+ NPCPhrase (SICK_TRICK_2);
+ else
+ {
+ NPCPhrase (TRICKED_US_2);
+
+ setSegue (Segue_hostile);
+ }
+ break;
+ }
+ SET_GAME_STATE (UTWIG_INFO, 1);
+ }
+ else
+ {
+ NPCPhrase (HAPPY_DAYS);
+ if (GET_GAME_STATE (KOHR_AH_FRENZY))
+ NPCPhrase (TOO_LATE);
+ else
+ {
+ NPCPhrase (OK_ATTACK_KOHRAH);
+
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, ADVANCE_UTWIG_SUPOX_MISSION);
+ }
+
+ SET_GAME_STATE (UTWIG_HAVE_ULTRON, 1);
+ SET_GAME_STATE (ULTRON_CONDITION, 5);
+
+ SET_GAME_STATE (UTWIG_VISITS, 0);
+ SET_GAME_STATE (SUPOX_VISITS, 0);
+ SET_GAME_STATE (UTWIG_HOME_VISITS, 0);
+ SET_GAME_STATE (SUPOX_HOME_VISITS, 0);
+ SET_GAME_STATE (BOMB_VISITS, 0);
+
+ SET_GAME_STATE (SUPOX_INFO, 0);
+ SET_GAME_STATE (UTWIG_INFO, 0);
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 0);
+ SET_GAME_STATE (UTWIG_WAR_NEWS, 0);
+ SET_GAME_STATE (SUPOX_HOSTILE, 0);
+ SET_GAME_STATE (UTWIG_HOSTILE, 0);
+
+ SetRaceAllied (UTWIG_SHIP, TRUE);
+ SetRaceAllied (SUPOX_SHIP, TRUE);
+ }
+ }
+ }
+ else if (PLAYER_SAID (R, can_you_help))
+ {
+ NPCPhrase (HOW_HELP);
+ if (EscortFeasibilityStudy (UTWIG_SHIP) == 0)
+ NPCPhrase (DONT_NEED);
+ else
+ {
+ NPCPhrase (HAVE_4_SHIPS);
+
+ AlienTalkSegue ((COUNT)~0);
+ AddEscortShips (UTWIG_SHIP, 4);
+ }
+ }
+}
+
+static void AlliedHome (RESPONSE_REF R);
+
+static void
+AlliedHome (RESPONSE_REF R)
+{
+ BYTE NumVisits, News;
+
+ News = GET_GAME_STATE (UTWIG_WAR_NEWS);
+ NumVisits = GET_GAME_STATE (UTWIG_SUPOX_MISSION);
+ if (PLAYER_SAID (R, how_went_war))
+ {
+ NPCPhrase (ABOUT_BATTLE);
+
+ News |= (1 << 0);
+ }
+ else if (PLAYER_SAID (R, how_goes_war))
+ {
+ if (NumVisits == 1)
+ {
+ NPCPhrase (FLEET_ON_WAY);
+
+ SET_GAME_STATE (UTWIG_WAR_NEWS, 1);
+ }
+ else switch (GET_GAME_STATE (UTWIG_WAR_NEWS))
+ {
+ case 0:
+ NPCPhrase (BATTLE_HAPPENS_1);
+ News = 1;
+ break;
+ case 1:
+ NPCPhrase (BATTLE_HAPPENS_2);
+ News = 2;
+ break;
+ }
+
+ DISABLE_PHRASE (how_goes_war);
+ }
+ else if (PLAYER_SAID (R, learn_new_info))
+ {
+ if (NumVisits < 5)
+ NPCPhrase (NO_NEW_INFO);
+ else
+ {
+ NPCPhrase (SAMATRA);
+
+ News |= (1 << 1);
+ }
+
+ DISABLE_PHRASE (learn_new_info);
+ }
+ else if (PLAYER_SAID (R, what_now_homeworld))
+ {
+ if (NumVisits < 5)
+ NPCPhrase (UP_TO_YOU);
+ else
+ NPCPhrase (HOPE_KILL_EACH_OTHER);
+
+ DISABLE_PHRASE (what_now_homeworld);
+ }
+ else if (PLAYER_SAID (R, how_is_ultron))
+ {
+ NPCPhrase (ULTRON_IS_GREAT);
+
+ DISABLE_PHRASE (how_is_ultron);
+ }
+ SET_GAME_STATE (UTWIG_WAR_NEWS, News);
+
+ if (NumVisits >= 5)
+ {
+ if (!(News & (1 << 0)))
+ Response (how_went_war, AlliedHome);
+ }
+ else if (PHRASE_ENABLED (how_goes_war)
+ && ((NumVisits == 1 && News == 0)
+ || (NumVisits && News < 2)))
+ Response (how_goes_war, AlliedHome);
+ if (PHRASE_ENABLED (learn_new_info))
+ Response (learn_new_info, AlliedHome);
+ if (PHRASE_ENABLED (what_now_homeworld))
+ Response (what_now_homeworld, AlliedHome);
+ if (PHRASE_ENABLED (how_is_ultron))
+ Response (how_is_ultron, AlliedHome);
+ if (NumVisits == 0 && EscortFeasibilityStudy (UTWIG_SHIP) != 0)
+ Response (can_you_help, ExitConversation);
+ Response (bye_allied_homeworld, ExitConversation);
+}
+
+static void
+BeforeKohrAh (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_before_space))
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_BEFORE_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_BEFORE_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_before_space);
+ }
+ else if (PLAYER_SAID (R, what_now_before_space))
+ {
+ NPCPhrase (DO_THIS_BEFORE_SPACE);
+
+ DISABLE_PHRASE (what_now_before_space);
+ }
+
+ if (PHRASE_ENABLED (whats_up_before_space))
+ Response (whats_up_before_space, BeforeKohrAh);
+ if (PHRASE_ENABLED (what_now_before_space))
+ Response (what_now_before_space, BeforeKohrAh);
+ Response (bye_before_space, ExitConversation);
+}
+
+static void
+AfterKohrAh (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_after_space))
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_AFTER_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_AFTER_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_after_space);
+ }
+ else if (PLAYER_SAID (R, what_now_after_space))
+ {
+ NPCPhrase (DO_THIS_AFTER_SPACE);
+
+ DISABLE_PHRASE (what_now_after_space);
+ }
+
+ if (PHRASE_ENABLED (whats_up_after_space))
+ Response (whats_up_after_space, AfterKohrAh);
+ if (PHRASE_ENABLED (what_now_after_space))
+ Response (what_now_after_space, AfterKohrAh);
+ Response (bye_after_space, ExitConversation);
+}
+
+static void
+NeutralUtwig (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[4];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = pStr[3] = 0;
+ if (PLAYER_SAID (R, we_are_vindicator0))
+ {
+ NPCPhrase (WOULD_BE_HAPPY_BUT);
+
+ SET_GAME_STATE (UTWIG_STACK1, 1);
+ }
+ else if (PLAYER_SAID (R, why_sad))
+ {
+ NPCPhrase (ULTRON_BROKE);
+
+ SET_GAME_STATE (UTWIG_STACK1, 2);
+ }
+ else if (PLAYER_SAID (R, what_ultron))
+ {
+ NPCPhrase (GLORIOUS_ULTRON);
+
+ SET_GAME_STATE (UTWIG_STACK1, 3);
+ }
+ else if (PLAYER_SAID (R, dont_be_babies))
+ {
+ NPCPhrase (MOCK_OUR_PAIN);
+
+ setSegue (Segue_hostile);
+ SET_GAME_STATE (UTWIG_STACK1, 4);
+ SET_GAME_STATE (UTWIG_HOSTILE, 1);
+ SET_GAME_STATE (UTWIG_INFO, 0);
+ SET_GAME_STATE (UTWIG_HOME_VISITS, 0);
+ SET_GAME_STATE (UTWIG_VISITS, 0);
+ SET_GAME_STATE (BOMB_VISITS, 0);
+ return;
+ }
+ else if (PLAYER_SAID (R, real_sorry_about_ultron))
+ {
+ NPCPhrase (APPRECIATE_SYMPATHY);
+
+ SET_GAME_STATE (UTWIG_STACK1, 4);
+ return;
+ }
+ else if (PLAYER_SAID (R, what_about_you_1))
+ {
+ NPCPhrase (ABOUT_US_1);
+
+ LastStack = 2;
+ SET_GAME_STATE (UTWIG_WAR_NEWS, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_you_2))
+ {
+ NPCPhrase (ABOUT_US_2);
+
+ LastStack = 2;
+ StartSphereTracking (SUPOX_SHIP);
+ SET_GAME_STATE (UTWIG_WAR_NEWS, 2);
+ }
+ else if (PLAYER_SAID (R, what_about_you_3))
+ {
+ NPCPhrase (ABOUT_US_3);
+
+ SET_GAME_STATE (UTWIG_WAR_NEWS, 3);
+ }
+ else if (PLAYER_SAID (R, what_about_urquan_1))
+ {
+ NPCPhrase (ABOUT_URQUAN_1);
+
+ LastStack = 3;
+ SET_GAME_STATE (UTWIG_STACK2, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_urquan_2))
+ {
+ NPCPhrase (ABOUT_URQUAN_2);
+
+ SET_GAME_STATE (UTWIG_STACK2, 2);
+ }
+
+ switch (GET_GAME_STATE (UTWIG_STACK1))
+ {
+ case 0:
+ {
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ we_are_vindicator0,
+ GLOBAL_SIS (CommanderName),
+ we_are_vindicator1,
+ buf,
+ we_are_vindicator2,
+ (UNICODE*)NULL);
+ }
+ pStr[0] = we_are_vindicator0;
+ break;
+ case 1:
+ pStr[0] = why_sad;
+ break;
+ case 2:
+ pStr[0] = what_ultron;
+ break;
+ case 3:
+ pStr[0] = dont_be_babies;
+ pStr[1] = real_sorry_about_ultron;
+ break;
+ }
+ switch (GET_GAME_STATE (UTWIG_WAR_NEWS))
+ {
+ case 0:
+ pStr[2] = what_about_you_1;
+ break;
+ case 1:
+ pStr[2] = what_about_you_2;
+ break;
+ case 2:
+ pStr[2] = what_about_you_3;
+ break;
+ }
+ switch (GET_GAME_STATE (UTWIG_STACK2))
+ {
+ case 0:
+ pStr[2] = what_about_urquan_1;
+ break;
+ case 1:
+ pStr[2] = what_about_urquan_2;
+ break;
+ }
+
+ if (pStr[LastStack])
+ {
+ if (pStr[LastStack] != we_are_vindicator0)
+ Response (pStr[LastStack], NeutralUtwig);
+ else
+ DoResponsePhrase (pStr[LastStack], NeutralUtwig, shared_phrase_buf);
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ if (pStr[i] != we_are_vindicator0)
+ Response (pStr[i], NeutralUtwig);
+ else
+ DoResponsePhrase (pStr[i], NeutralUtwig, shared_phrase_buf);
+ }
+ }
+ if (GET_GAME_STATE (ULTRON_CONDITION))
+ Response (got_ultron, ExitConversation);
+ Response (bye_neutral, ExitConversation);
+}
+
+static void
+BombWorld (RESPONSE_REF R)
+{
+ BYTE LastStack;
+ RESPONSE_REF pStr[2];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = 0;
+ if (PLAYER_SAID (R, why_you_here))
+ {
+ NPCPhrase (WE_GUARD_BOMB);
+
+ SET_GAME_STATE (BOMB_STACK1, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_bomb))
+ {
+ NPCPhrase (ABOUT_BOMB);
+
+ SET_GAME_STATE (BOMB_STACK1, 2);
+ }
+ else if (PLAYER_SAID (R, give_us_bomb_or_die))
+ {
+ NPCPhrase (GUARDS_WARN);
+
+ SET_GAME_STATE (BOMB_STACK1, 3);
+ }
+ else if (PLAYER_SAID (R, demand_bomb))
+ {
+ NPCPhrase (GUARDS_FIGHT);
+
+ setSegue (Segue_hostile);
+ SET_GAME_STATE (UTWIG_HOSTILE, 1);
+ SET_GAME_STATE (UTWIG_INFO, 0);
+ SET_GAME_STATE (UTWIG_HOME_VISITS, 0);
+ SET_GAME_STATE (UTWIG_VISITS, 0);
+ SET_GAME_STATE (BOMB_VISITS, 0);
+ return;
+ }
+ else if (PLAYER_SAID (R, may_we_have_bomb))
+ {
+ NPCPhrase (NO_BOMB);
+
+ LastStack = 1;
+ SET_GAME_STATE (BOMB_STACK2, 1);
+ }
+ else if (PLAYER_SAID (R, please))
+ {
+ NPCPhrase (SORRY_NO_BOMB);
+
+ SET_GAME_STATE (BOMB_STACK2, 2);
+ }
+ else if (PLAYER_SAID (R, whats_up_bomb))
+ {
+ if (GET_GAME_STATE (BOMB_INFO))
+ NPCPhrase (GENERAL_INFO_BOMB_2);
+ else
+ {
+ NPCPhrase (GENERAL_INFO_BOMB_1);
+
+ SET_GAME_STATE (BOMB_INFO, 1);
+ }
+
+ DISABLE_PHRASE (whats_up_bomb);
+ }
+
+ switch (GET_GAME_STATE (BOMB_STACK2))
+ {
+ case 0:
+ pStr[1] = may_we_have_bomb;
+ break;
+ case 1:
+ pStr[1] = please;
+ break;
+ }
+ switch (GET_GAME_STATE (BOMB_STACK1))
+ {
+ case 0:
+ pStr[0] = why_you_here;
+ pStr[1] = 0;
+ break;
+ case 1:
+ pStr[0] = what_about_bomb;
+ pStr[1] = 0;
+ break;
+ case 2:
+ pStr[0] = give_us_bomb_or_die;
+ break;
+ case 3:
+ pStr[0] = demand_bomb;
+ break;
+ }
+
+ if (pStr[LastStack])
+ Response (pStr[LastStack], BombWorld);
+ LastStack ^= 1;
+ if (pStr[LastStack])
+ Response (pStr[LastStack], BombWorld);
+
+ if (PHRASE_ENABLED (whats_up_bomb) && (GET_GAME_STATE (BOMB_STACK1) > 1))
+ Response (whats_up_bomb, BombWorld);
+
+ if (GET_GAME_STATE (ULTRON_CONDITION)
+ && !GET_GAME_STATE (REFUSED_ULTRON_AT_BOMB))
+ Response (got_ultron, ExitConversation);
+
+ if (GET_GAME_STATE (BOMB_INFO))
+ {
+ Response (bye_bomb, ExitConversation);
+ }
+ else
+ {
+ Response (bye_neutral, ExitConversation);
+ }
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (UTWIG_HOSTILE))
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ NumVisits = GET_GAME_STATE (BOMB_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_BOMB_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_BOMB_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (BOMB_VISITS, NumVisits);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_HOMEWORLD_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOSTILE_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOSTILE_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_VISITS, NumVisits);
+ }
+
+ if (!GET_GAME_STATE (ULTRON_CONDITION)
+ || (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6)))
+ {
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ Response (hey_wait_got_ultron, ExitConversation);
+ }
+ }
+ else if (CheckAlliance (UTWIG_SHIP) == GOOD_GUY)
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (ALLIED_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_HOME_VISITS, NumVisits);
+
+ AlliedHome ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_SUPOX_MISSION);
+ if (NumVisits == 1)
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_BEFORE_KOHRAH_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_BEFORE_KOHRAH_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_VISITS, NumVisits);
+
+ BeforeKohrAh ((RESPONSE_REF)0);
+ }
+ else if (NumVisits < 5)
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_DURING_KOHRAH_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_DURING_KOHRAH_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HELLO_AFTER_KOHRAH_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (HELLO_AFTER_KOHRAH_SPACE_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_VISITS, NumVisits);
+
+ AfterKohrAh ((RESPONSE_REF)0);
+ }
+ }
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ NumVisits = GET_GAME_STATE (BOMB_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (BOMB_WORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (BOMB_WORLD_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (BOMB_VISITS, NumVisits);
+
+ BombWorld ((RESPONSE_REF)0);
+ }
+ else
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (NEUTRAL_HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (UTWIG_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (NEUTRAL_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (UTWIG_VISITS, NumVisits);
+ }
+
+ NeutralUtwig ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_utwig (void)
+{
+ return (0);
+}
+
+static void
+post_utwig_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_utwig_comm (void)
+{
+ LOCDATA *retval;
+
+ utwig_desc.init_encounter_func = Intro;
+ utwig_desc.post_encounter_func = post_utwig_enc;
+ utwig_desc.uninit_encounter_func = uninit_utwig;
+
+ utwig_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1);
+ utwig_desc.AlienTextBaseline.y = 70;
+ utwig_desc.AlienTextWidth = SIS_TEXT_WIDTH - 16;
+
+ if (GET_GAME_STATE (UTWIG_HAVE_ULTRON))
+ { // use alternate 'Happy Utwig!' track
+ utwig_desc.AlienAltSongRes = UTWIG_ULTRON_MUSIC;
+ utwig_desc.AlienSongFlags |= LDASF_USE_ALTERNATE;
+ }
+ else
+ { // regular track -- let's make sure
+ utwig_desc.AlienSongFlags &= ~LDASF_USE_ALTERNATE;
+ }
+
+ if (GET_GAME_STATE (UTWIG_HAVE_ULTRON)
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &utwig_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/vux/Makeinfo b/src/uqm/comm/vux/Makeinfo
new file mode 100644
index 0000000..da9446f
--- /dev/null
+++ b/src/uqm/comm/vux/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="vuxc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/vux/resinst.h b/src/uqm/comm/vux/resinst.h
new file mode 100644
index 0000000..5004523
--- /dev/null
+++ b/src/uqm/comm/vux/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define VUX_COLOR_MAP "comm.vux.colortable"
+#define VUX_CONVERSATION_PHRASES "comm.vux.dialogue"
+#define VUX_FONT "comm.vux.font"
+#define VUX_MUSIC "comm.vux.music"
+#define VUX_PMAP_ANIM "comm.vux.graphics"
diff --git a/src/uqm/comm/vux/strings.h b/src/uqm/comm/vux/strings.h
new file mode 100644
index 0000000..5bf58b7
--- /dev/null
+++ b/src/uqm/comm/vux/strings.h
@@ -0,0 +1,129 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef VUX_STRINGS_H
+#define VUX_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ ZEX_HELLO_1,
+ ZEX_HELLO_2,
+ ZEX_HELLO_3,
+ ZEX_HELLO_4,
+ FIGHT_OR_TRADE_1,
+ FIGHT_OR_TRADE_2,
+ what_you_do_here,
+ MY_MENAGERIE,
+ what_about_menagerie,
+ NEED_NEW_CREATURE,
+ what_about_creature,
+ ABOUT_CREATURE,
+ about_creature_again,
+ CREATURE_AGAIN,
+ i_have_beast,
+ GIVE_BEAST,
+ ok_take_beast,
+ FOOL_AIEE0,
+ FOOL_AIEE1,
+ why_trust_1,
+ TRUST_1,
+ why_trust_2,
+ TRUST_2,
+ why_trust_3,
+ TRUST_3,
+ why_dont_you_attack,
+ LIKE_YOU,
+ why_like_me,
+ LIKE_BECAUSE,
+ are_you_a_pervert,
+ CALL_ME_WHAT_YOU_WISH,
+ take_by_force,
+ PRECURSOR_DEVICE,
+ regardless,
+ THEN_FIGHT,
+ you_lied,
+ YUP_LIED,
+ kill_you,
+ FIGHT_AGAIN,
+ bye_zex,
+ GOODBYE_ZEX,
+ HOMEWORLD_HELLO_1,
+ HOMEWORLD_HELLO_2,
+ HOMEWORLD_HELLO_3,
+ HOMEWORLD_HELLO_4,
+ SPACE_HELLO_1,
+ SPACE_HELLO_2,
+ SPACE_HELLO_3,
+ SPACE_HELLO_4,
+ kill_you_squids_1,
+ kill_you_squids_2,
+ kill_you_squids_3,
+ kill_you_squids_4,
+ WE_FIGHT,
+ why_so_mean,
+ URQUAN_SLAVES,
+ deeper_reason,
+ OLD_INSULT,
+ if_we_apologize,
+ PROBABLY_NOT,
+ try_any_way,
+ NOPE,
+ APOLOGIZE_IN_SPACE,
+ apology_1,
+ NOT_ACCEPTED_1,
+ apology_2,
+ NOT_ACCEPTED_2,
+ apology_3,
+ NOT_ACCEPTED_3,
+ apology_4,
+ NOT_ACCEPTED_4,
+ apology_5,
+ NOT_ACCEPTED_5,
+ apology_6,
+ NOT_ACCEPTED_6,
+ apology_7,
+ NOT_ACCEPTED_7,
+ apology_8,
+ NOT_ACCEPTED_8,
+ apology_9,
+ NOT_ACCEPTED_9,
+ apology_10,
+ TRUTH,
+ whats_up_hostile,
+ GENERAL_INFO_HOSTILE_1,
+ GENERAL_INFO_HOSTILE_2,
+ GENERAL_INFO_HOSTILE_3,
+ GENERAL_INFO_HOSTILE_4,
+ cant_we_be_friends_1,
+ NEVER_UGLY_HUMANS_1,
+ cant_we_be_friends_2,
+ NEVER_UGLY_HUMANS_2,
+ cant_we_be_friends_3,
+ NEVER_UGLY_HUMANS_3,
+ cant_we_be_friends_4,
+ NEVER_UGLY_HUMANS_4,
+ bye_hostile_space,
+ GOODBYE_AND_DIE_HOSTILE_SPACE_1,
+ GOODBYE_AND_DIE_HOSTILE_SPACE_2,
+ GOODBYE_AND_DIE_HOSTILE_SPACE_3,
+ GOODBYE_AND_DIE_HOSTILE_SPACE_4,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/vux/vuxc.c b/src/uqm/comm/vux/vuxc.c
new file mode 100644
index 0000000..7f7419c
--- /dev/null
+++ b/src/uqm/comm/vux/vuxc.c
@@ -0,0 +1,796 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+static LOCDATA vux_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ VUX_PMAP_ANIM, /* AlienFrame */
+ VUX_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* (SIS_TEXT_WIDTH - 16) >> 1, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_TOP, /* AlienTextValign */
+ VUX_COLOR_MAP, /* AlienColorMap */
+ VUX_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ VUX_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 17, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ {
+ 12, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 15, /* StartIndex */
+ 5, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 20, /* StartIndex */
+ 14, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND / 30, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 34, /* StartIndex */
+ 7, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 41, /* StartIndex */
+ 6, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 47, /* StartIndex */
+ 11, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 58, /* StartIndex */
+ 3, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 61, /* StartIndex */
+ 4, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 65, /* StartIndex */
+ 4, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 69, /* StartIndex */
+ 2, /* NumFrames */
+ RANDOM_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 71, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 74, /* StartIndex */
+ 6, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 80, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* RestartRate */
+ (1 << 14), /* BlockMask */
+ },
+ {
+ 85, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 90, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* RestartRate */
+ (1 << 12), /* BlockMask */
+ },
+ {
+ 95, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 5, ONE_SECOND * 5,/* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 99, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 5, ONE_SECOND * 5,/* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 11, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+CombatIsInevitable (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, ok_take_beast))
+ {
+ NPCPhrase (FOOL_AIEE0);
+ NPCPhrase (FOOL_AIEE1);
+
+ AlienTalkSegue (1);
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap, 1)
+ ), ONE_SECOND / 4);
+ AlienTalkSegue ((COUNT)~0);
+
+ SET_GAME_STATE (VUX_BEAST_ON_SHIP, 0);
+ SET_GAME_STATE (ZEX_IS_DEAD, 1);
+ setSegue (Segue_peace);
+ }
+ else if (PLAYER_SAID (R, try_any_way))
+ {
+ NPCPhrase (NOPE);
+
+ SET_GAME_STATE (VUX_STACK_1, 4);
+ }
+ else if (PLAYER_SAID (R, kill_you_squids_1)
+ || PLAYER_SAID (R, kill_you_squids_2)
+ || PLAYER_SAID (R, kill_you_squids_3)
+ || PLAYER_SAID (R, kill_you_squids_4))
+ {
+ NPCPhrase (WE_FIGHT);
+
+ NumVisits = GET_GAME_STATE (VUX_STACK_2) + 1;
+ if (NumVisits <= 3)
+ {
+ SET_GAME_STATE (VUX_STACK_2, NumVisits);
+ }
+ }
+ else if (PLAYER_SAID (R, cant_we_be_friends_1)
+ || PLAYER_SAID (R, cant_we_be_friends_2)
+ || PLAYER_SAID (R, cant_we_be_friends_3)
+ || PLAYER_SAID (R, cant_we_be_friends_4))
+ {
+ NumVisits = GET_GAME_STATE (VUX_STACK_3);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (NEVER_UGLY_HUMANS_1);
+ break;
+ case 1:
+ NPCPhrase (NEVER_UGLY_HUMANS_2);
+ break;
+ case 2:
+ NPCPhrase (NEVER_UGLY_HUMANS_3);
+ break;
+ case 3:
+ NPCPhrase (NEVER_UGLY_HUMANS_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (VUX_STACK_3, NumVisits);
+ }
+ else if (PLAYER_SAID (R, bye_hostile_space))
+ {
+ NumVisits = GET_GAME_STATE (VUX_STACK_4);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GOODBYE_AND_DIE_HOSTILE_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GOODBYE_AND_DIE_HOSTILE_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (GOODBYE_AND_DIE_HOSTILE_SPACE_3);
+ break;
+ case 3:
+ NPCPhrase (GOODBYE_AND_DIE_HOSTILE_SPACE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (VUX_STACK_4, NumVisits);
+ }
+ else if (PLAYER_SAID (R, bye_zex))
+ {
+ NPCPhrase (GOODBYE_ZEX);
+
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (VUX_STACK_1);
+ switch (NumVisits++)
+ {
+ case 4:
+ NPCPhrase (NOT_ACCEPTED_1);
+ break;
+ case 5:
+ NPCPhrase (NOT_ACCEPTED_2);
+ break;
+ case 6:
+ NPCPhrase (NOT_ACCEPTED_3);
+ break;
+ case 7:
+ NPCPhrase (NOT_ACCEPTED_4);
+ break;
+ case 8:
+ NPCPhrase (NOT_ACCEPTED_5);
+ break;
+ case 9:
+ NPCPhrase (NOT_ACCEPTED_6);
+ break;
+ case 10:
+ NPCPhrase (NOT_ACCEPTED_7);
+ break;
+ case 11:
+ NPCPhrase (NOT_ACCEPTED_8);
+ break;
+ case 12:
+ NPCPhrase (NOT_ACCEPTED_9);
+ break;
+ case 13:
+ NPCPhrase (TRUTH);
+ break;
+ }
+ SET_GAME_STATE (VUX_STACK_1, NumVisits);
+ }
+}
+
+static void
+Menagerie (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[3];
+
+ if (PLAYER_SAID (R, i_have_beast)
+ || PLAYER_SAID (R, why_trust_1)
+ || PLAYER_SAID (R, why_trust_2)
+ || PLAYER_SAID (R, why_trust_3))
+ {
+ if (PLAYER_SAID (R, i_have_beast))
+ NPCPhrase (GIVE_BEAST);
+ else if (PLAYER_SAID (R, why_trust_1))
+ {
+ NPCPhrase (TRUST_1);
+
+ DISABLE_PHRASE (why_trust_1);
+ }
+ else if (PLAYER_SAID (R, why_trust_2))
+ {
+ NPCPhrase (TRUST_2);
+
+ DISABLE_PHRASE (why_trust_2);
+ }
+ else if (PLAYER_SAID (R, why_trust_3))
+ {
+ NPCPhrase (TRUST_3);
+
+ DISABLE_PHRASE (why_trust_3);
+ }
+
+ if (PHRASE_ENABLED (why_trust_1))
+ Response (why_trust_1, Menagerie);
+ else if (PHRASE_ENABLED (why_trust_2))
+ Response (why_trust_2, Menagerie);
+ else if (PHRASE_ENABLED (why_trust_3))
+ Response (why_trust_3, Menagerie);
+ Response (ok_take_beast, CombatIsInevitable);
+ }
+ else if (PLAYER_SAID (R, kill_you))
+ {
+ NPCPhrase (FIGHT_AGAIN);
+
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, regardless))
+ {
+ NPCPhrase (THEN_FIGHT);
+
+ setSegue (Segue_hostile);
+ SET_GAME_STATE (ZEX_STACK_3, 2);
+ SET_GAME_STATE (ZEX_VISITS, 0);
+ }
+ else
+ {
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = 0;
+ if (R == 0)
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (ZEX_VISITS);
+ if (GET_GAME_STATE (ZEX_STACK_3) >= 2)
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (FIGHT_OR_TRADE_1);
+ break;
+ case 1:
+ NPCPhrase (FIGHT_OR_TRADE_2);
+ --NumVisits;
+ break;
+ }
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ZEX_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ZEX_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (ZEX_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (ZEX_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (ZEX_VISITS, NumVisits);
+ }
+ else if (PLAYER_SAID (R, what_you_do_here))
+ {
+ NPCPhrase (MY_MENAGERIE);
+
+ SET_GAME_STATE (ZEX_STACK_1, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_menagerie))
+ {
+ NPCPhrase (NEED_NEW_CREATURE);
+
+ SET_GAME_STATE (ZEX_STACK_1, 2);
+ }
+ else if (PLAYER_SAID (R, what_about_creature))
+ {
+ NPCPhrase (ABOUT_CREATURE);
+
+ SET_GAME_STATE (KNOW_ZEX_WANTS_MONSTER, 1);
+ SET_GAME_STATE (ZEX_STACK_1, 3);
+
+ R = about_creature_again;
+ DISABLE_PHRASE (what_about_creature);
+ }
+ else if (PLAYER_SAID (R, about_creature_again))
+ {
+ NPCPhrase (CREATURE_AGAIN);
+
+ DISABLE_PHRASE (about_creature_again);
+ }
+ else if (PLAYER_SAID (R, why_dont_you_attack))
+ {
+ NPCPhrase (LIKE_YOU);
+
+ LastStack = 1;
+ SET_GAME_STATE (ZEX_STACK_2, 1);
+ }
+ else if (PLAYER_SAID (R, why_like_me))
+ {
+ NPCPhrase (LIKE_BECAUSE);
+
+ LastStack = 1;
+ SET_GAME_STATE (ZEX_STACK_2, 2);
+ }
+ else if (PLAYER_SAID (R, are_you_a_pervert))
+ {
+ NPCPhrase (CALL_ME_WHAT_YOU_WISH);
+
+ SET_GAME_STATE (ZEX_STACK_2, 3);
+ }
+ else if (PLAYER_SAID (R, take_by_force))
+ {
+ NPCPhrase (PRECURSOR_DEVICE);
+
+ LastStack = 2;
+ SET_GAME_STATE (ZEX_STACK_3, 1);
+ }
+ else if (PLAYER_SAID (R, you_lied))
+ {
+ NPCPhrase (YUP_LIED);
+
+ LastStack = 2;
+ SET_GAME_STATE (ZEX_STACK_3, 3);
+ }
+
+ if (GET_GAME_STATE (KNOW_ZEX_WANTS_MONSTER)
+ && GET_GAME_STATE (VUX_BEAST_ON_SHIP))
+ pStr[0] = i_have_beast;
+ else
+ {
+ switch (GET_GAME_STATE (ZEX_STACK_1))
+ {
+ case 0:
+ pStr[0] = what_you_do_here;
+ break;
+ case 1:
+ pStr[0] = what_about_menagerie;
+ break;
+ case 2:
+ pStr[0] = what_about_creature;
+ break;
+ case 3:
+ if (PHRASE_ENABLED (about_creature_again))
+ pStr[0] = about_creature_again;
+ break;
+ }
+ }
+ switch (GET_GAME_STATE (ZEX_STACK_2))
+ {
+ case 0:
+ pStr[1] = why_dont_you_attack;
+ break;
+ case 1:
+ pStr[1] = why_like_me;
+ break;
+ case 2:
+ pStr[1] = are_you_a_pervert;
+ break;
+ }
+ switch (GET_GAME_STATE (ZEX_STACK_3))
+ {
+ case 0:
+ pStr[2] = take_by_force;
+ break;
+ case 1:
+ pStr[2] = regardless;
+ break;
+ case 2:
+ pStr[2] = you_lied;
+ break;
+ case 3:
+ pStr[2] = kill_you;
+ break;
+ }
+
+ if (pStr[LastStack])
+ Response (pStr[LastStack], Menagerie);
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ Response (pStr[i], Menagerie);
+ }
+ Response (bye_zex, CombatIsInevitable);
+ }
+}
+
+static void
+NormalVux (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, why_so_mean))
+ {
+ NPCPhrase (URQUAN_SLAVES);
+
+ SET_GAME_STATE (VUX_STACK_1, 1);
+ }
+ else if (PLAYER_SAID (R, deeper_reason))
+ {
+ NPCPhrase (OLD_INSULT);
+
+ SET_GAME_STATE (VUX_STACK_1, 2);
+ }
+ else if (PLAYER_SAID (R, if_we_apologize))
+ {
+ NPCPhrase (PROBABLY_NOT);
+
+ SET_GAME_STATE (VUX_STACK_1, 3);
+ }
+ else if (PLAYER_SAID (R, whats_up_hostile))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (VUX_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_HOSTILE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_HOSTILE_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_HOSTILE_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_HOSTILE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (VUX_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_hostile);
+ }
+
+ switch (GET_GAME_STATE (VUX_STACK_1))
+ {
+ case 0:
+ Response (why_so_mean, NormalVux);
+ break;
+ case 1:
+ Response (deeper_reason, NormalVux);
+ break;
+ case 2:
+ Response (if_we_apologize, NormalVux);
+ break;
+ case 3:
+ Response (try_any_way, CombatIsInevitable);
+ break;
+ case 4:
+ Response (apology_1, CombatIsInevitable);
+ break;
+ case 5:
+ Response (apology_2, CombatIsInevitable);
+ break;
+ case 6:
+ Response (apology_3, CombatIsInevitable);
+ break;
+ case 7:
+ Response (apology_4, CombatIsInevitable);
+ break;
+ case 8:
+ Response (apology_5, CombatIsInevitable);
+ break;
+ case 9:
+ Response (apology_6, CombatIsInevitable);
+ break;
+ case 10:
+ Response (apology_7, CombatIsInevitable);
+ break;
+ case 11:
+ Response (apology_8, CombatIsInevitable);
+ break;
+ case 12:
+ Response (apology_9, CombatIsInevitable);
+ break;
+ case 13:
+ Response (apology_10, CombatIsInevitable);
+ break;
+ }
+
+ switch (GET_GAME_STATE (VUX_STACK_2))
+ {
+ case 0:
+ Response (kill_you_squids_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (kill_you_squids_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (kill_you_squids_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (kill_you_squids_4, CombatIsInevitable);
+ break;
+ }
+
+ if (PHRASE_ENABLED (whats_up_hostile))
+ {
+ Response (whats_up_hostile, NormalVux);
+ }
+
+ if (GET_GAME_STATE (VUX_STACK_1) > 13)
+ {
+ switch (GET_GAME_STATE (VUX_STACK_3))
+ {
+ case 0:
+ Response (cant_we_be_friends_1, CombatIsInevitable);
+ break;
+ case 1:
+ Response (cant_we_be_friends_2, CombatIsInevitable);
+ break;
+ case 2:
+ Response (cant_we_be_friends_3, CombatIsInevitable);
+ break;
+ case 3:
+ Response (cant_we_be_friends_4, CombatIsInevitable);
+ break;
+ }
+ }
+
+ Response (bye_hostile_space, CombatIsInevitable);
+}
+
+static void
+Intro (void)
+{
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ {
+ Menagerie ((RESPONSE_REF)0);
+ }
+ else
+ {
+ BYTE NumVisits;
+
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (VUX_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOMEWORLD_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (HOMEWORLD_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (HOMEWORLD_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (VUX_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (VUX_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (SPACE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (VUX_VISITS, NumVisits);
+ }
+
+ NormalVux ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_vux (void)
+{
+ return (0);
+}
+
+static void
+post_vux_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_vux_comm (void)
+{
+ LOCDATA *retval;
+
+ vux_desc.init_encounter_func = Intro;
+ vux_desc.post_encounter_func = post_vux_enc;
+ vux_desc.uninit_encounter_func = uninit_vux;
+
+ vux_desc.AlienTextBaseline.x = TEXT_X_OFFS + (SIS_TEXT_WIDTH >> 1)
+ + (SIS_TEXT_WIDTH >> 2);
+ vux_desc.AlienTextBaseline.y = 0;
+ vux_desc.AlienTextWidth = (SIS_TEXT_WIDTH - 16) >> 1;
+
+ if ((GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 6))
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+ retval = &vux_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/yehat/Makeinfo b/src/uqm/comm/yehat/Makeinfo
new file mode 100644
index 0000000..f38674b
--- /dev/null
+++ b/src/uqm/comm/yehat/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="yehatc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/yehat/resinst.h b/src/uqm/comm/yehat/resinst.h
new file mode 100644
index 0000000..3b0b203
--- /dev/null
+++ b/src/uqm/comm/yehat/resinst.h
@@ -0,0 +1,11 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define REBEL_CONVERSATION_PHRASES "comm.yehat.rebel.dialogue"
+#define REBEL_MUSIC "comm.yehat.rebel.music"
+#define YEHAT_COLOR_MAP "comm.yehat.colortable"
+#define YEHAT_CONVERSATION_PHRASES "comm.yehat.dialogue"
+#define YEHAT_FONT "comm.yehat.font"
+#define YEHAT_MUSIC "comm.yehat.music"
+#define YEHAT_PMAP_ANIM "comm.yehat.graphics"
diff --git a/src/uqm/comm/yehat/strings.h b/src/uqm/comm/yehat/strings.h
new file mode 100644
index 0000000..9bea2e0
--- /dev/null
+++ b/src/uqm/comm/yehat/strings.h
@@ -0,0 +1,102 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef YEHAT_STRINGS_H
+#define YEHAT_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ HOMEWORLD_HELLO_1,
+ HOMEWORLD_HELLO_2,
+ whats_up_homeworld,
+ GENERAL_INFO_HOMEWORLD_1,
+ GENERAL_INFO_HOMEWORLD_2,
+ i_demand_you_ally_homeworld0,
+ i_demand_you_ally_homeworld1,
+ i_demand_you_ally_homeworld2,
+ i_demand_you_ally_homeworld3,
+ ENEMY_MUST_DIE,
+ at_least_help_us_homeworld,
+ NO_HELP_ENEMY,
+ give_info,
+ NO_INFO_FOR_ENEMY,
+ what_about_pkunk_royalist,
+ PKUNK_ABSORBED_ROYALIST,
+ HATE_PKUNK_ROYALIST,
+ bye_homeworld,
+ GOODBYE_AND_DIE_HOMEWORLD,
+ SPACE_HELLO_1,
+ SPACE_HELLO_2,
+ SPACE_HELLO_3,
+ SPACE_HELLO_4,
+ whats_up_space_1,
+ GENERAL_INFO_SPACE_1,
+ whats_up_space_2,
+ GENERAL_INFO_SPACE_2,
+ whats_up_space_3,
+ GENERAL_INFO_SPACE_3,
+ whats_up_space_4,
+ GENERAL_INFO_SPACE_4,
+ i_demand_you_ally_space0,
+ i_demand_you_ally_space1,
+ i_demand_you_ally_space2,
+ i_demand_you_ally_space3,
+ WE_CANNOT_1,
+ obligation,
+ WE_CANNOT_2,
+ at_least_help_us_space,
+ SORRY_CANNOT,
+ dishonor,
+ HERES_A_HINT,
+ what_about_pkunk_space,
+ PKUNK_ABSORBED_SPACE,
+ HATE_PKUNK_SPACE,
+ bye_space,
+ GO_IN_PEACE,
+ GOODBYE_AND_DIE_SPACE,
+ shofixti_alive_1,
+ shofixti_alive_2,
+ SEND_HIM_OVER_1,
+ SEND_HIM_OVER_2,
+ not_here,
+ not_send,
+ JUST_A_TRICK_1,
+ JUST_A_TRICK_2,
+ ok_send,
+ WE_REVOLT,
+ ROYALIST_SPACE_HELLO_1,
+ ROYALIST_SPACE_HELLO_2,
+ ROYALIST_HOMEWORLD_HELLO_1,
+ ROYALIST_HOMEWORLD_HELLO_2,
+ how_is_rebellion,
+ ROYALIST_REBELLION_1,
+ ROYALIST_REBELLION_2,
+ sorry_about_revolution,
+ ALL_YOUR_FAULT,
+ bye_royalist,
+ GOODBYE_AND_DIE_ROYALIST,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ OUT_TAKES,
+};
+
+#endif /* _STRINGS_H */
diff --git a/src/uqm/comm/yehat/yehatc.c b/src/uqm/comm/yehat/yehatc.c
new file mode 100644
index 0000000..c1860bf
--- /dev/null
+++ b/src/uqm/comm/yehat/yehatc.c
@@ -0,0 +1,685 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/gameev.h"
+#include "libs/mathlib.h"
+
+
+static LOCDATA yehat_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ YEHAT_PMAP_ANIM, /* AlienFrame */
+ YEHAT_FONT, /* AlienFont */
+ WHITE_COLOR_INIT, /* AlienTextFColor */
+ BLACK_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* (SIS_TEXT_WIDTH - 16) * 2 / 3, */ /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ YEHAT_COLOR_MAP, /* AlienColorMap */
+ YEHAT_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ YEHAT_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 15, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ { /* right hand-wing tapping keyboard; front guy */
+ 4, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 10, /* FrameRate */
+ ONE_SECOND / 4, ONE_SECOND / 2,/* RestartRate */
+ (1 << 6) | (1 << 7),
+ },
+ { /* left hand-wing tapping keyboard; front guy */
+ 7, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM
+ | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 10, ONE_SECOND / 10, /* FrameRate */
+ ONE_SECOND / 4, ONE_SECOND / 2,/* RestartRate */
+ (1 << 6) | (1 << 7),
+ },
+ {
+ 10, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 4) | (1 << 14),
+ },
+ {
+ 13, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 20, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 5),
+ },
+ {
+ 16, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 6, ONE_SECOND * 3,/* RestartRate */
+ (1 << 2) | (1 << 14),
+ },
+ {
+ 21, /* StartIndex */
+ 5, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 6, ONE_SECOND * 3,/* RestartRate */
+ (1 << 3),
+ },
+ { /* right arm-wing rising; front guy */
+ 26, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 6, ONE_SECOND * 3,/* RestartRate */
+ (1 << 0) | (1 << 1),
+ },
+ { /* left arm-wing rising; front guy */
+ 28, /* StartIndex */
+ 2, /* NumFrames */
+ YOYO_ANIM | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 15, ONE_SECOND / 15, /* FrameRate */
+ ONE_SECOND * 6, ONE_SECOND * 3,/* RestartRate */
+ (1 << 0) | (1 << 1),
+ },
+ {
+ 30, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 33, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 36, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 39, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 42, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 45, /* StartIndex */
+ 3, /* NumFrames */
+ YOYO_ANIM, /* AnimFlags */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* FrameRate */
+ ONE_SECOND / 30, ONE_SECOND / 30, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ {
+ 48, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM | WAIT_TALKING, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ ONE_SECOND, ONE_SECOND * 3, /* RestartRate */
+ (1 << 2) | (1 << 4),
+ },
+ },
+ { /* AlienTransitionDesc - empty */
+ 0, /* StartIndex */
+ 0, /* NumFrames */
+ 0, /* AnimFlags */
+ 0, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ 1, /* StartIndex */
+ 3, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_hostile);
+
+ if (PLAYER_SAID (R, bye_homeworld))
+ NPCPhrase (GOODBYE_AND_DIE_HOMEWORLD);
+ else if (PLAYER_SAID (R, bye_royalist))
+ NPCPhrase (GOODBYE_AND_DIE_ROYALIST);
+ else if (PLAYER_SAID (R, i_demand_you_ally_homeworld0))
+ {
+ NPCPhrase (ENEMY_MUST_DIE);
+
+ SET_GAME_STATE (NO_YEHAT_ALLY_HOME, 1);
+ }
+ else if (PLAYER_SAID (R, bye_space))
+ {
+ if ((BYTE)TFB_Random () & 1)
+ NPCPhrase (GOODBYE_AND_DIE_SPACE);
+ else
+ {
+ NPCPhrase (GO_IN_PEACE);
+
+ setSegue (Segue_peace);
+ }
+ }
+ else if (PLAYER_SAID (R, not_here)
+ || PLAYER_SAID (R, not_send))
+ {
+ switch (GET_GAME_STATE (YEHAT_REBEL_VISITS))
+ {
+ case 0:
+ NPCPhrase (JUST_A_TRICK_1);
+ break;
+ case 1:
+ NPCPhrase (JUST_A_TRICK_2);
+ break;
+ }
+ SET_GAME_STATE (YEHAT_REBEL_VISITS, 1);
+ }
+ else if (PLAYER_SAID (R, ok_send))
+ {
+ NPCPhrase (WE_REVOLT);
+
+ setSegue (Segue_peace);
+ SET_GAME_STATE (YEHAT_CIVIL_WAR, 1);
+ SET_GAME_STATE (YEHAT_VISITS, 0);
+ SET_GAME_STATE (YEHAT_HOME_VISITS, 0);
+ SET_GAME_STATE (YEHAT_REBEL_VISITS, 0);
+ SET_GAME_STATE (YEHAT_REBEL_INFO, 0);
+ SET_GAME_STATE (YEHAT_REBEL_TOLD_PKUNK, 0);
+ SET_GAME_STATE (NO_YEHAT_INFO, 0);
+
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, YEHAT_REBEL_EVENT);
+ }
+}
+
+static void
+Royalists (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, how_is_rebellion))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (YEHAT_ROYALIST_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ROYALIST_REBELLION_1);
+ break;
+ case 1:
+ NPCPhrase (ROYALIST_REBELLION_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_ROYALIST_INFO, NumVisits);
+
+ DISABLE_PHRASE (how_is_rebellion);
+ }
+ else if (PLAYER_SAID (R, what_about_pkunk_royalist))
+ {
+ if (GET_GAME_STATE (YEHAT_ABSORBED_PKUNK))
+ NPCPhrase (PKUNK_ABSORBED_ROYALIST);
+ else
+ NPCPhrase (HATE_PKUNK_ROYALIST);
+
+ SET_GAME_STATE (YEHAT_ROYALIST_TOLD_PKUNK, 1);
+ }
+ else if (PLAYER_SAID (R, sorry_about_revolution))
+ {
+ NPCPhrase (ALL_YOUR_FAULT);
+
+ SET_GAME_STATE (NO_YEHAT_INFO, 1);
+ }
+
+ if (PHRASE_ENABLED (how_is_rebellion))
+ Response (how_is_rebellion, Royalists);
+ if (!GET_GAME_STATE (YEHAT_ROYALIST_TOLD_PKUNK)
+ && GET_GAME_STATE (PKUNK_VISITS)
+ && GET_GAME_STATE (PKUNK_HOME_VISITS))
+ Response (what_about_pkunk_royalist, Royalists);
+ if (!GET_GAME_STATE (NO_YEHAT_INFO))
+ Response (sorry_about_revolution, Royalists);
+ Response (bye_royalist, ExitConversation);
+}
+
+static void
+StartRevolt (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, shofixti_alive_1))
+ {
+ NPCPhrase (SEND_HIM_OVER_1);
+
+ SET_GAME_STATE (YEHAT_REBEL_TOLD_PKUNK, 1);
+ }
+ else if (PLAYER_SAID (R, shofixti_alive_2))
+ NPCPhrase (SEND_HIM_OVER_2);
+
+ if (HaveEscortShip (SHOFIXTI_SHIP))
+ Response (ok_send, ExitConversation);
+ else
+ Response (not_here, ExitConversation);
+ Response (not_send, ExitConversation);
+}
+
+static void
+YehatHome (RESPONSE_REF R)
+{
+
+ if (PLAYER_SAID (R, whats_up_homeworld))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (YEHAT_ROYALIST_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_HOMEWORLD_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_HOMEWORLD_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_ROYALIST_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_homeworld);
+ }
+ else if (PLAYER_SAID (R, at_least_help_us_homeworld))
+ {
+ NPCPhrase (NO_HELP_ENEMY);
+
+ SET_GAME_STATE (NO_YEHAT_HELP_HOME, 1);
+ }
+ else if (PLAYER_SAID (R, give_info))
+ {
+ NPCPhrase (NO_INFO_FOR_ENEMY);
+
+ SET_GAME_STATE (NO_YEHAT_INFO, 1);
+ }
+ else if (PLAYER_SAID (R, what_about_pkunk_royalist))
+ {
+ if (GET_GAME_STATE (YEHAT_ABSORBED_PKUNK))
+ NPCPhrase (PKUNK_ABSORBED_ROYALIST);
+ else
+ NPCPhrase (HATE_PKUNK_ROYALIST);
+
+ SET_GAME_STATE (YEHAT_ROYALIST_TOLD_PKUNK, 1);
+ }
+
+ if (PHRASE_ENABLED (whats_up_homeworld))
+ Response (whats_up_homeworld, YehatHome);
+ if (!GET_GAME_STATE (YEHAT_ROYALIST_TOLD_PKUNK)
+ && GET_GAME_STATE (PKUNK_VISITS)
+ && GET_GAME_STATE (PKUNK_HOME_VISITS))
+ Response (what_about_pkunk_royalist, YehatHome);
+ if (!GET_GAME_STATE (NO_YEHAT_HELP_HOME))
+ Response (at_least_help_us_homeworld, YehatHome);
+ if (!GET_GAME_STATE (NO_YEHAT_INFO))
+ Response (give_info, YehatHome);
+ if (!GET_GAME_STATE (NO_YEHAT_ALLY_HOME))
+ {
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ i_demand_you_ally_homeworld0,
+ GLOBAL_SIS (CommanderName),
+ i_demand_you_ally_homeworld1,
+ buf,
+ i_demand_you_ally_homeworld2,
+ GLOBAL_SIS (ShipName),
+ i_demand_you_ally_homeworld3,
+ (UNICODE*)NULL);
+ DoResponsePhrase (i_demand_you_ally_homeworld0,
+ ExitConversation, shared_phrase_buf);
+ }
+ Response (bye_homeworld, ExitConversation);
+}
+
+static void
+YehatSpace (RESPONSE_REF R)
+{
+ BYTE i, LastStack;
+ RESPONSE_REF pStr[3];
+
+ LastStack = 0;
+ pStr[0] = pStr[1] = pStr[2] = 0;
+ if (PLAYER_SAID (R, whats_up_space_1)
+ || PLAYER_SAID (R, whats_up_space_2)
+ || PLAYER_SAID (R, whats_up_space_3)
+ || PLAYER_SAID (R, whats_up_space_4))
+ {
+ BYTE NumVisits;
+
+ NumVisits = GET_GAME_STATE (YEHAT_REBEL_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (GENERAL_INFO_SPACE_1);
+ break;
+ case 1:
+ NPCPhrase (GENERAL_INFO_SPACE_2);
+ break;
+ case 2:
+ NPCPhrase (GENERAL_INFO_SPACE_3);
+ break;
+ case 3:
+ NPCPhrase (GENERAL_INFO_SPACE_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_REBEL_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_space_1);
+ }
+ else if (PLAYER_SAID (R, i_demand_you_ally_space0))
+ {
+ NPCPhrase (WE_CANNOT_1);
+
+ LastStack = 2;
+ SET_GAME_STATE (NO_YEHAT_ALLY_SPACE, 1);
+ }
+ else if (PLAYER_SAID (R, obligation))
+ {
+ NPCPhrase (WE_CANNOT_2);
+
+ setSegue (Segue_peace);
+ SET_GAME_STATE (NO_YEHAT_ALLY_SPACE, 2);
+
+ return;
+ }
+ else if (PLAYER_SAID (R, at_least_help_us_space))
+ {
+ NPCPhrase (SORRY_CANNOT);
+
+ LastStack = 1;
+ SET_GAME_STATE (NO_YEHAT_HELP_SPACE, 1);
+ }
+ else if (PLAYER_SAID (R, dishonor))
+ {
+ NPCPhrase (HERES_A_HINT);
+
+ SET_GAME_STATE (NO_YEHAT_HELP_SPACE, 2);
+ }
+ else if (PLAYER_SAID (R, what_about_pkunk_royalist))
+ {
+ if (GET_GAME_STATE (YEHAT_ABSORBED_PKUNK))
+ NPCPhrase (PKUNK_ABSORBED_ROYALIST);
+ else
+ NPCPhrase (HATE_PKUNK_ROYALIST);
+
+ SET_GAME_STATE (YEHAT_ROYALIST_TOLD_PKUNK, 1);
+ }
+
+// SET_FUNCPTR (&PtrDesc, YehatSpace);
+ if (PHRASE_ENABLED (whats_up_space_1))
+ {
+ switch (GET_GAME_STATE (YEHAT_REBEL_INFO))
+ {
+ case 0:
+ pStr[0] = whats_up_space_1;
+ break;
+ case 1:
+ pStr[0] = whats_up_space_2;
+ break;
+ case 2:
+ pStr[0] = whats_up_space_3;
+ break;
+ case 3:
+ pStr[0] = whats_up_space_4;
+ break;
+ }
+ }
+ switch (GET_GAME_STATE (NO_YEHAT_HELP_SPACE))
+ {
+ case 0:
+ pStr[1] = at_least_help_us_space;
+ break;
+ case 1:
+ pStr[1] = dishonor;
+ break;
+ }
+ switch (GET_GAME_STATE (NO_YEHAT_ALLY_SPACE))
+ {
+ case 0:
+ {
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ i_demand_you_ally_space0,
+ GLOBAL_SIS (CommanderName),
+ i_demand_you_ally_space1,
+ GLOBAL_SIS (ShipName),
+ i_demand_you_ally_space2,
+ buf,
+ i_demand_you_ally_space3,
+ (UNICODE*)NULL);
+ pStr[2] = i_demand_you_ally_space0;
+ break;
+ }
+ case 1:
+ pStr[2] = obligation;
+ break;
+ }
+
+ if (pStr[LastStack])
+ {
+ if (pStr[LastStack] != i_demand_you_ally_space0)
+ Response (pStr[LastStack], YehatSpace);
+ else
+ DoResponsePhrase (pStr[LastStack], YehatSpace, shared_phrase_buf);
+ }
+ for (i = 0; i < 3; ++i)
+ {
+ if (i != LastStack && pStr[i])
+ {
+ if (pStr[i] != i_demand_you_ally_space0)
+ Response (pStr[i], YehatSpace);
+ else
+ DoResponsePhrase (pStr[i], YehatSpace, shared_phrase_buf);
+ }
+ }
+ if (!GET_GAME_STATE (YEHAT_ROYALIST_TOLD_PKUNK)
+ && GET_GAME_STATE (PKUNK_VISITS)
+ && GET_GAME_STATE (PKUNK_HOME_VISITS))
+ Response (what_about_pkunk_royalist, YehatSpace);
+ if (GET_GAME_STATE (SHOFIXTI_VISITS))
+ {
+ switch (GET_GAME_STATE (YEHAT_REBEL_TOLD_PKUNK))
+ {
+ case 0:
+ Response (shofixti_alive_1, StartRevolt);
+ break;
+ case 1:
+ Response (shofixti_alive_2, StartRevolt);
+ break;
+ }
+ }
+ Response (bye_space, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase (OUT_TAKES);
+
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (YEHAT_CIVIL_WAR))
+ {
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (YEHAT_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ROYALIST_HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ROYALIST_HOMEWORLD_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_HOME_VISITS, NumVisits);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (YEHAT_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (ROYALIST_SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (ROYALIST_SPACE_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_VISITS, NumVisits);
+ }
+
+ Royalists ((RESPONSE_REF)0);
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NumVisits = GET_GAME_STATE (YEHAT_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (HOMEWORLD_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (HOMEWORLD_HELLO_2);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_HOME_VISITS, NumVisits);
+
+ YehatHome ((RESPONSE_REF)0);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (YEHAT_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase (SPACE_HELLO_1);
+ break;
+ case 1:
+ NPCPhrase (SPACE_HELLO_2);
+ break;
+ case 2:
+ NPCPhrase (SPACE_HELLO_3);
+ break;
+ case 3:
+ NPCPhrase (SPACE_HELLO_4);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (YEHAT_VISITS, NumVisits);
+
+ YehatSpace ((RESPONSE_REF)0);
+ }
+}
+
+static COUNT
+uninit_yehat (void)
+{
+ return (0);
+}
+
+static void
+post_yehat_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_yehat_comm (void)
+{
+ LOCDATA *retval;
+
+ yehat_desc.init_encounter_func = Intro;
+ yehat_desc.post_encounter_func = post_yehat_enc;
+ yehat_desc.uninit_encounter_func = uninit_yehat;
+
+ yehat_desc.AlienTextBaseline.x = SIS_SCREEN_WIDTH * 2 / 3;
+ yehat_desc.AlienTextBaseline.y = 60;
+ yehat_desc.AlienTextWidth = (SIS_TEXT_WIDTH - 16) * 2 / 3;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != WON_LAST_BATTLE)
+ {
+ setSegue (Segue_hostile);
+ }
+ else
+ {
+ setSegue (Segue_peace);
+ }
+ retval = &yehat_desc;
+
+ return (retval);
+}
diff --git a/src/uqm/comm/zoqfot/Makeinfo b/src/uqm/comm/zoqfot/Makeinfo
new file mode 100644
index 0000000..6e1a268
--- /dev/null
+++ b/src/uqm/comm/zoqfot/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="zoqfotc.c"
+uqm_HFILES="resinst.h strings.h"
diff --git a/src/uqm/comm/zoqfot/resinst.h b/src/uqm/comm/zoqfot/resinst.h
new file mode 100644
index 0000000..a9430d9
--- /dev/null
+++ b/src/uqm/comm/zoqfot/resinst.h
@@ -0,0 +1,9 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ZOQFOTPIK_COLOR_MAP "comm.zoqfotpik.colortable"
+#define ZOQFOTPIK_CONVERSATION_PHRASES "comm.zoqfotpik.dialogue"
+#define ZOQFOTPIK_FONT "comm.zoqfotpik.font"
+#define ZOQFOTPIK_MUSIC "comm.zoqfotpik.music"
+#define ZOQFOTPIK_PMAP_ANIM "comm.zoqfotpik.graphics"
diff --git a/src/uqm/comm/zoqfot/strings.h b/src/uqm/comm/zoqfot/strings.h
new file mode 100644
index 0000000..3278082
--- /dev/null
+++ b/src/uqm/comm/zoqfot/strings.h
@@ -0,0 +1,365 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ZOQFOT_STRINGS_H
+#define ZOQFOT_STRINGS_H
+
+enum
+{
+ NULL_PHRASE,
+ WE_ARE0,
+ WE_ARE1,
+ WE_ARE2,
+ WE_ARE3,
+ WE_ARE4,
+ WE_ARE5,
+ WE_ARE6,
+ WE_ARE7,
+ SCOUT_HELLO0,
+ SCOUT_HELLO1,
+ SCOUT_HELLO2,
+ SCOUT_HELLO3,
+ INIT_HOME_HELLO0,
+ INIT_HOME_HELLO1,
+ INIT_HOME_HELLO2,
+ INIT_HOME_HELLO3,
+ which_fot,
+ HE_IS0,
+ HE_IS1,
+ HE_IS2,
+ HE_IS3,
+ HE_IS4,
+ HE_IS5,
+ HE_IS6,
+ HE_IS7,
+ we_are_vindicator0,
+ we_are_vindicator1,
+ we_are_vindicator2,
+ WE_GLAD0,
+ WE_GLAD1,
+ WE_GLAD2,
+ WE_GLAD3,
+ WE_GLAD4,
+ WE_GLAD5,
+ quiet_toadies,
+ TOLD_YOU0,
+ TOLD_YOU1,
+ TOLD_YOU2,
+ TOLD_YOU3,
+ TOLD_YOU4,
+ TOLD_YOU5,
+ TOLD_YOU6,
+ TOLD_YOU7,
+ your_race,
+ YEARS_AGO0,
+ YEARS_AGO1,
+ YEARS_AGO2,
+ YEARS_AGO3,
+ YEARS_AGO4,
+ YEARS_AGO5,
+ YEARS_AGO6,
+ YEARS_AGO7,
+ YEARS_AGO8,
+ YEARS_AGO9,
+ YEARS_AGO10,
+ YEARS_AGO11,
+ YEARS_AGO12,
+ YEARS_AGO13,
+ where_from,
+ TRAVELED_FAR0,
+ TRAVELED_FAR1,
+ TRAVELED_FAR2,
+ TRAVELED_FAR3,
+ TRAVELED_FAR4,
+ TRAVELED_FAR5,
+ what_emergency,
+ UNDER_ATTACK0,
+ UNDER_ATTACK1,
+ UNDER_ATTACK2,
+ UNDER_ATTACK3,
+ UNDER_ATTACK4,
+ UNDER_ATTACK5,
+ UNDER_ATTACK6,
+ UNDER_ATTACK7,
+ UNDER_ATTACK8,
+ UNDER_ATTACK9,
+ UNDER_ATTACK10,
+ UNDER_ATTACK11,
+ tough_luck,
+ NOT_HELPFUL0,
+ NOT_HELPFUL1,
+ NOT_HELPFUL2,
+ NOT_HELPFUL3,
+ NOT_HELPFUL4,
+ NOT_HELPFUL5,
+ what_look_like,
+ LOOK_LIKE0,
+ LOOK_LIKE1,
+ LOOK_LIKE2,
+ LOOK_LIKE3,
+ valuable_info,
+ GOODBYE0,
+ GOODBYE1,
+ GOODBYE2,
+ GOODBYE3,
+ all_very_interesting,
+ SEE_TOLD_YOU0,
+ SEE_TOLD_YOU1,
+ SEE_TOLD_YOU2,
+ SEE_TOLD_YOU3,
+ how_can_i_help,
+ ALLY_WITH_US0,
+ ALLY_WITH_US1,
+ ALLY_WITH_US2,
+ ALLY_WITH_US3,
+ ALLY_WITH_US4,
+ ALLY_WITH_US5,
+ decide_later,
+ PLEASE_HURRY0,
+ PLEASE_HURRY1,
+ EMMISSARIES0,
+ EMMISSARIES1,
+ EMMISSARIES2,
+ EMMISSARIES3,
+ EMMISSARIES4,
+ EMMISSARIES5,
+ EMMISSARIES6,
+ EMMISSARIES7,
+ sure,
+ WE_ALLY0,
+ WE_ALLY1,
+ WE_ALLY2,
+ WE_ALLY3,
+ WE_ALLY4,
+ WE_ALLY5,
+ never,
+ WE_ENEMIES0,
+ WE_ENEMIES1,
+ HOSTILE_HELLO_10,
+ HOSTILE_HELLO_11,
+ HOSTILE_HELLO_20,
+ HOSTILE_HELLO_21,
+ HOSTILE_HELLO_22,
+ HOSTILE_HELLO_23,
+ HOSTILE_HELLO_24,
+ HOSTILE_HELLO_25,
+ HOSTILE_HELLO_30,
+ HOSTILE_HELLO_31,
+ HOSTILE_HELLO_40,
+ HOSTILE_HELLO_41,
+ NEUTRAL_HOME_HELLO_10,
+ NEUTRAL_HOME_HELLO_11,
+ NEUTRAL_HOME_HELLO_12,
+ NEUTRAL_HOME_HELLO_13,
+ NEUTRAL_HOME_HELLO_20,
+ NEUTRAL_HOME_HELLO_21,
+ NEUTRAL_HOME_HELLO_22,
+ NEUTRAL_HOME_HELLO_23,
+ ALLIED_HOME_HELLO_10,
+ ALLIED_HOME_HELLO_11,
+ ALLIED_HOME_HELLO_12,
+ ALLIED_HOME_HELLO_13,
+ ALLIED_HOME_HELLO_20,
+ ALLIED_HOME_HELLO_21,
+ ALLIED_HOME_HELLO_22,
+ ALLIED_HOME_HELLO_23,
+ ALLIED_HOME_HELLO_24,
+ ALLIED_HOME_HELLO_25,
+ ALLIED_HOME_HELLO_26,
+ ALLIED_HOME_HELLO_27,
+ ALLIED_HOME_HELLO_30,
+ ALLIED_HOME_HELLO_31,
+ ALLIED_HOME_HELLO_40,
+ ALLIED_HOME_HELLO_41,
+ THANKS_FOR_RESCUE0,
+ THANKS_FOR_RESCUE1,
+ THANKS_FOR_RESCUE2,
+ THANKS_FOR_RESCUE3,
+ THANKS_FOR_RESCUE4,
+ THANKS_FOR_RESCUE5,
+ THANKS_FOR_RESCUE6,
+ THANKS_FOR_RESCUE7,
+ THANKS_FOR_RESCUE8,
+ THANKS_FOR_RESCUE9,
+ THANKS_FOR_RESCUE10,
+ THANKS_FOR_RESCUE11,
+ bye_homeworld,
+ GOODBYE_HOME0,
+ GOODBYE_HOME1,
+ whats_up_homeworld,
+ GENERAL_INFO_10,
+ GENERAL_INFO_11,
+ GENERAL_INFO_12,
+ GENERAL_INFO_13,
+ GENERAL_INFO_20,
+ GENERAL_INFO_21,
+ GENERAL_INFO_22,
+ GENERAL_INFO_23,
+ GENERAL_INFO_24,
+ GENERAL_INFO_25,
+ GENERAL_INFO_26,
+ GENERAL_INFO_27,
+ GENERAL_INFO_30,
+ GENERAL_INFO_31,
+ GENERAL_INFO_32,
+ GENERAL_INFO_33,
+ GENERAL_INFO_34,
+ GENERAL_INFO_35,
+ GENERAL_INFO_40,
+ GENERAL_INFO_41,
+ GENERAL_INFO_42,
+ GENERAL_INFO_43,
+ GENERAL_INFO_44,
+ GENERAL_INFO_45,
+ GENERAL_INFO_46,
+ GENERAL_INFO_47,
+ GENERAL_INFO_48,
+ GENERAL_INFO_49,
+ GENERAL_INFO_410,
+ GENERAL_INFO_411,
+ any_war_news,
+ UTWIG_DELAY0,
+ UTWIG_DELAY1,
+ UTWIG_DELAY2,
+ UTWIG_DELAY3,
+ UTWIG_DELAY4,
+ UTWIG_DELAY5,
+ UTWIG_DELAY6,
+ UTWIG_DELAY7,
+ UTWIG_DELAY8,
+ UTWIG_DELAY9,
+ UTWIG_DELAY10,
+ UTWIG_DELAY11,
+ UTWIG_DELAY12,
+ UTWIG_DELAY13,
+ KOHRAH_WINNING0,
+ KOHRAH_WINNING1,
+ KOHRAH_WINNING2,
+ KOHRAH_WINNING3,
+ KOHRAH_WINNING4,
+ KOHRAH_WINNING5,
+ KOHRAH_WINNING6,
+ KOHRAH_WINNING7,
+ KOHRAH_WINNING8,
+ KOHRAH_WINNING9,
+ URQUAN_NEARLY_GONE0,
+ URQUAN_NEARLY_GONE1,
+ URQUAN_NEARLY_GONE2,
+ URQUAN_NEARLY_GONE3,
+ URQUAN_NEARLY_GONE4,
+ URQUAN_NEARLY_GONE5,
+ KOHRAH_FRENZY0,
+ KOHRAH_FRENZY1,
+ KOHRAH_FRENZY2,
+ KOHRAH_FRENZY3,
+ KOHRAH_FRENZY4,
+ KOHRAH_FRENZY5,
+ KOHRAH_FRENZY6,
+ KOHRAH_FRENZY7,
+ KOHRAH_FRENZY8,
+ KOHRAH_FRENZY9,
+ KOHRAH_FRENZY10,
+ KOHRAH_FRENZY11,
+ NO_WAR_NEWS0,
+ NO_WAR_NEWS1,
+ i_want_alliance,
+ GOOD0,
+ GOOD1,
+ GOOD2,
+ GOOD3,
+ GOOD4,
+ GOOD5,
+ GOOD6,
+ GOOD7,
+ GOOD8,
+ GOOD9,
+ want_specific_info,
+ WHAT_SPECIFIC_INFO0,
+ WHAT_SPECIFIC_INFO1,
+ enough_info,
+ OK_ENOUGH_INFO,
+ what_about_others,
+ ABOUT_OTHERS0,
+ ABOUT_OTHERS1,
+ ABOUT_OTHERS2,
+ ABOUT_OTHERS3,
+ ABOUT_OTHERS4,
+ ABOUT_OTHERS5,
+ ABOUT_OTHERS6,
+ ABOUT_OTHERS7,
+ ABOUT_OTHERS8,
+ ABOUT_OTHERS9,
+ ABOUT_OTHERS10,
+ ABOUT_OTHERS11,
+ ABOUT_OTHERS12,
+ ABOUT_OTHERS13,
+ what_about_zebranky,
+ ABOUT_ZEBRANKY0,
+ ABOUT_ZEBRANKY1,
+ ABOUT_ZEBRANKY2,
+ ABOUT_ZEBRANKY3,
+ ABOUT_ZEBRANKY4,
+ ABOUT_ZEBRANKY5,
+ ABOUT_ZEBRANKY6,
+ ABOUT_ZEBRANKY7,
+ what_about_past,
+ ABOUT_PAST0,
+ ABOUT_PAST1,
+ ABOUT_PAST2,
+ ABOUT_PAST3,
+ ABOUT_PAST4,
+ ABOUT_PAST5,
+ ABOUT_PAST6,
+ ABOUT_PAST7,
+ ABOUT_PAST8,
+ ABOUT_PAST9,
+ ABOUT_PAST10,
+ ABOUT_PAST11,
+ what_about_stinger,
+ ABOUT_STINGER0,
+ ABOUT_STINGER1,
+ ABOUT_STINGER2,
+ ABOUT_STINGER3,
+ ABOUT_STINGER4,
+ ABOUT_STINGER5,
+ what_about_guy_in_back,
+ ABOUT_GUY0,
+ ABOUT_GUY1,
+ name_1,
+ name_2,
+ name_3,
+ name_40,
+ name_41,
+ OUT_TAKES0,
+ OUT_TAKES1,
+ OUT_TAKES2,
+ OUT_TAKES3,
+ OUT_TAKES4,
+ OUT_TAKES5,
+ OUT_TAKES6,
+ OUT_TAKES7,
+ OUT_TAKES8,
+ OUT_TAKES9,
+ OUT_TAKES10,
+ OUT_TAKES11,
+ OUT_TAKES12,
+ OUT_TAKES13,
+};
+
+#endif /* _STRINGS_H */
+
diff --git a/src/uqm/comm/zoqfot/zoqfotc.c b/src/uqm/comm/zoqfot/zoqfotc.c
new file mode 100644
index 0000000..d09a7f7
--- /dev/null
+++ b/src/uqm/comm/zoqfot/zoqfotc.c
@@ -0,0 +1,975 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../commall.h"
+#include "resinst.h"
+#include "strings.h"
+
+#include "uqm/build.h"
+#include "uqm/gameev.h"
+
+
+#define ZOQ_FG_COLOR WHITE_COLOR
+#define ZOQ_BG_COLOR BLACK_COLOR
+#define ZOQ_BASE_X (TEXT_X_OFFS + ((SIS_TEXT_WIDTH >> 1) >> 1))
+#define ZOQ_BASE_Y 24
+#define ZOQ_TALK_INDEX 18
+#define ZOQ_TALK_FRAMES 5
+#define FOT_TO_ZOQ 23
+
+#define PIK_FG_COLOR WHITE_COLOR
+#define PIK_BG_COLOR BLACK_COLOR
+#define PIK_BASE_X (SIS_SCREEN_WIDTH - (TEXT_X_OFFS + ((SIS_TEXT_WIDTH >> 1) >> 1)))
+#define PIK_BASE_Y 24
+#define PIK_TALK_INDEX 29
+#define PIK_TALK_FRAMES 2
+#define FOT_TO_PIK 26
+
+static LOCDATA zoqfot_desc =
+{
+ NULL, /* init_encounter_func */
+ NULL, /* post_encounter_func */
+ NULL, /* uninit_encounter_func */
+ ZOQFOTPIK_PMAP_ANIM, /* AlienFrame */
+ ZOQFOTPIK_FONT, /* AlienFont */
+ UNDEFINED_COLOR_INIT, /* AlienTextFColor */
+ UNDEFINED_COLOR_INIT, /* AlienTextBColor */
+ {0, 0}, /* AlienTextBaseline */
+ 0, /* AlienTextWidth */
+ ALIGN_CENTER, /* AlienTextAlign */
+ VALIGN_MIDDLE, /* AlienTextValign */
+ ZOQFOTPIK_COLOR_MAP, /* AlienColorMap */
+ ZOQFOTPIK_MUSIC, /* AlienSong */
+ NULL_RESOURCE, /* AlienAltSong */
+ 0, /* AlienSongFlags */
+ ZOQFOTPIK_CONVERSATION_PHRASES, /* PlayerPhrases */
+ 3, /* NumAnimations */
+ { /* AlienAmbientArray (ambient animations) */
+ { /* Eye blink */
+ 1, /* StartIndex */
+ 4, /* NumFrames */
+ YOYO_ANIM /* AnimFlags */
+ | WAIT_TALKING,
+ ONE_SECOND / 24, 0, /* FrameRate */
+ 0, ONE_SECOND * 10, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Blow smoke */
+ 5, /* StartIndex */
+ 5, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND * 7 / 120, 0, /* FrameRate */
+ ONE_SECOND * 2, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* Gulp */
+ 10, /* StartIndex */
+ 8, /* NumFrames */
+ CIRCULAR_ANIM, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ 0, ONE_SECOND * 10, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ },
+ { /* AlienTransitionDesc - Move Eye */
+ FOT_TO_ZOQ, /* StartIndex */
+ 3, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 30, 0, /* FrameRate */
+ 0, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ { /* AlienTalkDesc */
+ ZOQ_TALK_INDEX, /* StartIndex */
+ ZOQ_TALK_FRAMES, /* NumFrames */
+ 0, /* AnimFlags */
+ ONE_SECOND / 15, 0, /* FrameRate */
+ ONE_SECOND / 12, 0, /* RestartRate */
+ 0, /* BlockMask */
+ },
+ NULL, /* AlienNumberSpeech - none */
+ /* Filler for loaded resources */
+ NULL, NULL, NULL,
+ NULL,
+ NULL,
+};
+
+enum
+{
+ ZOQ_ALIEN,
+ FOT_ALIEN,
+ PIK_ALIEN
+};
+
+static int LastAlien;
+
+// Queued and executes synchronously on the Starcon2Main thread
+static void
+SelectAlienZOQ (CallbackArg arg)
+{
+ if (LastAlien != ZOQ_ALIEN)
+ {
+ // Transition to neutral state first if Pik was talking
+ if (LastAlien != FOT_ALIEN)
+ CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
+ LastAlien = ZOQ_ALIEN;
+ CommData.AlienTransitionDesc.AnimFlags |= TALK_INTRO;
+ CommData.AlienTransitionDesc.StartIndex = FOT_TO_ZOQ;
+ CommData.AlienTalkDesc.StartIndex = ZOQ_TALK_INDEX;
+ CommData.AlienTalkDesc.NumFrames = ZOQ_TALK_FRAMES;
+ CommData.AlienAmbientArray[1].AnimFlags &= ~WAIT_TALKING;
+
+ CommData.AlienTextBaseline.x = (SWORD)ZOQ_BASE_X;
+ CommData.AlienTextBaseline.y = ZOQ_BASE_Y;
+ CommData.AlienTextFColor = ZOQ_FG_COLOR;
+ CommData.AlienTextBColor = ZOQ_BG_COLOR;
+ }
+
+ (void)arg; // ignored
+}
+
+// Queued and executes synchronously on the Starcon2Main thread
+static void
+SelectAlienPIK (CallbackArg arg)
+{
+ if (LastAlien != PIK_ALIEN)
+ {
+ // Transition to neutral state first if Zoq was talking
+ if (LastAlien != FOT_ALIEN)
+ CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
+ LastAlien = PIK_ALIEN;
+ CommData.AlienTransitionDesc.AnimFlags |= TALK_INTRO;
+ CommData.AlienTransitionDesc.StartIndex = FOT_TO_PIK;
+ CommData.AlienTalkDesc.StartIndex = PIK_TALK_INDEX;
+ CommData.AlienTalkDesc.NumFrames = PIK_TALK_FRAMES;
+ CommData.AlienAmbientArray[1].AnimFlags |= WAIT_TALKING;
+
+ CommData.AlienTextBaseline.x = (SWORD)PIK_BASE_X;
+ CommData.AlienTextBaseline.y = PIK_BASE_Y;
+ CommData.AlienTextFColor = PIK_FG_COLOR;
+ CommData.AlienTextBColor = PIK_BG_COLOR;
+ }
+
+ (void)arg; // ignored
+}
+
+static void
+ZFPTalkSegue (COUNT wait_track)
+{
+ LastAlien = FOT_ALIEN;
+ SelectAlienZOQ (0);
+ AlienTalkSegue (wait_track);
+}
+
+static void
+ExitConversation (RESPONSE_REF R)
+{
+ setSegue (Segue_peace);
+
+ if (PLAYER_SAID (R, bye_homeworld))
+ {
+ NPCPhrase_cb (GOODBYE_HOME0, &SelectAlienZOQ);
+ NPCPhrase_cb (GOODBYE_HOME1, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ }
+ else if (PLAYER_SAID (R, decide_later))
+ {
+ NPCPhrase_cb (PLEASE_HURRY0, &SelectAlienZOQ);
+ NPCPhrase_cb (PLEASE_HURRY1, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ }
+ else if (PLAYER_SAID (R, valuable_info))
+ {
+ NPCPhrase_cb (GOODBYE0, &SelectAlienZOQ);
+ NPCPhrase_cb (GOODBYE1, &SelectAlienPIK);
+ NPCPhrase_cb (GOODBYE2, &SelectAlienZOQ);
+ NPCPhrase_cb (GOODBYE3, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ }
+ else if (PLAYER_SAID (R, how_can_i_help))
+ {
+ NPCPhrase_cb (EMMISSARIES0, &SelectAlienZOQ);
+ NPCPhrase_cb (EMMISSARIES1, &SelectAlienPIK);
+ NPCPhrase_cb (EMMISSARIES2, &SelectAlienZOQ);
+ NPCPhrase_cb (EMMISSARIES3, &SelectAlienPIK);
+ NPCPhrase_cb (EMMISSARIES4, &SelectAlienZOQ);
+ NPCPhrase_cb (EMMISSARIES5, &SelectAlienPIK);
+ NPCPhrase_cb (EMMISSARIES6, &SelectAlienZOQ);
+ NPCPhrase_cb (EMMISSARIES7, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ }
+ else if (PLAYER_SAID (R, sure))
+ {
+ NPCPhrase_cb (WE_ALLY0, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ALLY1, &SelectAlienPIK);
+ NPCPhrase_cb (WE_ALLY2, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ALLY3, &SelectAlienPIK);
+ NPCPhrase_cb (WE_ALLY4, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ALLY5, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ SetRaceAllied (ZOQFOTPIK_SHIP, TRUE);
+ AddEvent (RELATIVE_EVENT, 3, 0, 0, ZOQFOT_DISTRESS_EVENT);
+ SET_GAME_STATE (ZOQFOT_HOME_VISITS, 0);
+ }
+ else if (PLAYER_SAID (R, all_very_interesting))
+ {
+ NPCPhrase_cb (SEE_TOLD_YOU0, &SelectAlienZOQ);
+ NPCPhrase_cb (SEE_TOLD_YOU1, &SelectAlienPIK);
+ NPCPhrase_cb (SEE_TOLD_YOU2, &SelectAlienZOQ);
+ NPCPhrase_cb (SEE_TOLD_YOU3, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ SET_GAME_STATE (ZOQFOT_HOSTILE, 1);
+ SET_GAME_STATE (ZOQFOT_HOME_VISITS, 0);
+ setSegue (Segue_hostile);
+ }
+ else if (PLAYER_SAID (R, never))
+ {
+ NPCPhrase_cb (WE_ENEMIES0, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ENEMIES1, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ SET_GAME_STATE (ZOQFOT_HOME_VISITS, 0);
+ SET_GAME_STATE (ZOQFOT_HOSTILE, 1);
+ setSegue (Segue_hostile);
+ }
+}
+
+static void
+FormAlliance (RESPONSE_REF R)
+{
+ (void) R; // ignored
+ NPCPhrase_cb (ALLY_WITH_US0, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLY_WITH_US1, &SelectAlienPIK);
+ NPCPhrase_cb (ALLY_WITH_US2, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLY_WITH_US3, &SelectAlienPIK);
+ NPCPhrase_cb (ALLY_WITH_US4, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLY_WITH_US5, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ Response (sure, ExitConversation);
+ Response (never, ExitConversation);
+ Response (decide_later, ExitConversation);
+}
+
+static void
+ZoqFotIntro (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, we_are_vindicator0))
+ {
+ NPCPhrase_cb (WE_GLAD0, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_GLAD1, &SelectAlienPIK);
+ NPCPhrase_cb (WE_GLAD2, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_GLAD3, &SelectAlienPIK);
+ NPCPhrase_cb (WE_GLAD4, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_GLAD5, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (we_are_vindicator0);
+ }
+ else if (PLAYER_SAID (R, your_race))
+ {
+ NPCPhrase_cb (YEARS_AGO0, &SelectAlienZOQ);
+ NPCPhrase_cb (YEARS_AGO1, &SelectAlienPIK);
+ NPCPhrase_cb (YEARS_AGO2, &SelectAlienZOQ);
+ NPCPhrase_cb (YEARS_AGO3, &SelectAlienPIK);
+ NPCPhrase_cb (YEARS_AGO4, &SelectAlienZOQ);
+ NPCPhrase_cb (YEARS_AGO5, &SelectAlienPIK);
+ NPCPhrase_cb (YEARS_AGO6, &SelectAlienZOQ);
+ NPCPhrase_cb (YEARS_AGO7, &SelectAlienPIK);
+ NPCPhrase_cb (YEARS_AGO8, &SelectAlienZOQ);
+ NPCPhrase_cb (YEARS_AGO9, &SelectAlienPIK);
+ NPCPhrase_cb (YEARS_AGO10, &SelectAlienZOQ);
+ NPCPhrase_cb (YEARS_AGO11, &SelectAlienPIK);
+ NPCPhrase_cb (YEARS_AGO12, &SelectAlienZOQ);
+ NPCPhrase_cb (YEARS_AGO13, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (your_race);
+ }
+ else if (PLAYER_SAID (R, where_from))
+ {
+ NPCPhrase_cb (TRAVELED_FAR0, &SelectAlienZOQ);
+ NPCPhrase_cb (TRAVELED_FAR1, &SelectAlienPIK);
+ NPCPhrase_cb (TRAVELED_FAR2, &SelectAlienZOQ);
+ NPCPhrase_cb (TRAVELED_FAR3, &SelectAlienPIK);
+ NPCPhrase_cb (TRAVELED_FAR4, &SelectAlienZOQ);
+ NPCPhrase_cb (TRAVELED_FAR5, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (where_from);
+ }
+ else if (PLAYER_SAID (R, what_emergency))
+ {
+ NPCPhrase_cb (UNDER_ATTACK0, &SelectAlienZOQ);
+ NPCPhrase_cb (UNDER_ATTACK1, &SelectAlienPIK);
+ NPCPhrase_cb (UNDER_ATTACK2, &SelectAlienZOQ);
+ NPCPhrase_cb (UNDER_ATTACK3, &SelectAlienPIK);
+ NPCPhrase_cb (UNDER_ATTACK4, &SelectAlienZOQ);
+ NPCPhrase_cb (UNDER_ATTACK5, &SelectAlienPIK);
+ NPCPhrase_cb (UNDER_ATTACK6, &SelectAlienZOQ);
+ NPCPhrase_cb (UNDER_ATTACK7, &SelectAlienPIK);
+ NPCPhrase_cb (UNDER_ATTACK8, &SelectAlienZOQ);
+ NPCPhrase_cb (UNDER_ATTACK9, &SelectAlienPIK);
+ NPCPhrase_cb (UNDER_ATTACK10, &SelectAlienZOQ);
+ NPCPhrase_cb (UNDER_ATTACK11, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (what_emergency);
+ }
+ else if (PLAYER_SAID (R, tough_luck))
+ {
+ NPCPhrase_cb (NOT_HELPFUL0, &SelectAlienZOQ);
+ NPCPhrase_cb (NOT_HELPFUL1, &SelectAlienPIK);
+ NPCPhrase_cb (NOT_HELPFUL2, &SelectAlienZOQ);
+ NPCPhrase_cb (NOT_HELPFUL3, &SelectAlienPIK);
+ NPCPhrase_cb (NOT_HELPFUL4, &SelectAlienZOQ);
+ NPCPhrase_cb (NOT_HELPFUL5, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (tough_luck);
+ }
+ else if (PLAYER_SAID (R, what_look_like))
+ {
+ NPCPhrase_cb (LOOK_LIKE0, &SelectAlienZOQ);
+ NPCPhrase_cb (LOOK_LIKE1, &SelectAlienPIK);
+ NPCPhrase_cb (LOOK_LIKE2, &SelectAlienZOQ);
+ NPCPhrase_cb (LOOK_LIKE3, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (what_look_like);
+ }
+
+ if (PHRASE_ENABLED (your_race)
+ || PHRASE_ENABLED (where_from)
+ || PHRASE_ENABLED (what_emergency))
+ {
+ if (PHRASE_ENABLED (your_race))
+ Response (your_race, ZoqFotIntro);
+ if (PHRASE_ENABLED (where_from))
+ Response (where_from, ZoqFotIntro);
+ if (PHRASE_ENABLED (what_emergency))
+ Response (what_emergency, ZoqFotIntro);
+ }
+ else
+ {
+ if (PHRASE_ENABLED (tough_luck))
+ Response (tough_luck, ZoqFotIntro);
+ if (PHRASE_ENABLED (what_look_like))
+ Response (what_look_like, ZoqFotIntro);
+ Response (all_very_interesting, ExitConversation);
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ Response (how_can_i_help, FormAlliance);
+ }
+ else
+ {
+ Response (how_can_i_help, ExitConversation);
+ }
+ Response (valuable_info, ExitConversation);
+ }
+}
+
+static void
+AquaintZoqFot (RESPONSE_REF R)
+{
+ if (PLAYER_SAID (R, which_fot))
+ {
+ NPCPhrase_cb (HE_IS0, &SelectAlienZOQ);
+ NPCPhrase_cb (HE_IS1, &SelectAlienPIK);
+ NPCPhrase_cb (HE_IS2, &SelectAlienZOQ);
+ NPCPhrase_cb (HE_IS3, &SelectAlienPIK);
+ NPCPhrase_cb (HE_IS4, &SelectAlienZOQ);
+ NPCPhrase_cb (HE_IS5, &SelectAlienPIK);
+ NPCPhrase_cb (HE_IS6, &SelectAlienZOQ);
+ NPCPhrase_cb (HE_IS7, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (which_fot);
+ }
+ else if (PLAYER_SAID (R, quiet_toadies))
+ {
+ NPCPhrase_cb (TOLD_YOU0, &SelectAlienZOQ);
+ NPCPhrase_cb (TOLD_YOU1, &SelectAlienPIK);
+ NPCPhrase_cb (TOLD_YOU2, &SelectAlienZOQ);
+ NPCPhrase_cb (TOLD_YOU3, &SelectAlienPIK);
+ NPCPhrase_cb (TOLD_YOU4, &SelectAlienZOQ);
+ NPCPhrase_cb (TOLD_YOU5, &SelectAlienPIK);
+ NPCPhrase_cb (TOLD_YOU6, &SelectAlienZOQ);
+ NPCPhrase_cb (TOLD_YOU7, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (quiet_toadies);
+ }
+
+ if (PHRASE_ENABLED (we_are_vindicator0))
+ {
+ UNICODE buf[ALLIANCE_NAME_BUFSIZE];
+
+ GetAllianceName (buf, name_1);
+ construct_response (
+ shared_phrase_buf,
+ we_are_vindicator0,
+ buf,
+ we_are_vindicator1,
+ GLOBAL_SIS (ShipName),
+ we_are_vindicator2,
+ (UNICODE*)NULL);
+ }
+
+ if (PHRASE_ENABLED (which_fot))
+ Response (which_fot, AquaintZoqFot);
+ if (PHRASE_ENABLED (we_are_vindicator0))
+ DoResponsePhrase (we_are_vindicator0, ZoqFotIntro, shared_phrase_buf);
+ if (PHRASE_ENABLED (quiet_toadies))
+ Response (quiet_toadies, AquaintZoqFot);
+ Response (all_very_interesting, ExitConversation);
+ Response (valuable_info, ExitConversation);
+}
+
+static void ZoqFotHome (RESPONSE_REF R);
+
+static void
+ZoqFotInfo (RESPONSE_REF R)
+{
+ BYTE InfoLeft;
+
+ if (PLAYER_SAID (R, want_specific_info))
+ {
+ NPCPhrase_cb (WHAT_SPECIFIC_INFO0, &SelectAlienZOQ);
+ NPCPhrase_cb (WHAT_SPECIFIC_INFO1, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ }
+ else if (PLAYER_SAID (R, what_about_others))
+ {
+ NPCPhrase_cb (ABOUT_OTHERS0, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_OTHERS1, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_OTHERS2, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_OTHERS3, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_OTHERS4, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_OTHERS5, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_OTHERS6, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_OTHERS7, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_OTHERS8, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_OTHERS9, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_OTHERS10, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_OTHERS11, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_OTHERS12, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_OTHERS13, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (what_about_others);
+ }
+ else if (PLAYER_SAID (R, what_about_zebranky))
+ {
+ NPCPhrase_cb (ABOUT_ZEBRANKY0, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_ZEBRANKY1, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_ZEBRANKY2, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_ZEBRANKY3, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_ZEBRANKY4, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_ZEBRANKY5, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_ZEBRANKY6, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_ZEBRANKY7, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (what_about_zebranky);
+ }
+ else if (PLAYER_SAID (R, what_about_stinger))
+ {
+ NPCPhrase_cb (ABOUT_STINGER0, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_STINGER1, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_STINGER2, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_STINGER3, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_STINGER4, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_STINGER5, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (what_about_stinger);
+ }
+ else if (PLAYER_SAID (R, what_about_guy_in_back))
+ {
+ NPCPhrase_cb (ABOUT_GUY0, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_GUY1, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (what_about_guy_in_back);
+ }
+ else if (PLAYER_SAID (R, what_about_past))
+ {
+ NPCPhrase_cb (ABOUT_PAST0, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_PAST1, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_PAST2, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_PAST3, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_PAST4, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_PAST5, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_PAST6, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_PAST7, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_PAST8, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_PAST9, &SelectAlienPIK);
+ NPCPhrase_cb (ABOUT_PAST10, &SelectAlienZOQ);
+ NPCPhrase_cb (ABOUT_PAST11, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ DISABLE_PHRASE (what_about_past);
+ }
+
+ InfoLeft = FALSE;
+ if (PHRASE_ENABLED (what_about_others))
+ {
+ Response (what_about_others, ZoqFotInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_zebranky))
+ {
+ Response (what_about_zebranky, ZoqFotInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_stinger))
+ {
+ Response (what_about_stinger, ZoqFotInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_guy_in_back))
+ {
+ Response (what_about_guy_in_back, ZoqFotInfo);
+ InfoLeft = TRUE;
+ }
+ if (PHRASE_ENABLED (what_about_past))
+ {
+ Response (what_about_past, ZoqFotInfo);
+ InfoLeft = TRUE;
+ }
+ Response (enough_info, ZoqFotHome);
+
+ if (!InfoLeft)
+ {
+ DISABLE_PHRASE (want_specific_info);
+ }
+}
+
+static void
+ZoqFotHome (RESPONSE_REF R)
+{
+ BYTE NumVisits;
+
+ if (PLAYER_SAID (R, whats_up_homeworld))
+ {
+ NumVisits = GET_GAME_STATE (ZOQFOT_INFO);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase_cb (GENERAL_INFO_10, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_11, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_12, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_13, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 1:
+ NPCPhrase_cb (GENERAL_INFO_20, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_21, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_22, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_23, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_24, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_25, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_26, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_27, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 2:
+ NPCPhrase_cb (GENERAL_INFO_30, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_31, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_32, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_33, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_34, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_35, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 3:
+ NPCPhrase_cb (GENERAL_INFO_40, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_41, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_42, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_43, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_44, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_45, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_46, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_47, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_48, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_49, &SelectAlienPIK);
+ NPCPhrase_cb (GENERAL_INFO_410, &SelectAlienZOQ);
+ NPCPhrase_cb (GENERAL_INFO_411, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ZOQFOT_INFO, NumVisits);
+
+ DISABLE_PHRASE (whats_up_homeworld);
+ }
+ else if (PLAYER_SAID (R, any_war_news))
+ {
+#define UTWIG_BUY_TIME (1 << 0)
+#define KOHR_AH_WIN (1 << 1)
+#define URQUAN_LOSE (1 << 2)
+#define KOHR_AH_KILL (1 << 3)
+#define KNOW_ALL (UTWIG_BUY_TIME | KOHR_AH_WIN | URQUAN_LOSE | KOHR_AH_KILL)
+ BYTE KnowMask;
+
+ NumVisits = GET_GAME_STATE (UTWIG_SUPOX_MISSION);
+ KnowMask = GET_GAME_STATE (ZOQFOT_KNOW_MASK);
+ if (!(KnowMask & KOHR_AH_KILL) && GET_GAME_STATE (KOHR_AH_FRENZY))
+ {
+ NPCPhrase_cb (KOHRAH_FRENZY0, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_FRENZY1, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_FRENZY2, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_FRENZY3, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_FRENZY4, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_FRENZY5, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_FRENZY6, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_FRENZY7, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_FRENZY8, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_FRENZY9, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_FRENZY10, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_FRENZY11, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ KnowMask = KNOW_ALL;
+ }
+ else if (!(KnowMask & UTWIG_BUY_TIME)
+ && NumVisits > 0 && NumVisits < 5)
+ {
+ NPCPhrase_cb (UTWIG_DELAY0, &SelectAlienZOQ);
+ NPCPhrase_cb (UTWIG_DELAY1, &SelectAlienPIK);
+ NPCPhrase_cb (UTWIG_DELAY2, &SelectAlienZOQ);
+ NPCPhrase_cb (UTWIG_DELAY3, &SelectAlienPIK);
+ NPCPhrase_cb (UTWIG_DELAY4, &SelectAlienZOQ);
+ NPCPhrase_cb (UTWIG_DELAY5, &SelectAlienPIK);
+ NPCPhrase_cb (UTWIG_DELAY6, &SelectAlienZOQ);
+ NPCPhrase_cb (UTWIG_DELAY7, &SelectAlienPIK);
+ NPCPhrase_cb (UTWIG_DELAY8, &SelectAlienZOQ);
+ NPCPhrase_cb (UTWIG_DELAY9, &SelectAlienPIK);
+ NPCPhrase_cb (UTWIG_DELAY10, &SelectAlienZOQ);
+ NPCPhrase_cb (UTWIG_DELAY11, &SelectAlienPIK);
+ NPCPhrase_cb (UTWIG_DELAY12, &SelectAlienZOQ);
+ NPCPhrase_cb (UTWIG_DELAY13, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ KnowMask |= UTWIG_BUY_TIME;
+ }
+ else
+ {
+ SIZE i;
+
+ i = START_YEAR + YEARS_TO_KOHRAH_VICTORY;
+ if (NumVisits)
+ ++i;
+ if ((i -= GLOBAL (GameClock.year_index)) == 1
+ && GLOBAL (GameClock.month_index) > 2)
+ i = 0;
+ if (!(KnowMask & URQUAN_LOSE) && i <= 0)
+ {
+ NPCPhrase_cb (URQUAN_NEARLY_GONE0, &SelectAlienZOQ);
+ NPCPhrase_cb (URQUAN_NEARLY_GONE1, &SelectAlienPIK);
+ NPCPhrase_cb (URQUAN_NEARLY_GONE2, &SelectAlienZOQ);
+ NPCPhrase_cb (URQUAN_NEARLY_GONE3, &SelectAlienPIK);
+ NPCPhrase_cb (URQUAN_NEARLY_GONE4, &SelectAlienZOQ);
+ NPCPhrase_cb (URQUAN_NEARLY_GONE5, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ KnowMask |= KOHR_AH_WIN | URQUAN_LOSE;
+ }
+ else if (!(KnowMask & KOHR_AH_WIN) && i == 1)
+ {
+ NPCPhrase_cb (KOHRAH_WINNING0, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_WINNING1, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_WINNING2, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_WINNING3, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_WINNING4, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_WINNING5, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_WINNING6, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_WINNING7, &SelectAlienPIK);
+ NPCPhrase_cb (KOHRAH_WINNING8, &SelectAlienZOQ);
+ NPCPhrase_cb (KOHRAH_WINNING9, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ KnowMask |= KOHR_AH_WIN;
+ }
+ else
+ {
+ NPCPhrase_cb (NO_WAR_NEWS0, &SelectAlienZOQ);
+ NPCPhrase_cb (NO_WAR_NEWS1, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ }
+ }
+ SET_GAME_STATE (ZOQFOT_KNOW_MASK, KnowMask);
+
+ DISABLE_PHRASE (any_war_news);
+ }
+ else if (PLAYER_SAID (R, i_want_alliance))
+ {
+ NPCPhrase_cb (GOOD0, &SelectAlienZOQ);
+ NPCPhrase_cb (GOOD1, &SelectAlienPIK);
+ NPCPhrase_cb (GOOD2, &SelectAlienZOQ);
+ NPCPhrase_cb (GOOD3, &SelectAlienPIK);
+ NPCPhrase_cb (GOOD4, &SelectAlienZOQ);
+ NPCPhrase_cb (GOOD5, &SelectAlienPIK);
+ NPCPhrase_cb (GOOD6, &SelectAlienZOQ);
+ NPCPhrase_cb (GOOD7, &SelectAlienPIK);
+ NPCPhrase_cb (GOOD8, &SelectAlienZOQ);
+ NPCPhrase_cb (GOOD9, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ SetRaceAllied (ZOQFOTPIK_SHIP, TRUE);
+ AddEvent (RELATIVE_EVENT, 3, 0, 0, ZOQFOT_DISTRESS_EVENT);
+ }
+ else if (PLAYER_SAID (R, enough_info))
+ {
+ NPCPhrase_cb (OK_ENOUGH_INFO, &SelectAlienZOQ);
+ ZFPTalkSegue ((COUNT)~0);
+ }
+
+ if (PHRASE_ENABLED (whats_up_homeworld))
+ Response (whats_up_homeworld, ZoqFotHome);
+ if (PHRASE_ENABLED (any_war_news))
+ Response (any_war_news, ZoqFotHome);
+ if (CheckAlliance (ZOQFOTPIK_SHIP) != GOOD_GUY)
+ Response (i_want_alliance, ZoqFotHome);
+ else if (PHRASE_ENABLED (want_specific_info))
+ {
+ Response (want_specific_info, ZoqFotInfo);
+ }
+ Response (bye_homeworld, ExitConversation);
+}
+
+static void
+Intro (void)
+{
+ BYTE NumVisits;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ NPCPhrase_cb (OUT_TAKES0, &SelectAlienZOQ);
+ NPCPhrase_cb (OUT_TAKES1, &SelectAlienPIK);
+ NPCPhrase_cb (OUT_TAKES2, &SelectAlienZOQ);
+ NPCPhrase_cb (OUT_TAKES3, &SelectAlienPIK);
+ NPCPhrase_cb (OUT_TAKES4, &SelectAlienZOQ);
+ NPCPhrase_cb (OUT_TAKES5, &SelectAlienPIK);
+ NPCPhrase_cb (OUT_TAKES6, &SelectAlienZOQ);
+ NPCPhrase_cb (OUT_TAKES7, &SelectAlienPIK);
+ NPCPhrase_cb (OUT_TAKES8, &SelectAlienZOQ);
+ NPCPhrase_cb (OUT_TAKES9, &SelectAlienPIK);
+ NPCPhrase_cb (OUT_TAKES10, &SelectAlienZOQ);
+ NPCPhrase_cb (OUT_TAKES11, &SelectAlienPIK);
+ NPCPhrase_cb (OUT_TAKES12, &SelectAlienZOQ);
+ NPCPhrase_cb (OUT_TAKES13, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ setSegue (Segue_peace);
+ return;
+ }
+
+ if (GET_GAME_STATE (ZOQFOT_HOSTILE))
+ {
+ NumVisits = GET_GAME_STATE (ZOQFOT_HOME_VISITS);
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase_cb (HOSTILE_HELLO_10, &SelectAlienZOQ);
+ NPCPhrase_cb (HOSTILE_HELLO_11, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 1:
+ NPCPhrase_cb (HOSTILE_HELLO_20, &SelectAlienZOQ);
+ NPCPhrase_cb (HOSTILE_HELLO_21, &SelectAlienPIK);
+ NPCPhrase_cb (HOSTILE_HELLO_22, &SelectAlienZOQ);
+ NPCPhrase_cb (HOSTILE_HELLO_23, &SelectAlienPIK);
+ NPCPhrase_cb (HOSTILE_HELLO_24, &SelectAlienZOQ);
+ NPCPhrase_cb (HOSTILE_HELLO_25, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 2:
+ NPCPhrase_cb (HOSTILE_HELLO_30, &SelectAlienZOQ);
+ NPCPhrase_cb (HOSTILE_HELLO_31, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 3:
+ NPCPhrase_cb (HOSTILE_HELLO_40, &SelectAlienZOQ);
+ NPCPhrase_cb (HOSTILE_HELLO_41, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ --NumVisits;
+ break;
+ }
+ SET_GAME_STATE (ZOQFOT_HOME_VISITS, NumVisits);
+
+ setSegue (Segue_hostile);
+ }
+ else if (!GET_GAME_STATE (MET_ZOQFOT))
+ {
+ SET_GAME_STATE (MET_ZOQFOT, 1);
+
+ NPCPhrase_cb (WE_ARE0, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ARE1, &SelectAlienPIK);
+ NPCPhrase_cb (WE_ARE2, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ARE3, &SelectAlienPIK);
+ NPCPhrase_cb (WE_ARE4, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ARE5, &SelectAlienPIK);
+ NPCPhrase_cb (WE_ARE6, &SelectAlienZOQ);
+ NPCPhrase_cb (WE_ARE7, &SelectAlienPIK);
+
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7))
+ {
+ NPCPhrase_cb (INIT_HOME_HELLO0, &SelectAlienZOQ);
+ NPCPhrase_cb (INIT_HOME_HELLO1, &SelectAlienPIK);
+ NPCPhrase_cb (INIT_HOME_HELLO2, &SelectAlienZOQ);
+ NPCPhrase_cb (INIT_HOME_HELLO3, &SelectAlienPIK);
+ }
+ else
+ {
+ NPCPhrase_cb (SCOUT_HELLO0, &SelectAlienZOQ);
+ NPCPhrase_cb (SCOUT_HELLO1, &SelectAlienPIK);
+ NPCPhrase_cb (SCOUT_HELLO2, &SelectAlienZOQ);
+ NPCPhrase_cb (SCOUT_HELLO3, &SelectAlienPIK);
+ }
+
+ ZFPTalkSegue ((COUNT)~0);
+
+ AquaintZoqFot (0);
+ }
+ else
+ {
+ if (GET_GAME_STATE (ZOQFOT_DISTRESS))
+ {
+#define MAX_ZFP_SHIPS 4
+ NPCPhrase_cb (THANKS_FOR_RESCUE0, &SelectAlienZOQ);
+ NPCPhrase_cb (THANKS_FOR_RESCUE1, &SelectAlienPIK);
+ NPCPhrase_cb (THANKS_FOR_RESCUE2, &SelectAlienZOQ);
+ NPCPhrase_cb (THANKS_FOR_RESCUE3, &SelectAlienPIK);
+ NPCPhrase_cb (THANKS_FOR_RESCUE4, &SelectAlienZOQ);
+ NPCPhrase_cb (THANKS_FOR_RESCUE5, &SelectAlienPIK);
+ NPCPhrase_cb (THANKS_FOR_RESCUE6, &SelectAlienZOQ);
+ NPCPhrase_cb (THANKS_FOR_RESCUE7, &SelectAlienPIK);
+ NPCPhrase_cb (THANKS_FOR_RESCUE8, &SelectAlienZOQ);
+ NPCPhrase_cb (THANKS_FOR_RESCUE9, &SelectAlienPIK);
+ NPCPhrase_cb (THANKS_FOR_RESCUE10, &SelectAlienZOQ);
+ NPCPhrase_cb (THANKS_FOR_RESCUE11, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+
+ SET_GAME_STATE (ZOQFOT_DISTRESS, 0);
+ AddEscortShips (ZOQFOTPIK_SHIP, MAX_ZFP_SHIPS);
+ }
+ else
+ {
+ NumVisits = GET_GAME_STATE (ZOQFOT_HOME_VISITS);
+ if (CheckAlliance (ZOQFOTPIK_SHIP) != GOOD_GUY)
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_10, &SelectAlienZOQ);
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_11, &SelectAlienPIK);
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_12, &SelectAlienZOQ);
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_13, &SelectAlienPIK);
+ break;
+ case 1:
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_20, &SelectAlienZOQ);
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_21, &SelectAlienPIK);
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_22, &SelectAlienZOQ);
+ NPCPhrase_cb (NEUTRAL_HOME_HELLO_23, &SelectAlienPIK);
+ --NumVisits;
+ break;
+ }
+ ZFPTalkSegue ((COUNT)~0);
+ }
+ else
+ {
+ switch (NumVisits++)
+ {
+ case 0:
+ NPCPhrase_cb (ALLIED_HOME_HELLO_10, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_11, &SelectAlienPIK);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_12, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_13, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 1:
+ NPCPhrase_cb (ALLIED_HOME_HELLO_20, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_21, &SelectAlienPIK);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_22, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_23, &SelectAlienPIK);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_24, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_25, &SelectAlienPIK);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_26, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_27, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 2:
+ NPCPhrase_cb (ALLIED_HOME_HELLO_30, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_31, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ break;
+ case 3:
+ NPCPhrase_cb (ALLIED_HOME_HELLO_40, &SelectAlienZOQ);
+ NPCPhrase_cb (ALLIED_HOME_HELLO_41, &SelectAlienPIK);
+ ZFPTalkSegue ((COUNT)~0);
+ --NumVisits;
+ break;
+ }
+ }
+ SET_GAME_STATE (ZOQFOT_HOME_VISITS, NumVisits);
+ }
+
+ ZoqFotHome (0);
+ }
+}
+
+static COUNT
+uninit_zoqfot (void)
+{
+ return (0);
+}
+
+static void
+post_zoqfot_enc (void)
+{
+ // nothing defined so far
+}
+
+LOCDATA*
+init_zoqfot_comm (void)
+{
+ LOCDATA *retval;
+
+ zoqfot_desc.init_encounter_func = Intro;
+ zoqfot_desc.post_encounter_func = post_zoqfot_enc;
+ zoqfot_desc.uninit_encounter_func = uninit_zoqfot;
+
+ zoqfot_desc.AlienTextWidth = (SIS_TEXT_WIDTH >> 1) - TEXT_X_OFFS;
+
+ if (CheckAlliance (ZOQFOTPIK_SHIP) == GOOD_GUY
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ {
+ setSegue (Segue_peace);
+ }
+ else
+ {
+ setSegue (Segue_hostile);
+ }
+
+ retval = &zoqfot_desc;
+
+ return (retval);
+}
+
diff --git a/src/uqm/commanim.c b/src/uqm/commanim.c
new file mode 100644
index 0000000..02e9362
--- /dev/null
+++ b/src/uqm/commanim.c
@@ -0,0 +1,623 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define COMM_INTERNAL
+#include "commanim.h"
+
+#include "comm.h"
+#include "element.h"
+#include "setup.h"
+#include "libs/compiler.h"
+#include "libs/graphics/cmap.h"
+#include "libs/mathlib.h"
+
+
+static TimeCount LastTime;
+static SEQUENCE Sequences[MAX_ANIMATIONS + 2];
+ // 2 extra for Talk and Transition animations
+static DWORD ActiveMask;
+ // Bit mask of all animations that are currently active.
+ // Bit 'i' is set if the animation with index 'i' is active.
+static ANIMATION_DESC TalkDesc;
+static ANIMATION_DESC TransitDesc;
+static SEQUENCE* Talk;
+static SEQUENCE* Transit;
+static COUNT FirstAmbient;
+static COUNT TotalSequences;
+
+
+static inline DWORD
+randomFrameRate (SEQUENCE *pSeq)
+{
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ return ADPtr->BaseFrameRate +
+ TFB_Random () % (ADPtr->RandomFrameRate + 1);
+}
+
+static inline DWORD
+randomRestartRate (SEQUENCE *pSeq)
+{
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ return ADPtr->BaseRestartRate +
+ TFB_Random () % (ADPtr->RandomRestartRate + 1);
+}
+
+static inline COUNT
+randomFrameIndex (SEQUENCE *pSeq, COUNT from)
+{
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ return from + TFB_Random () % (ADPtr->NumFrames - from);
+}
+
+static void
+SetupAmbientSequences (SEQUENCE *pSeq, COUNT Num)
+{
+ COUNT i;
+
+ for (i = 0; i < Num; ++i, ++pSeq)
+ {
+ ANIMATION_DESC *ADPtr = &CommData.AlienAmbientArray[i];
+
+ memset (pSeq, 0, sizeof (*pSeq));
+
+ pSeq->ADPtr = ADPtr;
+ if (ADPtr->AnimFlags & COLORXFORM_ANIM)
+ pSeq->AnimType = COLOR_ANIM;
+ else
+ pSeq->AnimType = PICTURE_ANIM;
+ pSeq->Direction = UP_DIR;
+ pSeq->FramesLeft = ADPtr->NumFrames;
+ // Default: first frame is neutral
+ if (ADPtr->AnimFlags & RANDOM_ANIM)
+ { // Set a random frame/colormap
+ pSeq->NextIndex = TFB_Random () % ADPtr->NumFrames;
+ }
+ else if (ADPtr->AnimFlags & YOYO_ANIM)
+ { // Skip the first frame/colormap (it's neutral)
+ pSeq->NextIndex = 1;
+ --pSeq->FramesLeft;
+ }
+ else if (ADPtr->AnimFlags & CIRCULAR_ANIM)
+ { // Exception that makes everything more painful:
+ // *Last* frame is neutral
+ pSeq->CurIndex = ADPtr->NumFrames - 1;
+ pSeq->NextIndex = 0;
+ }
+
+ pSeq->Alarm = randomRestartRate (pSeq) + 1;
+ }
+}
+
+static void
+SetupTalkSequence (SEQUENCE *pSeq, ANIMATION_DESC *ADPtr)
+{
+ memset (pSeq, 0, sizeof (*pSeq));
+ // Initially disabled, and until needed
+ ADPtr->AnimFlags |= ANIM_DISABLED;
+ pSeq->ADPtr = ADPtr;
+ pSeq->AnimType = PICTURE_ANIM;
+}
+
+static inline BOOLEAN
+animAtNeutralIndex (SEQUENCE *pSeq)
+{
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ if (ADPtr->AnimFlags & CIRCULAR_ANIM)
+ { // CIRCULAR_ANIM's neutral frame is the last
+ return pSeq->NextIndex == 0;
+ }
+ else
+ { // All others, neutral frame is the first
+ return pSeq->CurIndex == 0;
+ }
+}
+
+static inline BOOLEAN
+conflictsWithTalkingAnim (SEQUENCE *pSeq)
+{
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ return ADPtr->AnimFlags & CommData.AlienTalkDesc.AnimFlags & WAIT_TALKING;
+}
+
+static void
+ProcessColormapAnims (SEQUENCE *pSeq, COUNT Num)
+{
+ COUNT i;
+
+ for (i = 0; i < Num; ++i, ++pSeq)
+ {
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ if ((ADPtr->AnimFlags & ANIM_DISABLED)
+ || pSeq->AnimType != COLOR_ANIM
+ || !pSeq->Change)
+ continue;
+
+ XFormColorMap (GetColorMapAddress (
+ SetAbsColorMapIndex (CommData.AlienColorMap,
+ ADPtr->StartIndex + pSeq->CurIndex)),
+ pSeq->Alarm - 1);
+ pSeq->Change = FALSE;
+ }
+}
+
+static BOOLEAN
+AdvanceAmbientSequence (SEQUENCE *pSeq)
+{
+ BOOLEAN active;
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ --pSeq->FramesLeft;
+ // YOYO_ANIM does not actually end until it comes back
+ // in reverse direction, even if FramesLeft gets to 0 here
+ if (pSeq->FramesLeft
+ || ((ADPtr->AnimFlags & YOYO_ANIM) && pSeq->NextIndex != 0))
+ {
+ active = TRUE;
+ pSeq->Alarm = randomFrameRate (pSeq) + 1;
+ }
+ else
+ { // last animation frame
+ active = FALSE;
+ pSeq->Alarm = randomRestartRate (pSeq) + 1;
+
+ // RANDOM_ANIM must end on a neutral frame
+ if (ADPtr->AnimFlags & RANDOM_ANIM)
+ pSeq->NextIndex = 0;
+ }
+
+ // Will draw the next frame or change to next colormap
+ pSeq->CurIndex = pSeq->NextIndex;
+ pSeq->Change = TRUE;
+
+ if (pSeq->FramesLeft == 0)
+ { // Animation ended
+ // Set it up for the next round
+ pSeq->FramesLeft = ADPtr->NumFrames;
+
+ if (ADPtr->AnimFlags & YOYO_ANIM)
+ { // YOYO_ANIM never draws the first frame
+ // ("first" depends on direction)
+ --pSeq->FramesLeft;
+ pSeq->Direction = -pSeq->Direction;
+ }
+ else if (ADPtr->AnimFlags & CIRCULAR_ANIM)
+ { // Rewind the CIRCULAR_ANIM
+ // NextIndex will be brought to 0 just below
+ pSeq->NextIndex = -1;
+ }
+ // RANDOM_ANIM is setup just below
+ }
+
+ if (ADPtr->AnimFlags & RANDOM_ANIM)
+ pSeq->NextIndex = randomFrameIndex (pSeq, 0);
+ else
+ pSeq->NextIndex += pSeq->Direction;
+
+ return active;
+}
+
+static void
+ResetSequence (SEQUENCE *pSeq)
+{
+ // Reset the animation and cause a redraw of the neutral frame,
+ // assuming it is not ANIM_DISABLED
+ // NOTE: This does not handle CIRCULAR_ANIM properly
+ pSeq->Direction = NO_DIR;
+ pSeq->CurIndex = 0;
+ pSeq->Change = TRUE;
+}
+
+static void
+AdvanceTalkingSequence (SEQUENCE *pSeq, DWORD ElapsedTicks)
+{
+ // We use the actual descriptor for flags processing and
+ // a copied one for drawing. A copied one is updated only
+ // when it is safe to do so.
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ if (pSeq->Direction == NO_DIR)
+ { // just starting now
+ pSeq->Direction = UP_DIR;
+ // It's now safe to pick up new Talk descriptor if changed
+ // (e.g. Zoq and Pik taking turns to talk)
+ if (CommData.AlienTalkDesc.StartIndex != ADPtr->StartIndex)
+ { // copy the new one
+ *ADPtr = CommData.AlienTalkDesc;
+ }
+
+ assert (pSeq->CurIndex == 0);
+ pSeq->Alarm = 0; // now!
+ ADPtr->AnimFlags &= ~ANIM_DISABLED;
+ }
+
+ if (pSeq->Alarm > ElapsedTicks)
+ { // Not time yet
+ pSeq->Alarm -= ElapsedTicks;
+ return;
+ }
+
+ // Time to start or advance the animation
+ pSeq->Alarm = randomFrameRate (pSeq);
+ pSeq->Change = TRUE;
+ // Talking animation is like RANDOM_ANIM, except that
+ // random frames always alternate with the neutral one
+ // The animation does not stop until we reset it
+ if (pSeq->CurIndex == 0)
+ { // random frame next
+ pSeq->CurIndex = randomFrameIndex (pSeq, 1);
+ pSeq->Alarm += randomRestartRate (pSeq);
+ }
+ else
+ { // neutral frame next
+ pSeq->CurIndex = 0;
+ }
+}
+
+static BOOLEAN
+AdvanceTransitSequence (SEQUENCE *pSeq, DWORD ElapsedTicks)
+{
+ BOOLEAN done = FALSE;
+ // We use the actual descriptor for flags processing and
+ // a copied one for drawing. A copied one is updated only
+ // when it is safe to do so.
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ if (pSeq->Direction == NO_DIR)
+ { // just starting now
+ pSeq->Alarm = 0; // now!
+ ADPtr->AnimFlags &= ~ANIM_DISABLED;
+ }
+
+ if (pSeq->Alarm > ElapsedTicks)
+ { // Not time yet
+ pSeq->Alarm -= ElapsedTicks;
+ return FALSE;
+ }
+
+ // Time to start or advance the animation
+ pSeq->Change = TRUE;
+
+ if (pSeq->Direction == NO_DIR)
+ { // just starting now
+ pSeq->FramesLeft = ADPtr->NumFrames;
+ // Both INTRO and DONE may be set at the same time,
+ // when e.g. Zoq and Pik are taking turns to talk
+ // Process the DONE transition first to go into
+ // a neutral state before switching over.
+ if (CommData.AlienTransitionDesc.AnimFlags & TALK_DONE)
+ {
+ pSeq->Direction = DOWN_DIR;
+ pSeq->CurIndex = ADPtr->NumFrames - 1;
+ }
+ else if (CommData.AlienTransitionDesc.AnimFlags & TALK_INTRO)
+ {
+ pSeq->Direction = UP_DIR;
+ // It's now safe to pick up new Transition descriptor if changed
+ // (e.g. Zoq and Pik taking turns to talk)
+ if (CommData.AlienTransitionDesc.StartIndex
+ != ADPtr->StartIndex)
+ { // copy the new one
+ *ADPtr = CommData.AlienTransitionDesc;
+ }
+
+ pSeq->CurIndex = 0;
+ }
+ }
+
+ --pSeq->FramesLeft;
+ if (pSeq->FramesLeft == 0)
+ { // animation is done
+ if (pSeq->Direction == UP_DIR)
+ { // done with TALK_INTRO transition
+ CommData.AlienTransitionDesc.AnimFlags &= ~TALK_INTRO;
+ }
+ else if (pSeq->Direction == DOWN_DIR)
+ { // done with TALK_DONE transition
+ CommData.AlienTransitionDesc.AnimFlags &= ~TALK_DONE;
+
+ // Done with all transition frames
+ ADPtr->AnimFlags |= ANIM_DISABLED;
+ done = TRUE;
+ }
+ pSeq->Direction = NO_DIR;
+ }
+ else
+ { // next frame
+ pSeq->Alarm = randomFrameRate (pSeq);
+ pSeq->CurIndex += pSeq->Direction;
+ }
+
+ return done;
+}
+
+void
+InitCommAnimations (void)
+{
+ ActiveMask = 0;
+
+ TalkDesc = CommData.AlienTalkDesc;
+ TransitDesc = CommData.AlienTransitionDesc;
+
+ // Animation sequences have to be drawn in reverse, and
+ // talk animations have to be drawn last (so we add them first)
+ TotalSequences = 0;
+ // Transition animation last
+ Transit = Sequences + TotalSequences;
+ SetupTalkSequence (Transit, &TransitDesc);
+ ++TotalSequences;
+ // Talk animation second last
+ Talk = Sequences + TotalSequences;
+ SetupTalkSequence (Talk, &TalkDesc);
+ ++TotalSequences;
+ FirstAmbient = TotalSequences;
+ SetupAmbientSequences (Sequences + FirstAmbient, CommData.NumAnimations);
+ TotalSequences += CommData.NumAnimations;
+
+ LastTime = GetTimeCounter ();
+}
+
+BOOLEAN
+ProcessCommAnimations (BOOLEAN FullRedraw, BOOLEAN paused)
+{
+ if (paused)
+ { // Drive colormap xforms and nothing else
+ XFormColorMap_step ();
+ return FALSE;
+ }
+ else
+ {
+ COUNT i;
+ SEQUENCE *pSeq;
+ BOOLEAN Change;
+ BOOLEAN CanTalk = TRUE;
+ TimeCount CurTime;
+ DWORD ElapsedTicks;
+ DWORD NextActiveMask;
+
+ CurTime = GetTimeCounter ();
+ ElapsedTicks = CurTime - LastTime;
+ LastTime = CurTime;
+
+ // Process ambient animations
+ NextActiveMask = ActiveMask;
+ pSeq = Sequences + FirstAmbient;
+ for (i = 0; i < CommData.NumAnimations; ++i, ++pSeq)
+ {
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+ DWORD ActiveBit = 1L << i;
+
+ if (ADPtr->AnimFlags & ANIM_DISABLED)
+ continue;
+
+ if (pSeq->Direction == NO_DIR)
+ { // animation is paused
+ if (!conflictsWithTalkingAnim (pSeq))
+ { // start it up
+ pSeq->Direction = UP_DIR;
+ }
+ }
+ else if (pSeq->Alarm > ElapsedTicks)
+ { // not time yet
+ pSeq->Alarm -= ElapsedTicks;
+ }
+ else if (ActiveMask & ADPtr->BlockMask)
+ { // animation is blocked
+ assert (!(ActiveMask & ActiveBit) &&
+ "Check animations' mutual blocking masks");
+ assert (animAtNeutralIndex (pSeq));
+ // reschedule
+ pSeq->Alarm = randomRestartRate (pSeq) + 1;
+ continue;
+ }
+ else
+ { // Time to start or advance the animation
+ if (AdvanceAmbientSequence (pSeq))
+ { // Animation is active this frame and the next
+ ActiveMask |= ActiveBit;
+ NextActiveMask |= ActiveBit;
+ }
+ else
+ { // Animation remains active this frame but not the next
+ // This keeps any conflicting animations (BlockMask)
+ // from activating in the same frame and scribbling over
+ // our last image.
+ NextActiveMask &= ~ActiveBit;
+ }
+ }
+
+ if (pSeq->AnimType == PICTURE_ANIM && pSeq->Direction != NO_DIR
+ && conflictsWithTalkingAnim (pSeq))
+ {
+ // We want to talk, but this is a running picture animation
+ // which conflicts with the talking animation
+ // See if it is safe to stop it now.
+ if (animAtNeutralIndex (pSeq))
+ { // pause the animation
+ pSeq->Direction = NO_DIR;
+ NextActiveMask &= ~ActiveBit;
+ // Talk animation is drawn last, so it's not a conflict
+ // for this frame. The talk animation will be drawn
+ // over the neutral frame.
+ }
+ else
+ { // Otherwise, let the animation run until it's safe
+ CanTalk = FALSE;
+ }
+ }
+ }
+ // All ambient animations have been processed. Advance the mask.
+ ActiveMask = NextActiveMask;
+
+ // Process the talking and transition animations
+ if (CanTalk && haveTalkingAnim () && runningTalkingAnim ())
+ {
+ BOOLEAN done = FALSE;
+
+ if (signaledStopTalkingAnim () && haveTransitionAnim ())
+ { // Run the transition. We will clear everything
+ // when it is done
+ CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
+ }
+
+ if (CommData.AlienTransitionDesc.AnimFlags
+ & (TALK_INTRO | TALK_DONE))
+ { // Transitioning in or out of talking
+ if ((CommData.AlienTransitionDesc.AnimFlags & TALK_DONE)
+ && Transit->Direction == NO_DIR)
+ { // This is needed when switching talking anims
+ ResetSequence (Talk);
+ }
+ done = AdvanceTransitSequence (Transit, ElapsedTicks);
+ }
+ else if (!signaledStopTalkingAnim ())
+ { // Talking, transition is done
+ AdvanceTalkingSequence (Talk, ElapsedTicks);
+ }
+ else
+ { // Not talking
+ ResetSequence (Talk);
+ done = TRUE;
+ }
+
+ if (signaledStopTalkingAnim () && done)
+ {
+ clearRunTalkingAnim ();
+ clearStopTalkingAnim ();
+ }
+ }
+ else
+ { // Not talking -- disable talking anim if it is done
+ if (Talk->Direction == NO_DIR)
+ TalkDesc.AnimFlags |= ANIM_DISABLED;
+ }
+
+ BatchGraphics ();
+
+ // Draw all animations
+ {
+ BOOLEAN ColorChange = XFormColorMap_step ();
+
+ if (ColorChange)
+ FullRedraw = TRUE;
+
+ // Colormap animations are processed separately
+ // from picture anims (see XFormColorMap_step)
+ ProcessColormapAnims (Sequences + FirstAmbient,
+ CommData.NumAnimations);
+
+ Change = DrawAlienFrame (Sequences, TotalSequences, FullRedraw);
+ if (FullRedraw)
+ Change = TRUE;
+ }
+
+ UnbatchGraphics ();
+
+ // Post-process ambient animations
+ pSeq = Sequences + FirstAmbient;
+ for (i = 0; i < CommData.NumAnimations; ++i, ++pSeq)
+ {
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+ DWORD ActiveBit = 1L << i;
+
+ if (ADPtr->AnimFlags & ANIM_DISABLED)
+ continue;
+
+ // We can only disable a one-shot anim here, otherwise the
+ // last frame will not be drawn
+ if ((ADPtr->AnimFlags & ONE_SHOT_ANIM)
+ && !(NextActiveMask & ActiveBit))
+ { // One-shot animation, inactive next frame
+ ADPtr->AnimFlags |= ANIM_DISABLED;
+ }
+ }
+
+ return Change;
+ }
+}
+
+BOOLEAN
+DrawAlienFrame (SEQUENCE *Sequences, COUNT Num, BOOLEAN fullRedraw)
+{
+ int i;
+ STAMP s;
+ BOOLEAN Change = FALSE;
+
+ BatchGraphics ();
+
+ s.origin.x = -SAFE_X;
+ s.origin.y = 0;
+
+ if (fullRedraw)
+ {
+ // Draw the main frame
+ s.frame = CommData.AlienFrame;
+ DrawStamp (&s);
+
+ // Draw any static frames (has to be in reverse)
+ for (i = CommData.NumAnimations - 1; i >= 0; --i)
+ {
+ ANIMATION_DESC *ADPtr = &CommData.AlienAmbientArray[i];
+
+ if (ADPtr->AnimFlags & ANIM_MASK)
+ continue;
+
+ ADPtr->AnimFlags |= ANIM_DISABLED;
+
+ if (!(ADPtr->AnimFlags & COLORXFORM_ANIM))
+ { // It's a static frame (e.g. Flagship picture at Starbase)
+ s.frame = SetAbsFrameIndex (CommData.AlienFrame,
+ ADPtr->StartIndex);
+ DrawStamp (&s);
+ }
+ }
+ }
+
+ if (Sequences)
+ { // Draw the animation sequences (has to be in reverse)
+ for (i = Num - 1; i >= 0; --i)
+ {
+ SEQUENCE *pSeq = &Sequences[i];
+ ANIMATION_DESC *ADPtr = pSeq->ADPtr;
+
+ if ((ADPtr->AnimFlags & ANIM_DISABLED)
+ || pSeq->AnimType != PICTURE_ANIM)
+ continue;
+
+ // Draw current animation frame only if changed
+ if (!fullRedraw && !pSeq->Change)
+ continue;
+
+ s.frame = SetAbsFrameIndex (CommData.AlienFrame,
+ ADPtr->StartIndex + pSeq->CurIndex);
+ DrawStamp (&s);
+ pSeq->Change = FALSE;
+
+ Change = TRUE;
+ }
+ }
+
+ UnbatchGraphics ();
+
+ return Change;
+}
diff --git a/src/uqm/commanim.h b/src/uqm/commanim.h
new file mode 100644
index 0000000..40527f5
--- /dev/null
+++ b/src/uqm/commanim.h
@@ -0,0 +1,141 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_COMMANIM_H_
+#define UQM_COMMANIM_H_
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// Some background: every animation has a neutral frame which returns
+// the image to the state it was in before the animation began. Which
+// frame is neutral depends on the animation type.
+// Animation types:
+#define RANDOM_ANIM (1 << 0)
+ // The next index is randomly chosen.
+ // The first frame is neutral
+#define CIRCULAR_ANIM (1 << 1)
+ // After the last index has been reached, the animation starts over.
+ // The last frame is neutral. This complicates the animation task.
+#define YOYO_ANIM (1 << 2)
+ // After the last index has been reached, the order that the
+ // animation frames are used is reversed.
+ // The first frame is neutral
+#define ANIM_MASK (RANDOM_ANIM | CIRCULAR_ANIM | YOYO_ANIM)
+ // Mask of all animation types.
+ // Static frames do not have any of these flags set.
+
+#define WAIT_TALKING (1 << 3)
+ // This is set in AlienTalkDesc when the talking animation is active
+ // or should be active.
+ // In AlienAmbientArray, this is set for those ambient animations
+ // which can not be active while the talking animation is active.
+ // Such animations stop at the end of the current animation cycle
+ // when the talking animation activates.
+#define PAUSE_TALKING (1 << 4)
+ // Set in AlienTalkDesc when we do not want the talking animation
+#define TALK_INTRO (1 << 5)
+ // In AlienTransitionDesc: indicates a transition to talking state
+#define TALK_DONE (1 << 6)
+ // In AlienTransitionDesc: indicates a transition to silent state
+ // In AlienTalkDesc: signals the end of talking animation
+#define ANIM_DISABLED (1 << 7)
+
+#define COLORXFORM_ANIM PAUSE_TALKING
+
+#define ONE_SHOT_ANIM TALK_INTRO
+ // Set in AlienAmbientArray for animations that should be
+ // disabled after they run once.
+
+typedef struct
+{
+ COUNT StartIndex;
+ // Index of the first image (for image animation) or
+ // index of the first color map (for palette animation)
+ BYTE NumFrames;
+ // Number of frames in the animation.
+
+ BYTE AnimFlags;
+ // One of RANDOM_ANIM, CIRCULAR_ANIM, or YOYO_ANIM
+ // plus flags (WAIT_TALKING, ANIM_DISABLED)
+
+ COUNT BaseFrameRate;
+ // Minimum interframe delay
+ COUNT RandomFrameRate;
+ // Maximum additional interframe delay
+ // Actual delay: BaseFrameRate + Random(0..RandomFrameRate)
+ COUNT BaseRestartRate;
+ // Minimum delay before restarting animation
+ COUNT RandomRestartRate;
+ // Maximum additional delay before restarting animation
+ // Actual delay: BaseRestartRate + Random(0..RandomRestartRate)
+
+ DWORD BlockMask;
+ // Bit mask of the indices of all animations that can not
+ // be active at the same time as this animation, usually,
+ // due to the image overlap conflicts.
+} ANIMATION_DESC;
+
+#define MAX_ANIMATIONS 20
+
+
+#ifdef COMM_INTERNAL
+
+typedef enum
+{
+ DOWN_DIR = -1, // Animation indices are decreasing
+ NO_DIR = 0,
+ UP_DIR = 1, // Animation indices are increasing
+} ANIM_DIR;
+
+typedef enum
+{
+ PICTURE_ANIM,
+ // Parts of a picture are replaced
+ COLOR_ANIM
+ // Colormap tricks on a picture
+} ANIM_TYPE;
+
+// Describes an active animation.
+struct SEQUENCE
+{
+ ANIMATION_DESC *ADPtr;
+ DWORD Alarm;
+ ANIM_DIR Direction;
+ COUNT CurIndex;
+ COUNT NextIndex;
+ COUNT FramesLeft;
+ ANIM_TYPE AnimType;
+ BOOLEAN Change;
+};
+#endif
+
+typedef struct SEQUENCE SEQUENCE;
+
+// Returns TRUE if there was an animation change
+extern BOOLEAN DrawAlienFrame (SEQUENCE *pSeq, COUNT Num, BOOLEAN fullRedraw);
+extern void InitCommAnimations (void);
+extern BOOLEAN ProcessCommAnimations (BOOLEAN fullRedraw, BOOLEAN paused);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_COMMANIM_H_ */
diff --git a/src/uqm/commglue.c b/src/uqm/commglue.c
new file mode 100644
index 0000000..a7b514c
--- /dev/null
+++ b/src/uqm/commglue.c
@@ -0,0 +1,421 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "commglue.h"
+
+#include "battle.h"
+ // For instantVictory
+#include "races.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <assert.h>
+#include "libs/log.h"
+
+static int NPCNumberPhrase (int number, const char *fmt, UNICODE **ptrack);
+
+// The CallbackFunction is queued and executes synchronously
+// on the Starcon2Main thread
+void
+NPCPhrase_cb (int index, CallbackFunction cb)
+{
+ UNICODE *pStr, buf[400];
+ void *pClip, *pTimeStamp;
+
+ switch (index)
+ {
+ case GLOBAL_PLAYER_NAME:
+ pStr = GLOBAL_SIS (CommanderName);
+ pClip = 0;
+ pTimeStamp = 0;
+ break;
+ case GLOBAL_SHIP_NAME:
+ pStr = GLOBAL_SIS (ShipName);
+ pClip = 0;
+ pTimeStamp = 0;
+ break;
+ case 0:
+ {
+ return;
+ }
+ default:
+ if (index < 0)
+ { // One of the alliance name variants
+ COUNT i;
+ STRING S;
+
+ index -= GLOBAL_ALLIANCE_NAME;
+
+ i = GET_GAME_STATE (NEW_ALLIANCE_NAME);
+ S = SetAbsStringTableIndex (CommData.ConversationPhrases, (index - 1) + i);
+ strcpy (buf, (UNICODE *)GetStringAddress (S));
+ if (i == 3)
+ strcat (buf, GLOBAL_SIS (CommanderName));
+
+ pStr = buf;
+ pClip = 0;
+ pTimeStamp = 0;
+ }
+ else
+ {
+ pStr = (UNICODE *)GetStringAddress (
+ SetAbsStringTableIndex (CommData.ConversationPhrases, index - 1)
+ );
+ pClip = GetStringSoundClip (
+ SetAbsStringTableIndex (CommData.ConversationPhrases, index - 1)
+ );
+ pTimeStamp = GetStringTimeStamp (
+ SetAbsStringTableIndex (CommData.ConversationPhrases, index - 1)
+ );
+ }
+ break;
+ }
+
+ SpliceTrack (pClip, pStr, pTimeStamp, cb);
+}
+
+// Special case variant: prevents page breaks.
+void
+NPCPhrase_splice (int index)
+{
+ UNICODE *pStr;
+ void *pClip;
+
+ assert (index >= 0);
+ if (index == 0)
+ return;
+
+ pStr = (UNICODE *)GetStringAddress (
+ SetAbsStringTableIndex (CommData.ConversationPhrases, index - 1));
+ pClip = GetStringSoundClip (
+ SetAbsStringTableIndex (CommData.ConversationPhrases, index - 1));
+
+ if (!pClip)
+ { // Just appending some text
+ SpliceTrack (NULL, pStr, NULL, NULL);
+ }
+ else
+ { // Splicing in some voice
+ UNICODE *tracks[] = {NULL, NULL};
+
+ tracks[0] = pClip;
+ SpliceMultiTrack (tracks, pStr);
+ }
+}
+
+void
+NPCNumber (int number, const char *fmt)
+{
+ UNICODE buf[32];
+
+ if (!fmt)
+ fmt = "%d";
+
+ if (CommData.AlienNumberSpeech)
+ {
+ NPCNumberPhrase (number, fmt, NULL);
+ return;
+ }
+
+ // just splice in the subtitle text
+ snprintf (buf, sizeof buf, fmt, number);
+ SpliceTrack (NULL, buf, NULL, NULL);
+}
+
+static int
+NPCNumberPhrase (int number, const char *fmt, UNICODE **ptrack)
+{
+#define MAX_NUMBER_TRACKS 20
+ NUMBER_SPEECH speech = CommData.AlienNumberSpeech;
+ COUNT i;
+ int queued = 0;
+ int toplevel = 0;
+ UNICODE *TrackNames[MAX_NUMBER_TRACKS];
+ UNICODE numbuf[60];
+ const SPEECH_DIGIT* dig = NULL;
+
+ if (!speech)
+ return 0;
+
+ if (!ptrack)
+ {
+ toplevel = 1;
+ if (!fmt)
+ fmt = "%d";
+ sprintf (numbuf, fmt, number);
+ ptrack = TrackNames;
+ }
+
+ for (i = 0; i < speech->NumDigits; ++i)
+ {
+ int quot;
+
+ dig = speech->Digits + i;
+ quot = number / dig->Divider;
+
+ if (quot == 0)
+ continue;
+ quot -= dig->Subtrahend;
+ if (quot < 0)
+ continue;
+
+ if (dig->StrDigits)
+ {
+ COUNT index;
+
+ assert (quot < 10);
+ index = dig->StrDigits[quot];
+ if (index == 0)
+ continue;
+ index -= 1;
+
+ *ptrack++ = GetStringSoundClip (SetAbsStringTableIndex (
+ CommData.ConversationPhrases, index
+ ));
+ queued++;
+ }
+ else
+ {
+ int ctracks = NPCNumberPhrase (quot, NULL, ptrack);
+ ptrack += ctracks;
+ queued += ctracks;
+ }
+
+ if (dig->Names != 0)
+ {
+ SPEECH_DIGITNAME* name;
+
+ for (name = dig->Names; name->Divider; ++name)
+ {
+ if (number % name->Divider <= name->MaxRemainder)
+ {
+ *ptrack++ = GetStringSoundClip (
+ SetAbsStringTableIndex (
+ CommData.ConversationPhrases, name->StrIndex - 1));
+ queued++;
+ break;
+ }
+ }
+ }
+ else if (dig->CommonNameIndex != 0)
+ {
+ *ptrack++ = GetStringSoundClip (SetAbsStringTableIndex (
+ CommData.ConversationPhrases, dig->CommonNameIndex - 1));
+ queued++;
+ }
+
+ number %= dig->Divider;
+ }
+
+ if (toplevel)
+ {
+ if (queued == 0)
+ { // nothing queued, say "zero"
+ assert (number == 0);
+ *ptrack++ = GetStringSoundClip (SetAbsStringTableIndex (
+ CommData.ConversationPhrases, dig->StrDigits[number] - 1));
+ }
+ *ptrack++ = NULL; // term
+
+ SpliceMultiTrack (TrackNames, numbuf);
+ }
+
+ return queued;
+}
+
+void
+GetAllianceName (UNICODE *buf, RESPONSE_REF name_1)
+{
+ COUNT i;
+ STRING S;
+
+ i = GET_GAME_STATE (NEW_ALLIANCE_NAME);
+ S = SetAbsStringTableIndex (CommData.ConversationPhrases, (name_1 - 1) + i);
+ // XXX: this should someday be changed so that the function takes
+ // the buffer size as an argument
+ strcpy (buf, (UNICODE *)GetStringAddress (S));
+ if (i == 3)
+ {
+ strcat (buf, GLOBAL_SIS (CommanderName));
+ strcat (buf, (UNICODE *)GetStringAddress (SetRelStringTableIndex (S, 1)));
+ }
+}
+
+void
+construct_response (UNICODE *buf, int R /* promoted from RESPONSE_REF */, ...)
+{
+ UNICODE *buf_start = buf;
+ UNICODE *name;
+ va_list vlist;
+
+ va_start (vlist, R);
+
+ do
+ {
+ COUNT len;
+ STRING S;
+
+ S = SetAbsStringTableIndex (CommData.ConversationPhrases, R - 1);
+
+ strcpy (buf, (UNICODE *)GetStringAddress (S));
+
+ len = strlen (buf);
+
+ buf += len;
+
+ name = va_arg (vlist, UNICODE *);
+
+ if (name)
+ {
+ len = strlen (name);
+ strcpy (buf, name);
+ buf += len;
+
+ /*
+ if ((R = va_arg (vlist, RESPONSE_REF)) == (RESPONSE_REF)-1)
+ name = 0;
+ */
+
+ R = va_arg(vlist, int);
+ if (R == ((RESPONSE_REF) -1))
+ name = 0;
+ }
+ } while (name);
+ va_end (vlist);
+
+ *buf = '\0';
+
+ // XXX: this should someday be changed so that the function takes
+ // the buffer size as an argument
+ if ((buf_start == shared_phrase_buf) &&
+ (buf > shared_phrase_buf + sizeof (shared_phrase_buf)))
+ {
+ log_add (log_Fatal, "Error: shared_phrase_buf size exceeded,"
+ " please increase!\n");
+ exit (EXIT_FAILURE);
+ }
+}
+
+void
+setSegue (Segue segue)
+{
+ switch (segue)
+ {
+ case Segue_peace:
+ SET_GAME_STATE (BATTLE_SEGUE, 0);
+ break;
+ case Segue_hostile:
+ SET_GAME_STATE (BATTLE_SEGUE, 1);
+ break;
+ case Segue_victory:
+ instantVictory = TRUE;
+ SET_GAME_STATE (BATTLE_SEGUE, 1);
+ break;
+ case Segue_defeat:
+ SET_GAME_STATE (BATTLE_SEGUE, 0);
+ GLOBAL_SIS(CrewEnlisted) = (COUNT)~0;
+ GLOBAL(CurrentActivity) |= CHECK_RESTART;
+ break;
+ }
+}
+
+Segue
+getSegue (void)
+{
+ if (GET_GAME_STATE(BATTLE_SEGUE) == 0) {
+ if (GLOBAL_SIS(CrewEnlisted) == (COUNT)~0 &&
+ (GLOBAL(CurrentActivity) & CHECK_RESTART)) {
+ return Segue_defeat;
+ } else {
+ return Segue_peace;
+ }
+ } else /* GET_GAME_STATE(BATTLE_SEGUE) == 1) */ {
+ if (instantVictory) {
+ return Segue_victory;
+ } else {
+ return Segue_hostile;
+ }
+ }
+}
+
+LOCDATA*
+init_race (CONVERSATION comm_id)
+{
+ switch (comm_id)
+ {
+ case ARILOU_CONVERSATION:
+ return init_arilou_comm ();
+ case BLACKURQ_CONVERSATION:
+ return init_blackurq_comm ();
+ case CHMMR_CONVERSATION:
+ return init_chmmr_comm ();
+ case COMMANDER_CONVERSATION:
+ if (!GET_GAME_STATE (STARBASE_AVAILABLE))
+ return init_commander_comm ();
+ else
+ return init_starbase_comm ();
+ case DRUUGE_CONVERSATION:
+ return init_druuge_comm ();
+ case ILWRATH_CONVERSATION:
+ return init_ilwrath_comm ();
+ case MELNORME_CONVERSATION:
+ return init_melnorme_comm ();
+ case MYCON_CONVERSATION:
+ return init_mycon_comm ();
+ case ORZ_CONVERSATION:
+ return init_orz_comm ();
+ case PKUNK_CONVERSATION:
+ return init_pkunk_comm ();
+ case SHOFIXTI_CONVERSATION:
+ return init_shofixti_comm ();
+ case SLYLANDRO_CONVERSATION:
+ return init_slyland_comm ();
+ case SLYLANDRO_HOME_CONVERSATION:
+ return init_slylandro_comm ();
+ case SPATHI_CONVERSATION:
+ if (!(GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) & (1 << 7)))
+ return init_spathi_comm ();
+ else
+ return init_spahome_comm ();
+ case SUPOX_CONVERSATION:
+ return init_supox_comm ();
+ case SYREEN_CONVERSATION:
+ return init_syreen_comm ();
+ case TALKING_PET_CONVERSATION:
+ return init_talkpet_comm ();
+ case THRADD_CONVERSATION:
+ return init_thradd_comm ();
+ case UMGAH_CONVERSATION:
+ return init_umgah_comm ();
+ case URQUAN_CONVERSATION:
+ return init_urquan_comm ();
+ case UTWIG_CONVERSATION:
+ return init_utwig_comm ();
+ case VUX_CONVERSATION:
+ return init_vux_comm ();
+ case YEHAT_REBEL_CONVERSATION:
+ return init_rebel_yehat_comm ();
+ case YEHAT_CONVERSATION:
+ return init_yehat_comm ();
+ case ZOQFOTPIK_CONVERSATION:
+ return init_zoqfot_comm ();
+ default:
+ return init_chmmr_comm ();
+ }
+}
diff --git a/src/uqm/commglue.h b/src/uqm/commglue.h
new file mode 100644
index 0000000..5a1e440
--- /dev/null
+++ b/src/uqm/commglue.h
@@ -0,0 +1,183 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_COMMGLUE_H_
+#define UQM_COMMGLUE_H_
+
+#include "globdata.h"
+#include "resinst.h"
+#include "libs/sound/trackplayer.h"
+#include "libs/callback.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef enum {
+ ARILOU_CONVERSATION,
+ CHMMR_CONVERSATION,
+ COMMANDER_CONVERSATION,
+ ORZ_CONVERSATION,
+ PKUNK_CONVERSATION,
+ SHOFIXTI_CONVERSATION,
+ SPATHI_CONVERSATION,
+ SUPOX_CONVERSATION,
+ THRADD_CONVERSATION,
+ UTWIG_CONVERSATION,
+ VUX_CONVERSATION,
+ YEHAT_CONVERSATION,
+ MELNORME_CONVERSATION,
+ DRUUGE_CONVERSATION,
+ ILWRATH_CONVERSATION,
+ MYCON_CONVERSATION,
+ SLYLANDRO_CONVERSATION,
+ UMGAH_CONVERSATION,
+ URQUAN_CONVERSATION,
+ ZOQFOTPIK_CONVERSATION,
+ SYREEN_CONVERSATION,
+ BLACKURQ_CONVERSATION,
+ TALKING_PET_CONVERSATION,
+ SLYLANDRO_HOME_CONVERSATION,
+ URQUAN_DRONE_CONVERSATION,
+ YEHAT_REBEL_CONVERSATION,
+ INVALID_CONVERSATION,
+} CONVERSATION;
+
+extern LOCDATA CommData;
+extern UNICODE shared_phrase_buf[2048];
+
+#define PLAYER_SAID(r,i) ((r)==(i))
+#define PHRASE_ENABLED(p) \
+ (*(UNICODE *)GetStringAddress ( \
+ SetAbsStringTableIndex (CommData.ConversationPhrases, (p)-1) \
+ ) != '\0')
+#define DISABLE_PHRASE(p) \
+ (*(UNICODE *)GetStringAddress ( \
+ SetAbsStringTableIndex (CommData.ConversationPhrases, (p)-1) \
+ ) = '\0')
+
+#define Response(i,a) \
+ DoResponsePhrase(i,(RESPONSE_FUNC)a,0)
+
+enum
+{
+ GLOBAL_PLAYER_NAME = -1000000,
+ GLOBAL_SHIP_NAME,
+ GLOBAL_ALLIANCE_NAME,
+};
+
+typedef COUNT RESPONSE_REF;
+
+typedef void (*RESPONSE_FUNC) (RESPONSE_REF R);
+
+extern void DoResponsePhrase (RESPONSE_REF R, RESPONSE_FUNC
+ response_func, UNICODE *ContstructStr);
+extern void DoNPCPhrase (UNICODE *pStr);
+
+// The CallbackFunction is queued and executes synchronously
+// on the Starcon2Main thread
+extern void NPCPhrase_cb (int index, CallbackFunction cb);
+#define NPCPhrase(index) NPCPhrase_cb ((index), NULL)
+extern void NPCPhrase_splice (int index);
+extern void NPCNumber (int number, const char *fmt);
+
+#define ALLIANCE_NAME_BUFSIZE 256
+extern void GetAllianceName (UNICODE *buf, RESPONSE_REF name_1);
+
+extern void construct_response (UNICODE *buf, int R /* promoted from
+ RESPONSE_REF */, ...);
+
+typedef enum {
+ Segue_peace,
+ // When initiating a conversation, open comms directly.
+ // When terminating a conversation, depart in peace.
+ Segue_hostile,
+ // When initiating a conversation, offer the choice to attack.
+ // When terminating a conversation, go into battle.
+ Segue_victory,
+ // (when terminating a conversation) instant victory
+ Segue_defeat,
+ // (when terminating a conversation) game over
+} Segue;
+
+void setSegue (Segue segue);
+Segue getSegue (void);
+
+extern LOCDATA* init_race (CONVERSATION comm_id);
+
+extern LOCDATA* init_arilou_comm (void);
+
+extern LOCDATA* init_blackurq_comm (void);
+
+extern LOCDATA* init_chmmr_comm (void);
+
+extern LOCDATA* init_commander_comm (void);
+
+extern LOCDATA* init_druuge_comm (void);
+
+extern LOCDATA* init_ilwrath_comm (void);
+
+extern LOCDATA* init_melnorme_comm (void);
+
+extern LOCDATA* init_mycon_comm (void);
+
+extern LOCDATA* init_orz_comm (void);
+
+extern LOCDATA* init_pkunk_comm (void);
+
+extern LOCDATA* init_rebel_yehat_comm (void);
+
+extern LOCDATA* init_shofixti_comm (void);
+
+extern LOCDATA* init_slyland_comm (void);
+
+extern LOCDATA* init_slylandro_comm (void);
+
+extern LOCDATA* init_spahome_comm (void);
+
+extern LOCDATA* init_spathi_comm (void);
+
+extern LOCDATA* init_starbase_comm (void);
+
+extern LOCDATA* init_supox_comm (void);
+
+extern LOCDATA* init_syreen_comm (void);
+
+extern LOCDATA* init_talkpet_comm (void);
+
+extern LOCDATA* init_thradd_comm (void);
+
+extern LOCDATA* init_umgah_comm (void);
+
+extern LOCDATA* init_urquan_comm (void);
+
+extern LOCDATA* init_utwig_comm (void);
+
+extern LOCDATA* init_vux_comm (void);
+
+extern LOCDATA* init_yehat_comm (void);
+
+extern LOCDATA* init_zoqfot_comm (void);
+
+extern LOCDATA* init_umgah_comm (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_COMMGLUE_H_ */
diff --git a/src/uqm/confirm.c b/src/uqm/confirm.c
new file mode 100644
index 0000000..a24472b
--- /dev/null
+++ b/src/uqm/confirm.c
@@ -0,0 +1,250 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "controls.h"
+#include "colors.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "gamestr.h"
+#include "util.h"
+#include "libs/graphics/widgets.h"
+#include "libs/sound/trackplayer.h"
+#include "libs/log.h"
+#include "libs/resource/stringbank.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+
+
+#define CONFIRM_WIN_WIDTH 80
+#define CONFIRM_WIN_HEIGHT 22
+
+static void
+DrawConfirmationWindow (BOOLEAN answer)
+{
+ Color oldfg = SetContextForeGroundColor (MENU_TEXT_COLOR);
+ FONT oldfont = SetContextFont (StarConFont);
+ FRAME oldFontEffect = SetContextFontEffect (NULL);
+ RECT r;
+ TEXT t;
+
+ BatchGraphics ();
+ r.corner.x = (SCREEN_WIDTH - CONFIRM_WIN_WIDTH) >> 1;
+ r.corner.y = (SCREEN_HEIGHT - CONFIRM_WIN_HEIGHT) >> 1;
+ r.extent.width = CONFIRM_WIN_WIDTH;
+ r.extent.height = CONFIRM_WIN_HEIGHT;
+ DrawShadowedBox (&r, SHADOWBOX_BACKGROUND_COLOR,
+ SHADOWBOX_DARK_COLOR, SHADOWBOX_MEDIUM_COLOR);
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + 8;
+ t.pStr = GAME_STRING (QUITMENU_STRING_BASE); // "Really Quit?"
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 10;
+ t.baseline.x = r.corner.x + (r.extent.width >> 2);
+ t.pStr = GAME_STRING (QUITMENU_STRING_BASE + 1); // "Yes"
+ SetContextForeGroundColor (answer ? MENU_HIGHLIGHT_COLOR : MENU_TEXT_COLOR);
+ font_DrawText (&t);
+ t.baseline.x += (r.extent.width >> 1);
+ t.pStr = GAME_STRING (QUITMENU_STRING_BASE + 2); // "No"
+ SetContextForeGroundColor (answer ? MENU_TEXT_COLOR : MENU_HIGHLIGHT_COLOR);
+ font_DrawText (&t);
+
+ UnbatchGraphics ();
+
+ SetContextFontEffect (oldFontEffect);
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldfg);
+}
+
+BOOLEAN
+DoConfirmExit (void)
+{
+ BOOLEAN result;
+
+ if (PlayingTrack ())
+ PauseTrack ();
+
+ PauseFlash ();
+
+ {
+ RECT r;
+ STAMP s;
+ RECT ctxRect;
+ CONTEXT oldContext;
+ RECT oldRect;
+ BOOLEAN response = FALSE, done;
+
+ oldContext = SetContext (ScreenContext);
+ GetContextClipRect (&oldRect);
+ SetContextClipRect (NULL);
+
+ GetContextClipRect (&ctxRect);
+ r.extent.width = CONFIRM_WIN_WIDTH + 4;
+ r.extent.height = CONFIRM_WIN_HEIGHT + 4;
+ r.corner.x = (ctxRect.extent.width - r.extent.width) >> 1;
+ r.corner.y = (ctxRect.extent.height - r.extent.height) >> 1;
+ s = SaveContextFrame (&r);
+ SetSystemRect (&r);
+
+ DrawConfirmationWindow (response);
+ FlushGraphics ();
+
+ FlushInput ();
+ done = FALSE;
+
+ do {
+ // Forbid recursive calls or pausing here!
+ ExitRequested = FALSE;
+ GamePaused = FALSE;
+ UpdateInputState ();
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ { // something else triggered an exit
+ done = TRUE;
+ response = TRUE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ done = TRUE;
+ PlayMenuSound (MENU_SOUND_SUCCESS);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ done = TRUE;
+ response = FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT] || PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ response = !response;
+ DrawConfirmationWindow (response);
+ PlayMenuSound (MENU_SOUND_MOVE);
+ }
+ SleepThread (ONE_SECOND / 30);
+ } while (!done);
+
+ // Restore the screen under the confirmation window
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+ ClearSystemRect ();
+ if (response || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ result = TRUE;
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ }
+ else
+ {
+ result = FALSE;
+ }
+ ExitRequested = FALSE;
+ GamePaused = FALSE;
+ FlushInput ();
+ SetContextClipRect (&oldRect);
+ SetContext (oldContext);
+ }
+
+ ContinueFlash ();
+
+ if (PlayingTrack ())
+ ResumeTrack ();
+
+ return (result);
+}
+
+typedef struct popup_state
+{
+ // standard state required by DoInput
+ BOOLEAN (*InputFunc) (struct popup_state *self);
+} POPUP_STATE;
+
+static BOOLEAN
+DoPopup (struct popup_state *self)
+{
+ (void)self;
+ SleepThread (ONE_SECOND / 20);
+ return !(PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL] ||
+ (GLOBAL (CurrentActivity) & CHECK_ABORT));
+}
+
+void
+DoPopupWindow (const char *msg)
+{
+ stringbank *bank = StringBank_Create ();
+ const char *lines[30];
+ WIDGET_LABEL label;
+ STAMP s;
+ CONTEXT oldContext;
+ RECT oldRect;
+ RECT windowRect;
+ POPUP_STATE state;
+ MENU_SOUND_FLAGS s0, s1;
+ InputFrameCallback *oldCallback;
+
+ if (!bank)
+ {
+ log_add (log_Fatal, "FATAL: Memory exhaustion when preparing popup window");
+ exit (EXIT_FAILURE);
+ }
+
+ label.tag = WIDGET_TYPE_LABEL;
+ label.parent = NULL;
+ label.handleEvent = Widget_HandleEventIgnoreAll;
+ label.receiveFocus = Widget_ReceiveFocusRefuseFocus;
+ label.draw = Widget_DrawLabel;
+ label.height = Widget_HeightLabel;
+ label.width = Widget_WidthFullScreen;
+ label.line_count = SplitString (msg, '\n', 30, lines, bank);
+ label.lines = lines;
+
+ PauseFlash ();
+
+ oldContext = SetContext (ScreenContext);
+ GetContextClipRect (&oldRect);
+ SetContextClipRect (NULL);
+
+ // TODO: Maybe DrawLabelAsWindow() should return a saved STAMP?
+ // We do not know the dimensions here, and so save the whole context
+ s = SaveContextFrame (NULL);
+
+ Widget_SetFont (StarConFont);
+ Widget_SetWindowColors (SHADOWBOX_BACKGROUND_COLOR, SHADOWBOX_DARK_COLOR,
+ SHADOWBOX_MEDIUM_COLOR);
+ DrawLabelAsWindow (&label, &windowRect);
+ SetSystemRect (&windowRect);
+
+ GetMenuSounds (&s0, &s1);
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ oldCallback = SetInputCallback (NULL);
+
+ state.InputFunc = DoPopup;
+ DoInput (&state, TRUE);
+
+ SetInputCallback (oldCallback);
+ ClearSystemRect ();
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+ SetContextClipRect (&oldRect);
+ SetContext (oldContext);
+ ContinueFlash ();
+ SetMenuSounds (s0, s1);
+ StringBank_Free (bank);
+}
+
diff --git a/src/uqm/cons_res.c b/src/uqm/cons_res.c
new file mode 100644
index 0000000..9db9258
--- /dev/null
+++ b/src/uqm/cons_res.c
@@ -0,0 +1,112 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+
+#include "cons_res.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "setup.h"
+#include "units.h"
+ // for NUM_VIEWS
+#include "planets/planets.h"
+ // for NUMBER_OF_PLANET_TYPES, PLANET_SHIELDED
+
+static const char *planet_types[] = {
+ "oolite", "yttric", "quasidegenerate", "lanthanide", "treasure",
+ "urea", "metal", "radioactive", "opalescent", "cyanic",
+ "acid", "alkali", "halide", "green", "copper",
+ "carbide", "ultramarine", "noble", "azure", "chondrite",
+ "purple", "superdense", "pellucid", "dust", "crimson",
+ "cimmerian", "infrared", "selenic", "auric", "fluorescent",
+ "ultraviolet", "plutonic", "rainbow", "shattered", "sapphire",
+ "organic", "xenolithic", "redux", "primordial", "emerald",
+ "chlorine", "magnetic", "water", "telluric", "hydrocarbon",
+ "iodine", "vinylogous", "ruby", "magma", "maroon",
+ "bluegas", "cyangas", "greengas", "greygas", "orangegas",
+ "purplegas", "redgas", "violetgas", "yellowgas"
+};
+
+static const char *planet_sizes[] = {
+ "large", "medium", "small"
+};
+
+FRAME planet[NUM_VIEWS];
+static char buffer[80];
+
+void
+load_gravity_well (BYTE selector)
+{
+ COUNT i;
+
+ if (selector == NUMBER_OF_PLANET_TYPES)
+ {
+ planet[0] = CaptureDrawable (
+ LoadGraphic (SAMATRA_BIG_MASK_PMAP_ANIM)
+ );
+ planet[1] = planet[2] = 0;
+ }
+ else
+ {
+ const char *ptype;
+ if (selector & PLANET_SHIELDED)
+ {
+ ptype = "slaveshield";
+ }
+ else
+ {
+ ptype = planet_types[selector];
+ }
+
+ for (i = 0; i < NUM_VIEWS; ++i)
+ {
+ snprintf (buffer, 79, "planet.%s.%s", ptype, planet_sizes[i]);
+ buffer[79] = '\0';
+ planet[i] = CaptureDrawable (LoadGraphic (buffer));
+ }
+ }
+
+}
+
+void
+free_gravity_well (void)
+{
+ COUNT i;
+
+ for (i = 0; i < NUM_VIEWS; ++i)
+ {
+ DestroyDrawable (ReleaseDrawable (planet[i]));
+ planet[i] = 0;
+ }
+}
+
+FRAME
+load_life_form (BYTE selector)
+{
+ snprintf (buffer, 79, "graphics.life.%d", selector);
+ buffer[79] = '\0'; /* Shouldn't be necessary, but better safe than sorry */
+ return CaptureDrawable (LoadGraphic (buffer));
+}
+
+MUSIC_REF
+load_orbit_theme (BYTE selector)
+{
+ snprintf (buffer, 79, "music.orbit%d", selector + 1);
+ buffer[79] = '\0'; /* Shouldn't be necessary, but better safe than sorry */
+ return LoadMusic (buffer);
+}
diff --git a/src/uqm/cons_res.h b/src/uqm/cons_res.h
new file mode 100644
index 0000000..2325e16
--- /dev/null
+++ b/src/uqm/cons_res.h
@@ -0,0 +1,38 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CONS_RES_H_
+#define CONS_RES_H_
+
+#include "libs/gfxlib.h"
+#include "libs/sndlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void load_gravity_well (BYTE selector);
+void free_gravity_well (void);
+
+FRAME load_life_form (BYTE selector);
+
+MUSIC_REF load_orbit_theme (BYTE selector);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* CONS_RES_H_ */
diff --git a/src/uqm/controls.h b/src/uqm/controls.h
new file mode 100644
index 0000000..8273aed
--- /dev/null
+++ b/src/uqm/controls.h
@@ -0,0 +1,172 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_CONTROLS_H_
+#define UQM_CONTROLS_H_
+
+#include "libs/compiler.h"
+#include "libs/strlib.h"
+#include "libs/timelib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// Enumerated type for controls
+enum {
+ KEY_UP,
+ KEY_DOWN,
+ KEY_LEFT,
+ KEY_RIGHT,
+ KEY_WEAPON,
+ KEY_SPECIAL,
+ KEY_ESCAPE,
+ NUM_KEYS
+};
+enum {
+ KEY_PAUSE,
+ KEY_EXIT,
+ KEY_ABORT,
+ KEY_DEBUG,
+ KEY_FULLSCREEN,
+ KEY_MENU_UP,
+ KEY_MENU_DOWN,
+ KEY_MENU_LEFT,
+ KEY_MENU_RIGHT,
+ KEY_MENU_SELECT,
+ KEY_MENU_CANCEL,
+ KEY_MENU_SPECIAL,
+ KEY_MENU_PAGE_UP,
+ KEY_MENU_PAGE_DOWN,
+ KEY_MENU_HOME,
+ KEY_MENU_END,
+ KEY_MENU_ZOOM_IN,
+ KEY_MENU_ZOOM_OUT,
+ KEY_MENU_DELETE,
+ KEY_MENU_BACKSPACE,
+ KEY_MENU_EDIT_CANCEL,
+ KEY_MENU_SEARCH,
+ KEY_MENU_NEXT,
+ KEY_MENU_ANY, /* abstract char key */
+ NUM_MENU_KEYS
+};
+
+typedef enum {
+ CONTROL_TEMPLATE_KB_1,
+ CONTROL_TEMPLATE_KB_2,
+ CONTROL_TEMPLATE_KB_3,
+ CONTROL_TEMPLATE_JOY_1,
+ CONTROL_TEMPLATE_JOY_2,
+ CONTROL_TEMPLATE_JOY_3,
+ NUM_TEMPLATES
+} CONTROL_TEMPLATE;
+
+typedef struct _controller_input_state {
+ int key[NUM_TEMPLATES][NUM_KEYS];
+ int menu[NUM_MENU_KEYS];
+} CONTROLLER_INPUT_STATE;
+
+typedef UBYTE BATTLE_INPUT_STATE;
+#define BATTLE_LEFT ((BATTLE_INPUT_STATE)(1 << 0))
+#define BATTLE_RIGHT ((BATTLE_INPUT_STATE)(1 << 1))
+#define BATTLE_THRUST ((BATTLE_INPUT_STATE)(1 << 2))
+#define BATTLE_WEAPON ((BATTLE_INPUT_STATE)(1 << 3))
+#define BATTLE_SPECIAL ((BATTLE_INPUT_STATE)(1 << 4))
+#define BATTLE_ESCAPE ((BATTLE_INPUT_STATE)(1 << 5))
+#define BATTLE_DOWN ((BATTLE_INPUT_STATE)(1 << 6))
+
+BATTLE_INPUT_STATE CurrentInputToBattleInput (COUNT player);
+BATTLE_INPUT_STATE PulsedInputToBattleInput (COUNT player);
+
+extern CONTROLLER_INPUT_STATE CurrentInputState;
+extern CONTROLLER_INPUT_STATE PulsedInputState;
+extern volatile CONTROLLER_INPUT_STATE ImmediateInputState;
+extern CONTROL_TEMPLATE PlayerControls[];
+
+void UpdateInputState (void);
+extern void FlushInput (void);
+void SetMenuRepeatDelay (DWORD min, DWORD max, DWORD step, BOOLEAN gestalt);
+void SetDefaultMenuRepeatDelay (void);
+void ResetKeyRepeat (void);
+BOOLEAN PauseGame (void);
+void SleepGame (void);
+BOOLEAN DoConfirmExit (void);
+BOOLEAN ConfirmExit (void);
+
+#define WAIT_INFINITE ((TimePeriod)-1)
+BOOLEAN WaitForAnyButton (BOOLEAN newButton, TimePeriod duration,
+ BOOLEAN resetInput);
+BOOLEAN WaitForAnyButtonUntil (BOOLEAN newButton, TimeCount timeOut,
+ BOOLEAN resetInput);
+BOOLEAN WaitForNoInput (TimePeriod duration, BOOLEAN resetInput);
+BOOLEAN WaitForNoInputUntil (TimeCount timeOut, BOOLEAN resetInput);
+
+void DoPopupWindow(const char *msg);
+
+typedef void (InputFrameCallback) (void);
+InputFrameCallback* SetInputCallback (InputFrameCallback *);
+// pInputState must point to a struct derived from INPUT_STATE_DESC
+void DoInput (void *pInputState, BOOLEAN resetInput);
+
+extern volatile BOOLEAN GamePaused;
+extern volatile BOOLEAN ExitRequested;
+
+typedef struct joy_char joy_char_t;
+
+typedef struct textentry_state
+{
+ // standard state required by DoInput
+ BOOLEAN (*InputFunc) (struct textentry_state *pTES);
+
+ // these are semi-private read-only
+ BOOLEAN Initialized;
+ DWORD NextTime; // use this for input frame timing
+ BOOLEAN Success; // edit confirmed or canceled
+ UNICODE *CacheStr; // cached copy to revert immediate changes
+ STRING JoyAlphaString; // joystick alphabet definition
+ BOOLEAN JoystickMode; // TRUE when doing joystick input
+ BOOLEAN UpperRegister; // TRUE when entering Caps
+ joy_char_t *JoyAlpha; // joystick alphabet
+ int JoyAlphaLength;
+ joy_char_t *JoyUpper; // joystick upper register
+ joy_char_t *JoyLower; // joystick lower register
+ int JoyRegLength;
+ UNICODE *InsPt; // set to current pos of insertion point
+ // these are public and must be set before calling DoTextEntry
+ UNICODE *BaseStr; // set to string to edit
+ int CursorPos; // set to current cursor pos in chars
+ int MaxSize; // set to max size of edited string
+
+ BOOLEAN (*ChangeCallback) (struct textentry_state *pTES);
+ // returns TRUE if last change is OK
+ BOOLEAN (*FrameCallback) (struct textentry_state *pTES);
+ // called on every input frame; do whatever;
+ // returns TRUE to continue processing
+ void *CbParam; // callback parameter, use as you like
+
+} TEXTENTRY_STATE;
+
+extern BOOLEAN DoTextEntry (TEXTENTRY_STATE *pTES);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
+
diff --git a/src/uqm/corecode.h b/src/uqm/corecode.h
new file mode 100644
index 0000000..11bd449
--- /dev/null
+++ b/src/uqm/corecode.h
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CORECODE_H_
+#define CORECODE_H_
+
+#include "ships/androsyn/icode.h"
+#include "ships/arilou/icode.h"
+#include "ships/blackurq/icode.h"
+#include "ships/chenjesu/icode.h"
+#include "ships/chmmr/icode.h"
+#include "ships/druuge/icode.h"
+#include "ships/human/icode.h"
+#include "ships/ilwrath/icode.h"
+#include "ships/lastbat/icode.h"
+#include "ships/melnorme/icode.h"
+#include "ships/mmrnmhrm/icode.h"
+#include "ships/mycon/icode.h"
+#include "ships/orz/icode.h"
+#include "ships/pkunk/icode.h"
+#include "ships/probe/icode.h"
+#include "ships/shofixti/icode.h"
+#include "ships/sis_ship/icode.h"
+#include "ships/slylandr/icode.h"
+#include "ships/spathi/icode.h"
+#include "ships/supox/icode.h"
+#include "ships/syreen/icode.h"
+#include "ships/thradd/icode.h"
+#include "ships/umgah/icode.h"
+#include "ships/urquan/icode.h"
+#include "ships/utwig/icode.h"
+#include "ships/vux/icode.h"
+#include "ships/yehat/icode.h"
+#include "ships/zoqfot/icode.h"
+
+#endif /* CORECODE_H_ */
diff --git a/src/uqm/credits.c b/src/uqm/credits.c
new file mode 100644
index 0000000..1889484
--- /dev/null
+++ b/src/uqm/credits.c
@@ -0,0 +1,839 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "credits.h"
+
+#include "controls.h"
+#include "colors.h"
+#include "options.h"
+#include "oscill.h"
+#include "comm.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "settings.h"
+#include "sounds.h"
+#include "setup.h"
+#include "libs/graphics/drawable.h"
+#include <math.h>
+
+// Rates in pixel lines per second
+#define CREDITS_BASE_RATE 9
+#define CREDITS_MAX_RATE 130
+// Maximum frame rate
+#define CREDITS_FRAME_RATE 36
+
+#define CREDITS_TIMEOUT (ONE_SECOND * 5)
+
+#define TRANS_COLOR BRIGHT_BLUE_COLOR
+
+// Positive or negative scroll rate in pixel lines per second
+static int CreditsRate;
+
+static BOOLEAN OutTakesRunning;
+static BOOLEAN CreditsRunning;
+static STRING CreditsTab;
+static FRAME CreditsBack;
+
+// Context used for drawing to the screen
+static CONTEXT DrawContext;
+// Context used for pre-rendering a credits frame
+static CONTEXT LocalContext;
+// Pre-rendered frame, possibly with a cutout
+static FRAME CreditsFrame;
+// Size of the credits "window" (normally screen size)
+static EXTENT CreditsExtent;
+
+typedef struct
+{
+ FRAME frame;
+ int strIndex;
+} CreditTextFrame;
+
+#define MAX_CREDIT_FRAMES 32
+// Circular text frame buffer for scrolling
+// Text frames are generated as needed, and when a text frame scrolls
+// of the screen, it is destroyed
+static CreditTextFrame textFrames[MAX_CREDIT_FRAMES];
+// Index of first active frame in the circular buffer (the first frame
+// is the one on top)
+static int firstFrame;
+// Index of last active frame in the circular buffer + 1
+static int lastFrame;
+// Total height of all active frames in the circular buffer
+static int totalHeight;
+// Current vertical offset into the first text frame
+static int curFrameOfs;
+
+typedef struct
+{
+ int size;
+ RESOURCE res;
+ FONT font;
+} FONT_SIZE_DEF;
+
+static FONT_SIZE_DEF CreditsFont[] =
+{
+ { 13, PT13AA_FONT, 0 },
+ { 17, PT17AA_FONT, 0 },
+ { 45, PT45AA_FONT, 0 },
+ { 0, 0, 0 },
+};
+
+
+static FRAME
+Credits_MakeTransFrame (int w, int h, Color TransColor)
+{
+ FRAME OldFrame;
+ FRAME f;
+
+ f = CaptureDrawable (CreateDrawable (WANT_PIXMAP, w, h, 1));
+ SetFrameTransparentColor (f, TransColor);
+
+ OldFrame = SetContextFGFrame (f);
+ SetContextBackGroundColor (TransColor);
+ ClearDrawable ();
+ SetContextFGFrame (OldFrame);
+
+ return f;
+}
+
+static int
+ParseTextLines (TEXT *Lines, int MaxLines, char *Buffer)
+{
+ int i;
+ const char* pEnd = Buffer + strlen (Buffer);
+
+ for (i = 0; i < MaxLines && Buffer < pEnd; ++i, ++Lines)
+ {
+ char* pTerm = strchr (Buffer, '\n');
+ if (!pTerm)
+ pTerm = Buffer + strlen (Buffer);
+ *pTerm = '\0'; /* terminate string */
+ Lines->pStr = Buffer;
+ Lines->CharCount = ~0;
+ Buffer = pTerm + 1;
+ }
+ return i;
+}
+
+#define MAX_TEXT_LINES 50
+#define MAX_TEXT_COLS 5
+
+static FRAME
+Credits_RenderTextFrame (CONTEXT TempContext, int *istr, int dir,
+ Color BackColor, Color ForeColor)
+{
+ FRAME f;
+ CONTEXT OldContext;
+ FRAME OldFrame;
+ TEXT TextLines[MAX_TEXT_LINES];
+ char *pStr = NULL;
+ int size;
+ char salign[32];
+ char *scol;
+ int scaned;
+ int i, rows, cnt;
+ char buf[2048];
+ FONT_SIZE_DEF *fdef;
+ SIZE leading;
+ TEXT t;
+ RECT r;
+ typedef struct
+ {
+ TEXT_ALIGN align;
+ COORD basex;
+ } col_format_t;
+ col_format_t colfmt[MAX_TEXT_COLS];
+
+ if (*istr < 0 || *istr >= GetStringTableCount (CreditsTab))
+ { // check if next one is within range
+ int next_s = *istr + dir;
+
+ if (next_s < 0 || next_s >= GetStringTableCount (CreditsTab))
+ return 0;
+
+ *istr = next_s;
+ }
+
+ // skip empty lines
+ while (*istr >= 0 && *istr < GetStringTableCount (CreditsTab))
+ {
+ pStr = GetStringAddress (
+ SetAbsStringTableIndex (CreditsTab, *istr));
+ *istr += dir;
+ if (pStr && *pStr != '\0')
+ break;
+ }
+
+ if (!pStr || *pStr == '\0')
+ return 0;
+
+ if (2 != sscanf (pStr, "%d %31s %n", &size, salign, &scaned)
+ || size <= 0)
+ return 0;
+ pStr += scaned;
+
+ utf8StringCopy (buf, sizeof (buf), pStr);
+ rows = ParseTextLines (TextLines, MAX_TEXT_LINES, buf);
+ if (rows == 0)
+ return 0;
+ // parse text columns
+ for (i = 0, cnt = rows; i < rows; ++i)
+ {
+ char *nextcol;
+ int icol;
+
+ // we abuse the baseline here, but only a tiny bit
+ // every line starts at col 0
+ TextLines[i].baseline.x = 0;
+ TextLines[i].baseline.y = i + 1;
+
+ for (icol = 1, nextcol = strchr (TextLines[i].pStr, '\t');
+ icol < MAX_TEXT_COLS && nextcol;
+ ++icol, nextcol = strchr (nextcol, '\t'))
+ {
+ *nextcol = '\0';
+ ++nextcol;
+
+ if (cnt < MAX_TEXT_LINES)
+ {
+ TextLines[cnt].pStr = nextcol;
+ TextLines[cnt].CharCount = ~0;
+ TextLines[cnt].baseline.x = icol;
+ TextLines[cnt].baseline.y = i + 1;
+ ++cnt;
+ }
+ }
+ }
+
+ // init alignments
+ for (i = 0; i < MAX_TEXT_COLS; ++i)
+ {
+ colfmt[i].align = ALIGN_LEFT;
+ colfmt[i].basex = CreditsExtent.width / 64;
+ }
+
+ // find the right font
+ for (fdef = CreditsFont; fdef->size && size > fdef->size; ++fdef)
+ ;
+ if (!fdef->size)
+ return 0;
+
+ t.align = ALIGN_LEFT;
+ t.baseline.x = 100; // any value will do
+ t.baseline.y = 100; // any value will do
+ t.pStr = " ";
+ t.CharCount = 1;
+
+ OldContext = SetContext (TempContext);
+
+ // get font dimensions
+ SetContextFont (fdef->font);
+ GetContextFontLeading (&leading);
+ // get left/right margin
+ TextRect (&t, &r, NULL);
+
+ // parse text column alignment
+ for (i = 0, scol = strtok (salign, ",");
+ scol && i < MAX_TEXT_COLS;
+ ++i, scol = strtok (NULL, ","))
+ {
+ char c;
+ int x;
+ int n;
+
+ // default
+ colfmt[i].align = ALIGN_LEFT;
+ colfmt[i].basex = r.extent.width;
+
+ n = sscanf (scol, "%c/%d", &c, &x);
+ if (n < 1)
+ { // DOES NOT COMPUTE! :)
+ continue;
+ }
+
+ switch (c)
+ {
+ case 'L':
+ colfmt[i].align = ALIGN_LEFT;
+ if (n >= 2)
+ colfmt[i].basex = x;
+ break;
+ case 'C':
+ colfmt[i].align = ALIGN_CENTER;
+ if (n >= 2)
+ colfmt[i].basex = x;
+ else
+ colfmt[i].basex = CreditsExtent.width / 2;
+ break;
+ case 'R':
+ colfmt[i].align = ALIGN_RIGHT;
+ if (n >= 2)
+ colfmt[i].basex = x;
+ else
+ colfmt[i].basex = CreditsExtent.width - r.extent.width;
+ break;
+ }
+ }
+
+ for (i = 0; i < cnt; ++i)
+ {
+ // baseline contains coords in row/col quantities
+ col_format_t *fmt = colfmt + TextLines[i].baseline.x;
+
+ TextLines[i].align = fmt->align;
+ TextLines[i].baseline.x = fmt->basex;
+ TextLines[i].baseline.y *= leading;
+ }
+
+ f = Credits_MakeTransFrame (CreditsExtent.width, leading * rows + (leading >> 1),
+ BackColor);
+ OldFrame = SetContextFGFrame (f);
+ // draw text
+ SetContextForeGroundColor (ForeColor);
+ for (i = 0; i < cnt; ++i)
+ font_DrawText (TextLines + i);
+
+ SetContextFGFrame (OldFrame);
+ SetContext (OldContext);
+
+ return f;
+}
+
+static inline int
+frameIndex (int index)
+{
+ // Make sure index is positive before %
+ return (index + MAX_CREDIT_FRAMES) % MAX_CREDIT_FRAMES;
+}
+
+static void
+RenderCreditsScreen (CONTEXT targetContext)
+{
+ CONTEXT oldContext;
+ STAMP s;
+ int i;
+
+ oldContext = SetContext (targetContext);
+ // draw background
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = CreditsBack;
+ DrawStamp (&s);
+
+ // draw text frames
+ s.origin.y = -curFrameOfs;
+ for (i = firstFrame; i != lastFrame; i = frameIndex (i + 1))
+ {
+ RECT fr;
+
+ s.frame = textFrames[i].frame;
+ DrawStamp (&s);
+ GetFrameRect (s.frame, &fr);
+ s.origin.y += fr.extent.height;
+ }
+
+ if (OutTakesRunning)
+ { // Cut out the Outtakes rect
+ SetContextForeGroundColor (TRANS_COLOR);
+ DrawFilledRectangle (&CommWndRect);
+ }
+
+ SetContext (oldContext);
+}
+
+static void
+InitCredits (void)
+{
+ RECT ctxRect;
+ CONTEXT oldContext;
+ FRAME targetFrame;
+
+ memset (textFrames, 0, sizeof textFrames);
+
+ LocalContext = CreateContext ("Credits.LocalContext");
+ DrawContext = CreateContext ("Credits.DrawContext");
+
+ targetFrame = GetContextFGFrame ();
+ GetContextClipRect (&ctxRect);
+ CreditsExtent = ctxRect.extent;
+
+ // prep our local context
+ oldContext = SetContext (LocalContext);
+ // Local screen copy. We draw everything to this frame, then cut
+ // the Outtakes rect out and draw this frame to the screen.
+ CreditsFrame = Credits_MakeTransFrame (CreditsExtent.width,
+ CreditsExtent.height, TRANS_COLOR);
+ SetContextFGFrame (CreditsFrame);
+
+ // The first credits frame is fake, the height of the screen,
+ // so that the credits can roll in from the bottom
+ textFrames[0].frame = Credits_MakeTransFrame (1, CreditsExtent.height,
+ TRANS_COLOR);
+ textFrames[0].strIndex = -1;
+ firstFrame = 0;
+ lastFrame = firstFrame + 1;
+
+ totalHeight = GetFrameHeight (textFrames[0].frame);
+ curFrameOfs = 0;
+
+ // We use an own screen draw context to avoid collisions
+ SetContext (DrawContext);
+ SetContextFGFrame (targetFrame);
+
+ SetContext (oldContext);
+
+ // Prepare the first screen frame
+ RenderCreditsScreen (LocalContext);
+
+ CreditsRate = CREDITS_BASE_RATE;
+ CreditsRunning = TRUE;
+}
+
+static void
+freeCreditTextFrame (CreditTextFrame *tf)
+{
+ DestroyDrawable (ReleaseDrawable (tf->frame));
+ tf->frame = NULL;
+}
+
+static void
+UninitCredits (void)
+{
+ DestroyContext (DrawContext);
+ DrawContext = NULL;
+ DestroyContext (LocalContext);
+ LocalContext = NULL;
+
+ // free remaining frames
+ DestroyDrawable (ReleaseDrawable (CreditsFrame));
+ CreditsFrame = NULL;
+ for ( ; firstFrame != lastFrame; firstFrame = frameIndex (firstFrame + 1))
+ freeCreditTextFrame (&textFrames[firstFrame]);
+}
+
+static int
+calcDeficitHeight (void)
+{
+ int i;
+ int maxPos;
+
+ maxPos = -curFrameOfs;
+ for (i = firstFrame; i != lastFrame; i = frameIndex (i + 1))
+ {
+ RECT fr;
+
+ GetFrameRect (textFrames[i].frame, &fr);
+ maxPos += fr.extent.height;
+ }
+
+ return CreditsExtent.height - maxPos;
+}
+
+static void
+processCreditsFrame (void)
+{
+ static TimeCount NextTime;
+ TimeCount Now = GetTimeCounter ();
+
+ if (Now >= NextTime)
+ {
+ RECT fr;
+ CONTEXT OldContext;
+ int rate, direction, dirstep;
+ int deficitHeight;
+ STAMP s;
+
+ rate = abs (CreditsRate);
+ if (rate != 0)
+ {
+ // scroll direction; forward or backward
+ direction = CreditsRate / rate;
+ // step in pixels
+ dirstep = (rate + CREDITS_FRAME_RATE - 1) / CREDITS_FRAME_RATE;
+ rate = ONE_SECOND * dirstep / rate;
+ // step is also directional
+ dirstep *= direction;
+ }
+ else
+ { // scroll stopped
+ direction = 0;
+ dirstep = 0;
+ // one second interframe
+ rate = ONE_SECOND;
+ }
+
+ NextTime = GetTimeCounter () + rate;
+
+ // draw the credits
+ // comm animations play with contexts so we need to make
+ // sure the context is not desynced
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = CreditsFrame;
+
+ OldContext = SetContext (DrawContext);
+ DrawStamp (&s);
+ SetContext (OldContext);
+ FlushGraphics ();
+
+ // prepare next screen frame
+ deficitHeight = calcDeficitHeight ();
+ curFrameOfs += dirstep;
+ // cap scroll
+ if (curFrameOfs < -(CreditsExtent.height / 20))
+ { // at the begining, deceleration
+ if (CreditsRate < 0)
+ CreditsRate -= CreditsRate / 10 - 1;
+ }
+ else if (deficitHeight > CreditsExtent.height / 25)
+ { // frame deficit -- credits almost over, deceleration
+ if (CreditsRate > 0)
+ CreditsRate -= CreditsRate / 10 + 1;
+
+ CreditsRunning = (CreditsRate != 0);
+ }
+ else if (!CreditsRunning)
+ { // resumed
+ CreditsRunning = TRUE;
+ }
+
+ if (firstFrame != lastFrame)
+ { // clean up frames that scrolled off the screen
+ if (direction > 0)
+ { // forward scroll
+ GetFrameRect (textFrames[firstFrame].frame, &fr);
+ if (curFrameOfs >= fr.extent.height)
+ { // past this frame already
+ totalHeight -= fr.extent.height;
+ freeCreditTextFrame (&textFrames[firstFrame]);
+ // next frame
+ firstFrame = frameIndex (firstFrame + 1);
+ curFrameOfs -= fr.extent.height;
+ }
+ }
+ else if (direction < 0)
+ { // backward scroll
+ int index = frameIndex (lastFrame - 1);
+ int framePos;
+
+ GetFrameRect (textFrames[index].frame, &fr);
+ framePos = totalHeight - curFrameOfs - fr.extent.height;
+ if (framePos >= CreditsExtent.height)
+ { // past this frame already
+ lastFrame = index;
+ totalHeight -= fr.extent.height;
+ freeCreditTextFrame (&textFrames[lastFrame]);
+ }
+ }
+ }
+
+ // render new text frames if needed
+ if (direction > 0)
+ { // forward scroll
+ int next_s = 0;
+
+ // get next string
+ if (firstFrame != lastFrame)
+ next_s = textFrames[frameIndex (lastFrame - 1)].strIndex + 1;
+
+ while (totalHeight - curFrameOfs < CreditsExtent.height
+ && next_s < GetStringTableCount (CreditsTab))
+ {
+ CreditTextFrame *tf = &textFrames[lastFrame];
+
+ tf->frame = Credits_RenderTextFrame (LocalContext, &next_s,
+ direction, BLACK_COLOR, CREDITS_TEXT_COLOR);
+ tf->strIndex = next_s - 1;
+ if (tf->frame)
+ {
+ GetFrameRect (tf->frame, &fr);
+ totalHeight += fr.extent.height;
+
+ lastFrame = frameIndex (lastFrame + 1);
+ }
+ }
+ }
+ else if (direction < 0)
+ { // backward scroll
+ int next_s = GetStringTableCount (CreditsTab) - 1;
+
+ // get next string
+ if (firstFrame != lastFrame)
+ next_s = textFrames[firstFrame].strIndex - 1;
+
+ while (curFrameOfs < 0 && next_s >= 0)
+ {
+ int index = frameIndex (firstFrame - 1);
+ CreditTextFrame *tf = &textFrames[index];
+
+ tf->frame = Credits_RenderTextFrame (LocalContext, &next_s,
+ direction, BLACK_COLOR, CREDITS_TEXT_COLOR);
+ tf->strIndex = next_s + 1;
+ if (tf->frame)
+ {
+ GetFrameRect (tf->frame, &fr);
+ totalHeight += fr.extent.height;
+
+ firstFrame = index;
+ curFrameOfs += fr.extent.height;
+ }
+ }
+ }
+
+ // draw next screen frame
+ RenderCreditsScreen (LocalContext);
+ }
+}
+
+static BOOLEAN
+LoadCredits (void)
+{
+ FONT_SIZE_DEF *fdef;
+
+ CreditsTab = CaptureStringTable (LoadStringTable (CREDITS_STRTAB));
+ if (!CreditsTab)
+ return FALSE;
+ CreditsBack = CaptureDrawable (LoadGraphic (CREDITS_BACK_ANIM));
+ // load fonts
+ for (fdef = CreditsFont; fdef->size; ++fdef)
+ fdef->font = LoadFont (fdef->res);
+
+ return TRUE;
+}
+
+static void
+FreeCredits (void)
+{
+ FONT_SIZE_DEF *fdef;
+
+ DestroyStringTable (ReleaseStringTable (CreditsTab));
+ CreditsTab = NULL;
+
+ DestroyDrawable (ReleaseDrawable (CreditsBack));
+ CreditsBack = NULL;
+
+ // free fonts
+ for (fdef = CreditsFont; fdef->size; ++fdef)
+ {
+ DestroyFont (fdef->font);
+ fdef->font = NULL;
+ }
+}
+
+static void
+OutTakes (void)
+{
+#define NUM_OUTTAKES 15
+ static CONVERSATION outtake_list[NUM_OUTTAKES] =
+ {
+ ZOQFOTPIK_CONVERSATION,
+ TALKING_PET_CONVERSATION,
+ ORZ_CONVERSATION,
+ UTWIG_CONVERSATION,
+ THRADD_CONVERSATION,
+ SUPOX_CONVERSATION,
+ SYREEN_CONVERSATION,
+ SHOFIXTI_CONVERSATION,
+ PKUNK_CONVERSATION,
+ YEHAT_CONVERSATION,
+ DRUUGE_CONVERSATION,
+ URQUAN_CONVERSATION,
+ VUX_CONVERSATION,
+ BLACKURQ_CONVERSATION,
+ ARILOU_CONVERSATION
+ };
+
+ BOOLEAN oldsubtitles = optSubtitles;
+ int i = 0;
+
+ // Outtakes have no voice tracks, so the subtitles are always on
+ optSubtitles = TRUE;
+ sliderDisabled = TRUE;
+ oscillDisabled = TRUE;
+
+ for (i = 0; (i < NUM_OUTTAKES) &&
+ !(GLOBAL (CurrentActivity) & CHECK_ABORT); i++)
+ {
+ SetCommIntroMode (CIM_CROSSFADE_WINDOW, 0);
+ InitCommunication (outtake_list[i]);
+ }
+
+ optSubtitles = oldsubtitles;
+ sliderDisabled = FALSE;
+ oscillDisabled = FALSE;
+}
+
+typedef struct
+{
+ // standard state required by DoInput
+ BOOLEAN (*InputFunc) (void *pInputState);
+
+ BOOLEAN AllowCancel;
+ BOOLEAN AllowSpeedChange;
+ BOOLEAN CloseWhenDone;
+ DWORD CloseTimeOut;
+
+} CREDITS_INPUT_STATE;
+
+static BOOLEAN
+DoCreditsInput (void *pIS)
+{
+ CREDITS_INPUT_STATE *pCIS = (CREDITS_INPUT_STATE *) pIS;
+
+ if (CreditsRunning)
+ { // cancel timeout if resumed (or just running)
+ pCIS->CloseTimeOut = 0;
+ }
+
+ if ((GLOBAL (CurrentActivity) & CHECK_ABORT)
+ || (pCIS->AllowCancel &&
+ (PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL]))
+ )
+ { // aborted
+ return FALSE;
+ }
+
+ if (pCIS->AllowSpeedChange
+ && (PulsedInputState.menu[KEY_MENU_UP]
+ || PulsedInputState.menu[KEY_MENU_DOWN]))
+ { // speed adjustment
+ int newrate = CreditsRate;
+ int step = abs (CreditsRate) / 5 + 1;
+
+ if (PulsedInputState.menu[KEY_MENU_DOWN])
+ newrate += step;
+ else if (PulsedInputState.menu[KEY_MENU_UP])
+ newrate -= step;
+ if (newrate < -CREDITS_MAX_RATE)
+ newrate = -CREDITS_MAX_RATE;
+ else if (newrate > CREDITS_MAX_RATE)
+ newrate = CREDITS_MAX_RATE;
+
+ CreditsRate = newrate;
+ }
+
+ if (!CreditsRunning)
+ { // always allow cancelling once credits run through
+ pCIS->AllowCancel = TRUE;
+ }
+
+ if (!CreditsRunning && pCIS->CloseWhenDone)
+ { // auto-close controlled by timeout
+ if (pCIS->CloseTimeOut == 0)
+ { // set timeout
+ pCIS->CloseTimeOut = GetTimeCounter () + CREDITS_TIMEOUT;
+ }
+ else if (GetTimeCounter () > pCIS->CloseTimeOut)
+ { // all done!
+ return FALSE;
+ }
+ }
+
+ if (!CreditsRunning
+ && (PulsedInputState.menu[KEY_MENU_SELECT]
+ || PulsedInputState.menu[KEY_MENU_CANCEL]))
+ { // credits finished and exit requested
+ return FALSE;
+ }
+
+ SleepThread (ONE_SECOND / CREDITS_FRAME_RATE);
+
+ return TRUE;
+}
+
+static void
+on_input_frame (void)
+{
+ processCreditsFrame ();
+}
+
+void
+Credits (BOOLEAN WithOuttakes)
+{
+ MUSIC_REF hMusic;
+ CREDITS_INPUT_STATE cis;
+ RECT screenRect;
+ STAMP s;
+
+ hMusic = LoadMusic (CREDITS_MUSIC);
+
+ SetContext (ScreenContext);
+ SetContextClipRect (NULL);
+ GetContextClipRect (&screenRect);
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+
+ if (!LoadCredits ())
+ return;
+
+ // Fade in the background
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = CreditsBack;
+ DrawStamp (&s);
+ FadeScreen (FadeAllToColor, ONE_SECOND / 2);
+
+ // set the position of outtakes comm
+ CommWndRect.corner.x = (screenRect.extent.width - CommWndRect.extent.width)
+ / 2;
+ CommWndRect.corner.y = 5;
+
+ InitCredits ();
+ SetInputCallback (on_input_frame);
+
+ if (WithOuttakes)
+ {
+ OutTakesRunning = TRUE;
+ OutTakes ();
+ OutTakesRunning = FALSE;
+ }
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (hMusic)
+ PlayMusic (hMusic, TRUE, 1);
+
+ // nothing to do now but wait until credits
+ // are done or canceled by user
+ cis.InputFunc = DoCreditsInput;
+ cis.AllowCancel = !WithOuttakes;
+ cis.CloseWhenDone = !WithOuttakes;
+ cis.AllowSpeedChange = TRUE;
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ DoInput (&cis, TRUE);
+ }
+
+ SetInputCallback (NULL);
+ FadeMusic (0, ONE_SECOND / 2);
+ UninitCredits ();
+
+ SetContext (ScreenContext);
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+ FlushColorXForms ();
+
+ if (hMusic)
+ {
+ StopMusic ();
+ DestroyMusic (hMusic);
+ }
+ FadeMusic (NORMAL_VOLUME, 0);
+
+ FreeCredits ();
+}
diff --git a/src/uqm/credits.h b/src/uqm/credits.h
new file mode 100644
index 0000000..c007b35
--- /dev/null
+++ b/src/uqm/credits.h
@@ -0,0 +1,32 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_CREDITS_H_
+#define UQM_CREDITS_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void Credits (BOOLEAN WithOuttakes);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_CREDITS_H_ */
diff --git a/src/uqm/cyborg.c b/src/uqm/cyborg.c
new file mode 100644
index 0000000..a50f3b8
--- /dev/null
+++ b/src/uqm/cyborg.c
@@ -0,0 +1,1339 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "colors.h"
+#include "collide.h"
+#include "element.h"
+#include "ship.h"
+#include "globdata.h"
+#include "intel.h"
+#include "setup.h"
+#include "units.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+//#define DEBUG_CYBORG
+
+COUNT
+PlotIntercept (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1,
+ COUNT max_turns, COUNT margin_of_error)
+{
+ SIZE dy;
+ SIZE time_y_0, time_y_1;
+ POINT dst[2];
+ RECT r0 = {{0, 0}, {0, 0}};
+ RECT r1 = {{0, 0}, {0, 0}};
+ SIZE dx_0, dy_0, dx_1, dy_1;
+
+ if ((ElementPtr0->state_flags | ElementPtr1->state_flags) & FINITE_LIFE)
+ {
+ if (!(ElementPtr0->state_flags & FINITE_LIFE))
+ {
+ if (ElementPtr1->life_span < max_turns)
+ max_turns = ElementPtr1->life_span;
+ }
+ else if (!(ElementPtr1->state_flags & FINITE_LIFE))
+ {
+ if (ElementPtr0->life_span < max_turns)
+ max_turns = ElementPtr0->life_span;
+ }
+ else
+ {
+ if (ElementPtr0->life_span < max_turns)
+ max_turns = ElementPtr0->life_span;
+ if (ElementPtr1->life_span < max_turns)
+ max_turns = ElementPtr1->life_span;
+ }
+ }
+
+ dst[0] = ElementPtr0->current.location;
+ GetCurrentVelocityComponents (&ElementPtr0->velocity, &dx_0, &dy_0);
+ dx_0 = (SIZE)VELOCITY_TO_WORLD ((long)dx_0 * (long)max_turns);
+ dy_0 = (SIZE)VELOCITY_TO_WORLD ((long)dy_0 * (long)max_turns);
+
+ dst[1] = ElementPtr1->current.location;
+ GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx_1, &dy_1);
+ dx_1 = (SIZE)VELOCITY_TO_WORLD ((long)dx_1 * (long)max_turns);
+ dy_1 = (SIZE)VELOCITY_TO_WORLD ((long)dy_1 * (long)max_turns);
+
+ if (margin_of_error)
+ {
+ dst[1].y -= margin_of_error;
+ time_y_0 = 1;
+ time_y_1 = margin_of_error << 1;
+ }
+ else
+ {
+ GetFrameRect (ElementPtr0->IntersectControl.IntersectStamp.frame, &r0);
+ GetFrameRect (ElementPtr1->IntersectControl.IntersectStamp.frame, &r1);
+
+ dst[0].y += DISPLAY_TO_WORLD (r0.corner.y);
+ dst[1].y += DISPLAY_TO_WORLD (r1.corner.y);
+ time_y_0 = DISPLAY_TO_WORLD (r0.extent.height);
+ time_y_1 = DISPLAY_TO_WORLD (r1.extent.height);
+ }
+
+ dy = dst[1].y - dst[0].y;
+ time_y_0 = dy - time_y_0 + 1;
+ time_y_1 = dy + time_y_1 - 1;
+ dy = dy_0 - dy_1;
+
+ if ((time_y_0 <= 0 && time_y_1 >= 0)
+ || (time_y_0 > 0 && dy >= time_y_0)
+ || (time_y_1 < 0 && dy <= time_y_1))
+ {
+ SIZE dx;
+ SIZE time_x_0, time_x_1;
+
+ if (margin_of_error)
+ {
+ dst[1].x -= margin_of_error;
+ time_x_0 = 1;
+ time_x_1 = margin_of_error << 1;
+ }
+ else
+ {
+ dst[0].x += DISPLAY_TO_WORLD (r0.corner.x);
+ dst[1].x += DISPLAY_TO_WORLD (r1.corner.x);
+ time_x_0 = DISPLAY_TO_WORLD (r0.extent.width);
+ time_x_1 = DISPLAY_TO_WORLD (r1.extent.width);
+ }
+
+ dx = dst[1].x - dst[0].x;
+ time_x_0 = dx - time_x_0 + 1;
+ time_x_1 = dx + time_x_1 - 1;
+ dx = dx_0 - dx_1;
+
+ if ((time_x_0 <= 0 && time_x_1 >= 0)
+ || (time_x_0 > 0 && dx >= time_x_0)
+ || (time_x_1 < 0 && dx <= time_x_1))
+ {
+ if (dx == 0 && dy == 0)
+ time_y_0 = time_y_1 = 0;
+ else
+ {
+ SIZE t;
+ long time_beg, time_end, fract;
+
+ if (time_y_1 < 0)
+ {
+ t = time_y_0;
+ time_y_0 = -time_y_1;
+ time_y_1 = -t;
+ }
+ else if (time_y_0 <= 0)
+ {
+ if (dy < 0)
+ time_y_1 = -time_y_0;
+ time_y_0 = 0;
+ }
+ if (dy < 0)
+ dy = -dy;
+ if (dy < time_y_1)
+ time_y_1 = dy;
+
+ if (time_x_1 < 0)
+ {
+ t = time_x_0;
+ time_x_0 = -time_x_1;
+ time_x_1 = -t;
+ }
+ else if (time_x_0 <= 0)
+ {
+ if (dx < 0)
+ time_x_1 = -time_x_0;
+ time_x_0 = 0;
+ }
+ if (dx < 0)
+ dx = -dx;
+ if (dx < time_x_1)
+ time_x_1 = dx;
+
+ if (dx == 0)
+ {
+ time_beg = time_y_0;
+ time_end = time_y_1;
+ fract = dy;
+ }
+ else if (dy == 0)
+ {
+ time_beg = time_x_0;
+ time_end = time_x_1;
+ fract = dx;
+ }
+ else
+ {
+ long time_x, time_y;
+
+ time_x = (long)time_x_0 * (long)dy;
+ time_y = (long)time_y_0 * (long)dx;
+ time_beg = time_x < time_y ? time_y : time_x;
+
+ time_x = (long)time_x_1 * (long)dy;
+ time_y = (long)time_y_1 * (long)dx;
+ time_end = time_x > time_y ? time_y : time_x;
+
+ fract = (long)dx * (long)dy;
+ }
+
+ if ((time_beg *= max_turns) < fract)
+ time_y_0 = 0;
+ else
+ time_y_0 = (SIZE)(time_beg / fract);
+
+ if (time_end >= fract) /* just in case of overflow */
+ time_y_1 = max_turns - 1;
+ else
+ time_y_1 = (SIZE)((time_end * max_turns) / fract);
+ }
+
+ if (time_y_0 <= time_y_1)
+ {
+ if (margin_of_error != 0)
+ return ((COUNT)time_y_0 + 1);
+ else
+ {
+ POINT Pt0, Pt1;
+ VELOCITY_DESC Velocity0, Velocity1;
+ INTERSECT_CONTROL Control0, Control1;
+
+ Pt0 = ElementPtr0->current.location;
+ Velocity0 = ElementPtr0->velocity;
+ Control0 = ElementPtr0->IntersectControl;
+
+ Pt1 = ElementPtr1->current.location;
+ Velocity1 = ElementPtr1->velocity;
+ Control1 = ElementPtr1->IntersectControl;
+
+ if (time_y_0)
+ {
+ GetNextVelocityComponents (&Velocity0, &dx_0, &dy_0, time_y_0);
+ Pt0.x += dx_0;
+ Pt0.y += dy_0;
+ Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x);
+ Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y);
+
+ GetNextVelocityComponents (&Velocity1, &dx_1, &dy_1, time_y_0);
+ Pt1.x += dx_1;
+ Pt1.y += dy_1;
+ Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x);
+ Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y);
+ }
+
+ do
+ {
+ TIME_VALUE when;
+
+ ++time_y_0;
+
+ GetNextVelocityComponents (&Velocity0, &dx_0, &dy_0, 1);
+ Pt0.x += dx_0;
+ Pt0.y += dy_0;
+
+ GetNextVelocityComponents (&Velocity1, &dx_1, &dy_1, 1);
+ Pt1.x += dx_1;
+ Pt1.y += dy_1;
+
+ Control0.IntersectStamp.origin = Control0.EndPoint;
+ Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x);
+ Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y);
+
+ Control1.IntersectStamp.origin = Control1.EndPoint;
+ Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x);
+ Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y);
+ when = DrawablesIntersect (&Control0,
+ &Control1, MAX_TIME_VALUE);
+ if (when)
+ {
+ if (when == 1
+ && time_y_0 == 1
+ && ((ElementPtr0->state_flags
+ | ElementPtr1->state_flags) & APPEARING))
+ {
+ when = 0;
+ Control0.EndPoint.x = WORLD_TO_DISPLAY (Pt0.x);
+ Control0.EndPoint.y = WORLD_TO_DISPLAY (Pt0.y);
+
+ Control1.EndPoint.x = WORLD_TO_DISPLAY (Pt1.x);
+ Control1.EndPoint.y = WORLD_TO_DISPLAY (Pt1.y);
+ }
+
+ if (when)
+ return ((COUNT)time_y_0);
+ }
+ } while (time_y_0 < time_y_1);
+ }
+ }
+ }
+ }
+
+ return (0);
+}
+
+static void
+InitCyborg (STARSHIP *StarShipPtr)
+{
+ COUNT Index, Divisor;
+
+ Index = StarShipPtr->RaceDescPtr->characteristics.max_thrust
+ * StarShipPtr->RaceDescPtr->characteristics.thrust_increment;
+ if ((Divisor = StarShipPtr->RaceDescPtr->characteristics.turn_wait
+ + StarShipPtr->RaceDescPtr->characteristics.thrust_wait) > 0)
+ Index /= Divisor;
+ else
+ Index >>= 1;
+#ifdef PRINT_MI
+ {
+ char *shipName;
+
+ shipName = GetStringAddress (
+ StarShipPtr->RaceDescPtr->ship_data.race_strings);
+ log_add (log_Debug, "MI(%s) -- <%u:%u> = %u", shipName,
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust *
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment,
+ Divisor, Index);
+ }
+#endif /* PRINT_MI */
+ StarShipPtr->RaceDescPtr->cyborg_control.ManeuverabilityIndex = Index;
+}
+
+static void
+ship_movement (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
+{
+ if (EvalDescPtr->which_turn == 0)
+ EvalDescPtr->which_turn = 1;
+
+ switch (EvalDescPtr->MoveState)
+ {
+ case PURSUE:
+ Pursue (ShipPtr, EvalDescPtr);
+ break;
+ case AVOID:
+#ifdef NOTYET
+ Avoid (ShipPtr, EvalDescPtr);
+ break;
+#endif /* NOTYET */
+ case ENTICE:
+ Entice (ShipPtr, EvalDescPtr);
+ break;
+ case NO_MOVEMENT:
+ break;
+ }
+}
+
+BOOLEAN
+ship_weapons (ELEMENT *ShipPtr, ELEMENT *OtherPtr, COUNT margin_of_error)
+{
+ SIZE delta_x, delta_y;
+ COUNT n, num_weapons;
+ ELEMENT Ship;
+ HELEMENT Weapon[6];
+ STARSHIP *StarShipPtr;
+
+ if (OBJECT_CLOAKED (OtherPtr))
+ margin_of_error += DISPLAY_TO_WORLD (40);
+
+ Ship = *ShipPtr;
+ GetNextVelocityComponents (&Ship.velocity,
+ &delta_x, &delta_y, 1);
+ Ship.next.location.x =
+ Ship.current.location.x + delta_x;
+ Ship.next.location.y =
+ Ship.current.location.y + delta_y;
+ Ship.current.location = Ship.next.location;
+
+ GetElementStarShip (&Ship, &StarShipPtr);
+ num_weapons =
+ (*StarShipPtr->RaceDescPtr->init_weapon_func) (&Ship, Weapon);
+
+ if ((n = num_weapons))
+ {
+ HELEMENT *WeaponPtr, w;
+ //STARSHIP *StarShipPtr;
+ ELEMENT *EPtr;
+
+ WeaponPtr = &Weapon[0];
+ do
+ {
+ w = *WeaponPtr;
+ if (w)
+ {
+ LockElement (w, &EPtr);
+ if (EPtr->state_flags & APPEARING)
+ {
+ EPtr->next = EPtr->current;
+ InitIntersectStartPoint (EPtr);
+ InitIntersectEndPoint (EPtr);
+ InitIntersectFrame (EPtr);
+ }
+
+ if (PlotIntercept (EPtr, OtherPtr,
+ EPtr->life_span, margin_of_error))
+ {
+ UnlockElement (w);
+ break;
+ }
+
+ UnlockElement (w);
+ FreeElement (w);
+ }
+ ++WeaponPtr;
+ } while (--n);
+
+ if ((num_weapons = n))
+ {
+ do
+ {
+ w = *WeaponPtr++;
+ if (w)
+ FreeElement (w);
+ } while (--n);
+ }
+ }
+
+ return (num_weapons > 0);
+}
+
+void
+ship_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ BOOLEAN ShipMoved, ShipFired;
+ COUNT margin_of_error;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ ShipMoved = TRUE;
+ if (ShipPtr->turn_wait == 0)
+ ShipMoved = FALSE;
+ if (ShipPtr->thrust_wait == 0)
+ ShipMoved = FALSE;
+
+ ShipFired = TRUE;
+ if (StarShipPtr->weapon_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ if (!(StarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON))
+ ShipFired = FALSE;
+ }
+
+ if (StarShipPtr->control & AWESOME_RATING)
+ margin_of_error = 0;
+ else if (StarShipPtr->control & GOOD_RATING)
+ margin_of_error = DISPLAY_TO_WORLD (20);
+ else /* if (StarShipPtr->control & STANDARD_RATING) */
+ margin_of_error = DISPLAY_TO_WORLD (40);
+
+ ObjectsOfConcern += ConcernCounter;
+ while (ConcernCounter--)
+ {
+ --ObjectsOfConcern;
+ if (ObjectsOfConcern->ObjectPtr)
+ {
+ if (!ShipMoved
+ && (ConcernCounter != ENEMY_WEAPON_INDEX
+ || ObjectsOfConcern->MoveState == PURSUE
+ || (ObjectsOfConcern->ObjectPtr->state_flags & CREW_OBJECT)
+ || MANEUVERABILITY (
+ &StarShipPtr->RaceDescPtr->cyborg_control
+ ) >= MEDIUM_SHIP))
+ {
+ ship_movement (ShipPtr, ObjectsOfConcern);
+ ShipMoved = TRUE;
+ }
+ if (!ShipFired
+ && (ConcernCounter == ENEMY_SHIP_INDEX
+ || (ConcernCounter == ENEMY_WEAPON_INDEX
+ && ObjectsOfConcern->MoveState != AVOID
+#ifdef NEVER
+ && !(StarShipPtr->control & STANDARD_RATING)
+#endif /* NEVER */
+ )))
+ {
+ ShipFired = ship_weapons (ShipPtr,
+ ObjectsOfConcern->ObjectPtr, margin_of_error);
+ if (ShipFired)
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+ }
+ }
+}
+
+BOOLEAN
+TurnShip (ELEMENT *ShipPtr, COUNT angle)
+{
+ COUNT f, ship_delta_facing;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ f = StarShipPtr->ShipFacing;
+ ship_delta_facing = NORMALIZE_FACING (ANGLE_TO_FACING (angle) - f);
+ if (ship_delta_facing)
+ {
+ if (ship_delta_facing == ANGLE_TO_FACING (HALF_CIRCLE))
+ ship_delta_facing =
+ NORMALIZE_FACING (ship_delta_facing +
+ (TFB_Random () & 1 ?
+ ANGLE_TO_FACING (OCTANT >> 1) :
+ -ANGLE_TO_FACING (OCTANT >> 1)));
+
+ if (ship_delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
+ {
+ StarShipPtr->ship_input_state |= RIGHT;
+ ++f;
+ ShipPtr->next.image.frame =
+ IncFrameIndex (ShipPtr->current.image.frame);
+ }
+ else
+ {
+ StarShipPtr->ship_input_state |= LEFT;
+ --f;
+ ShipPtr->next.image.frame =
+ DecFrameIndex (ShipPtr->current.image.frame);
+ }
+
+#ifdef NOTYET
+ if (((StarShipPtr->ship_input_state & (LEFT | RIGHT))
+ ^ (StarShipPtr->cur_status_flags & (LEFT | RIGHT))) == (LEFT | RIGHT))
+ StarShipPtr->ship_input_state &= ~(LEFT | RIGHT);
+ else
+#endif /* NOTYET */
+ {
+ StarShipPtr->ShipFacing = NORMALIZE_FACING (f);
+
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+BOOLEAN
+ThrustShip (ELEMENT *ShipPtr, COUNT angle)
+{
+ BOOLEAN ShouldThrust;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->ship_input_state & THRUST)
+ ShouldThrust = TRUE;
+ else if (NORMALIZE_FACING (ANGLE_TO_FACING (angle)
+ - ANGLE_TO_FACING (GetVelocityTravelAngle (&ShipPtr->velocity))) == 0
+ && (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
+ && !(StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL))
+ ShouldThrust = FALSE;
+ else
+ {
+ SIZE ship_delta_facing;
+
+ ship_delta_facing =
+ NORMALIZE_FACING (ANGLE_TO_FACING (angle)
+ - StarShipPtr->ShipFacing + ANGLE_TO_FACING (QUADRANT));
+ if (ship_delta_facing == ANGLE_TO_FACING (QUADRANT)
+ || ((StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED)
+ && ship_delta_facing <= ANGLE_TO_FACING (HALF_CIRCLE)))
+ ShouldThrust = TRUE;
+ else
+ ShouldThrust = FALSE;
+ }
+
+ if (ShouldThrust)
+ {
+ inertial_thrust (ShipPtr);
+
+ StarShipPtr->ship_input_state |= THRUST;
+ }
+
+ return (ShouldThrust);
+}
+
+void
+Pursue (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
+{
+ BYTE maneuver_state;
+ COUNT desired_thrust_angle, desired_turn_angle;
+ SIZE delta_x, delta_y;
+ SIZE ship_delta_x, ship_delta_y;
+ SIZE other_delta_x, other_delta_y;
+ ELEMENT *OtherObjPtr;
+ VELOCITY_DESC ShipVelocity, OtherVelocity;
+
+ ShipVelocity = ShipPtr->velocity;
+ GetNextVelocityComponents (&ShipVelocity,
+ &ship_delta_x, &ship_delta_y, EvalDescPtr->which_turn);
+ ShipPtr->next.location.x =
+ ShipPtr->current.location.x + ship_delta_x;
+ ShipPtr->next.location.y =
+ ShipPtr->current.location.y + ship_delta_y;
+
+ OtherObjPtr = EvalDescPtr->ObjectPtr;
+ OtherVelocity = OtherObjPtr->velocity;
+ GetNextVelocityComponents (&OtherVelocity,
+ &other_delta_x, &other_delta_y, EvalDescPtr->which_turn);
+
+ delta_x = (OtherObjPtr->current.location.x + other_delta_x)
+ - ShipPtr->next.location.x;
+ delta_y = (OtherObjPtr->current.location.y + other_delta_y)
+ - ShipPtr->next.location.y;
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+ desired_thrust_angle = ARCTAN (delta_x, delta_y);
+
+ maneuver_state = 0;
+ if (ShipPtr->turn_wait == 0)
+ maneuver_state |= LEFT | RIGHT;
+ if (ShipPtr->thrust_wait == 0
+ && ((OtherObjPtr->state_flags & PLAYER_SHIP)
+ || elementsOfSamePlayer (OtherObjPtr, ShipPtr)
+ || OtherObjPtr->preprocess_func == crew_preprocess))
+ maneuver_state |= THRUST;
+
+ desired_turn_angle = NORMALIZE_ANGLE (desired_thrust_angle + HALF_CIRCLE);
+ /* other player's ship */
+ if ((OtherObjPtr->state_flags & PLAYER_SHIP)
+ && OtherObjPtr->mass_points <= MAX_SHIP_MASS)
+ {
+ STARSHIP *StarShipPtr;
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ GetElementStarShip (OtherObjPtr, &EnemyStarShipPtr);
+ if ((MANEUVERABILITY (
+ &StarShipPtr->RaceDescPtr->cyborg_control
+ ) >= FAST_SHIP
+ && WEAPON_RANGE (&StarShipPtr->RaceDescPtr->cyborg_control)
+ > CLOSE_RANGE_WEAPON)
+ || (EvalDescPtr->which_turn >= 24
+ && (StarShipPtr->RaceDescPtr->characteristics.max_thrust * 2 / 3 <
+ EnemyStarShipPtr->RaceDescPtr->characteristics.max_thrust
+ || (EnemyStarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED))))
+ {
+ UWORD ship_flags;
+
+ ship_flags = EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags;
+ /* you're maneuverable */
+ if (MANEUVERABILITY (
+ &StarShipPtr->RaceDescPtr->cyborg_control
+ ) >= MEDIUM_SHIP)
+ {
+ UWORD fire_flags;
+ COUNT facing;
+
+ for (fire_flags = FIRES_FORE, facing = EvalDescPtr->facing;
+ fire_flags <= FIRES_LEFT;
+ fire_flags <<= 1, facing += QUADRANT)
+ {
+ if
+ (
+ /* he's dangerous in this direction */
+ (ship_flags & fire_flags)
+ /* he's facing direction you want to go */
+ && NORMALIZE_ANGLE (
+ desired_turn_angle - facing + OCTANT
+ ) <= QUADRANT
+ && (
+ /* he's moving */
+ (other_delta_x != 0 || other_delta_y != 0)
+ &&
+ /* he's coasting backwards */
+ NORMALIZE_ANGLE (
+ (GetVelocityTravelAngle (&OtherVelocity) + HALF_CIRCLE)
+ - facing + (OCTANT + (OCTANT >> 1)))
+ <= ((OCTANT + (OCTANT >> 1)) << 1))
+ )
+ {
+ /* catch him on the back side */
+ desired_thrust_angle = desired_turn_angle;
+ break;
+ }
+ }
+ }
+
+ if (desired_thrust_angle != desired_turn_angle
+ && (other_delta_x || other_delta_y)
+ && EvalDescPtr->which_turn >= 24
+ && NORMALIZE_ANGLE (desired_thrust_angle
+ - GetVelocityTravelAngle (&OtherVelocity)
+ + OCTANT) <= QUADRANT
+ && ((NORMALIZE_ANGLE (
+ GetVelocityTravelAngle (&OtherVelocity)
+ - GetVelocityTravelAngle (&ShipVelocity)
+ + OCTANT) <= QUADRANT
+ && (((StarShipPtr->cur_status_flags & SHIP_AT_MAX_SPEED)
+ && !(StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED))
+ || (ship_flags & DONT_CHASE)))
+ || NORMALIZE_ANGLE (
+ desired_turn_angle
+ - FACING_TO_ANGLE (StarShipPtr->ShipFacing)
+ + OCTANT) <= QUADRANT))
+ desired_thrust_angle = desired_turn_angle;
+ }
+ }
+
+ if (maneuver_state & (LEFT | RIGHT))
+ TurnShip (ShipPtr, desired_thrust_angle);
+
+ if (maneuver_state & THRUST)
+ ThrustShip (ShipPtr, desired_thrust_angle);
+}
+
+void
+Entice (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
+{
+ BYTE maneuver_state;
+ COUNT desired_thrust_angle, desired_turn_angle;
+ COUNT cone_of_fire, travel_angle;
+ SIZE delta_x, delta_y;
+ SIZE ship_delta_x, ship_delta_y;
+ SIZE other_delta_x, other_delta_y;
+ ELEMENT *OtherObjPtr;
+ VELOCITY_DESC ShipVelocity, OtherVelocity;
+ STARSHIP *StarShipPtr;
+ RACE_DESC *RDPtr;
+
+ ShipVelocity = ShipPtr->velocity;
+ GetNextVelocityComponents (&ShipVelocity,
+ &ship_delta_x, &ship_delta_y, EvalDescPtr->which_turn);
+ ShipPtr->next.location.x =
+ ShipPtr->current.location.x + ship_delta_x;
+ ShipPtr->next.location.y =
+ ShipPtr->current.location.y + ship_delta_y;
+
+ OtherObjPtr = EvalDescPtr->ObjectPtr;
+ OtherVelocity = OtherObjPtr->velocity;
+ GetNextVelocityComponents (&OtherVelocity,
+ &other_delta_x, &other_delta_y, EvalDescPtr->which_turn);
+
+ delta_x = (OtherObjPtr->current.location.x + other_delta_x)
+ - ShipPtr->next.location.x;
+ delta_y = (OtherObjPtr->current.location.y + other_delta_y)
+ - ShipPtr->next.location.y;
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+ desired_thrust_angle = ARCTAN (delta_x, delta_y);
+
+ maneuver_state = 0;
+ if (ShipPtr->turn_wait == 0)
+ maneuver_state |= LEFT | RIGHT;
+ if (ShipPtr->thrust_wait == 0)
+ maneuver_state |= THRUST;
+
+ delta_x = ship_delta_x - other_delta_x;
+ delta_y = ship_delta_y - other_delta_y;
+ travel_angle = ARCTAN (delta_x, delta_y);
+ desired_turn_angle = NORMALIZE_ANGLE (desired_thrust_angle + HALF_CIRCLE);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ RDPtr = StarShipPtr->RaceDescPtr;
+ if (EvalDescPtr->MoveState == AVOID)
+ {
+ desired_turn_angle =
+ NORMALIZE_ANGLE (desired_turn_angle - EvalDescPtr->facing);
+
+ if (NORMALIZE_FACING (ANGLE_TO_FACING (desired_turn_angle)))
+ {
+ if (desired_turn_angle <= HALF_CIRCLE)
+ desired_thrust_angle = RIGHT;
+ else /* if (desired_turn_angle > HALF_CIRCLE) */
+ desired_thrust_angle = LEFT;
+ }
+ else
+ {
+ desired_turn_angle = NORMALIZE_ANGLE (
+ FACING_TO_ANGLE (StarShipPtr->ShipFacing)
+ - EvalDescPtr->facing
+ );
+ if ((desired_turn_angle & (HALF_CIRCLE - 1)) == 0)
+ desired_thrust_angle = TFB_Random () & 1 ? RIGHT : LEFT;
+ else
+ desired_thrust_angle = desired_turn_angle < HALF_CIRCLE ? RIGHT : LEFT;
+ }
+
+ if (desired_thrust_angle == LEFT)
+ {
+#define FLANK_LEFT -QUADRANT
+#define SHIP_LEFT -OCTANT
+ desired_thrust_angle = EvalDescPtr->facing
+ + FLANK_LEFT - (SHIP_LEFT >> 1);
+ }
+ else
+ {
+#define FLANK_RIGHT QUADRANT
+#define SHIP_RIGHT OCTANT
+ desired_thrust_angle = EvalDescPtr->facing
+ + FLANK_RIGHT - (SHIP_RIGHT >> 1);
+ }
+
+ desired_thrust_angle = NORMALIZE_ANGLE (desired_thrust_angle);
+ }
+ else if (GRAVITY_MASS (OtherObjPtr->mass_points))
+ {
+ COUNT planet_facing;
+
+ planet_facing = NORMALIZE_FACING (ANGLE_TO_FACING (desired_thrust_angle));
+ cone_of_fire = NORMALIZE_FACING (
+ planet_facing
+ - StarShipPtr->ShipFacing
+ + ANGLE_TO_FACING (QUADRANT));
+
+ if (RDPtr->characteristics.thrust_increment !=
+ RDPtr->characteristics.max_thrust)
+ maneuver_state &= ~THRUST;
+
+ /* if not pointing towards planet */
+ if (cone_of_fire > ANGLE_TO_FACING (QUADRANT << 1))
+ desired_turn_angle = desired_thrust_angle;
+ /* if pointing directly at planet */
+ else if (cone_of_fire == ANGLE_TO_FACING (QUADRANT)
+ && NORMALIZE_FACING (ANGLE_TO_FACING (travel_angle)) != planet_facing)
+ desired_turn_angle = travel_angle;
+ else if (cone_of_fire == 0
+ || cone_of_fire == ANGLE_TO_FACING (QUADRANT << 1)
+ || (!(maneuver_state & THRUST)
+ && (cone_of_fire < ANGLE_TO_FACING (OCTANT)
+ || cone_of_fire > ANGLE_TO_FACING ((QUADRANT << 1) - OCTANT))))
+ {
+ desired_turn_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ if (NORMALIZE_ANGLE (desired_turn_angle
+ - travel_angle + QUADRANT) > HALF_CIRCLE)
+ desired_turn_angle = travel_angle;
+ if (ShipPtr->thrust_wait == 0)
+ maneuver_state |= THRUST;
+ }
+
+ desired_thrust_angle = desired_turn_angle;
+ }
+ else
+ {
+ COUNT WRange;
+
+ WRange = WEAPON_RANGE (
+ &RDPtr->cyborg_control
+ );
+
+ cone_of_fire = NORMALIZE_ANGLE (desired_turn_angle
+ - EvalDescPtr->facing + OCTANT);
+ if (OtherObjPtr->state_flags & PLAYER_SHIP)
+ {
+ UWORD fire_flags, ship_flags;
+ COUNT facing;
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (OtherObjPtr, &EnemyStarShipPtr);
+ ship_flags = EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags;
+ for (fire_flags = FIRES_FORE, facing = EvalDescPtr->facing;
+ fire_flags <= FIRES_LEFT;
+ fire_flags <<= 1, facing += QUADRANT)
+ {
+ if
+ (
+ /* he's dangerous in this direction */
+ (ship_flags & fire_flags)
+ /* he's facing direction you want to go */
+ && (cone_of_fire = NORMALIZE_ANGLE (
+ desired_turn_angle - facing + OCTANT
+ )) <= QUADRANT
+ /* he's moving */
+ && ((other_delta_x != 0 || other_delta_y != 0)
+ /* he's coasting backwards */
+ && NORMALIZE_ANGLE (
+ (GetVelocityTravelAngle (&OtherVelocity) + HALF_CIRCLE)
+ - facing + OCTANT) <= QUADRANT)
+ )
+ {
+ /* need to be close for a kill */
+ if (WRange < LONG_RANGE_WEAPON
+ && EvalDescPtr->which_turn <= 32)
+ {
+ /* catch him on the back side */
+ desired_thrust_angle = desired_turn_angle;
+ goto DoManeuver;
+ }
+
+ break;
+ }
+ }
+
+ if (EvalDescPtr->which_turn <= 8
+ && RDPtr->characteristics.max_thrust <=
+ EnemyStarShipPtr->RaceDescPtr->characteristics.max_thrust)
+ goto DoManeuver;
+ }
+
+ if
+ (
+#ifdef NOTYET
+ WRange < LONG_RANGE_WEAPON
+ &&
+#endif /* NOTYET */
+ /* not at full speed */
+ !(StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
+ && (PlotIntercept (
+ ShipPtr, OtherObjPtr, 40, CLOSE_RANGE_WEAPON << 1
+ )
+#ifdef NOTYET
+ ||
+ (
+ /* object's facing direction you want to go */
+ cone_of_fire <= QUADRANT
+ /* and you're basically going in that direction */
+ && (travel_angle == FULL_CIRCLE
+ || NORMALIZE_ANGLE (travel_angle
+ - desired_thrust_angle + QUADRANT) <= HALF_CIRCLE)
+ /* and object's in range */
+ && PlotIntercept (ShipPtr, OtherObjPtr, 1, WRange)
+ )
+#endif /* NOTYET */
+ )
+ )
+ {
+ if
+ (
+ /* pointed straight at him */
+ NORMALIZE_ANGLE (desired_thrust_angle
+ - FACING_TO_ANGLE (StarShipPtr->ShipFacing) + OCTANT) <= QUADRANT
+ /* or not exposed to business end */
+ || cone_of_fire > QUADRANT
+ )
+ {
+ desired_thrust_angle = desired_turn_angle;
+ }
+ else
+ {
+#ifdef NOTYET
+ if
+ (
+ travel_angle != FULL_CIRCLE
+ && NORMALIZE_ANGLE (travel_angle
+ - desired_turn_angle + OCTANT) <= QUADRANT
+ )
+ {
+ desired_turn_angle =
+ NORMALIZE_ANGLE ((EvalDescPtr->facing + HALF_CIRCLE)
+ + (travel_angle - desired_turn_angle));
+ if (!(maneuver_state & (LEFT | RIGHT)))
+ maneuver_state &= ~THRUST;
+ }
+
+ if (maneuver_state & (LEFT | RIGHT))
+ {
+ TurnShip (ShipPtr, desired_turn_angle);
+ maneuver_state &= ~(LEFT | RIGHT);
+ }
+#endif /* NOTYET */
+
+ desired_thrust_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+desired_turn_angle = desired_thrust_angle;
+ }
+ }
+ else if ((cone_of_fire = PlotIntercept (
+ ShipPtr, OtherObjPtr, 10, WRange
+#ifdef OLD
+ - (WRange >> 3)
+#else /* !OLD */
+ - (WRange >> 2)
+#endif /* OLD */
+ )))
+ {
+ if (RDPtr->characteristics.thrust_increment !=
+ RDPtr->characteristics.max_thrust
+ /* and already at full speed */
+ && (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
+ /* and facing away from enemy */
+ && (NORMALIZE_ANGLE (desired_turn_angle
+ - ARCTAN (ship_delta_x, ship_delta_y)
+ + (OCTANT + 2)) <= ((OCTANT + 2) << 1)
+ /* or not on collision course */
+ || !PlotIntercept (
+ ShipPtr, OtherObjPtr, 30, CLOSE_RANGE_WEAPON << 1
+ )))
+ maneuver_state &= ~THRUST;
+ /* veer off */
+ else if (cone_of_fire == 1
+ || RDPtr->characteristics.thrust_increment !=
+ RDPtr->characteristics.max_thrust)
+ {
+ if (maneuver_state & (LEFT | RIGHT))
+ {
+ TurnShip (ShipPtr, desired_turn_angle);
+ maneuver_state &= ~(LEFT | RIGHT);
+ }
+
+ if (NORMALIZE_ANGLE (desired_thrust_angle
+ - ARCTAN (ship_delta_x, ship_delta_y)
+ + (OCTANT + 2)) <= ((OCTANT + 2) << 1))
+ desired_thrust_angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ else
+ desired_thrust_angle = desired_turn_angle;
+ }
+ }
+ }
+
+DoManeuver:
+ if (maneuver_state & (LEFT | RIGHT))
+ TurnShip (ShipPtr, desired_thrust_angle);
+
+ if (maneuver_state & THRUST)
+ ThrustShip (ShipPtr, desired_thrust_angle);
+}
+
+void
+Avoid (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr)
+{
+ (void) ShipPtr; /* Satisfying compiler (unused parameter) */
+ (void) EvalDescPtr; /* Satisfying compiler (unused parameter) */
+}
+
+BATTLE_INPUT_STATE
+tactical_intelligence (ComputerInputContext *context, STARSHIP *StarShipPtr)
+{
+ ELEMENT *ShipPtr;
+ ELEMENT Ship;
+ COUNT ShipFacing;
+ HELEMENT hElement, hNextElement;
+ COUNT ConcernCounter;
+ EVALUATE_DESC ObjectsOfConcern[10];
+ BOOLEAN ShipMoved, UltraManeuverable;
+ STARSHIP *EnemyStarShipPtr;
+ RACE_DESC *RDPtr;
+ RACE_DESC *EnemyRDPtr;
+
+ RDPtr = StarShipPtr->RaceDescPtr;
+
+ if (RDPtr->cyborg_control.ManeuverabilityIndex == 0)
+ InitCyborg (StarShipPtr);
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ if (RDPtr->ship_info.crew_level == 0
+ || GetPrimType (&DisplayArray[ShipPtr->PrimIndex]) == NO_PRIM)
+ {
+ UnlockElement (StarShipPtr->hShip);
+ return (0);
+ }
+
+ ShipMoved = TRUE;
+ /* Disable ship's special completely for the Standard AI */
+ if (StarShipPtr->control & STANDARD_RATING)
+ ++StarShipPtr->special_counter;
+
+#ifdef DEBUG_CYBORG
+if (!(ShipPtr->state_flags & FINITE_LIFE)
+ && ShipPtr->life_span == NORMAL_LIFE)
+ ShipPtr->life_span += 2; /* make ship invulnerable */
+#endif /* DEBUG_CYBORG */
+ Ship = *ShipPtr;
+ UnlockElement (StarShipPtr->hShip);
+ ShipFacing = StarShipPtr->ShipFacing;
+
+ for (ConcernCounter = 0;
+ ConcernCounter <= FIRST_EMPTY_INDEX; ++ConcernCounter)
+ {
+ ObjectsOfConcern[ConcernCounter].ObjectPtr = 0;
+ ObjectsOfConcern[ConcernCounter].MoveState = NO_MOVEMENT;
+ ObjectsOfConcern[ConcernCounter].which_turn = (COUNT)~0;
+ }
+ --ConcernCounter;
+
+ UltraManeuverable = (BOOLEAN)(
+ RDPtr->characteristics.thrust_increment ==
+ RDPtr->characteristics.max_thrust
+ && MANEUVERABILITY (&RDPtr->cyborg_control) >= MEDIUM_SHIP
+ );
+
+ if (Ship.turn_wait == 0)
+ {
+ ShipMoved = FALSE;
+ StarShipPtr->ship_input_state &= ~(LEFT | RIGHT);
+ }
+ if (Ship.thrust_wait == 0)
+ {
+ ShipMoved = FALSE;
+ StarShipPtr->ship_input_state &= ~THRUST;
+ }
+
+ for (hElement = GetHeadElement ();
+ hElement != 0; hElement = hNextElement)
+ {
+ EVALUATE_DESC ed;
+
+ ed.MoveState = NO_MOVEMENT;
+
+ LockElement (hElement, &ed.ObjectPtr);
+ hNextElement = GetSuccElement (ed.ObjectPtr);
+ if (CollisionPossible (ed.ObjectPtr, &Ship))
+ {
+ SIZE dx, dy;
+
+ dx = ed.ObjectPtr->next.location.x
+ - Ship.next.location.x;
+ dy = ed.ObjectPtr->next.location.y
+ - Ship.next.location.y;
+ dx = WRAP_DELTA_X (dx);
+ dy = WRAP_DELTA_Y (dy);
+ if (GRAVITY_MASS (ed.ObjectPtr->mass_points))
+ {
+ COUNT maneuver_turn, ship_bounds;
+ RECT ship_footprint = {{0, 0}, {0, 0}};
+
+ if (UltraManeuverable)
+ maneuver_turn = 16;
+ else if (MANEUVERABILITY (&RDPtr->cyborg_control) <= MEDIUM_SHIP)
+ maneuver_turn = 48;
+ else
+ maneuver_turn = 32;
+
+ GetFrameRect (SetAbsFrameIndex (
+ Ship.IntersectControl.IntersectStamp.frame, 0
+ ), &ship_footprint);
+ ship_bounds = (COUNT)(ship_footprint.extent.width
+ + ship_footprint.extent.height);
+
+ if (!ShipMoved && (ed.which_turn =
+ PlotIntercept (ed.ObjectPtr, &Ship, maneuver_turn,
+ DISPLAY_TO_WORLD (30 + (ship_bounds * 3 /* << 2 */)))))
+ {
+ if (ed.which_turn > 1
+ || PlotIntercept (ed.ObjectPtr, &Ship, 1,
+ DISPLAY_TO_WORLD (35 + ship_bounds))
+ || PlotIntercept (ed.ObjectPtr, &Ship,
+ maneuver_turn << 1,
+ DISPLAY_TO_WORLD (40 + ship_bounds)) > 1)
+ {
+ ed.facing = ARCTAN (-dx, -dy);
+ if (UltraManeuverable)
+ ed.MoveState = AVOID;
+ else // Try a gravity whip
+ ed.MoveState = ENTICE;
+
+ ObjectsOfConcern[GRAVITY_MASS_INDEX] = ed;
+ }
+ else if (!UltraManeuverable &&
+ !IsVelocityZero (&Ship.velocity))
+ { // Try an orbital insertion, don't thrust
+ ++Ship.thrust_wait;
+ if (Ship.turn_wait)
+ ShipMoved = TRUE;
+ }
+ }
+ }
+ else if (ed.ObjectPtr->state_flags & PLAYER_SHIP)
+ {
+ GetElementStarShip (ed.ObjectPtr, &EnemyStarShipPtr);
+ EnemyRDPtr = EnemyStarShipPtr->RaceDescPtr;
+ if (EnemyRDPtr->cyborg_control.ManeuverabilityIndex == 0)
+ InitCyborg (EnemyStarShipPtr);
+
+ ed.which_turn = WORLD_TO_TURN (
+ square_root ((long)dx * dx + (long)dy * dy));
+ if (ed.which_turn >
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn)
+ {
+ UnlockElement (hElement);
+ continue;
+ }
+ else if (ed.which_turn == 0)
+ ed.which_turn = 1;
+
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr = ed.ObjectPtr;
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].facing =
+#ifdef MAYBE
+ OBJECT_CLOAKED (ed.ObjectPtr) ?
+ GetVelocityTravelAngle (&ed.ObjectPtr->velocity) :
+#endif /* MAYBE */
+ FACING_TO_ANGLE (EnemyStarShipPtr->ShipFacing);
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn = ed.which_turn;
+
+ if (ShipMoved
+ || ed.ObjectPtr->mass_points > MAX_SHIP_MASS
+ || (WEAPON_RANGE (&RDPtr->cyborg_control) < LONG_RANGE_WEAPON
+ && (WEAPON_RANGE (&RDPtr->cyborg_control) <= CLOSE_RANGE_WEAPON
+ || (WEAPON_RANGE (&EnemyRDPtr->cyborg_control) >= LONG_RANGE_WEAPON
+ && (EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON))
+ || (
+#ifdef OLD
+ MANEUVERABILITY (&RDPtr->cyborg_control) <
+ MANEUVERABILITY (&EnemyRDPtr->cyborg_control)
+#else /* !OLD */
+ RDPtr->characteristics.max_thrust <
+ EnemyRDPtr->characteristics.max_thrust
+#endif /* !OLD */
+ && WEAPON_RANGE (&RDPtr->cyborg_control) <
+ WEAPON_RANGE (&EnemyRDPtr->cyborg_control)))))
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = PURSUE;
+ else
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = ENTICE;
+
+ if ((EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & IMMEDIATE_WEAPON)
+ && ship_weapons (ed.ObjectPtr, &Ship, 0))
+ {
+ ed.which_turn = 1;
+ ed.MoveState = AVOID;
+ ed.facing = ObjectsOfConcern[ENEMY_SHIP_INDEX].facing;
+
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX] = ed;
+ }
+ }
+ else if (ed.ObjectPtr->pParent == 0)
+ {
+ if (!(ed.ObjectPtr->state_flags & FINITE_LIFE))
+ {
+ ed.which_turn = WORLD_TO_TURN (
+ square_root ((long)dx * dx + (long)dy * dy)
+ );
+
+ if (ed.which_turn <
+ ObjectsOfConcern[FIRST_EMPTY_INDEX].which_turn)
+ {
+ ed.MoveState = PURSUE;
+ ed.facing = GetVelocityTravelAngle (
+ &ed.ObjectPtr->velocity
+ );
+
+ ObjectsOfConcern[FIRST_EMPTY_INDEX] = ed;
+ }
+ }
+ }
+ else if (!elementsOfSamePlayer (ed.ObjectPtr, &Ship)
+ && ed.ObjectPtr->preprocess_func != crew_preprocess
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn > 1
+ && ed.ObjectPtr->life_span > 0)
+ {
+ GetElementStarShip (ed.ObjectPtr, &EnemyStarShipPtr);
+ EnemyRDPtr = EnemyStarShipPtr->RaceDescPtr;
+ if (((EnemyRDPtr->ship_info.ship_flags & SEEKING_WEAPON)
+ && ed.ObjectPtr->next.image.farray !=
+ EnemyRDPtr->ship_data.special)
+ || ((EnemyRDPtr->ship_info.ship_flags & SEEKING_SPECIAL)
+ && ed.ObjectPtr->next.image.farray ==
+ EnemyRDPtr->ship_data.special))
+ {
+ if ((!(ed.ObjectPtr->state_flags & (FINITE_LIFE | CREW_OBJECT))
+ && RDPtr->characteristics.max_thrust > DISPLAY_TO_WORLD (8))
+ || NORMALIZE_ANGLE (GetVelocityTravelAngle (
+ &ed.ObjectPtr->velocity
+ ) - ARCTAN (-dx, -dy)
+ + QUADRANT) > HALF_CIRCLE)
+ ed.which_turn = 0;
+ else
+ {
+ ed.which_turn = WORLD_TO_TURN (
+ square_root ((long)dx * dx + (long)dy * dy)
+ );
+
+ ed.MoveState = ENTICE;
+ if (UltraManeuverable)
+ {
+ if (ed.which_turn == 0)
+ ed.which_turn = 1;
+ else if (ed.which_turn > 16)
+ ed.which_turn = 0;
+ }
+ else if (ed.which_turn == 0)
+ ed.which_turn = 1;
+ else if (ed.which_turn > 16
+ || (MANEUVERABILITY (
+ &RDPtr->cyborg_control
+ ) > MEDIUM_SHIP
+ && ed.which_turn > 8))
+ ed.which_turn = 0;
+ }
+ }
+ else if (!(StarShipPtr->control & AWESOME_RATING))
+ ed.which_turn = 0;
+ else
+ {
+ ed.which_turn =
+ PlotIntercept (ed.ObjectPtr,
+ &Ship, ed.ObjectPtr->life_span,
+ DISPLAY_TO_WORLD (40));
+ ed.MoveState = AVOID;
+ }
+
+ if (ed.which_turn > 0
+ && (ed.which_turn <
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn
+ || (ed.which_turn ==
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn
+ && ed.MoveState == AVOID)))
+ {
+ ed.facing = GetVelocityTravelAngle (
+ &ed.ObjectPtr->velocity
+ );
+
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX] = ed;
+ }
+ }
+ else if ((ed.ObjectPtr->state_flags & CREW_OBJECT)
+ && ((!(ed.ObjectPtr->state_flags & IGNORE_SIMILAR)
+ && elementsOfSamePlayer (ed.ObjectPtr, &Ship))
+ || ed.ObjectPtr->preprocess_func == crew_preprocess)
+ && ObjectsOfConcern[CREW_OBJECT_INDEX].which_turn > 1)
+ {
+ ed.which_turn = WORLD_TO_TURN (
+ square_root ((long)dx * dx + (long)dy * dy)
+ );
+
+ if (ed.which_turn == 0)
+ ed.which_turn = 1;
+
+ if (ObjectsOfConcern[CREW_OBJECT_INDEX].which_turn >
+ ed.which_turn
+ && (ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 32
+ || (ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 8
+ && StarShipPtr->hShip == ed.ObjectPtr->hTarget)))
+ {
+ ed.MoveState = PURSUE;
+ ed.facing = 0;
+ ObjectsOfConcern[CREW_OBJECT_INDEX] = ed;
+ }
+ }
+ }
+ UnlockElement (hElement);
+ }
+
+ RDPtr->cyborg_control.intelligence_func (&Ship, ObjectsOfConcern,
+ ConcernCounter);
+#ifdef DEBUG_CYBORG
+StarShipPtr->ship_input_state &= ~SPECIAL;
+#endif /* DEBUG_CYBORG */
+
+ StarShipPtr->ShipFacing = ShipFacing;
+ {
+ BATTLE_INPUT_STATE InputState;
+
+ InputState = 0;
+ if (StarShipPtr->ship_input_state & LEFT)
+ InputState |= BATTLE_LEFT;
+ else if (StarShipPtr->ship_input_state & RIGHT)
+ InputState |= BATTLE_RIGHT;
+ if (StarShipPtr->ship_input_state & THRUST)
+ InputState |= BATTLE_THRUST;
+ if (StarShipPtr->ship_input_state & WEAPON)
+ InputState |= BATTLE_WEAPON;
+ if (StarShipPtr->ship_input_state & SPECIAL)
+ InputState |= BATTLE_SPECIAL;
+
+ (void) context;
+ return (InputState);
+ }
+}
+
diff --git a/src/uqm/demo.c b/src/uqm/demo.c
new file mode 100644
index 0000000..78b26fa
--- /dev/null
+++ b/src/uqm/demo.c
@@ -0,0 +1,141 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "demo.h"
+#include "libs/declib.h"
+#include "setup.h"
+
+#if DEMO_MODE || CREATE_JOURNAL
+static DECODE_REF journal_fh;
+static char journal_buf[1024
+#if CREATE_JOURNAL
+ * 8
+#else /* DEMO_MODE */
+ * 2
+#endif
+ ];
+INPUT_REF DemoInput;
+#endif
+
+#if DEMO_MODE
+
+static INPUT_REF OldArrowInput;
+
+INPUT_STATE
+demo_input (INPUT_REF InputRef, INPUT_STATE InputState)
+{
+ if (InputState || AnyButtonPress () || cread (
+ &InputState, sizeof (InputState), 1, journal_fh
+ ) == 0)
+ {
+ cclose (journal_fh);
+ journal_fh = 0;
+
+ StopMusic ();
+ StopSound ();
+
+ FreeKernel ();
+ exit (1);
+ }
+
+ return (InputState);
+}
+
+#endif /* DEMO_MODE */
+
+#if CREATE_JOURNAL
+
+void
+JournalInput (INPUT_STATE InputState)
+{
+ if (ArrowInput != DemoInput && journal_fh)
+ cwrite (&InputState, sizeof (InputState), 1, journal_fh);
+}
+
+#endif /* CREATE_JOURNAL */
+
+#if DEMO_MODE || CREATE_JOURNAL
+
+void
+OpenJournal (void)
+{
+ DWORD start_seed;
+
+#if CREATE_JOURNAL
+ if (create_journal)
+ {
+ if (journal_fh = copen (journal_buf, MEMORY_STREAM, STREAM_WRITE))
+ {
+ start_seed = SeedRandomNumbers ();
+ cwrite (&start_seed, sizeof (start_seed), 1, journal_fh);
+ }
+ }
+ else
+#endif /* CREATE_JOURNAL */
+ {
+ uio_Stream *fp;
+
+ if (fp = res_OpenResFile ("starcon.jnl", "rb"))
+ {
+ ReadResFile (journal_buf, 1, sizeof (journal_buf), fp);
+ res_CloseResFile (fp);
+
+ if (journal_fh = copen (journal_buf, MEMORY_STREAM, STREAM_READ))
+ {
+ OldArrowInput = ArrowInput;
+ ArrowInput = DemoInput;
+ PlayerInput[0] = PlayerInput[1] = DemoInput;
+
+ FlushInput ();
+
+ cread (&start_seed, sizeof (start_seed), 1, journal_fh);
+ TFB_SeedRandom (start_seed);
+ }
+ }
+ }
+}
+
+BOOLEAN
+CloseJournal (void)
+{
+ if (journal_fh)
+ {
+ uio_Stream *fp;
+
+ cclose (journal_fh);
+ journal_fh = 0;
+
+ if (ArrowInput == DemoInput)
+ {
+ ArrowInput = OldArrowInput;
+ return (FALSE);
+ }
+#if CREATE_JOURNAL
+ else if (fp = res_OpenResFile ("starcon.jnl", "wb"))
+ {
+ WriteResFile (journal_buf, 1, sizeof (journal_buf), fp);
+ res_CloseResFile (fp);
+ }
+#endif /* CREATE_JOURNAL */
+ }
+
+ return (TRUE);
+}
+
+#endif
+
diff --git a/src/uqm/demo.h b/src/uqm/demo.h
new file mode 100644
index 0000000..25009c0
--- /dev/null
+++ b/src/uqm/demo.h
@@ -0,0 +1,55 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_DEMO_H_
+#define UQM_DEMO_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef DEMO_MODE
+#define DEMO_MODE 0
+#endif /* DEMO_MODE */
+#ifndef CREATE_JOURNAL
+#define CREATE_JOURNAL 0
+#endif /* CREATE_JOURNAL */
+
+#if !(DEMO_MODE || CREATE_JOURNAL)
+
+#define OpenJournal SeedRandomNumbers
+#define CloseJournal() TRUE
+#define JournalInput(is)
+
+#else
+
+extern void OpenJournal (void);
+extern BOOLEAN CloseJournal (void);
+#if !CREATE_JOURNAL
+#define JournalInput(is)
+#else /* CREATE_JOURNAL */
+extern void JournalInput (INPUT_STATE InputState);
+#endif /* CREATE_JOURNAL */
+
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_DEMO_H_ */
diff --git a/src/uqm/displist.c b/src/uqm/displist.c
new file mode 100644
index 0000000..bdde704
--- /dev/null
+++ b/src/uqm/displist.c
@@ -0,0 +1,274 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "displist.h"
+#include "libs/log.h"
+
+#ifdef QUEUE_TABLE
+#define NULL_HANDLE NULL
+#endif
+
+/*
+ * This file contains code for generic doubly linked lists.
+ * If QUEUE_TABLE is defined, each lists has its own preallocated
+ * pool of link structures. The size is specific on InitQueue(),
+ * and poses a hard limit on the number of elements in the list.
+ */
+
+BOOLEAN
+InitQueue (QUEUE *pq, COUNT num_elements, OBJ_SIZE size)
+{
+ SetHeadLink (pq, NULL_HANDLE);
+ SetTailLink (pq, NULL_HANDLE);
+ SetLinkSize (pq, size);
+#ifndef QUEUE_TABLE
+ return (TRUE);
+#else /* QUEUE_TABLE */
+ SetFreeList (pq, NULL_HANDLE);
+#if 0
+ log_add (log_Debug, "InitQueue(): num_elements = %d (%d)",
+ num_elements, (BYTE)num_elements);
+#endif
+ if (AllocQueueTab (pq, num_elements) != NULL)
+ {
+ do
+ FreeLink (pq, GetLinkAddr (pq, num_elements));
+ while (--num_elements);
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+#endif /* QUEUE_TABLE */
+}
+
+BOOLEAN
+UninitQueue (QUEUE *pq)
+{
+#ifdef QUEUE_TABLE
+ SetHeadLink (pq, NULL_HANDLE);
+ SetTailLink (pq, NULL_HANDLE);
+ SetFreeList (pq, NULL_HANDLE);
+ FreeQueueTab (pq);
+
+ return (TRUE);
+#else /* !QUEUE_TABLE */
+ HLINK hLink;
+
+ while ((hLink = GetHeadLink (pq)) != NULL_HANDLE)
+ {
+ RemoveQueue (pq, hLink);
+ if (!FreeLink (pq, hLink))
+ return (FALSE);
+ }
+
+ return (TRUE);
+#endif /* QUEUE_TABLE */
+}
+
+// Empty the queue. The elements linked to in the queue are unchanged.
+void
+ReinitQueue (QUEUE *pq)
+{
+ SetHeadLink (pq, NULL_HANDLE);
+ SetTailLink (pq, NULL_HANDLE);
+#ifdef QUEUE_TABLE
+ {
+ COUNT num_elements;
+
+ SetFreeList (pq, NULL_HANDLE);
+
+ num_elements = SizeQueueTab (pq);
+ if (num_elements)
+ {
+ do
+ FreeLink (pq, GetLinkAddr (pq, num_elements));
+ while (--num_elements);
+ }
+ }
+#endif /* QUEUE_TABLE */
+}
+
+#ifdef QUEUE_TABLE
+HLINK
+AllocLink (QUEUE *pq)
+{
+ HLINK hLink;
+
+ hLink = GetFreeList (pq);
+ if (hLink)
+ {
+ LINK *LinkPtr;
+
+ LinkPtr = LockLink (pq, hLink);
+ SetFreeList (pq, _GetSuccLink (LinkPtr));
+ UnlockLink (pq, hLink);
+ }
+ else
+ log_add (log_Debug, "AllocLink(): No more elements");
+
+ return (hLink);
+}
+
+void
+FreeLink (QUEUE *pq, HLINK hLink)
+{
+ LINK *LinkPtr;
+
+ LinkPtr = LockLink (pq, hLink);
+ _SetSuccLink (LinkPtr, GetFreeList (pq));
+ UnlockLink (pq, hLink);
+
+ SetFreeList (pq, hLink);
+}
+#endif /* QUEUE_TABLE */
+
+void
+PutQueue (QUEUE *pq, HLINK hLink)
+{
+ LINK *LinkPtr;
+
+ if (GetHeadLink (pq) == 0)
+ SetHeadLink (pq, hLink);
+ else
+ {
+ HLINK hTail;
+ LINK *lpTail;
+
+ hTail = GetTailLink (pq);
+ lpTail = LockLink (pq, hTail);
+ _SetSuccLink (lpTail, hLink);
+ UnlockLink (pq, hTail);
+ }
+
+ LinkPtr = LockLink (pq, hLink);
+ _SetPredLink (LinkPtr, GetTailLink (pq));
+ _SetSuccLink (LinkPtr, NULL_HANDLE);
+ UnlockLink (pq, hLink);
+
+ SetTailLink (pq, hLink);
+}
+
+void
+InsertQueue (QUEUE *pq, HLINK hLink, HLINK hRefLink)
+{
+ if (hRefLink == 0)
+ PutQueue (pq, hLink);
+ else
+ {
+ LINK *LinkPtr;
+ LINK *RefLinkPtr;
+
+ LinkPtr = LockLink (pq, hLink);
+ RefLinkPtr = LockLink (pq, hRefLink);
+ _SetPredLink (LinkPtr, _GetPredLink (RefLinkPtr));
+ _SetPredLink (RefLinkPtr, hLink);
+ _SetSuccLink (LinkPtr, hRefLink);
+
+ if (GetHeadLink (pq) == hRefLink)
+ SetHeadLink (pq, hLink);
+ else
+ {
+ HLINK hPredLink;
+ LINK *PredLinkPtr;
+
+ hPredLink = _GetPredLink (LinkPtr);
+ PredLinkPtr = LockLink (pq, hPredLink);
+ _SetSuccLink (PredLinkPtr, hLink);
+ UnlockLink (pq, hPredLink);
+ }
+ UnlockLink (pq, hRefLink);
+ UnlockLink (pq, hLink);
+ }
+}
+
+void
+RemoveQueue (QUEUE *pq, HLINK hLink)
+{
+ LINK *LinkPtr;
+
+ LinkPtr = LockLink (pq, hLink);
+ if (GetHeadLink (pq) == hLink)
+ {
+ SetHeadLink (pq, _GetSuccLink (LinkPtr));
+ }
+ else
+ {
+ HLINK hPredLink;
+ LINK *PredLinkPtr;
+
+ hPredLink = _GetPredLink (LinkPtr);
+ PredLinkPtr = LockLink (pq, hPredLink);
+ _SetSuccLink (PredLinkPtr, _GetSuccLink (LinkPtr));
+ UnlockLink (pq, hPredLink);
+ }
+ if (GetTailLink (pq) == hLink)
+ {
+ SetTailLink (pq, _GetPredLink (LinkPtr));
+ }
+ else
+ {
+ HLINK hSuccLink, hPredLink;
+ LINK *SuccLinkPtr;
+
+ hSuccLink = _GetSuccLink (LinkPtr);
+ SuccLinkPtr = LockLink (pq, hSuccLink);
+ hPredLink = _GetPredLink (LinkPtr);
+ _SetPredLink (SuccLinkPtr, hPredLink);
+ UnlockLink (pq, hSuccLink);
+ }
+ UnlockLink (pq, hLink);
+}
+
+COUNT
+CountLinks (QUEUE *pq)
+{
+ COUNT LinkCount;
+ HLINK hLink, hNextLink;
+
+ LinkCount = 0;
+ for (hLink = GetHeadLink (pq); hLink; hLink = hNextLink)
+ {
+ LINK *LinkPtr;
+
+ ++LinkCount;
+
+ LinkPtr = LockLink (pq, hLink);
+ hNextLink = _GetSuccLink (LinkPtr);
+ UnlockLink (pq, hLink);
+ }
+
+ return (LinkCount);
+}
+
+void
+ForAllLinks (QUEUE *pq, void (*callback)(LINK *, void *), void *arg)
+{
+ HLINK hLink, hNextLink;
+
+ for (hLink = GetHeadLink (pq); hLink; hLink = hNextLink)
+ {
+ LINK *LinkPtr;
+ LinkPtr = LockLink (pq, hLink);
+ hNextLink = _GetSuccLink (LinkPtr);
+ (*callback) (LinkPtr, arg);
+ UnlockLink (pq, hLink);
+ }
+}
+
+
diff --git a/src/uqm/displist.h b/src/uqm/displist.h
new file mode 100644
index 0000000..d9c2fc0
--- /dev/null
+++ b/src/uqm/displist.h
@@ -0,0 +1,131 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_DISPLIST_H_
+#define UQM_DISPLIST_H_
+
+#include <assert.h>
+#include "port.h"
+#include "libs/compiler.h"
+#include "libs/memlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// Note that we MUST use the QUEUE_TABLE variant at this time, because
+// certain gameplay elements depend on it. Namely, the maximum number
+// of HyperSpace encounter globes chasing the player is defined by the
+// allocated size of the encounter_q. If switched to non-table variant,
+// the max number of encounters will be virtually unlimited the way the
+// code works now.
+#define QUEUE_TABLE
+
+typedef void* QUEUE_HANDLE;
+
+typedef UWORD OBJ_SIZE;
+typedef QUEUE_HANDLE HLINK;
+
+typedef struct link
+{
+ // Every queue element of any queue must have these
+ // two as the first members
+ HLINK pred;
+ HLINK succ;
+} LINK;
+
+typedef struct /* queue */
+{
+ HLINK head;
+ HLINK tail;
+#ifdef QUEUE_TABLE
+ BYTE *pq_tab;
+ HLINK free_list;
+#endif
+ COUNT object_size;
+#ifdef QUEUE_TABLE
+ BYTE num_objects;
+#endif /* QUEUE_TABLE */
+} QUEUE;
+
+#ifdef QUEUE_TABLE
+
+extern HLINK AllocLink (QUEUE *pq);
+extern void FreeLink (QUEUE *pq, HLINK hLink);
+
+static inline LINK *
+LockLink (const QUEUE *pq, HLINK h)
+{
+ if (h) // Apparently, h==0 is OK
+ { // Make sure the link is actually in our queue!
+ assert (pq->pq_tab && (BYTE*)h >= pq->pq_tab &&
+ (BYTE*)h < pq->pq_tab + pq->object_size * pq->num_objects);
+ }
+ return (LINK*)h;
+}
+
+static inline void
+UnlockLink (const QUEUE *pq, HLINK h)
+{
+ if (h) // Apparently, h==0 is OK
+ { // Make sure the link is actually in our queue!
+ assert (pq->pq_tab && (BYTE*)h >= pq->pq_tab &&
+ (BYTE*)h < pq->pq_tab + pq->object_size * pq->num_objects);
+ }
+}
+
+#define GetFreeList(pq) (pq)->free_list
+#define SetFreeList(pq, h) (pq)->free_list = (h)
+#define AllocQueueTab(pq,n) \
+ ((pq)->pq_tab = HMalloc (((COUNT)(pq)->object_size * \
+ (COUNT)((pq)->num_objects = (BYTE)(n)))))
+#define FreeQueueTab(pq) HFree ((pq)->pq_tab); (pq)->pq_tab = NULL
+#define SizeQueueTab(pq) (COUNT)((pq)->num_objects)
+#define GetLinkAddr(pq,i) (HLINK)((pq)->pq_tab + ((pq)->object_size * ((i) - 1)))
+#else /* !QUEUE_TABLE */
+#define AllocLink(pq) (HLINK)HMalloc ((pq)->object_size)
+#define LockLink(pq, h) ((LINK*)(h))
+#define UnlockLink(pq, h) ((void)(h))
+#define FreeLink(pq,h) HFree (h)
+#endif /* QUEUE_TABLE */
+
+#define SetLinkSize(pq,s) ((pq)->object_size = (COUNT)(s))
+#define GetLinkSize(pq) (COUNT)((pq)->object_size)
+#define GetHeadLink(pq) ((pq)->head)
+#define SetHeadLink(pq,h) ((pq)->head = (h))
+#define GetTailLink(pq) ((pq)->tail)
+#define SetTailLink(pq,h) ((pq)->tail = (h))
+#define _GetPredLink(lpE) ((lpE)->pred)
+#define _SetPredLink(lpE,h) ((lpE)->pred = (h))
+#define _GetSuccLink(lpE) ((lpE)->succ)
+#define _SetSuccLink(lpE,h) ((lpE)->succ = (h))
+
+extern BOOLEAN InitQueue (QUEUE *pq, COUNT num_elements, OBJ_SIZE size);
+extern BOOLEAN UninitQueue (QUEUE *pq);
+extern void ReinitQueue (QUEUE *pq);
+extern void PutQueue (QUEUE *pq, HLINK hLink);
+extern void InsertQueue (QUEUE *pq, HLINK hLink, HLINK hRefLink);
+extern void RemoveQueue (QUEUE *pq, HLINK hLink);
+extern COUNT CountLinks (QUEUE *pq);
+void ForAllLinks(QUEUE *pq, void (*callback)(LINK *, void *), void *arg);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_DISPLIST_H_ */
diff --git a/src/uqm/dummy.c b/src/uqm/dummy.c
new file mode 100644
index 0000000..9a95c36
--- /dev/null
+++ b/src/uqm/dummy.c
@@ -0,0 +1,207 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file seems to be a collection of functions that don't do
+ * much.
+ */
+
+#include "dummy.h"
+
+#include "coderes.h"
+#include "globdata.h"
+#include "races.h"
+
+#include "libs/compiler.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+
+
+typedef struct
+{
+ RACE_DESC data _ALIGNED_ANY;
+} CODERES_STRUCT;
+
+typedef enum
+{
+ ANDROSYN_CODE_RES,
+ ARILOU_CODE_RES,
+ BLACKURQ_CODE_RES,
+ CHENJESU_CODE_RES,
+ CHMMR_CODE_RES,
+ DRUUGE_CODE_RES,
+ HUMAN_CODE_RES,
+ ILWRATH_CODE_RES,
+ MELNORME_CODE_RES,
+ MMRNMHRM_CODE_RES,
+ MYCON_CODE_RES,
+ ORZ_CODE_RES,
+ PKUNK_CODE_RES,
+ SHOFIXTI_CODE_RES,
+ SLYLANDR_CODE_RES,
+ SPATHI_CODE_RES,
+ SUPOX_CODE_RES,
+ SYREEN_CODE_RES,
+ THRADD_CODE_RES,
+ UMGAH_CODE_RES,
+ URQUAN_CODE_RES,
+ UTWIG_CODE_RES,
+ VUX_CODE_RES,
+ YEHAT_CODE_RES,
+ ZOQFOT_CODE_RES,
+
+ SAMATRA_CODE_RES,
+ SIS_CODE_RES,
+ PROBE_CODE_RES
+} ShipCodeRes;
+
+typedef RACE_DESC *(*RaceDescInitFunc)(void);
+
+static RaceDescInitFunc
+CodeResToInitFunc(ShipCodeRes res)
+{
+ switch (res)
+ {
+ case ANDROSYN_CODE_RES: return &init_androsynth;
+ case ARILOU_CODE_RES: return &init_arilou;
+ case BLACKURQ_CODE_RES: return &init_black_urquan;
+ case CHENJESU_CODE_RES: return &init_chenjesu;
+ case CHMMR_CODE_RES: return &init_chmmr;
+ case DRUUGE_CODE_RES: return &init_druuge;
+ case HUMAN_CODE_RES: return &init_human;
+ case ILWRATH_CODE_RES: return &init_ilwrath;
+ case MELNORME_CODE_RES: return &init_melnorme;
+ case MMRNMHRM_CODE_RES: return &init_mmrnmhrm;
+ case MYCON_CODE_RES: return &init_mycon;
+ case ORZ_CODE_RES: return &init_orz;
+ case PKUNK_CODE_RES: return &init_pkunk;
+ case SHOFIXTI_CODE_RES: return &init_shofixti;
+ case SLYLANDR_CODE_RES: return &init_slylandro;
+ case SPATHI_CODE_RES: return &init_spathi;
+ case SUPOX_CODE_RES: return &init_supox;
+ case SYREEN_CODE_RES: return &init_syreen;
+ case THRADD_CODE_RES: return &init_thraddash;
+ case UMGAH_CODE_RES: return &init_umgah;
+ case URQUAN_CODE_RES: return &init_urquan;
+ case UTWIG_CODE_RES: return &init_utwig;
+ case VUX_CODE_RES: return &init_vux;
+ case YEHAT_CODE_RES: return &init_yehat;
+ case ZOQFOT_CODE_RES: return &init_zoqfotpik;
+ case SAMATRA_CODE_RES: return &init_samatra;
+ case SIS_CODE_RES: return &init_sis;
+ case PROBE_CODE_RES: return &init_probe;
+ default:
+ {
+ log_add (log_Warning, "Unknown SHIP identifier '%d'", res);
+ return NULL;
+ }
+ }
+}
+
+static void
+GetCodeResData (const char *ship_id, RESOURCE_DATA *resdata)
+{
+ BYTE which_res;
+ void *hData;
+
+ which_res = atoi (ship_id);
+ hData = HMalloc (sizeof (CODERES_STRUCT));
+ if (hData)
+ {
+ RaceDescInitFunc initFunc = CodeResToInitFunc (which_res);
+ RACE_DESC *RDPtr = (initFunc == NULL) ? NULL : (*initFunc)();
+ if (RDPtr == 0)
+ {
+ HFree (hData);
+ hData = 0;
+ }
+ else
+ {
+ CODERES_STRUCT *cs;
+
+ cs = (CODERES_STRUCT *) hData;
+ cs->data = *RDPtr; // Structure assignment.
+ }
+ }
+ resdata->ptr = hData;
+}
+
+static BOOLEAN
+_ReleaseCodeResData (void *data)
+{
+ HFree (data);
+ return TRUE;
+}
+
+BOOLEAN
+InstallCodeResType ()
+{
+ return (InstallResTypeVectors ("SHIP",
+ GetCodeResData, _ReleaseCodeResData, NULL));
+}
+
+
+void *
+LoadCodeResInstance (RESOURCE res)
+{
+ void *hData;
+
+ hData = res_GetResource (res);
+ if (hData)
+ res_DetachResource (res);
+
+ return hData;
+}
+
+
+BOOLEAN
+DestroyCodeRes (void *hCode)
+{
+ HFree (hCode);
+ return TRUE;
+}
+
+
+void*
+CaptureCodeRes (void *hCode, void *pData, void **ppLocData)
+{
+ CODERES_STRUCT *cs;
+
+ if (hCode == NULL)
+ {
+ log_add (log_Fatal, "dummy.c::CaptureCodeRes() hCode==NULL! FATAL!");
+ return(NULL);
+ }
+
+ cs = (CODERES_STRUCT *) hCode;
+ *ppLocData = &cs->data;
+
+ (void) pData; /* Satisfying compiler (unused parameter) */
+ return cs;
+}
+
+
+void *
+ReleaseCodeRes (void *CodeRef)
+{
+ return CodeRef;
+}
+
diff --git a/src/uqm/dummy.h b/src/uqm/dummy.h
new file mode 100644
index 0000000..5cb4221
--- /dev/null
+++ b/src/uqm/dummy.h
@@ -0,0 +1,52 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DUMMY_H
+#define DUMMY_H
+
+#include "races.h"
+
+#include "ships/androsyn/androsyn.h"
+#include "ships/arilou/arilou.h"
+#include "ships/blackurq/blackurq.h"
+#include "ships/chenjesu/chenjesu.h"
+#include "ships/chmmr/chmmr.h"
+#include "ships/druuge/druuge.h"
+#include "ships/human/human.h"
+#include "ships/ilwrath/ilwrath.h"
+#include "ships/melnorme/melnorme.h"
+#include "ships/mmrnmhrm/mmrnmhrm.h"
+#include "ships/mycon/mycon.h"
+#include "ships/orz/orz.h"
+#include "ships/pkunk/pkunk.h"
+#include "ships/shofixti/shofixti.h"
+#include "ships/slylandr/slylandr.h"
+#include "ships/spathi/spathi.h"
+#include "ships/supox/supox.h"
+#include "ships/syreen/syreen.h"
+#include "ships/thradd/thradd.h"
+#include "ships/umgah/umgah.h"
+#include "ships/urquan/urquan.h"
+#include "ships/utwig/utwig.h"
+#include "ships/vux/vux.h"
+#include "ships/yehat/yehat.h"
+#include "ships/zoqfot/zoqfot.h"
+#include "ships/lastbat/lastbat.h"
+#include "ships/sis_ship/sis_ship.h"
+#include "ships/probe/probe.h"
+
+#endif /* DUMMY_H */
+
diff --git a/src/uqm/element.h b/src/uqm/element.h
new file mode 100644
index 0000000..dd41340
--- /dev/null
+++ b/src/uqm/element.h
@@ -0,0 +1,242 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_ELEMENT_H_
+#define UQM_ELEMENT_H_
+
+#include "displist.h"
+#include "units.h"
+#include "velocity.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+#define NORMAL_LIFE 1
+
+typedef HLINK HELEMENT;
+
+// Bits for ELEMENT_FLAGS:
+// bits 0 and 1 are now available
+#define PLAYER_SHIP (1 << 2)
+ // The ELEMENT is a player controlable ship, and not some bullet,
+ // crew, asteroid, fighter, etc. This does not mean that the ship
+ // is actually controlled by a human; it may be a computer.
+
+#define APPEARING (1 << 3)
+#define DISAPPEARING (1 << 4)
+#define CHANGING (1 << 5)
+ // The element's graphical representation has changed.
+
+#define NONSOLID (1 << 6)
+#define COLLISION (1 << 7)
+#define IGNORE_SIMILAR (1 << 8)
+#define DEFY_PHYSICS (1 << 9)
+
+#define FINITE_LIFE (1 << 10)
+
+#define PRE_PROCESS (1 << 11)
+ // PreProcess() is to be called for the ELEMENT.
+#define POST_PROCESS (1 << 12)
+
+#define IGNORE_VELOCITY (1 << 13)
+#define CREW_OBJECT (1 << 14)
+#define BACKGROUND_OBJECT (1 << 15)
+ // The BACKGROUND_OBJECT flag existed originally but wasn't used.
+ // It can now be used for objects that never influence the state
+ // of other elements; elements that have this flag set are not
+ // included in the checksum used for netplay games.
+ // It can be used for graphical mods that don't impede netplay.
+
+
+#define HYPERJUMP_LIFE 15
+
+#define NUM_EXPLOSION_FRAMES 12
+
+#define GAME_SOUND_PRIORITY 2
+
+typedef enum
+{
+ VIEW_STABLE,
+ VIEW_SCROLL,
+ VIEW_CHANGE
+} VIEW_STATE;
+
+typedef UWORD ELEMENT_FLAGS;
+
+#define NO_PRIM NUM_PRIMS
+
+typedef struct state
+{
+ POINT location;
+ struct
+ {
+ FRAME frame;
+ FRAME *farray;
+ } image;
+} STATE;
+
+
+typedef struct element ELEMENT;
+
+typedef void (ElementProcessFunc) (ELEMENT *ElementPtr);
+typedef void (ElementCollisionFunc) (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1);
+
+// Any physical object in the simulation.
+struct element
+{
+ // LINK elements; must be first
+ HELEMENT pred, succ;
+
+ ElementProcessFunc *preprocess_func;
+ ElementProcessFunc *postprocess_func;
+ ElementCollisionFunc *collision_func;
+ ElementProcessFunc *death_func;
+
+ // Player this element belongs to
+ // -1: neutral (planets, asteroids, crew, etc.)
+ // 0: Melee: bottom player; Full-game: the human player
+ // 1: Melee: top player; Full-game: the NPC opponent
+ SIZE playerNr;
+
+ ELEMENT_FLAGS state_flags;
+ union
+ {
+ COUNT life_span;
+ COUNT scan_node; /* Planetside: scan type and node id */
+ };
+ union
+ {
+ COUNT crew_level;
+ COUNT hit_points;
+ COUNT facing; /* Planetside: lava-spot direction of travel */
+ COUNT cycle;
+ /* Planetside: lightning cycle length */
+ };
+ union
+ {
+ BYTE mass_points;
+ /* Planetside:
+ * - for living bio: Index in CreatureData, possibly OR'ed
+ * with CREATURE_AWARE
+ * - for canned bio: value of creature
+ */
+ // TODO: Use a different name for Planetside bio, like
+ // BYTE bio_state;
+ };
+ union
+ {
+ BYTE turn_wait;
+ BYTE sys_loc; /* IP flagship: location in system */
+ };
+ union
+ {
+ BYTE thrust_wait;
+ BYTE blast_offset;
+ BYTE next_turn; /* Battle: animation interframe for some elements */
+ };
+ BYTE colorCycleIndex;
+ // Melee: used to cycle ion trails and warp shadows, and
+ // to cycle the ship color when fleeing.
+
+ VELOCITY_DESC velocity;
+ INTERSECT_CONTROL IntersectControl;
+ COUNT PrimIndex;
+ STATE current, next;
+
+ void *pParent;
+ // The ship this element belongs to.
+ HELEMENT hTarget;
+};
+
+#define NEUTRAL_PLAYER_NUM -1
+
+static inline BOOLEAN
+elementsOfSamePlayer (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1)
+{
+ return ElementPtr0->playerNr == ElementPtr1->playerNr;
+}
+
+extern QUEUE disp_q;
+// The maximum number of elements is chosen to provide a slight margin.
+// Currently, it is maximum *known used* in Melee + 30
+#define MAX_DISPLAY_ELEMENTS 150
+
+#define MAX_DISPLAY_PRIMS 330
+extern COUNT DisplayFreeList;
+extern PRIMITIVE DisplayArray[MAX_DISPLAY_PRIMS];
+
+#define AllocDisplayPrim() DisplayFreeList; \
+ DisplayFreeList = GetSuccLink (GetPrimLinks (&DisplayArray[DisplayFreeList]))
+#define FreeDisplayPrim(p) SetPrimLinks (&DisplayArray[p], END_OF_LIST, DisplayFreeList); \
+ DisplayFreeList = (p)
+
+#define GetElementStarShip(e,ppsd) do { *(ppsd) = (e)->pParent; } while (0)
+#define SetElementStarShip(e,psd) do { (e)->pParent = psd; } while (0)
+
+#define MAX_CREW_SIZE 42
+#define MAX_ENERGY_SIZE 42
+#define MAX_SHIP_MASS 10
+#define GRAVITY_MASS(m) ((m) > MAX_SHIP_MASS * 10)
+#define GRAVITY_THRESHOLD (COUNT)255
+
+#define OBJECT_CLOAKED(eptr) \
+ (GetPrimType (&GLOBAL (DisplayArray[(eptr)->PrimIndex])) >= NUM_PRIMS \
+ || (GetPrimType (&GLOBAL (DisplayArray[(eptr)->PrimIndex])) == STAMPFILL_PRIM \
+ && sameColor (GetPrimColor (&GLOBAL (DisplayArray[(eptr)->PrimIndex])), BLACK_COLOR)))
+#define UNDEFINED_LEVEL 0
+
+extern HELEMENT AllocElement (void);
+extern void FreeElement (HELEMENT hElement);
+#define PutElement(h) PutQueue (&disp_q, h)
+#define InsertElement(h,i) InsertQueue (&disp_q, h, i)
+#define GetHeadElement() GetHeadLink (&disp_q)
+#define GetTailElement() GetTailLink (&disp_q)
+#define LockElement(h,ppe) (*(ppe) = (ELEMENT*)LockLink (&disp_q, h))
+#define UnlockElement(h) UnlockLink (&disp_q, h)
+#define GetPredElement(l) _GetPredLink (l)
+#define GetSuccElement(l) _GetSuccLink (l)
+extern void RemoveElement (HLINK hLink);
+
+// XXX: The following functions should not really be here
+extern void spawn_planet (void);
+extern void spawn_asteroid (ELEMENT *ElementPtr);
+extern void do_damage (ELEMENT *ElementPtr, SIZE damage);
+extern void crew_preprocess (ELEMENT *ElementPtr);
+extern void crew_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1);
+extern void AbandonShip (ELEMENT *ShipPtr, ELEMENT *TargetPtr,
+ COUNT crew_loss);
+extern BOOLEAN TimeSpaceMatterConflict (ELEMENT *ElementPtr);
+extern COUNT PlotIntercept (ELEMENT *ElementPtr0,
+ ELEMENT *ElementPtr1, COUNT max_turns, COUNT margin_of_error);
+
+extern void InitGalaxy (void);
+extern void MoveGalaxy (VIEW_STATE view_state, SIZE dx, SIZE dy);
+
+extern BOOLEAN CalculateGravity (ELEMENT *ElementPtr);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_ELEMENT_H_ */
diff --git a/src/uqm/encount.c b/src/uqm/encount.c
new file mode 100644
index 0000000..9ab75c5
--- /dev/null
+++ b/src/uqm/encount.c
@@ -0,0 +1,844 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "encount.h"
+
+#include "battle.h"
+#include "battlecontrols.h"
+#include "build.h"
+#include "colors.h"
+#include "starmap.h"
+#include "cons_res.h"
+#include "controls.h"
+#include "menustat.h"
+#include "gameopt.h"
+#include "gamestr.h"
+#include "globdata.h"
+#include "sis.h"
+ // for DrawStatusMessage(), SetStatusMessageMode()
+#include "init.h"
+#include "pickship.h"
+#include "intel.h"
+#include "nameref.h"
+#include "resinst.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/log.h"
+#include "libs/mathlib.h"
+#include "libs/inplib.h"
+#include "libs/misc.h"
+
+
+static void DrawFadeText (const UNICODE *str1, const UNICODE *str2,
+ BOOLEAN fade_in, RECT *pRect);
+
+
+static BOOLEAN
+DoSelectAction (MENU_STATE *pMS)
+{
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pMS->CurState = ATTACK + 1;
+ return (FALSE);
+ }
+ if (!pMS->Initialized)
+ {
+ pMS->Initialized = TRUE;
+ pMS->InputFunc = DoSelectAction;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ switch (pMS->CurState)
+ {
+ case HAIL:
+ case ATTACK:
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ pMS->CurState = HAIL;
+ return (FALSE);
+ case ATTACK + 1:
+ // Clearing FlashRect is not necessary
+ if (!GameOptions ())
+ return FALSE;
+ DrawMenuStateStrings (PM_CONVERSE, pMS->CurState);
+ SetFlashRect (SFR_MENU_3DO);
+ break;
+ default:
+ printf ("Unknown option: %d\n", pMS->CurState);
+ }
+ }
+ DoMenuChooser (pMS, PM_CONVERSE);
+ return (TRUE);
+}
+
+static QUEUE *
+GetShipFragQueueForPlayer (COUNT playerNr)
+{
+ if (playerNr == RPG_PLAYER_NUM)
+ return &GLOBAL (built_ship_q);
+ else
+ return &GLOBAL (npc_built_ship_q);
+}
+
+// Called by comm code to intialize battle fleets during encounter
+void
+BuildBattle (COUNT which_player)
+{
+ QUEUE *pQueue;
+ HSHIPFRAG hStarShip, hNextShip;
+ HSTARSHIP hBuiltShip;
+ STARSHIP *BuiltShipPtr;
+
+ EncounterRace = -1;
+
+ if (GetHeadLink (&GLOBAL (npc_built_ship_q)) == 0)
+ {
+ SET_GAME_STATE (BATTLE_SEGUE, 0);
+ return;
+ }
+
+ if (which_player != RPG_PLAYER_NUM)
+ { // This function is called first for the NPC character
+ // and this is when a centerpiece is loaded
+ switch (LOBYTE (GLOBAL (CurrentActivity)))
+ {
+ case IN_LAST_BATTLE:
+ load_gravity_well (NUMBER_OF_PLANET_TYPES);
+ break;
+ case IN_HYPERSPACE:
+ load_gravity_well ((BYTE)((COUNT)TFB_Random ()
+ % NUMBER_OF_PLANET_TYPES));
+ break;
+ default:
+ SET_GAME_STATE (ESCAPE_COUNTER, 110);
+ load_gravity_well (GET_GAME_STATE (BATTLE_PLANET));
+ break;
+ }
+ }
+ pQueue = GetShipFragQueueForPlayer (which_player);
+
+ ReinitQueue (&race_q[which_player]);
+ for (hStarShip = GetHeadLink (pQueue);
+ hStarShip != 0; hStarShip = hNextShip)
+ {
+ SHIP_FRAGMENT *FragPtr;
+
+ FragPtr = LockShipFrag (pQueue, hStarShip);
+ hNextShip = _GetSuccLink (FragPtr);
+
+ hBuiltShip = Build (&race_q[which_player],
+ FragPtr->race_id == SAMATRA_SHIP ?
+ SA_MATRA_ID : FragPtr->SpeciesID);
+ if (hBuiltShip)
+ {
+ BuiltShipPtr = LockStarShip (&race_q[which_player], hBuiltShip);
+ BuiltShipPtr->captains_name_index = FragPtr->captains_name_index;
+ BuiltShipPtr->playerNr = which_player;
+ if (FragPtr->crew_level != INFINITE_FLEET)
+ BuiltShipPtr->crew_level = FragPtr->crew_level;
+ else /* if infinite ships */
+ BuiltShipPtr->crew_level = FragPtr->max_crew;
+ BuiltShipPtr->max_crew = FragPtr->max_crew;
+ BuiltShipPtr->race_strings = FragPtr->race_strings;
+ BuiltShipPtr->icons = FragPtr->icons;
+ BuiltShipPtr->index = FragPtr->index;
+ BuiltShipPtr->ship_cost = 0;
+ BuiltShipPtr->RaceDescPtr = 0;
+
+ UnlockStarShip (&race_q[which_player], hBuiltShip);
+ }
+
+ UnlockShipFrag (pQueue, hStarShip);
+ }
+
+ if (which_player == RPG_PLAYER_NUM
+ && (hBuiltShip = Build (&race_q[0], SIS_SHIP_ID)))
+ {
+ BuiltShipPtr = LockStarShip (&race_q[0], hBuiltShip);
+ BuiltShipPtr->captains_name_index = 0;
+ BuiltShipPtr->playerNr = RPG_PLAYER_NUM;
+ BuiltShipPtr->crew_level = 0;
+ BuiltShipPtr->max_crew = 0;
+ // Crew will be copied directly from
+ // GLOBAL_SIS (CrewEnlisted) later.
+ BuiltShipPtr->race_strings = 0;
+ BuiltShipPtr->icons = 0;
+ BuiltShipPtr->index = -1;
+ BuiltShipPtr->ship_cost = 0;
+ BuiltShipPtr->energy_counter = MAX_ENERGY_SIZE;
+ BuiltShipPtr->RaceDescPtr = 0;
+ UnlockStarShip (&race_q[0], hBuiltShip);
+ }
+}
+
+BOOLEAN
+FleetIsInfinite (COUNT playerNr)
+{
+ QUEUE *pQueue;
+ HSHIPFRAG hShipFrag;
+ SHIP_FRAGMENT *FragPtr;
+ BOOLEAN ret;
+
+ pQueue = GetShipFragQueueForPlayer (playerNr);
+ hShipFrag = GetHeadLink (pQueue);
+ if (!hShipFrag)
+ { // Ship queue is empty in SuperMelee or for RPG player w/o escorts
+ return FALSE;
+ }
+
+ FragPtr = LockShipFrag (pQueue, hShipFrag);
+ ret = (FragPtr->crew_level == INFINITE_FLEET);
+ UnlockShipFrag (pQueue, hShipFrag);
+
+ return ret;
+}
+
+void
+UpdateShipFragCrew (STARSHIP *StarShipPtr)
+{
+ QUEUE *frag_q;
+ HSHIPFRAG hShipFrag, hNextFrag;
+ SHIP_FRAGMENT *frag;
+ QUEUE *ship_q;
+ HSTARSHIP hStarShip, hNextShip;
+ STARSHIP *ship;
+
+ frag_q = GetShipFragQueueForPlayer (StarShipPtr->playerNr);
+ ship_q = &race_q[StarShipPtr->playerNr];
+
+ // Find a SHIP_FRAGMENT that corresponds to the given STARSHIP
+ // The ships and fragments are in the same order in two queues
+ // XXX: It would probably be simpler to keep HSHIPFRAG in STARSHIP struct
+ for (hShipFrag = GetHeadLink (frag_q), hStarShip = GetHeadLink (ship_q);
+ hShipFrag != 0 && hStarShip != 0;
+ hShipFrag = hNextFrag, hStarShip = hNextShip)
+ {
+ ship = LockStarShip (ship_q, hStarShip);
+ hNextShip = _GetSuccLink (ship);
+ frag = LockShipFrag (frag_q, hShipFrag);
+ hNextFrag = _GetSuccLink (frag);
+
+ if (ship == StarShipPtr)
+ {
+ assert (frag->crew_level != INFINITE_FLEET);
+
+ // Record crew left after the battle */
+ frag->crew_level = ship->crew_level;
+
+ UnlockShipFrag (frag_q, hShipFrag);
+ UnlockStarShip (ship_q, hStarShip);
+ break;
+ }
+
+ UnlockShipFrag (frag_q, hShipFrag);
+ UnlockStarShip (ship_q, hStarShip);
+ }
+}
+
+/*
+ * Encountering an alien.
+ * Draws the encounter screen, plays the red alert music, and
+ * waits for a decision of the player on how to handle the situation.
+ * Returns either HAIL or ATTACK.
+ */
+COUNT
+InitEncounter (void)
+{
+ COUNT i;
+ FRAME SegueFrame;
+ STAMP s;
+ TEXT t;
+ extern FRAME planet[];
+ MUSIC_REF MR;
+
+
+ SetContext (SpaceContext);
+ SetContextFont (TinyFont);
+
+ MR = LoadMusic (REDALERT_MUSIC);
+ PlayMusic (MR, FALSE, 1);
+ SegueFrame = CaptureDrawable (LoadGraphic (SEGUE_PMAP_ANIM));
+ WaitForSoundEnd (TFBSOUND_WAIT_ALL);
+ StopMusic ();
+ DestroyMusic (MR);
+ s.origin.x = s.origin.y = 0;
+
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+ s.frame = SegueFrame;
+ DrawStamp (&s);
+
+// t.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ t.baseline.x = (SIS_SCREEN_WIDTH >> 1) + 1;
+ t.baseline.y = 10;
+ t.align = ALIGN_CENTER;
+
+ SetContextFont (MicroFont);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01));
+ if (inHQSpace ())
+ {
+ t.pStr = GAME_STRING (ENCOUNTER_STRING_BASE + 0);
+ // "ENCOUNTER IN"
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 12;
+ t.pStr = GAME_STRING (ENCOUNTER_STRING_BASE + 1);
+ // "DEEP SPACE"
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ }
+ else
+ {
+ UNICODE buf[256];
+
+ t.pStr = GAME_STRING (ENCOUNTER_STRING_BASE + 2);
+ // "ENCOUNTER AT"
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 12;
+ GetClusterName (CurStarDescPtr, buf);
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 12;
+ t.pStr = GLOBAL_SIS (PlanetName);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ }
+ DrawSISMessage (NULL);
+
+ s.origin.x = SIS_SCREEN_WIDTH >> 1;
+ s.origin.y = SIS_SCREEN_HEIGHT >> 1;
+ s.frame = planet[0];
+ DrawStamp (&s);
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != IN_LAST_BATTLE)
+ {
+#define NUM_DISPLAY_PTS (sizeof (display_pt) / sizeof (display_pt[0]))
+ HSHIPFRAG hStarShip, hNextShip;
+ POINT display_pt[] =
+ {
+ { 10, 51},
+ {-10, 51},
+ { 33, 40},
+ {-33, 40},
+ { 49, 18},
+ {-49, 18},
+ { 52, -6},
+ {-52, -6},
+ { 44, -27},
+ {-44, -27},
+ };
+
+ for (hStarShip = GetHeadLink (&GLOBAL (npc_built_ship_q)), i = 0;
+ hStarShip && i < 60; hStarShip = hNextShip, ++i)
+ {
+ RECT r;
+ SHIP_FRAGMENT *FragPtr;
+
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ if (FragPtr->crew_level != INFINITE_FLEET)
+ hNextShip = _GetSuccLink (FragPtr);
+ else /* if infinite ships */
+ hNextShip = hStarShip;
+
+ s.origin = display_pt[i % NUM_DISPLAY_PTS];
+ if (i >= NUM_DISPLAY_PTS)
+ {
+ COUNT angle, radius;
+
+ radius = square_root ((long)s.origin.x * s.origin.x
+ + (long)s.origin.y * s.origin.y)
+ + ((i / NUM_DISPLAY_PTS) * 18);
+
+ angle = ARCTAN (s.origin.x, s.origin.y);
+ s.origin.x = COSINE (angle, radius);
+ s.origin.y = SINE (angle, radius);
+ }
+ s.frame = SetAbsFrameIndex (FragPtr->icons, 0);
+ GetFrameRect (s.frame, &r);
+ s.origin.x += (SIS_SCREEN_WIDTH >> 1) - (r.extent.width >> 1);
+ s.origin.y += (SIS_SCREEN_HEIGHT >> 1) - (r.extent.height >> 1);
+ DrawStamp (&s);
+
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+ }
+
+ UnbatchGraphics ();
+ DestroyDrawable (ReleaseDrawable (SegueFrame));
+ ScreenTransition (3, NULL);
+
+
+ {
+ MENU_STATE MenuState;
+
+ MenuState.InputFunc = DoSelectAction;
+ MenuState.Initialized = FALSE;
+
+ DrawMenuStateStrings (PM_CONVERSE, MenuState.CurState = HAIL);
+ SetFlashRect (SFR_MENU_3DO);
+
+ DoInput (&MenuState, TRUE);
+
+ SetFlashRect (NULL);
+
+ return (MenuState.CurState);
+ }
+}
+
+static void
+DrawFadeText (const UNICODE *str1, const UNICODE *str2, BOOLEAN fade_in,
+ RECT *pRect)
+{
+ SIZE i;
+ DWORD TimeIn;
+ TEXT t1, t2;
+ static const Color fade_cycle[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0A, 0x0A, 0x0A), 0x1D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x09, 0x09, 0x09), 0x1E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x08, 0x08, 0x08), 0x1F),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x06, 0x06, 0x06), 0x20),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x05, 0x05, 0x05), 0x21),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x04, 0x04, 0x04), 0x22),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x03, 0x03, 0x03), 0x23),
+ };
+#define NUM_FADES (sizeof (fade_cycle) / sizeof (fade_cycle[0]))
+
+ t1.baseline.x = pRect->corner.x + 100;
+ t1.baseline.y = pRect->corner.y + 45;
+ t1.align = ALIGN_CENTER;
+ t1.pStr = str1;
+ t1.CharCount = (COUNT)~0;
+ t2 = t1;
+ t2.baseline.y += 11;
+ t2.pStr = str2;
+
+ FlushInput ();
+ TimeIn = GetTimeCounter ();
+ if (fade_in)
+ {
+ for (i = 0; i < (SIZE) NUM_FADES; ++i)
+ {
+ if (AnyButtonPress (TRUE))
+ i = NUM_FADES - 1;
+
+ SetContextForeGroundColor (fade_cycle[i]);
+ font_DrawText (&t1);
+ font_DrawText (&t2);
+ SleepThreadUntil (TimeIn + (ONE_SECOND / 20));
+ TimeIn = GetTimeCounter ();
+ }
+ }
+ else
+ {
+ for (i = NUM_FADES - 1; i >= 0; --i)
+ {
+ if (AnyButtonPress (TRUE))
+ i = 0;
+
+ SetContextForeGroundColor (fade_cycle[i]);
+ font_DrawText (&t1);
+ font_DrawText (&t2);
+ SleepThreadUntil (TimeIn + (ONE_SECOND / 20));
+ TimeIn = GetTimeCounter ();
+ }
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ font_DrawText (&t1);
+ font_DrawText (&t2);
+ }
+}
+
+COUNT
+UninitEncounter (void)
+{
+ COUNT ships_killed;
+
+ ships_killed = 0;
+
+ free_gravity_well ();
+
+ if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0
+ || LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE
+ || LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE)
+ goto ExitUninitEncounter;
+
+ if (GET_GAME_STATE (BATTLE_SEGUE) == 0)
+ {
+ ReinitQueue (&race_q[0]);
+ ReinitQueue (&race_q[1]);
+ }
+ else
+ {
+ BOOLEAN Sleepy;
+ SIZE VictoryState;
+ COUNT RecycleAmount = 0;
+ SIZE i;
+ RECT r;
+ RECT scavenge_r = {{0, 0}, {0, 0}};
+ TEXT t;
+ STAMP ship_s;
+ const UNICODE *str1 = NULL;
+ const UNICODE *str2 = NULL;
+ StatMsgMode prevMsgMode;
+ UNICODE buf[80];
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+ static const Color fade_ship_cycle[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x07, 0x00, 0x00), 0x2F),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x0A), 0x27),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x14, 0x14), 0x25),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x1F), 0x0F),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x14, 0x14), 0x25),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x0A), 0x27),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B),
+ };
+#define NUM_SHIP_FADES (sizeof (fade_ship_cycle) / \
+ sizeof (fade_ship_cycle[0]))
+
+ COUNT race_bounty[] =
+ {
+ RACE_SHIP_COST
+ };
+
+ SET_GAME_STATE (BATTLE_SEGUE, 0);
+ SET_GAME_STATE (BOMB_CARRIER, 0);
+
+ VictoryState = (
+ battle_counter[1] || !battle_counter[0]
+ || GET_GAME_STATE (URQUAN_PROTECTING_SAMATRA)
+ ) ? 0 : 1;
+
+ hStarShip = GetHeadLink (&GLOBAL (npc_built_ship_q));
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ EncounterRace = FragPtr->race_id;
+ if (GetStarShipFromIndex (&GLOBAL (avail_race_q), EncounterRace) == 0)
+ {
+ /* Suppress the final tally and salvage info */
+ VictoryState = -1;
+ InitSISContexts ();
+ }
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+
+ prevMsgMode = SetStatusMessageMode (SMM_RES_UNITS);
+ ship_s.origin.x = 0;
+ ship_s.origin.y = 0;
+ Sleepy = TRUE;
+ for (i = 0; i < NUM_SIDES; ++i)
+ {
+ QUEUE *pQueue;
+ HSHIPFRAG hNextShip;
+
+ if (i == 0)
+ pQueue = &GLOBAL (built_ship_q);
+ else
+ {
+ if (VictoryState < 0)
+ VictoryState = 0;
+ else
+ {
+ DrawSISFrame ();
+ DrawSISMessage (NULL);
+ if (inHQSpace ())
+ DrawHyperCoords (GLOBAL (ShipStamp.origin));
+ else if (GLOBAL (ip_planet) == 0)
+ DrawHyperCoords (CurStarDescPtr->star_pt);
+ else
+ DrawSISTitle(GLOBAL_SIS (PlanetName));
+
+ SetContext (SpaceContext);
+ if (VictoryState)
+ DrawArmadaPickShip (TRUE, &scavenge_r);
+ }
+ pQueue = &GLOBAL (npc_built_ship_q);
+ }
+
+ ReinitQueue (&race_q[(NUM_SIDES - 1) - i]);
+
+ for (hStarShip = GetHeadLink (pQueue); hStarShip;
+ hStarShip = hNextShip)
+ {
+ FragPtr = LockShipFrag (pQueue, hStarShip);
+ hNextShip = _GetSuccLink (FragPtr);
+
+ if (FragPtr->crew_level == 0
+ || (VictoryState && i == NUM_SIDES - 1))
+ {
+ if (i == NUM_SIDES - 1)
+ {
+ ++ships_killed;
+ if (VictoryState)
+ {
+#define MAX_DEAD_DISPLAYED 5
+ COUNT j;
+
+ if (ships_killed == 1)
+ {
+ RecycleAmount = 0;
+
+ DrawStatusMessage (NULL);
+
+ ship_s.origin.x = scavenge_r.corner.x + 32;
+ ship_s.origin.y = scavenge_r.corner.y + 56;
+ ship_s.frame = IncFrameIndex (FragPtr->icons);
+ DrawStamp (&ship_s);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F));
+ SetContextFont (TinyFont);
+
+ utf8StringCopy (buf, sizeof buf,
+ GetStringAddress (FragPtr->race_strings));
+ // XXX: this will not work with UTF-8 strings
+ strupr (buf);
+
+ t.baseline.x = scavenge_r.corner.x + 100;
+ t.baseline.y = scavenge_r.corner.y + 68;
+ t.align = ALIGN_CENTER;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 6;
+ t.pStr = GAME_STRING (
+ ENCOUNTER_STRING_BASE + 3);
+ // "BATTLE GROUP"
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ ship_s.frame = FragPtr->icons;
+
+ SetContextFont (MicroFont);
+ str1 = GAME_STRING (
+ ENCOUNTER_STRING_BASE + 4);
+ // "Enemy Ships"
+ str2 = GAME_STRING (
+ ENCOUNTER_STRING_BASE + 5),
+ // "Destroyed"
+ DrawFadeText (str1, str2, TRUE, &scavenge_r);
+ }
+
+ r.corner.y = scavenge_r.corner.y + 9;
+ r.extent.height = 22;
+
+ SetContextForeGroundColor (BLACK_COLOR);
+
+ r.extent.width = 34;
+ r.corner.x = scavenge_r.corner.x +
+ scavenge_r.extent.width
+ - (10 + r.extent.width);
+ DrawFilledRectangle (&r);
+
+ /* collect bounty ResUnits */
+ j = race_bounty[EncounterRace] >> 3;
+ RecycleAmount += j;
+ sprintf (buf, "%u", RecycleAmount);
+ t.baseline.x = r.corner.x + r.extent.width - 1;
+ t.baseline.y = r.corner.y + 14;
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x18), 0x50));
+ font_DrawText (&t);
+ DeltaSISGauges (0, 0, j);
+
+ if ((VictoryState++ - 1) % MAX_DEAD_DISPLAYED)
+ ship_s.origin.x += 17;
+ else
+ {
+ SetContextForeGroundColor (BLACK_COLOR);
+
+ r.corner.x = scavenge_r.corner.x + 10;
+ r.extent.width = 104;
+ DrawFilledRectangle (&r);
+
+ ship_s.origin.x = r.corner.x + 2;
+ ship_s.origin.y = scavenge_r.corner.y + 12;
+ }
+
+ if (Sleepy)
+ {
+ TimeCount Time = GetTimeCounter ();
+ for (j = 0; j < NUM_SHIP_FADES; ++j)
+ {
+ Sleepy = (BOOLEAN)!AnyButtonPress (TRUE) &&
+ !(GLOBAL (CurrentActivity) & CHECK_ABORT);
+ if (!Sleepy)
+ break;
+
+ SetContextForeGroundColor (fade_ship_cycle[j]);
+ DrawFilledStamp (&ship_s);
+
+ SleepThreadUntil (Time + (ONE_SECOND / 15));
+ Time = GetTimeCounter ();
+ }
+ }
+ DrawStamp (&ship_s);
+ }
+ }
+
+ UnlockShipFrag (pQueue, hStarShip);
+ RemoveQueue (pQueue, hStarShip);
+ FreeShipFrag (pQueue, hStarShip);
+
+ continue;
+ }
+
+ UnlockShipFrag (pQueue, hStarShip);
+ }
+ }
+ SetStatusMessageMode (prevMsgMode);
+
+ if (VictoryState)
+ {
+#ifdef NEVER
+ DestroyDrawable (ReleaseDrawable (s.frame));
+#endif /* NEVER */
+
+ WaitForAnyButton (TRUE, ONE_SECOND * 3, FALSE);
+ if (!CurrentInputState.key[PlayerControls[0]][KEY_ESCAPE])
+ {
+ DrawFadeText (str1, str2, FALSE, &scavenge_r);
+ if (!CurrentInputState.key[PlayerControls[0]][KEY_ESCAPE])
+ {
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.x = scavenge_r.corner.x + 10;
+ r.extent.width = 132;
+ DrawFilledRectangle (&r);
+ sprintf (buf, "%u %s", RecycleAmount,
+ GAME_STRING (STATUS_STRING_BASE + 1)); // "RU"
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + 14;
+ t.align = ALIGN_CENTER;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x18), 0x50));
+ font_DrawText (&t);
+
+ str1 = GAME_STRING (ENCOUNTER_STRING_BASE + 6);
+ // "Debris"
+ str2 = GAME_STRING (ENCOUNTER_STRING_BASE + 7);
+ // "Scavenged"
+ DrawFadeText (str1, str2, TRUE, &scavenge_r);
+ WaitForAnyButton (TRUE, ONE_SECOND * 2, FALSE);
+ if (!CurrentInputState.key[PlayerControls[0]][KEY_ESCAPE])
+ DrawFadeText (str1, str2, FALSE, &scavenge_r);
+ }
+ }
+
+ DrawStatusMessage (NULL);
+ }
+
+ if (ships_killed && EncounterRace == THRADDASH_SHIP
+ && !GET_GAME_STATE (THRADD_MANNER))
+ {
+ if ((ships_killed += GET_GAME_STATE (THRADDASH_BODY_COUNT)) >
+ THRADDASH_BODY_THRESHOLD)
+ ships_killed = THRADDASH_BODY_THRESHOLD;
+ SET_GAME_STATE (THRADDASH_BODY_COUNT, ships_killed);
+ }
+ }
+ExitUninitEncounter:
+
+ return (ships_killed);
+}
+
+void
+EncounterBattle (void)
+{
+ ACTIVITY OldActivity;
+ extern UWORD nth_frame;
+ InputContext *savedPlayerInput = NULL;
+
+
+ SET_GAME_STATE (BATTLE_SEGUE, 1);
+
+ OldActivity = GLOBAL (CurrentActivity);
+ if (LOBYTE (OldActivity) == IN_LAST_BATTLE)
+ GLOBAL (CurrentActivity) = MAKE_WORD (IN_LAST_BATTLE, 0);
+ else
+ GLOBAL (CurrentActivity) = MAKE_WORD (IN_ENCOUNTER, 0);
+
+// FreeSC2Data ();
+// DestroyFont (ReleaseFont (MicroFont));
+ WaitForSoundEnd (TFBSOUND_WAIT_ALL);
+// DestroySound (ReleaseSound (MenuSounds));
+
+ if (GLOBAL (glob_flags) & CYBORG_ENABLED)
+ {
+ BYTE cur_speed;
+
+ cur_speed = (BYTE)(GLOBAL (glob_flags) & COMBAT_SPEED_MASK)
+ >> COMBAT_SPEED_SHIFT;
+ if (cur_speed == 1)
+ cur_speed = 0; /* normal speed */
+ else if (cur_speed == 2)
+ ++cur_speed; /* 4x speed, 3 of 4 frames skipped */
+ else /* if (cur_speed == 3) */
+ cur_speed = (BYTE)~0; /* maximum speed - no rendering */
+ nth_frame = MAKE_WORD (1, cur_speed);
+ PlayerControl[0] = CYBORG_CONTROL | AWESOME_RATING;
+ savedPlayerInput = PlayerInput[0];
+ PlayerInput[0] = NULL;
+ if (!SetPlayerInput (0)) {
+ log_add (log_Fatal, "Could not set cyborg player input.");
+ explode (); // Does not return;
+ }
+ }
+
+ GameSounds = CaptureSound (LoadSound (GAME_SOUNDS));
+
+ Battle (NULL);
+
+ DestroySound (ReleaseSound (GameSounds));
+ GameSounds = 0;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
+
+ if (GLOBAL (glob_flags) & CYBORG_ENABLED)
+ {
+ nth_frame = MAKE_WORD (0, 0);
+ PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
+ ClearPlayerInput (0);
+ PlayerInput[0] = savedPlayerInput;
+ }
+
+// MicroFont = CaptureFont (
+// LoadFont (MICRO_FONT)
+// );
+// MenuSounds = CaptureSound (LoadSound (MENU_SOUNDS));
+// LoadSC2Data ();
+
+ GLOBAL (CurrentActivity) = OldActivity;
+
+}
+
diff --git a/src/uqm/encount.h b/src/uqm/encount.h
new file mode 100644
index 0000000..b623a41
--- /dev/null
+++ b/src/uqm/encount.h
@@ -0,0 +1,119 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_ENCOUNT_H_
+#define UQM_ENCOUNT_H_
+
+typedef struct brief_ship_info BRIEF_SHIP_INFO;
+typedef struct encounter ENCOUNTER;
+
+// XXX: temporary, for CONVERSATION
+#include "commglue.h"
+#include "displist.h"
+#include "libs/gfxlib.h"
+#include "planets/planets.h"
+#include "element.h"
+#include "races.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+typedef HLINK HENCOUNTER;
+
+#define MAX_HYPER_SHIPS 7
+
+// ENCOUNTER.flags
+// XXX: Currently, the flags are combined with num_ships into a single BYTE
+// in the savegames: num_ships occupy the low nibble and flags the high one.
+// Bits 4 and 5 are available for more flags in the savegames,
+// and bits 0-3 available in the game but will not be saved.
+#define ONE_SHOT_ENCOUNTER (1 << 7)
+#define ENCOUNTER_REFORMING (1 << 6)
+#define ENCOUNTER_SHIPS_MASK 0x0f
+#define ENCOUNTER_FLAGS_MASK 0xf0
+
+struct brief_ship_info
+{
+ // The only field actually used right now is crew_level
+ BYTE race_id;
+ COUNT crew_level;
+ COUNT max_crew;
+ BYTE max_energy;
+
+};
+
+struct encounter
+{
+ // LINK elements; must be first
+ HENCOUNTER pred, succ;
+
+ HELEMENT hElement;
+
+ SIZE transition_state;
+ POINT origin;
+ COUNT radius;
+ BYTE race_id;
+ BYTE num_ships;
+ BYTE flags;
+ // See ENCOUNTER.flags above
+ POINT loc_pt;
+
+ BRIEF_SHIP_INFO ShipList[MAX_HYPER_SHIPS];
+ // Only the crew_level member is currently used
+
+ SDWORD log_x, log_y;
+};
+
+#define AllocEncounter() AllocLink (&GLOBAL (encounter_q))
+#define PutEncounter(h) PutQueue (&GLOBAL (encounter_q), h)
+#define InsertEncounter(h,i) InsertQueue (&GLOBAL (encounter_q), h, i)
+#define GetHeadEncounter() GetHeadLink (&GLOBAL (encounter_q))
+#define GetTailEncounter() GetTailLink (&GLOBAL (encounter_q))
+#define LockEncounter(h,ppe) (*(ppe) = (ENCOUNTER*)LockLink (&GLOBAL (encounter_q), h))
+#define UnlockEncounter(h) UnlockLink (&GLOBAL (encounter_q), h)
+#define RemoveEncounter(h) RemoveQueue (&GLOBAL (encounter_q), h)
+#define FreeEncounter(h) FreeLink (&GLOBAL (encounter_q), h)
+#define GetPredEncounter(l) _GetPredLink (l)
+#define GetSuccEncounter(l) _GetSuccLink (l)
+
+enum
+{
+ HAIL = 0,
+ ATTACK
+};
+
+extern void EncounterBattle (void);
+extern void BuildBattle (COUNT which_player);
+extern COUNT InitEncounter (void);
+extern COUNT UninitEncounter (void);
+extern BOOLEAN FleetIsInfinite (COUNT playerNr);
+extern void UpdateShipFragCrew (STARSHIP *);
+
+// Last race the player battled with, or -1 if no battle took place.
+// Set to -1 by some funcs to inhibit IP groups from intercepting
+// the flagship.
+extern SIZE EncounterRace;
+extern BYTE EncounterGroup;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_ENCOUNT_H_ */
diff --git a/src/uqm/flash.c b/src/uqm/flash.c
new file mode 100644
index 0000000..2c73ed0
--- /dev/null
+++ b/src/uqm/flash.c
@@ -0,0 +1,805 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// NOTE: A lot of this code is untested. Only highlite and overlay flash
+// areas, drawing directly to the screen, using a cache, are
+// currently in use.
+
+// NOTE:
+// - If you change the properties of the original CONTEXT, specifically the
+// dimensions and origin, you'll need to call Flash_preUpdate() before and
+// Flash_postUpdate() after that change. Note that this may change which
+// part of the screen is flashing.
+
+// TODO:
+// - During a few frames during the sequence, the frame to be displayed
+// is equal to a frame which was supplied as a parameter to the flash
+// sequence (instead of generated during the sequence). It is not
+// necessary to make a copy in this case. Instead, the original can be
+// used. I had code to do this, but this doesn't work anymore with the
+// addition of startNumer, endNumer, and denom. This code is still
+// present, but disabled with BEGIN_AND_END_FRAME_EXCEPTIONS.
+
+#define FLASH_INTERNAL
+#include "flash.h"
+
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "libs/threadlib.h"
+
+
+static FlashContext *Flash_create (CONTEXT gfxContext);
+static void Flash_fixState (FlashContext *context);
+static void Flash_nextState (FlashContext *context);
+static void Flash_clearCache (FlashContext *context);
+static void Flash_initCache (FlashContext *context);
+static void Flash_grabOriginal (FlashContext *context);
+static void Flash_blendFraction (FlashContext *context, int numer, int denom,
+ int *resNumer, int *resDenom);
+static void Flash_makeFrame (FlashContext *context,
+ FRAME dest, int numer, int denom);
+static inline void Flash_prepareCacheFrame (FlashContext *context,
+ COUNT index);
+static void Flash_drawFrame (FlashContext *context, FRAME frame);
+static void Flash_drawCacheFrame (FlashContext *context, COUNT index);
+static inline void Flash_drawUncachedFrame (FlashContext *context,
+ int numer, int denom);
+static inline void Flash_drawCachedFrame (FlashContext *context,
+ int numer, int denom);
+static void Flash_drawCurrentFrame (FlashContext *context);
+
+static CONTEXT workGfxContext;
+ // Off-screen internal drawing context
+
+static FlashContext *
+Flash_create (CONTEXT gfxContext)
+{
+ FlashContext *context = HMalloc (sizeof (FlashContext));
+
+ context->gfxContext = gfxContext;
+
+ context->original = 0;
+
+ context->startNumer = 0;
+ context->endNumer = 1;
+ context->denom = 1;
+
+ context->fadeInTime = Flash_DEFAULT_FADE_IN_TIME;
+ context->onTime = Flash_DEFAULT_ON_TIME;
+ context->fadeOutTime = Flash_DEFAULT_FADE_OUT_TIME;
+ context->offTime = Flash_DEFAULT_OFF_TIME;
+
+ context->frameTime = 0;
+
+ context->state = FlashState_off;
+ context->lastStateTime = 0;
+ context->lastFrameTime = 0;
+
+ context->started = false;
+ context->paused = false;
+
+ context->cache = 0;
+ context->cacheSize = Flash_DEFAULT_CACHE_SIZE;
+
+ context->lastFrameIndex = (COUNT) -1;
+
+ // TODO: Delete the context somewhere
+ if (!workGfxContext)
+ workGfxContext = CreateContext ("Flash.workGfxContext");
+
+ return context;
+}
+
+// 'startNumer / denom' is the brightness in the start state of the sequence.
+// 'endNumer / denom' is the brightness in the end state of the sequence.
+// These numbers are relative to the brighness of the original image.
+FlashContext *
+Flash_createHighlight (CONTEXT gfxContext, const RECT *rect)
+{
+ FlashContext *context = Flash_create (gfxContext);
+
+ if (rect == NULL)
+ {
+ // No rectangle specified. It should be specified later with
+ // Flash_setRect(), before calling Flash_start().
+ context->rect.corner.x = 0;
+ context->rect.corner.y = 0;
+ context->rect.extent.width = 0;
+ context->rect.extent.height = 0;
+ }
+ else
+ context->rect = *rect;
+ context->type = FlashType_highlight;
+
+ return context;
+}
+
+FlashContext *
+Flash_createTransition (CONTEXT gfxContext, const POINT *origin,
+ FRAME first, FRAME final)
+{
+ FlashContext *context = Flash_create (gfxContext);
+
+ context->type = FlashType_transition;
+
+ context->u.transition.first = first;
+ context->u.transition.final = final;
+ GetFrameRect (final, &context->rect);
+ context->rect.corner = *origin;
+
+ return context;
+}
+
+FlashContext *
+Flash_createOverlay (CONTEXT gfxContext, const POINT *origin, FRAME overlay)
+{
+ FlashContext *context = Flash_create (gfxContext);
+
+ context->type = FlashType_overlay;
+
+ if (origin == NULL || overlay == NULL) {
+ // No overlay specified. It should be specified later with
+ // Flash_setOverlay(), before calling Flash_start().
+ context->u.overlay.frame = NULL;
+ context->rect.corner.x = 0;
+ context->rect.corner.y = 0;
+ context->rect.extent.width = 0;
+ context->rect.extent.height = 0;
+ } else
+ Flash_setOverlay (context, origin, overlay);
+
+ return context;
+}
+
+// Set the current state. 'timeSpentInState' determines how much time should
+// be considered to be already spent in this state.
+void
+Flash_setState (FlashContext *context, FlashState state,
+ TimeCount timeSpentInState)
+{
+ TimeCount now;
+
+ now = GetTimeCounter ();
+
+ context->state = state;
+ Flash_fixState (context);
+
+ context->lastStateTime = now - timeSpentInState;
+ context->lastFrameTime = now;
+
+ if (context->started)
+ Flash_drawCurrentFrame (context);
+}
+
+void
+Flash_start (FlashContext *context)
+{
+ if (context->started)
+ {
+ log_add (log_Warning, "Flash_start() called on already started "
+ "FlashContext.\n");
+ return;
+ }
+
+ Flash_initCache (context);
+
+ context->started = true;
+ context->paused = false;
+
+ Flash_grabOriginal (context);
+ context->lastFrameIndex = 0;
+
+ Flash_fixState (context);
+ Flash_drawCurrentFrame (context);
+}
+
+void
+Flash_terminate (FlashContext *context)
+{
+ if (context->started)
+ {
+ // Restore the flash rectangle:
+ Flash_drawFrame (context, context->original);
+
+ Flash_clearCache (context);
+ HFree (context->cache);
+ DestroyDrawable (ReleaseDrawable (context->original));
+ }
+
+ HFree (context);
+}
+
+void
+Flash_pause (FlashContext *context)
+{
+ context->paused = true;
+}
+
+void
+Flash_continue (FlashContext *context)
+{
+ context->paused = false;
+}
+
+// Change the state to the next state as long as the current state has
+// a zero-time duration.
+static void
+Flash_fixState (FlashContext *context)
+{
+ TimeCount stateTime = 0;
+
+ for (;;) {
+ switch (context->state) {
+ case FlashState_fadeIn:
+ stateTime = context->fadeInTime;
+ break;
+ case FlashState_on:
+ stateTime = context->onTime;
+ break;
+ case FlashState_fadeOut:
+ stateTime = context->fadeOutTime;
+ break;
+ case FlashState_off:
+ stateTime = context->offTime;
+ break;
+ }
+ if (stateTime != 0)
+ break;
+ context->state = (context->state + 1) & 0x3;
+ }
+}
+
+static void
+Flash_nextState (FlashContext *context)
+{
+ context->state = (context->state + 1) & 0x3;
+ Flash_fixState (context);
+}
+
+void
+Flash_process (FlashContext *context)
+{
+ TimeCount now;
+
+ if (!context->started || context->paused)
+ return;
+
+ now = GetTimeCounter ();
+
+ switch (context->state)
+ {
+ case FlashState_fadeIn:
+ if (now >= context->lastStateTime + context->fadeInTime)
+ {
+ Flash_nextState (context);
+ context->lastStateTime = now;
+ }
+ context->lastFrameTime = now;
+ break;
+ case FlashState_on:
+ if (now < context->lastStateTime + context->onTime)
+ return;
+ Flash_nextState (context);
+ context->lastStateTime = now;
+ break;
+ case FlashState_fadeOut:
+ if (now >= context->lastStateTime + context->fadeOutTime)
+ {
+ Flash_nextState (context);
+ context->lastStateTime = now;
+ }
+ context->lastFrameTime = now;
+ break;
+ case FlashState_off:
+ if (now < context->lastStateTime + context->offTime)
+ return;
+ Flash_nextState (context);
+ context->lastStateTime = now;
+ break;
+ }
+
+ Flash_drawCurrentFrame (context);
+}
+
+void
+Flash_setSpeed (FlashContext *context, TimeCount fadeInTime,
+ TimeCount onTime, TimeCount fadeOutTime, TimeCount offTime)
+{
+ context->fadeInTime = fadeInTime;
+ context->onTime = onTime;
+ context->fadeOutTime = fadeOutTime;
+ context->offTime = offTime;
+}
+
+// Determines how the brightness of the flashing changes.
+// For highlights:
+// 'startNumer / denom' is the brightness, at the start state of the flash.
+// 'endNumer / denom' is the brightness, at the end state of the flash.
+// For overlays:
+// 'startNumer / denom' is the brightness of the image to overlay, at
+// the start state of the flash.
+// 'endNumer / denom' is the brightness of the image to overlay, at
+// the end state of the flash.
+// For transitions:
+// 'startNumer / denom' is the brightness of the second image, at
+// the start state of the flash; '1 - startNumer / denom' is the
+// brightness of the first image at the start state of the flash.
+// 'endNumer / denom' is the brightness of the second image, at
+// the end state of the flash; '1 - endNumer / denom' is the
+// brightness of the first image at the end state of the flash.
+// These numbers are relative to the brighness of each original image.
+void
+Flash_setMergeFactors(FlashContext *context, int startNumer, int endNumer,
+ int denom) {
+ if (context->started)
+ {
+ Flash_drawFrame (context, context->original);
+ Flash_clearCache (context);
+ }
+
+ context->startNumer = startNumer;
+ context->endNumer = endNumer;
+ context->denom = denom;
+
+ if (context->started)
+ {
+ Flash_grabOriginal (context);
+ Flash_drawCurrentFrame (context);
+ }
+}
+
+// Set the time between updates of the flash area.
+void
+Flash_setFrameTime (FlashContext *context, TimeCount frameTime)
+{
+ context->frameTime = frameTime;
+}
+
+// Returns the time when the flash area is to be updated.
+TimeCount
+Flash_nextTime (FlashContext *context)
+{
+ if (!context->started || context->paused)
+ return (TimeCount) -1;
+
+ if (context->state == FlashState_fadeIn ||
+ context->state == FlashState_fadeOut)
+ {
+ // When we're fading in or out, we need updates during
+ // the fade.
+ return context->lastFrameTime + context->frameTime;
+ }
+ else
+ {
+ // When the flash area is completely on or off, we don't
+ // need an update until we're ready to change state again.
+ if (context->state == FlashState_on)
+ return context->lastStateTime + context->onTime;
+ else /* context->state == FlashState_off */
+ return context->lastStateTime + context->offTime;
+ }
+}
+
+static void
+Flash_clearCache (FlashContext *context)
+{
+ COUNT i;
+
+#ifdef BEGIN_AND_END_FRAME_EXCEPTIONS
+ if (context->type == FlashType_transition ||
+ context->type == FlashType_overlay)
+ {
+ // First frame is not allocated by the flash code, so
+ // we shouldn't free it.
+ context->cache[0] = (FRAME) 0;
+ }
+ if (context->type == FlashType_transition)
+ {
+ // Final frame is not allocated by the flash code, so
+ // we shouldn't free it.
+ context->cache[context->cacheSize - 1] = (FRAME) 0;
+ }
+#endif /* BEGIN_AND_END_FRMAE_EXCEPTIONS */
+
+ for (i = 0; i < context->cacheSize; i++)
+ {
+ if (context->cache[i] != (FRAME) 0)
+ {
+ DestroyDrawable (ReleaseDrawable (context->cache[i]));
+ context->cache[i] = (FRAME) 0;
+ }
+ }
+}
+
+void
+Flash_setRect (FlashContext *context, const RECT *rect)
+{
+ assert(context->type == FlashType_highlight);
+
+ if (context->started)
+ {
+ Flash_drawFrame (context, context->original);
+ Flash_clearCache (context);
+ }
+
+ context->rect = *rect;
+ context->lastFrameIndex = (COUNT) -1;
+
+ if (context->started)
+ {
+ Flash_grabOriginal (context);
+ Flash_drawCurrentFrame (context);
+ }
+}
+
+void
+Flash_getRect (FlashContext *context, RECT *rect)
+{
+ assert (!context->type == FlashType_highlight);
+
+ *rect = context->rect;
+}
+
+void
+Flash_setOverlay (FlashContext *context, const POINT *origin, FRAME overlay)
+{
+ assert(context->type == FlashType_overlay);
+
+ if (context->started)
+ {
+ Flash_drawFrame (context, context->original);
+ Flash_clearCache (context);
+ }
+
+ context->u.overlay.frame = overlay;
+ GetFrameRect (overlay, &context->rect);
+ context->rect.corner.x += origin->x;
+ context->rect.corner.y += origin->y;
+
+ if (context->started)
+ {
+ Flash_grabOriginal (context);
+ Flash_drawCurrentFrame (context);
+ }
+}
+
+// Call before you update the graphics in the currently flashing area,
+// or before you change the dimensions or origin of the graphics context.
+void
+Flash_preUpdate (FlashContext *context)
+{
+ if (context->started)
+ {
+ Flash_drawFrame (context, context->original);
+ Flash_clearCache (context);
+ }
+}
+
+// Call after you update the graphics in the currently flashing area,
+// or after you change the dimensions or origin of the graphics context.
+void
+Flash_postUpdate (FlashContext *context)
+{
+ if (context->started)
+ {
+ Flash_grabOriginal (context);
+ Flash_drawCurrentFrame (context);
+ }
+}
+
+// Pre: context->original has been initialised.
+static void
+Flash_initCache (FlashContext *context)
+{
+ COUNT i;
+
+ context->cache = HMalloc (context->cacheSize * sizeof (FRAME));
+ for (i = 0; i < context->cacheSize; i++)
+ context->cache[i] = (FRAME) 0;
+}
+
+void
+Flash_setCacheSize (FlashContext *context, COUNT size)
+{
+ assert (size == 0 || size >= 2);
+
+ if (context->cache != NULL)
+ {
+ Flash_clearCache (context);
+ HFree (context->cache);
+ context->cache = NULL;
+ }
+
+ context->cacheSize = size;
+
+ if (size != 0)
+ Flash_initCache (context);
+}
+
+COUNT
+Flash_getCacheSize (const FlashContext *context)
+{
+ return context->cacheSize;
+}
+
+static void
+Flash_grabOriginal (FlashContext *context)
+{
+ CONTEXT oldGfxContext;
+
+ if (context->original != (FRAME) 0)
+ DestroyDrawable (ReleaseDrawable (context->original));
+
+ oldGfxContext = SetContext (context->gfxContext);
+ context->original = CaptureDrawable (CopyContextRect (&context->rect));
+ SetContext (oldGfxContext);
+ FlushGraphics ();
+ // CopyContextRect() may have queued the command to read
+ // a rectangle from the screen; a FlushGraphics()
+ // is necessary to ensure that it can actually be used.
+}
+
+static inline void
+Flash_blendFraction (FlashContext *context, int numer, int denom,
+ int *resNumer, int *resDenom)
+{
+ // This function merges two fractions (F0 and F1),
+ // based on another fraction (P) (yielding R).
+ // F0 = context->u.highlight.startNumer / context->u.highlight.denom
+ // F1 = context->u.highlight.endNumer / context->u.highlight.denom
+ // P = *numer / *denom
+ // R = P * F1 + (1 - P) * F0
+ // = numer * context->endNumer / (denom * context->denom) +
+ // (denom - numer) * startNumer / denom * context->denom
+
+ assert (numer >= 0 && numer <= denom);
+
+ *resNumer = numer * context->endNumer +
+ (denom - numer) * context->startNumer;
+ *resDenom = denom * context->denom;
+}
+
+static void
+Flash_makeFrame (FlashContext *context, FRAME dest, int numer, int denom)
+{
+ CONTEXT oldGfxContext;
+ STAMP s;
+ int blendedNumer;
+ int blendedDenom;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+
+ Flash_blendFraction (context, numer, denom, &blendedNumer, &blendedDenom);
+
+ oldGfxContext = SetContext (workGfxContext);
+ SetContextFGFrame (dest);
+
+ switch (context->type) {
+ case FlashType_highlight:
+ {
+ // Clear the destination just in case
+ SetContextBackGroundColor (BUILD_COLOR_RGBA (0, 0, 0, 0));
+ ClearDrawable ();
+ // Draw the frame at modulated strength (0 < strength <= 128)
+ SetContextDrawMode (MAKE_DRAW_MODE (DRAW_ADDITIVE,
+ DRAW_FACTOR_1 * blendedNumer / blendedDenom));
+ s.frame = context->original;
+ DrawStamp (&s);
+ break;
+ }
+ case FlashType_transition:
+ {
+ FRAME first;
+ FRAME final;
+
+ first = context->u.transition.first;
+ if (first == (FRAME) 0)
+ first = context->original;
+ final = context->u.transition.final;
+ if (final == (FRAME) 0)
+ final = context->original;
+
+ // Draw the first frame at full strength
+ SetContextDrawMode (DRAW_REPLACE_MODE);
+ s.frame = first;
+ DrawStamp (&s);
+ // Merge in the final frame
+ SetContextDrawMode (MAKE_DRAW_MODE (DRAW_ALPHA,
+ DRAW_FACTOR_1 * blendedNumer / blendedDenom));
+ s.frame = final;
+ DrawStamp (&s);
+ break;
+ }
+ case FlashType_overlay:
+ {
+ POINT oldOrigin;
+
+ // Draw the original at full strength
+ SetContextDrawMode (DRAW_REPLACE_MODE);
+ s.frame = context->original;
+ DrawStamp (&s);
+ // Add or subtract the overlay at partial strength
+ SetContextDrawMode (MAKE_DRAW_MODE (DRAW_ADDITIVE,
+ DRAW_FACTOR_1 * blendedNumer / blendedDenom));
+ s.frame = context->u.overlay.frame;
+ // Offset the draw origin to hit the right area
+ oldOrigin = SetContextOrigin (GetFrameHot (s.frame));
+ DrawStamp (&s);
+ SetContextOrigin (oldOrigin);
+ break;
+ }
+ }
+
+ SetContext (oldGfxContext);
+}
+
+// Prepare an entry in the cache.
+static inline void
+Flash_prepareCacheFrame (FlashContext *context, COUNT index)
+{
+ if (context->cache[index] != (FRAME) 0)
+ return;
+
+#ifdef BEGIN_AND_END_FRAME_EXCEPTIONS
+ if (index == 0 && context->type == FlashType_overlay)
+ context->cache[index] = context->original;
+ else if (index == 0 && context->type == FlashType_transition)
+ context->cache[index] = context->u.transition.first != (FRAME) 0 ?
+ context->u.transition.first : context->original;
+ else if (index == context->cacheSize - 1 &&
+ context->type == FlashType_transition)
+ context->cache[index] = context->u.transition.final != (FRAME) 0 ?
+ context->u.transition.final : context->original;
+ else
+#endif /* BEGIN_AND_END_FRMAE_EXCEPTIONS */
+ {
+ context->cache[index] = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
+ context->rect.extent.width, context->rect.extent.height, 1));
+ Flash_makeFrame (context, context->cache[index],
+ index, context->cacheSize - 1);
+ }
+}
+
+static void
+Flash_drawFrame (FlashContext *context, FRAME frame)
+{
+ CONTEXT oldGfxContext;
+ STAMP stamp;
+
+ oldGfxContext = SetContext (context->gfxContext);
+
+ stamp.origin = context->rect.corner;
+ stamp.frame = frame;
+ DrawStamp(&stamp);
+
+ SetContext (oldGfxContext);
+}
+
+static void
+Flash_drawCacheFrame (FlashContext *context, COUNT index)
+{
+ FRAME frame;
+
+ if (context->lastFrameIndex == index)
+ return;
+
+ frame = context->cache[index];
+ Flash_drawFrame (context, frame);
+ context->lastFrameIndex = index;
+}
+
+static inline void
+Flash_drawUncachedFrame (FlashContext *context, int numer, int denom)
+{
+#ifdef BEGIN_AND_END_FRAME_EXCEPTIONS
+ // 'lastFrameIndex' is 0 for the first image, 1 for the final
+ // image, and 2 otherwise.
+
+ if (numer == 0 && context->type == FlashType_overlay)
+ {
+ if (context->lastFrameIndex != 0)
+ return;
+
+ Flash_drawFrame (context, context->original);
+ context->lastFrameIndex = 0;
+ return;
+ }
+ else if (numer == 0 && context->type == FlashType_transition)
+ {
+ if (context->lastFrameIndex == 0)
+ return;
+
+ Flash_drawFrame (context, context->u.transition.first);
+ context->lastFrameIndex = 0;
+ return;
+ }
+ else if (numer == denom && context->type == FlashType_transition)
+ {
+ if (context->lastFrameIndex == 1)
+ return;
+
+ Flash_drawFrame (context, context->u.transition.final);
+ context->lastFrameIndex = 1;
+ return;
+ }
+
+ context->lastFrameIndex = 2;
+#endif /* BEGIN_AND_END_FRMAE_EXCEPTIONS */
+
+ {
+ // Painting to the screen; we need a temporary frame to draw to.
+ FRAME work;
+
+ work = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
+ context->rect.extent.width, context->rect.extent.height, 1));
+ Flash_makeFrame (context, work, numer, denom);
+ Flash_drawFrame (context, work);
+
+ DestroyDrawable (ReleaseDrawable (work));
+ }
+}
+
+static inline void
+Flash_drawCachedFrame (FlashContext *context, int numer, int denom)
+{
+ COUNT cachePos;
+
+ cachePos = ((context->cacheSize - 1) * numer + (denom / 2)) / denom;
+ Flash_prepareCacheFrame (context, cachePos);
+ Flash_drawCacheFrame (context, cachePos);
+}
+
+static void
+Flash_drawCurrentFrame (FlashContext *context)
+{
+ int numer;
+ int denom;
+
+ if (context->state == FlashState_off)
+ {
+ numer = 0;
+ denom = 1;
+ }
+ else if (context->state == FlashState_on)
+ {
+ numer = 1;
+ denom = 1;
+ }
+ else
+ {
+ TimeCount now = GetTimeCounter ();
+
+ if (context->state == FlashState_fadeIn)
+ denom = (int) context->fadeInTime;
+ else
+ denom = (int) context->fadeOutTime;
+
+ numer = (int) (now - context->lastStateTime);
+
+ if (numer > denom)
+ numer = denom;
+
+ if (context->state == FlashState_fadeOut)
+ numer = (int) context->fadeOutTime - numer;
+ }
+
+ if (context->cacheSize == 0)
+ Flash_drawUncachedFrame (context, numer, denom);
+ else
+ Flash_drawCachedFrame (context, numer, denom);
+}
+
diff --git a/src/uqm/flash.h b/src/uqm/flash.h
new file mode 100644
index 0000000..0488fd3
--- /dev/null
+++ b/src/uqm/flash.h
@@ -0,0 +1,223 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_FLASH_H_
+#define UQM_FLASH_H_
+
+/*
+ * This code can draw three kinds of flashing areas.
+ * - a rectangular highlight area. The brightness of the area oscilates.
+ * - an overlay; an image is laid over an area, with oscilating brightness.
+ * - a transition/cross-fade between two images.
+ *
+ * NB. The graphics lock should not be held when any of the Flash functions
+ * is called.
+ *
+ *
+ * Example:
+ *
+ * // We create the flash context; it is used to manipulate the flash
+ * // rectangle while it exists.
+ * FlashContext *fc = Flash_createHighlight (gfxContext, rect);
+ *
+ * // Specify how bright the flash is at the beginning and ending of the
+ * // sequence.
+ * Flash_setMergeFactors(fc, 2, 3, 2);
+ *
+ * // We change the flashing speed from the defaults.
+ * Flash_setSpeed (fc, ONE_SECOND, ONE_SECOND, ONE_SECOND, ONE_SECOND);
+ *
+ * // During cross-fades, update 8 times per second.
+ * Flash_setFrameTime (fc, ONE_SECOND / 8);
+ *
+ * // We start the flashing. The default is to start from the "off" state.
+ * Flash_start (fc);
+ *
+ * // Some other stuff happens
+ * ...
+ *
+ * // The user has activated the selection. We pause for instance while
+ * // a pop-up window is active.
+ * Flash_pause (fc);
+ *
+ * // We set the flashing rectangle full on, to indicate the current
+ * // selection while the popup is active.
+ * Flash_setState (FlashState_on);
+ * ...
+ * // Continue the flashing.
+ * Flash_continue (fc);
+ * ...
+ * // Modifying the graphics of the area that is flashing:
+ * void Flash_preUpdate (fc);
+ * ... // do drawing
+ * void Flash_postUpdate (fc);
+ * ...
+ * // We're done. Terminating the flash restores the flash area to its
+ * // original state.
+ * Flash_terminate (fc);
+ *
+ *
+ * Periodically, Flash_process() should be called on the flash context,
+ * so that the flashing square is updated.
+ * You can use Flash_nextTime() to determine when the next update is needed,
+ * or just call Flash_process() to try (it does no updates if not needed).
+ *
+ * Limitations:
+ *
+ * * Functions that draw to the gfxContext or read the original gfxContext
+ * contents, which is most of them, must be called with gfxContext having
+ * the same clip-rect as it did when other drawing functions were called.
+ * Otherwise, original contents restoration may draw to the wrong area, or
+ * the wrong area may be read.
+ * There may be cases where one would *want* that to happen, and such
+ * cases are not covered by this limitation.
+ * * Multiple flashes may be used simultaneously, but don't let them
+ * overlap; artifacts would occur.
+ */
+
+#include "libs/gfxlib.h"
+#include "libs/timelib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef enum {
+ FlashState_fadeIn = 0,
+ // Someway between on and off, going towards on.
+ FlashState_on = 1,
+ // The overlay image is visible at 100% opacity.
+ FlashState_fadeOut = 2,
+ // Someway between on and off, going towards off.
+ FlashState_off = 3,
+ // The overlay image is not visible.
+} FlashState;
+
+typedef struct FlashContext FlashContext;
+
+#ifdef FLASH_INTERNAL
+typedef enum {
+ FlashType_highlight,
+ FlashType_transition,
+ FlashType_overlay,
+} FlashType;
+
+struct FlashContext {
+ CONTEXT gfxContext;
+ // The graphics context used for drawing.
+
+ RECT rect;
+ // The rectangle to flash.
+
+ FRAME original;
+ // The original contents of the flash area.
+
+ FlashType type;
+ // The type of flash animation.
+
+ union {
+ /*struct {
+ } highlight;*/
+ struct {
+ FRAME first;
+ // The first image from the transition (cross-fade).
+ // (FRAME) 0 means that the original is to be used.
+ FRAME final;
+ // The last image from the transition.
+ // (FRAME) 0 means that the original is to be used.
+ } transition;
+ struct {
+ FRAME frame;
+ } overlay;
+ } u;
+
+ int startNumer;
+ // Numerator for the merge factor for the on state.
+ int endNumer;
+ // Numerator for the merge factor for the off state.
+ int denom;
+ // Denominator for the merge factor.
+
+ TimeCount fadeInTime;
+ TimeCount onTime;
+ TimeCount fadeOutTime;
+ TimeCount offTime;
+
+ TimeCount frameTime;
+
+ FlashState state;
+ TimeCount lastStateTime;
+ // Time of the last state change.
+ TimeCount lastFrameTime;
+ // Time of the last frame draw.
+
+ BOOLEAN started;
+ BOOLEAN paused;
+
+ FRAME *cache;
+ COUNT cacheSize;
+
+ COUNT lastFrameIndex;
+ // Last frame drawn; used to determine whether a frame needs to
+ // be redawn. If a cache is used, this is the index in the cache.
+ // If no cache is used, this is either 0, 1, or 2, for
+ // the respectively first, last, or other frame for the flash
+ // animation.
+};
+
+# define Flash_DEFAULT_FADE_IN_TIME 0
+# define Flash_DEFAULT_ON_TIME (ONE_SECOND / 8)
+# define Flash_DEFAULT_FADE_OUT_TIME 0
+# define Flash_DEFAULT_OFF_TIME (ONE_SECOND / 8)
+
+# define Flash_DEFAULT_CACHE_SIZE 9
+#endif /* FLASH_INTERNAL */
+
+
+FlashContext *Flash_createHighlight (CONTEXT gfxContext, const RECT *rect);
+FlashContext *Flash_createTransition (CONTEXT gfxContext,
+ const POINT *origin, FRAME first, FRAME final);
+FlashContext *Flash_createOverlay (CONTEXT gfxContext,
+ const POINT *origin, FRAME overlay);
+
+void Flash_setState (FlashContext *context, FlashState state,
+ TimeCount timeSpentInState);
+void Flash_start (FlashContext *context);
+void Flash_terminate (FlashContext *context);
+void Flash_pause (FlashContext *context);
+void Flash_continue (FlashContext *context);
+void Flash_process (FlashContext *context);
+void Flash_setSpeed (FlashContext *context, TimeCount fadeInTime,
+ TimeCount onTime, TimeCount fadeOutTime, TimeCount offTime);
+void Flash_setMergeFactors(FlashContext *context, int startNumer,
+ int endNumer, int denom);
+void Flash_setFrameTime (FlashContext *context, TimeCount frameTime);
+TimeCount Flash_nextTime (FlashContext *context);
+void Flash_setRect (FlashContext *context, const RECT *rect);
+void Flash_getRect (FlashContext *context, RECT *rect);
+void Flash_setOverlay(FlashContext *context, const POINT *origin,
+ FRAME overlay);
+void Flash_preUpdate (FlashContext *context);
+void Flash_postUpdate (FlashContext *context);
+void Flash_setCacheSize (FlashContext *context, COUNT size);
+COUNT Flash_getCacheSize (const FlashContext *context);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_FLASH_H_ */
diff --git a/src/uqm/fmv.c b/src/uqm/fmv.c
new file mode 100644
index 0000000..b567901
--- /dev/null
+++ b/src/uqm/fmv.c
@@ -0,0 +1,134 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "fmv.h"
+
+#include "controls.h"
+#include "hyper.h"
+#include "options.h"
+#include "master.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "settings.h"
+#include "setup.h"
+#include "libs/vidlib.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/inplib.h"
+
+void
+DoShipSpin (COUNT index, MUSIC_REF hMusic)
+{
+#ifdef WANT_SHIP_SPINS
+ char vnbuf[32];
+ RECT old_r;
+
+ LoadIntoExtraScreen (NULL);
+#if 0
+ /* This is cut out right now but should be part of the 3DO side */
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 4));
+ FlushColorXForms ();
+#endif
+
+ if (hMusic)
+ StopMusic ();
+
+ FreeHyperData ();
+
+ // TODO: It would be nice to have better resource names for these.
+ sprintf (vnbuf, "slides.spins.%02u", (unsigned)index);
+ ShowPresentation (vnbuf);
+
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 4));
+ FlushColorXForms ();
+
+ GetContextClipRect (&old_r);
+ SetContextClipRect (NULL);
+ DrawFromExtraScreen (NULL);
+ SetContextClipRect (&old_r);
+
+ if (hMusic)
+ PlayMusic (hMusic, TRUE, 1);
+
+ SleepThreadUntil (FadeScreen (FadeAllToColor, ONE_SECOND / 4));
+ FlushColorXForms ();
+#else
+ (void) index; /* Satisfy compiler */
+ (void) hMusic; /* Satisfy compiler */
+#endif /* WANT_SHIP_SPINS */
+}
+
+void
+SplashScreen (void (* DoProcessing)(DWORD TimeOut))
+{
+ STAMP s;
+ DWORD TimeOut;
+
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 120));
+ SetContext (ScreenContext);
+ s.origin.x = s.origin.y = 0;
+ s.frame = CaptureDrawable (LoadGraphic (TITLE_ANIM));
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+
+ TimeOut = FadeScreen (FadeAllToColor, ONE_SECOND / 2);
+
+ if (DoProcessing)
+ DoProcessing (TimeOut);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ return;
+ }
+
+ /* There was a forcible setting of CHECK_ABORT here. I cannot
+ * find any purpose for this that DoRestart doesn't handle
+ * better (forcing all other threads but this one to quit out,
+ * I believe), and have thus removed it. It was interfering
+ * with the proper operation of the quit operation.
+ * --Michael */
+
+ WaitForAnyButton (FALSE, ONE_SECOND * 3, TRUE);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ return;
+ }
+ GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
+
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+}
+
+void
+Introduction (void)
+{
+ ShowPresentation (INTROPRES_STRTAB);
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+}
+
+void
+Victory (void)
+{
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+
+ /* by default we do 3DO cinematics; or PC slides when 3DO files are
+ * not present */
+ ShowPresentation (FINALPRES_STRTAB);
+
+ FadeScreen (FadeAllToBlack, 0);
+}
+
+
+
diff --git a/src/uqm/fmv.h b/src/uqm/fmv.h
new file mode 100644
index 0000000..6ff4dd8
--- /dev/null
+++ b/src/uqm/fmv.h
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_FMV_H_
+#define UQM_FMV_H_
+
+#include "libs/compiler.h"
+#include "libs/sndlib.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define WANT_SHIP_SPINS
+
+extern void SplashScreen (void (* DoProcessing)(DWORD TimeOut));
+extern void Introduction (void);
+extern void Victory (void);
+extern void DoShipSpin (COUNT index, MUSIC_REF hMusic);
+
+extern BOOLEAN ShowPresentation (RESOURCE presentation);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_FMV_H_ */
diff --git a/src/uqm/galaxy.c b/src/uqm/galaxy.c
new file mode 100644
index 0000000..7e14584
--- /dev/null
+++ b/src/uqm/galaxy.c
@@ -0,0 +1,464 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* background starfield - used to generate agalaxy.asm */
+
+#include "element.h"
+#include "globdata.h"
+#include "init.h"
+#include "process.h"
+#include "units.h"
+#include "options.h"
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+extern COUNT zoom_out;
+extern PRIM_LINKS DisplayLinks;
+
+
+#define BIG_STAR_COUNT 30
+#define MED_STAR_COUNT 60
+#define SML_STAR_COUNT 90
+#define NUM_STARS (BIG_STAR_COUNT \
+ + MED_STAR_COUNT \
+ + SML_STAR_COUNT)
+
+POINT SpaceOrg;
+static POINT log_star_array[NUM_STARS];
+
+#define NUM_STAR_PLANES 3
+
+typedef struct
+{
+ COUNT min_star_index;
+ COUNT num_stars;
+ POINT *star_array;
+ POINT *pmin_star;
+ POINT *plast_star;
+} STAR_BLOCK;
+
+STAR_BLOCK StarBlock[NUM_STAR_PLANES] =
+{
+ {
+ 0, BIG_STAR_COUNT,
+ &log_star_array[0],
+ NULL, NULL,
+ },
+ {
+ 0, MED_STAR_COUNT,
+ &log_star_array[BIG_STAR_COUNT],
+ NULL, NULL,
+ },
+ {
+ 0, SML_STAR_COUNT,
+ &log_star_array[BIG_STAR_COUNT + MED_STAR_COUNT],
+ NULL, NULL,
+ },
+};
+
+static void
+SortStarBlock (STAR_BLOCK *pStarBlock)
+{
+ COUNT i;
+
+ for (i = 0; i < pStarBlock->num_stars; ++i)
+ {
+ COUNT j;
+
+ for (j = pStarBlock->num_stars - 1; j > i; --j)
+ {
+ if (pStarBlock->star_array[i].y > pStarBlock->star_array[j].y)
+ {
+ POINT temp;
+
+ temp = pStarBlock->star_array[i];
+ pStarBlock->star_array[i] = pStarBlock->star_array[j];
+ pStarBlock->star_array[j] = temp;
+ }
+ }
+ }
+
+ pStarBlock->min_star_index = 0;
+ pStarBlock->pmin_star = &pStarBlock->star_array[0];
+ pStarBlock->plast_star =
+ &pStarBlock->star_array[pStarBlock->num_stars - 1];
+}
+
+static void
+WrapStarBlock (SIZE plane, SIZE dx, SIZE dy)
+{
+ COUNT i;
+ POINT *ppt;
+ SIZE offs_y;
+ COUNT num_stars;
+ STAR_BLOCK *pStarBlock;
+
+ pStarBlock = &StarBlock[plane];
+
+ i = pStarBlock->min_star_index;
+ ppt = pStarBlock->pmin_star;
+ num_stars = pStarBlock->num_stars;
+ if (dy < 0)
+ {
+ COUNT first;
+
+ first = i;
+
+ dy = -dy;
+ offs_y = (LOG_SPACE_HEIGHT << plane) - dy;
+
+ while (ppt->y < dy)
+ {
+ ppt->y += offs_y;
+ ppt->x += dx;
+ if (++i < num_stars)
+ ++ppt;
+ else
+ {
+ i = 0;
+ ppt = &pStarBlock->star_array[0];
+ }
+
+ if (i == first)
+ return;
+ }
+ pStarBlock->min_star_index = i;
+ pStarBlock->pmin_star = ppt;
+
+ if (first <= i)
+ {
+ i = num_stars - i;
+ do
+ {
+ ppt->y -= dy;
+ ppt->x += dx;
+ ++ppt;
+ } while (--i);
+ ppt = &pStarBlock->star_array[0];
+ }
+
+ if (first > i)
+ {
+ i = first - i;
+ do
+ {
+ ppt->y -= dy;
+ ppt->x += dx;
+ ++ppt;
+ } while (--i);
+ }
+ }
+ else
+ {
+ COUNT last;
+
+ --ppt;
+ if (i-- == 0)
+ {
+ i = num_stars - 1;
+ ppt = pStarBlock->plast_star;
+ }
+
+ last = i;
+
+ if (dy > 0)
+ {
+ offs_y = (LOG_SPACE_HEIGHT << plane) - dy;
+
+ while (ppt->y >= offs_y)
+ {
+ ppt->y -= offs_y;
+ ppt->x += dx;
+ if (i-- > 0)
+ --ppt;
+ else
+ {
+ i = num_stars - 1;
+ ppt = pStarBlock->plast_star;
+ }
+
+ if (i == last)
+ return;
+ }
+
+ pStarBlock->pmin_star = ppt + 1;
+ if ((pStarBlock->min_star_index = i + 1) == num_stars)
+ {
+ pStarBlock->min_star_index = 0;
+ pStarBlock->pmin_star = &pStarBlock->star_array[0];
+ }
+ }
+
+ if (last >= i)
+ {
+ ++i;
+ do
+ {
+ ppt->y += dy;
+ ppt->x += dx;
+ --ppt;
+ } while (--i);
+ i = num_stars - 1;
+ ppt = pStarBlock->plast_star;
+ }
+
+ if (last < i)
+ {
+ i = i - last;
+ do
+ {
+ ppt->y += dy;
+ ppt->x += dx;
+ --ppt;
+ } while (--i);
+ }
+ }
+}
+
+void
+InitGalaxy (void)
+{
+ COUNT i, factor;
+ POINT *ppt;
+ PRIM_LINKS Links;
+
+ log_add (log_Debug, "InitGalaxy(): transition_width = %d, "
+ "transition_height = %d",
+ TRANSITION_WIDTH, TRANSITION_HEIGHT);
+
+ Links = MakeLinks (END_OF_LIST, END_OF_LIST);
+ factor = ONE_SHIFT + MAX_REDUCTION + (BACKGROUND_SHIFT - 3);
+ for (i = 0, ppt = log_star_array; i < NUM_STARS; ++i, ++ppt)
+ {
+ COUNT p;
+
+ p = AllocDisplayPrim ();
+
+ if (i == BIG_STAR_COUNT || i == BIG_STAR_COUNT + MED_STAR_COUNT)
+ ++factor;
+
+ ppt->x = (COORD)((UWORD)TFB_Random () % SPACE_WIDTH) << factor;
+ ppt->y = (COORD)((UWORD)TFB_Random () % SPACE_HEIGHT) << factor;
+
+ if (i < BIG_STAR_COUNT + MED_STAR_COUNT)
+ {
+ SetPrimType (&DisplayArray[p], STAMP_PRIM);
+ SetPrimColor (&DisplayArray[p],
+ BUILD_COLOR (MAKE_RGB15 (0x0B, 0x0B, 0x1F), 0x09));
+ DisplayArray[p].Object.Stamp.frame = stars_in_space;
+ }
+ else
+ {
+ SetPrimType (&DisplayArray[p], POINT_PRIM);
+ if (!inHQSpace ())
+ SetPrimColor (&DisplayArray[p],
+ BUILD_COLOR (MAKE_RGB15 (0x15, 0x15, 0x15), 0x07));
+ else if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ SetPrimColor (&DisplayArray[p],
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x8C));
+ else
+ SetPrimColor (&DisplayArray[p],
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x0E, 0x00), 0x8C));
+ }
+
+ InsertPrim (&Links, p, GetPredLink (Links));
+ }
+
+ SortStarBlock (&StarBlock[0]);
+ SortStarBlock (&StarBlock[1]);
+ SortStarBlock (&StarBlock[2]);
+}
+
+static BOOLEAN
+CmpMovePoints (const POINT *pt1, const POINT *pt2, SIZE dx, SIZE dy,
+ SIZE reduction)
+{
+ if (optMeleeScale == TFB_SCALE_STEP)
+ {
+ return (int)pt1->x != (int)((pt2->x - dx) >> reduction)
+ || (int)pt1->y != (int)((pt2->y - dy) >> reduction);
+ }
+ else
+ {
+ return (int)pt1->x != (int)(((pt2->x - dx) << ZOOM_SHIFT) / reduction)
+ || (int)pt1->y != (int)(((pt2->y - dy) << ZOOM_SHIFT) / reduction);
+ }
+}
+
+void
+MoveGalaxy (VIEW_STATE view_state, SIZE dx, SIZE dy)
+{
+ PRIMITIVE *pprim;
+ static const COUNT star_counts[] =
+ {
+ BIG_STAR_COUNT,
+ MED_STAR_COUNT,
+ SML_STAR_COUNT
+ };
+ static const COUNT star_frame_ofs[] = { 32 + 26, 26, 0 };
+
+ if (view_state != VIEW_STABLE)
+ {
+ COUNT reduction;
+ COUNT i;
+ COUNT iss;
+ POINT *ppt;
+ int wrap_around;
+
+ reduction = zoom_out;
+
+ if (view_state == VIEW_CHANGE)
+ {
+ if (inHQSpace ())
+ {
+ for (iss = 0, pprim = DisplayArray; iss < 2; ++iss)
+ {
+ for (i = star_counts[iss]; i > 0; --i, ++pprim)
+ {
+ pprim->Object.Stamp.frame = SetAbsFrameIndex (
+ stars_in_space,
+ (COUNT)(TFB_Random () & 31)
+ + star_frame_ofs[iss]);
+ }
+ }
+ }
+ else
+ {
+ GRAPHICS_PRIM star_object[2];
+ FRAME star_frame[2];
+
+ star_frame[0] = IncFrameIndex (stars_in_space);
+ star_frame[1] = stars_in_space;
+
+ if (optMeleeScale == TFB_SCALE_STEP)
+ { /* on PC, the closest stars are images when zoomed out */
+ star_object[0] = STAMP_PRIM;
+ if (reduction > 0)
+ {
+ star_object[1] = POINT_PRIM;
+ star_frame[0] = star_frame[1];
+ }
+ else
+ {
+ star_object[1] = STAMP_PRIM;
+ }
+ }
+ else
+ { /* on 3DO, the closest stars are pixels when zoomed out */
+ star_object[1] = POINT_PRIM;
+ if (reduction > (1 << ZOOM_SHIFT))
+ {
+ star_object[0] = POINT_PRIM;
+ }
+ else
+ {
+ star_object[0] = STAMP_PRIM;
+ }
+ }
+
+ for (iss = 0, pprim = DisplayArray; iss < 2; ++iss)
+ {
+ for (i = star_counts[iss]; i > 0; --i, ++pprim)
+ {
+ SetPrimType (pprim, star_object[iss]);
+ pprim->Object.Stamp.frame = star_frame[iss];
+ }
+ }
+ }
+ }
+
+ if (inHQSpace ())
+ {
+ for (i = BIG_STAR_COUNT + MED_STAR_COUNT, pprim = DisplayArray;
+ i > 0; --i, ++pprim)
+ {
+ COUNT base_index;
+
+ base_index = GetFrameIndex (pprim->Object.Stamp.frame) - 26;
+ pprim->Object.Stamp.frame =
+ SetAbsFrameIndex (pprim->Object.Stamp.frame,
+ ((base_index & ~31) + ((base_index + 1) & 31)) + 26);
+ }
+
+ dx <<= 3;
+ dy <<= 3;
+ }
+
+ WrapStarBlock (2, dx, dy);
+ WrapStarBlock (1, dx, dy);
+ WrapStarBlock (0, dx, dy);
+
+ if (!inHQSpace ())
+ {
+ dx = SpaceOrg.x;
+ dy = SpaceOrg.y;
+ if (optMeleeScale == TFB_SCALE_STEP)
+ reduction += ONE_SHIFT;
+ else
+ reduction <<= ONE_SHIFT;
+ }
+ else
+ {
+ dx = (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> ((MAX_REDUCTION + 1)
+ - MAX_VIS_REDUCTION));
+ dy = (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> ((MAX_REDUCTION + 1)
+ - MAX_VIS_REDUCTION));
+ if (optMeleeScale == TFB_SCALE_STEP)
+ reduction = MAX_VIS_REDUCTION + ONE_SHIFT;
+ else
+ reduction = MAX_ZOOM_OUT << ONE_SHIFT;
+ }
+
+ ppt = log_star_array;
+ for (iss = 0, pprim = DisplayArray, wrap_around = LOG_SPACE_WIDTH;
+ iss < 3 &&
+ (view_state == VIEW_CHANGE || CmpMovePoints (
+ &pprim->Object.Point, ppt, dx, dy, reduction));
+ ++iss, wrap_around <<= 1, dx <<= 1, dy <<= 1)
+ {
+ for (i = star_counts[iss]; i > 0; --i, ++pprim, ++ppt)
+ {
+ // ppt->x &= (LOG_SPACE_WIDTH - 1);
+ ppt->x = WRAP_VAL (ppt->x, wrap_around);
+ if (optMeleeScale == TFB_SCALE_STEP)
+ {
+ pprim->Object.Point.x = (ppt->x - dx) >> reduction;
+ pprim->Object.Point.y = (ppt->y - dy) >> reduction;
+ }
+ else
+ {
+ pprim->Object.Point.x = ((ppt->x - dx) << ZOOM_SHIFT)
+ / reduction;
+ pprim->Object.Point.y = ((ppt->y - dy) << ZOOM_SHIFT)
+ / reduction;
+ }
+ }
+ if (optMeleeScale == TFB_SCALE_STEP)
+ ++reduction;
+ else
+ reduction <<= 1;
+ }
+ }
+
+ DisplayLinks = MakeLinks (NUM_STARS - 1, 0);
+}
diff --git a/src/uqm/gameev.c b/src/uqm/gameev.c
new file mode 100644
index 0000000..83df601
--- /dev/null
+++ b/src/uqm/gameev.c
@@ -0,0 +1,729 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "gameev.h"
+
+#include "build.h"
+#include "clock.h"
+#include "starmap.h"
+#include "gendef.h"
+#include "globdata.h"
+#include "hyper.h"
+#include "libs/compiler.h"
+#include "libs/mathlib.h"
+
+
+static void arilou_entrance_event (void);
+static void arilou_exit_event (void);
+static void check_race_growth (void);
+static void black_urquan_genocide (void);
+static void pkunk_mission (void);
+static void thradd_mission (void);
+static void ilwrath_mission (void);
+static void utwig_supox_mission (void);
+static void mycon_mission (void);
+
+
+void
+AddInitialGameEvents (void) {
+ AddEvent (RELATIVE_EVENT, 0, 1, 0, HYPERSPACE_ENCOUNTER_EVENT);
+ AddEvent (ABSOLUTE_EVENT, 3, 17, START_YEAR, ARILOU_ENTRANCE_EVENT);
+ AddEvent (RELATIVE_EVENT, 0, 0, YEARS_TO_KOHRAH_VICTORY,
+ KOHR_AH_VICTORIOUS_EVENT);
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, SLYLANDRO_RAMP_UP);
+}
+
+void
+EventHandler (BYTE selector)
+{
+ switch (selector)
+ {
+ case ARILOU_ENTRANCE_EVENT:
+ arilou_entrance_event ();
+ break;
+ case ARILOU_EXIT_EVENT:
+ arilou_exit_event ();
+ break;
+ case HYPERSPACE_ENCOUNTER_EVENT:
+ check_race_growth ();
+ if (inHyperSpace ())
+ check_hyperspace_encounter ();
+
+ AddEvent (RELATIVE_EVENT, 0, 1, 0, HYPERSPACE_ENCOUNTER_EVENT);
+ break;
+ case KOHR_AH_VICTORIOUS_EVENT:
+ if (GET_GAME_STATE (UTWIG_SUPOX_MISSION))
+ {
+ AddEvent (RELATIVE_EVENT, 0, 0, 1, KOHR_AH_GENOCIDE_EVENT);
+ break;
+ }
+ /* FALLTHROUGH */
+ case KOHR_AH_GENOCIDE_EVENT:
+ if (!GET_GAME_STATE (KOHR_AH_FRENZY)
+ && LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && CurStarDescPtr
+ && CurStarDescPtr->Index == SAMATRA_DEFINED)
+ AddEvent (RELATIVE_EVENT, 0, 7, 0, KOHR_AH_GENOCIDE_EVENT);
+ else
+ black_urquan_genocide ();
+ break;
+ case ADVANCE_PKUNK_MISSION:
+ pkunk_mission ();
+ break;
+ case ADVANCE_THRADD_MISSION:
+ thradd_mission ();
+ break;
+ case ZOQFOT_DISTRESS_EVENT:
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && CurStarDescPtr
+ && CurStarDescPtr->Index == ZOQFOT_DEFINED)
+ AddEvent (RELATIVE_EVENT, 0, 7, 0, ZOQFOT_DISTRESS_EVENT);
+ else
+ {
+ SET_GAME_STATE (ZOQFOT_DISTRESS, 1);
+ AddEvent (RELATIVE_EVENT, 6, 0, 0, ZOQFOT_DEATH_EVENT);
+ }
+ break;
+ case ZOQFOT_DEATH_EVENT:
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && CurStarDescPtr
+ && CurStarDescPtr->Index == ZOQFOT_DEFINED)
+ AddEvent (RELATIVE_EVENT, 0, 7, 0, ZOQFOT_DEATH_EVENT);
+ else if (GET_GAME_STATE (ZOQFOT_DISTRESS))
+ {
+ HFLEETINFO hZoqFot;
+ FLEET_INFO *ZoqFotPtr;
+
+ hZoqFot = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ ZOQFOTPIK_SHIP);
+ ZoqFotPtr = LockFleetInfo (&GLOBAL (avail_race_q), hZoqFot);
+ ZoqFotPtr->actual_strength = 0;
+ ZoqFotPtr->allied_state = DEAD_GUY;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hZoqFot);
+
+ SET_GAME_STATE (ZOQFOT_DISTRESS, 2);
+ }
+ break;
+ case SHOFIXTI_RETURN_EVENT:
+ SetRaceAllied (SHOFIXTI_SHIP, TRUE);
+ GLOBAL (CrewCost) -= 2;
+ /* crew is not an issue anymore */
+ SET_GAME_STATE (CREW_PURCHASED0, 0);
+ SET_GAME_STATE (CREW_PURCHASED1, 0);
+ break;
+ case ADVANCE_UTWIG_SUPOX_MISSION:
+ utwig_supox_mission ();
+ break;
+ case SPATHI_SHIELD_EVENT:
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && CurStarDescPtr
+ && CurStarDescPtr->Index == SPATHI_DEFINED)
+ AddEvent (RELATIVE_EVENT, 0, 7, 0, SPATHI_SHIELD_EVENT);
+ else
+ {
+ HFLEETINFO hSpathi;
+ FLEET_INFO *SpathiPtr;
+
+ hSpathi = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ SPATHI_SHIP);
+ SpathiPtr = LockFleetInfo (&GLOBAL (avail_race_q), hSpathi);
+
+ if (SpathiPtr->actual_strength)
+ {
+ SetRaceAllied (SPATHI_SHIP, FALSE);
+ SET_GAME_STATE (SPATHI_SHIELDED_SELVES, 1);
+ SpathiPtr->actual_strength = 0;
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hSpathi);
+ }
+ break;
+ case ADVANCE_ILWRATH_MISSION:
+ ilwrath_mission ();
+ break;
+ case ADVANCE_MYCON_MISSION:
+ mycon_mission ();
+ break;
+ case ARILOU_UMGAH_CHECK:
+ SET_GAME_STATE (ARILOU_CHECKED_UMGAH, 2);
+ break;
+ case YEHAT_REBEL_EVENT:
+ {
+ HFLEETINFO hRebel, hRoyalist;
+ FLEET_INFO *RebelPtr;
+ FLEET_INFO *RoyalistPtr;
+
+ hRebel = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ YEHAT_REBEL_SHIP);
+ RebelPtr = LockFleetInfo (&GLOBAL (avail_race_q), hRebel);
+ hRoyalist = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ YEHAT_SHIP);
+ RoyalistPtr = LockFleetInfo (&GLOBAL (avail_race_q), hRoyalist);
+ RoyalistPtr->actual_strength = RoyalistPtr->actual_strength *
+ 2 / 3;
+ RebelPtr->actual_strength = RoyalistPtr->actual_strength;
+ RebelPtr->loc.x = 5150;
+ RebelPtr->loc.y = 0;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hRoyalist);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hRebel);
+ StartSphereTracking (YEHAT_REBEL_SHIP);
+ break;
+ }
+ case SLYLANDRO_RAMP_UP:
+ if (!GET_GAME_STATE (DESTRUCT_CODE_ON_SHIP))
+ {
+ BYTE ramp_factor;
+
+ ramp_factor = GET_GAME_STATE (SLYLANDRO_MULTIPLIER);
+ if (++ramp_factor <= 4)
+ {
+ SET_GAME_STATE (SLYLANDRO_MULTIPLIER, ramp_factor);
+ AddEvent (RELATIVE_EVENT, 0, 182, 0, SLYLANDRO_RAMP_UP);
+ }
+ }
+ break;
+ case SLYLANDRO_RAMP_DOWN:
+ {
+ BYTE ramp_factor;
+
+ ramp_factor = GET_GAME_STATE (SLYLANDRO_MULTIPLIER);
+ if (--ramp_factor)
+ AddEvent (RELATIVE_EVENT, 0, 23, 0, SLYLANDRO_RAMP_DOWN);
+ SET_GAME_STATE (SLYLANDRO_MULTIPLIER, ramp_factor);
+ break;
+ }
+ }
+}
+
+void
+SetRaceDest (BYTE which_race, COORD x, COORD y, BYTE days_left, BYTE
+ func_index)
+{
+ HFLEETINFO hFleet;
+ FLEET_INFO *FleetPtr;
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), which_race);
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+
+ FleetPtr->dest_loc.x = x;
+ FleetPtr->dest_loc.y = y;
+ FleetPtr->days_left = days_left;
+ FleetPtr->func_index = func_index;
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+}
+
+
+
+static void
+arilou_entrance_event (void)
+{
+ SET_GAME_STATE (ARILOU_SPACE, OPENING);
+ AddEvent (RELATIVE_EVENT, 0, 3, 0, ARILOU_EXIT_EVENT);
+}
+
+static void
+arilou_exit_event (void)
+{
+ COUNT month_index, year_index;
+
+ year_index = GLOBAL (GameClock.year_index);
+ if ((month_index = GLOBAL (GameClock.month_index) % 12) == 0)
+ ++year_index;
+ ++month_index;
+
+ SET_GAME_STATE (ARILOU_SPACE, CLOSING);
+ AddEvent (ABSOLUTE_EVENT,
+ month_index, 17, year_index, ARILOU_ENTRANCE_EVENT);
+}
+
+static void
+check_race_growth (void)
+{
+ HFLEETINFO hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if (FleetPtr->actual_strength
+ && FleetPtr->actual_strength != INFINITE_RADIUS)
+ {
+ SIZE delta_strength;
+
+ delta_strength = (SBYTE)FleetPtr->growth;
+ if (FleetPtr->growth_err_term <= FleetPtr->growth_fract)
+ {
+ if (delta_strength <= 0)
+ --delta_strength;
+ else
+ ++delta_strength;
+ }
+ FleetPtr->growth_err_term -= FleetPtr->growth_fract;
+
+ delta_strength += FleetPtr->actual_strength;
+ if (delta_strength <= 0)
+ {
+ delta_strength = 0;
+ FleetPtr->allied_state = DEAD_GUY;
+ }
+ else if (delta_strength > MAX_FLEET_STRENGTH)
+ delta_strength = MAX_FLEET_STRENGTH;
+
+ FleetPtr->actual_strength = (COUNT)delta_strength;
+ if (FleetPtr->actual_strength && FleetPtr->days_left)
+ {
+ FleetPtr->loc.x += (FleetPtr->dest_loc.x - FleetPtr->loc.x)
+ / FleetPtr->days_left;
+ FleetPtr->loc.y += (FleetPtr->dest_loc.y - FleetPtr->loc.y)
+ / FleetPtr->days_left;
+
+ if (--FleetPtr->days_left == 0
+ && FleetPtr->func_index != (BYTE) ~0)
+ EventHandler (FleetPtr->func_index);
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+}
+
+static void
+black_urquan_genocide (void)
+{
+ BYTE Index;
+ long best_dist;
+ SIZE best_dx, best_dy;
+ HFLEETINFO hStarShip, hNextShip;
+ HFLEETINFO hBlackUrquan;
+ FLEET_INFO *BlackUrquanPtr;
+
+ hBlackUrquan = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ BLACK_URQUAN_SHIP);
+ BlackUrquanPtr = LockFleetInfo (&GLOBAL (avail_race_q), hBlackUrquan);
+
+ best_dist = -1;
+ best_dx = SOL_X - BlackUrquanPtr->loc.x;
+ best_dy = SOL_Y - BlackUrquanPtr->loc.y;
+ for (Index = 0, hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; ++Index, hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if (Index != BLACK_URQUAN_SHIP
+ && Index != URQUAN_SHIP
+ && FleetPtr->actual_strength != INFINITE_RADIUS)
+ {
+ SIZE dx, dy;
+
+ dx = FleetPtr->loc.x - BlackUrquanPtr->loc.x;
+ dy = FleetPtr->loc.y - BlackUrquanPtr->loc.y;
+ if (dx == 0 && dy == 0)
+ {
+ // Arrived at the victim's home world. Cleanse it.
+ FleetPtr->allied_state = DEAD_GUY;
+ FleetPtr->actual_strength = 0;
+ }
+ else if (FleetPtr->actual_strength)
+ {
+ long dist;
+
+ dist = (long)dx * dx + (long)dy * dy;
+ if (best_dist < 0 || dist < best_dist || Index == DRUUGE_SHIP)
+ {
+ best_dist = dist;
+ best_dx = dx;
+ best_dy = dy;
+
+ if (Index == DRUUGE_SHIP)
+ hNextShip = 0;
+ }
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ if (best_dist < 0 && best_dx == 0 && best_dy == 0)
+ {
+ // All spheres of influence are gone - game over.
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
+
+ SET_GAME_STATE (KOHR_AH_KILLED_ALL, 1);
+ }
+ else
+ {
+ // Moving towards new race to cleanse.
+ COUNT speed;
+
+ if (best_dist < 0)
+ best_dist = (long)best_dx * best_dx + (long)best_dy * best_dy;
+
+ speed = square_root (best_dist) / 158;
+ if (speed == 0)
+ speed = 1;
+ else if (speed > 255)
+ speed = 255;
+
+ SET_GAME_STATE (KOHR_AH_FRENZY, 1);
+ SET_GAME_STATE (KOHR_AH_VISITS, 0);
+ SET_GAME_STATE (KOHR_AH_REASONS, 0);
+ SET_GAME_STATE (KOHR_AH_PLEAD, 0);
+ SET_GAME_STATE (KOHR_AH_INFO, 0);
+ SET_GAME_STATE (URQUAN_VISITS, 0);
+ SetRaceDest (BLACK_URQUAN_SHIP,
+ BlackUrquanPtr->loc.x + best_dx,
+ BlackUrquanPtr->loc.y + best_dy,
+ (BYTE)speed, KOHR_AH_GENOCIDE_EVENT);
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hBlackUrquan);
+}
+
+static void
+pkunk_mission (void)
+{
+ HFLEETINFO hPkunk;
+ FLEET_INFO *PkunkPtr;
+
+ hPkunk = GetStarShipFromIndex (&GLOBAL (avail_race_q), PKUNK_SHIP);
+ PkunkPtr = LockFleetInfo (&GLOBAL (avail_race_q), hPkunk);
+
+ if (PkunkPtr->actual_strength)
+ {
+ BYTE MissionState;
+
+ MissionState = GET_GAME_STATE (PKUNK_MISSION);
+ if (PkunkPtr->days_left == 0 && MissionState)
+ {
+ if ((MissionState & 1)
+ /* made it to Yehat space */
+ || (PkunkPtr->loc.x == 4970
+ && PkunkPtr->loc.y == 400))
+ PkunkPtr->actual_strength = 0;
+ else if (PkunkPtr->loc.x == 502
+ && PkunkPtr->loc.y == 401
+ && GET_GAME_STATE (PKUNK_ON_THE_MOVE))
+ {
+ SET_GAME_STATE (PKUNK_ON_THE_MOVE, 0);
+ AddEvent (RELATIVE_EVENT, 3, 0, 0, ADVANCE_PKUNK_MISSION);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hPkunk);
+ return;
+ }
+ }
+
+ if (PkunkPtr->actual_strength == 0)
+ {
+ SET_GAME_STATE (YEHAT_ABSORBED_PKUNK, 1);
+ PkunkPtr->allied_state = DEAD_GUY;
+ StartSphereTracking (YEHAT_SHIP);
+ }
+ else
+ {
+ COORD x, y;
+
+ if (!(MissionState & 1))
+ {
+ x = 4970;
+ y = 400;
+ }
+ else
+ {
+ x = 502;
+ y = 401;
+ }
+ SET_GAME_STATE (PKUNK_ON_THE_MOVE, 1);
+ SET_GAME_STATE (PKUNK_SWITCH, 0);
+ SetRaceDest (PKUNK_SHIP, x, y,
+ (BYTE)((365 >> 1) - PkunkPtr->days_left),
+ ADVANCE_PKUNK_MISSION);
+ }
+ SET_GAME_STATE (PKUNK_MISSION, MissionState + 1);
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hPkunk);
+}
+
+static void
+thradd_mission (void)
+{
+ BYTE MissionState;
+ HFLEETINFO hThradd;
+ FLEET_INFO *ThraddPtr;
+
+ hThradd = GetStarShipFromIndex (&GLOBAL (avail_race_q), THRADDASH_SHIP);
+ ThraddPtr = LockFleetInfo (&GLOBAL (avail_race_q), hThradd);
+
+ MissionState = GET_GAME_STATE (THRADD_MISSION);
+ if (ThraddPtr->actual_strength && MissionState < 3)
+ {
+ COORD x, y;
+
+ if (MissionState < 2)
+ { /* attacking */
+ x = 4879;
+ y = 7201;
+ }
+ else
+ { /* returning */
+ x = 2535;
+ y = 8358;
+ }
+
+ if (MissionState == 1)
+ { /* arrived at Kohr-Ah, engaging */
+ SIZE strength_loss;
+
+ strength_loss = (SIZE)(ThraddPtr->actual_strength >> 1);
+ ThraddPtr->growth = (BYTE)(-strength_loss / 14);
+ ThraddPtr->growth_fract = (BYTE)(((strength_loss % 14) << 8) / 14);
+ ThraddPtr->growth_err_term = 255 >> 1;
+ }
+ else
+ {
+ if (MissionState != 0)
+ { /* stop losses */
+ ThraddPtr->growth = 0;
+ ThraddPtr->growth_fract = 0;
+ }
+ }
+ SetRaceDest (THRADDASH_SHIP, x, y, 14, ADVANCE_THRADD_MISSION);
+ }
+ ++MissionState;
+ SET_GAME_STATE (THRADD_MISSION, MissionState);
+
+ if (MissionState == 4 && GET_GAME_STATE (ILWRATH_FIGHT_THRADDASH))
+ { /* returned home - notify the Ilwrath */
+ AddEvent (RELATIVE_EVENT, 0, 0, 0, ADVANCE_ILWRATH_MISSION);
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hThradd);
+}
+
+static void
+ilwrath_mission (void)
+{
+ BYTE ThraddState;
+ HFLEETINFO hIlwrath, hThradd;
+ FLEET_INFO *IlwrathPtr;
+ FLEET_INFO *ThraddPtr;
+
+ hIlwrath = GetStarShipFromIndex (&GLOBAL (avail_race_q), ILWRATH_SHIP);
+ IlwrathPtr = LockFleetInfo (&GLOBAL (avail_race_q), hIlwrath);
+ hThradd = GetStarShipFromIndex (&GLOBAL (avail_race_q), THRADDASH_SHIP);
+ ThraddPtr = LockFleetInfo (&GLOBAL (avail_race_q), hThradd);
+
+ if (IlwrathPtr->loc.x == ((2500 + 2535) >> 1)
+ && IlwrathPtr->loc.y == ((8070 + 8358) >> 1))
+ {
+ IlwrathPtr->actual_strength = 0;
+ ThraddPtr->actual_strength = 0;
+ IlwrathPtr->allied_state = DEAD_GUY;
+ ThraddPtr->allied_state = DEAD_GUY;
+ }
+ else if (IlwrathPtr->actual_strength)
+ {
+ if (!GET_GAME_STATE (ILWRATH_FIGHT_THRADDASH)
+ && (IlwrathPtr->dest_loc.x != 2500
+ || IlwrathPtr->dest_loc.y != 8070))
+ {
+ SetRaceDest (ILWRATH_SHIP, 2500, 8070, 90,
+ ADVANCE_ILWRATH_MISSION);
+ }
+ else
+ {
+#define MADD_LENGTH 128
+ SIZE strength_loss;
+
+ if (IlwrathPtr->days_left == 0)
+ { /* arrived for battle */
+ SET_GAME_STATE (ILWRATH_FIGHT_THRADDASH, 1);
+ SET_GAME_STATE (HELIX_UNPROTECTED, 1);
+ strength_loss = (SIZE)IlwrathPtr->actual_strength;
+ IlwrathPtr->growth = (BYTE)(-strength_loss / MADD_LENGTH);
+ IlwrathPtr->growth_fract =
+ (BYTE)(((strength_loss % MADD_LENGTH) << 8) / MADD_LENGTH);
+ SetRaceDest (ILWRATH_SHIP,
+ (2500 + 2535) >> 1, (8070 + 8358) >> 1,
+ MADD_LENGTH - 1, ADVANCE_ILWRATH_MISSION);
+
+ strength_loss = (SIZE)ThraddPtr->actual_strength;
+ ThraddPtr->growth = (BYTE)(-strength_loss / MADD_LENGTH);
+ ThraddPtr->growth_fract =
+ (BYTE)(((strength_loss % MADD_LENGTH) << 8) / MADD_LENGTH);
+
+ SET_GAME_STATE (THRADD_VISITS, 0);
+ if (ThraddPtr->allied_state == GOOD_GUY)
+ SetRaceAllied (THRADDASH_SHIP, FALSE);
+ }
+
+ ThraddState = GET_GAME_STATE (THRADD_MISSION);
+ if (ThraddState == 0 || ThraddState > 3)
+ { /* never went to Kohr-Ah or returned */
+ SetRaceDest (THRADDASH_SHIP,
+ (2500 + 2535) >> 1, (8070 + 8358) >> 1,
+ IlwrathPtr->days_left + 1, (BYTE)~0);
+ }
+ else if (ThraddState < 3)
+ { /* recall on the double */
+ SetRaceDest (THRADDASH_SHIP, 2535, 8358, 10,
+ ADVANCE_THRADD_MISSION);
+ SET_GAME_STATE (THRADD_MISSION, 3);
+ }
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hThradd);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hIlwrath);
+}
+
+static void
+utwig_supox_mission (void)
+{
+ BYTE MissionState;
+ HFLEETINFO hUtwig, hSupox;
+ FLEET_INFO *UtwigPtr;
+ FLEET_INFO *SupoxPtr;
+
+ hUtwig = GetStarShipFromIndex (&GLOBAL (avail_race_q), UTWIG_SHIP);
+ UtwigPtr = LockFleetInfo (&GLOBAL (avail_race_q), hUtwig);
+ hSupox = GetStarShipFromIndex (&GLOBAL (avail_race_q), SUPOX_SHIP);
+ SupoxPtr = LockFleetInfo (&GLOBAL (avail_race_q), hSupox);
+
+ MissionState = GET_GAME_STATE (UTWIG_SUPOX_MISSION);
+ if (UtwigPtr->actual_strength && MissionState < 5)
+ {
+ if (MissionState == 1)
+ {
+ SIZE strength_loss;
+
+ AddEvent (RELATIVE_EVENT, 0, (160 >> 1), 0,
+ ADVANCE_UTWIG_SUPOX_MISSION);
+
+ strength_loss = (SIZE)(UtwigPtr->actual_strength >> 1);
+ UtwigPtr->growth = (BYTE)(-strength_loss / 160);
+ UtwigPtr->growth_fract =
+ (BYTE)(((strength_loss % 160) << 8) / 160);
+ UtwigPtr->growth_err_term = 255 >> 1;
+
+ strength_loss = (SIZE)(SupoxPtr->actual_strength >> 1);
+ if (strength_loss)
+ {
+ SupoxPtr->growth = (BYTE)(-strength_loss / 160);
+ SupoxPtr->growth_fract =
+ (BYTE)(((strength_loss % 160) << 8) / 160);
+ SupoxPtr->growth_err_term = 255 >> 1;
+ }
+
+ SET_GAME_STATE (UTWIG_WAR_NEWS, 0);
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 0);
+ }
+ else if (MissionState == 2)
+ {
+ AddEvent (RELATIVE_EVENT, 0, (160 >> 1), 0,
+ ADVANCE_UTWIG_SUPOX_MISSION);
+ ++MissionState;
+ }
+ else
+ {
+ COORD ux, uy, sx, sy;
+
+ if (MissionState == 0)
+ {
+ ux = 7208;
+ uy = 7000;
+
+ sx = 6479;
+ sy = 7541;
+ }
+ else
+ {
+ ux = 8534;
+ uy = 8797;
+
+ sx = 7468;
+ sy = 9246;
+
+ UtwigPtr->growth = 0;
+ UtwigPtr->growth_fract = 0;
+ SupoxPtr->growth = 0;
+ SupoxPtr->growth_fract = 0;
+
+ SET_GAME_STATE (UTWIG_WAR_NEWS, 0);
+ SET_GAME_STATE (SUPOX_WAR_NEWS, 0);
+ }
+ SET_GAME_STATE (UTWIG_VISITS, 0);
+ SET_GAME_STATE (UTWIG_INFO, 0);
+ SET_GAME_STATE (SUPOX_VISITS, 0);
+ SET_GAME_STATE (SUPOX_INFO, 0);
+ SetRaceDest (UTWIG_SHIP, ux, uy, 21, ADVANCE_UTWIG_SUPOX_MISSION);
+ SetRaceDest (SUPOX_SHIP, sx, sy, 21, (BYTE)~0);
+ }
+ }
+ SET_GAME_STATE (UTWIG_SUPOX_MISSION, MissionState + 1);
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hSupox);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hUtwig);
+}
+
+static void
+mycon_mission (void)
+{
+ HFLEETINFO hMycon;
+ FLEET_INFO *MyconPtr;
+
+ hMycon = GetStarShipFromIndex (&GLOBAL (avail_race_q), MYCON_SHIP);
+ MyconPtr = LockFleetInfo (&GLOBAL (avail_race_q), hMycon);
+
+ if (MyconPtr->actual_strength)
+ {
+ if (MyconPtr->growth)
+ {
+ // Head back.
+ SET_GAME_STATE (MYCON_KNOW_AMBUSH, 1);
+ SetRaceDest (MYCON_SHIP, 6392, 2200, 30, (BYTE)~0);
+
+ MyconPtr->growth = 0;
+ MyconPtr->growth_fract = 0;
+ }
+ else if (MyconPtr->loc.x != 6858 || MyconPtr->loc.y != 577)
+ SetRaceDest (MYCON_SHIP, 6858, 577, 30, ADVANCE_MYCON_MISSION);
+ // To Organon.
+ else
+ {
+ // Endure losses at Organon.
+ SIZE strength_loss;
+
+ AddEvent (RELATIVE_EVENT, 0, 14, 0, ADVANCE_MYCON_MISSION);
+ strength_loss = (SIZE)(MyconPtr->actual_strength >> 1);
+ MyconPtr->growth = (BYTE)(-strength_loss / 14);
+ MyconPtr->growth_fract = (BYTE)(((strength_loss % 14) << 8) / 14);
+ MyconPtr->growth_err_term = 255 >> 1;
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hMycon);
+}
+
diff --git a/src/uqm/gameev.h b/src/uqm/gameev.h
new file mode 100644
index 0000000..8c2e137
--- /dev/null
+++ b/src/uqm/gameev.h
@@ -0,0 +1,68 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_GAMEEV_H_
+#define UQM_GAMEEV_H_
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+enum
+{
+ ARILOU_ENTRANCE_EVENT = 0,
+ ARILOU_EXIT_EVENT,
+ HYPERSPACE_ENCOUNTER_EVENT,
+ KOHR_AH_VICTORIOUS_EVENT,
+ ADVANCE_PKUNK_MISSION,
+ ADVANCE_THRADD_MISSION,
+ ZOQFOT_DISTRESS_EVENT,
+ ZOQFOT_DEATH_EVENT,
+ SHOFIXTI_RETURN_EVENT,
+ ADVANCE_UTWIG_SUPOX_MISSION,
+ KOHR_AH_GENOCIDE_EVENT,
+ SPATHI_SHIELD_EVENT,
+ ADVANCE_ILWRATH_MISSION,
+ ADVANCE_MYCON_MISSION,
+ ARILOU_UMGAH_CHECK,
+ YEHAT_REBEL_EVENT,
+ SLYLANDRO_RAMP_UP,
+ SLYLANDRO_RAMP_DOWN,
+
+ NUM_EVENTS
+};
+
+typedef enum
+{
+ CLOSING = 0,
+ OPENING
+} ARILOU_GATE_STATE;
+
+extern void AddInitialGameEvents (void);
+extern void EventHandler (BYTE selector);
+extern void SetRaceDest (BYTE which_race, COORD x, COORD y, BYTE days_left,
+ BYTE func_index);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_GAMEEV_H_ */
diff --git a/src/uqm/gameinp.c b/src/uqm/gameinp.c
new file mode 100644
index 0000000..66e667f
--- /dev/null
+++ b/src/uqm/gameinp.c
@@ -0,0 +1,496 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "controls.h"
+#include "battlecontrols.h"
+#include "init.h"
+#include "intel.h"
+ // For computer_intelligence
+#ifdef NETPLAY
+# include "supermelee/netplay/netmelee.h"
+#endif
+#include "settings.h"
+#include "sounds.h"
+#include "tactrans.h"
+#include "uqmdebug.h"
+#include "libs/async.h"
+#include "libs/inplib.h"
+#include "libs/timelib.h"
+#include "libs/threadlib.h"
+
+
+#define ACCELERATION_INCREMENT (ONE_SECOND / 12)
+#define MENU_REPEAT_DELAY (ONE_SECOND / 2)
+
+
+typedef struct
+{
+ BOOLEAN (*InputFunc) (void *pInputState);
+} INPUT_STATE_DESC;
+
+/* These static variables are the values that are set by the controllers. */
+
+typedef struct
+{
+ DWORD key [NUM_TEMPLATES][NUM_KEYS];
+ DWORD menu [NUM_MENU_KEYS];
+} MENU_ANNOTATIONS;
+
+
+CONTROL_TEMPLATE PlayerControls[NUM_PLAYERS];
+CONTROLLER_INPUT_STATE CurrentInputState, PulsedInputState;
+static CONTROLLER_INPUT_STATE CachedInputState, OldInputState;
+static MENU_ANNOTATIONS RepeatDelays, Times;
+static DWORD GestaltRepeatDelay, GestaltTime;
+static BOOLEAN OldGestalt, CachedGestalt;
+static DWORD _max_accel, _min_accel, _step_accel;
+static BOOLEAN _gestalt_keys;
+
+static MENU_SOUND_FLAGS sound_0, sound_1;
+
+volatile CONTROLLER_INPUT_STATE ImmediateInputState;
+
+volatile BOOLEAN ExitRequested;
+volatile BOOLEAN GamePaused;
+
+static InputFrameCallback *inputCallback;
+
+static void
+_clear_menu_state (void)
+{
+ int i, j;
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ PulsedInputState.key[i][j] = 0;
+ CachedInputState.key[i][j] = 0;
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ PulsedInputState.menu[i] = 0;
+ CachedInputState.menu[i] = 0;
+ }
+ CachedGestalt = FALSE;
+}
+
+void
+ResetKeyRepeat (void)
+{
+ DWORD initTime = GetTimeCounter ();
+ int i, j;
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ RepeatDelays.key[i][j] = _max_accel;
+ Times.key[i][j] = initTime;
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ RepeatDelays.menu[i] = _max_accel;
+ Times.menu[i] = initTime;
+ }
+ GestaltRepeatDelay = _max_accel;
+ GestaltTime = initTime;
+}
+
+static void
+_check_for_pulse (int *current, int *cached, int *old, DWORD *accel,
+ DWORD *newtime, DWORD *oldtime)
+{
+ if (*cached && *old)
+ {
+ if (*newtime - *oldtime < *accel)
+ {
+ *current = 0;
+ }
+ else
+ {
+ *current = *cached;
+ if (*accel > _min_accel)
+ *accel -= _step_accel;
+ if (*accel < _min_accel)
+ *accel = _min_accel;
+ *oldtime = *newtime;
+ }
+ }
+ else
+ {
+ *current = *cached;
+ *oldtime = *newtime;
+ *accel = _max_accel;
+ }
+}
+
+/* BUG: If a key from a currently unused control template is held,
+ * this will affect the gestalt repeat rate. This isn't a problem
+ * *yet*, but it will be once the user gets to define control
+ * templates on his own --McM */
+static void
+_check_gestalt (DWORD NewTime)
+{
+ BOOLEAN CurrentGestalt;
+ int i, j;
+ OldGestalt = CachedGestalt;
+
+ CachedGestalt = 0;
+ CurrentGestalt = 0;
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ CachedGestalt |= ImmediateInputState.key[i][j];
+ CurrentGestalt |= PulsedInputState.key[i][j];
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ CachedGestalt |= ImmediateInputState.menu[i];
+ CurrentGestalt |= PulsedInputState.menu[i];
+ }
+
+ if (OldGestalt && CachedGestalt)
+ {
+ if (NewTime - GestaltTime < GestaltRepeatDelay)
+ {
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ PulsedInputState.key[i][j] = 0;
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ PulsedInputState.menu[i] = 0;
+ }
+ }
+ else
+ {
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ PulsedInputState.key[i][j] = CachedInputState.key[i][j];
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ PulsedInputState.menu[i] = CachedInputState.menu[i];
+ }
+ if (GestaltRepeatDelay > _min_accel)
+ GestaltRepeatDelay -= _step_accel;
+ if (GestaltRepeatDelay < _min_accel)
+ GestaltRepeatDelay = _min_accel;
+ GestaltTime = NewTime;
+ }
+ }
+ else
+ {
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ PulsedInputState.key[i][j] = CachedInputState.key[i][j];
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ PulsedInputState.menu[i] = CachedInputState.menu[i];
+ }
+ GestaltTime = NewTime;
+ GestaltRepeatDelay = _max_accel;
+ }
+}
+
+void
+UpdateInputState (void)
+{
+ DWORD NewTime;
+ /* First, if the game is, in fact, paused, we stall until
+ * unpaused. Every thread with control over game logic calls
+ * UpdateInputState routinely, so we handle pause and exit
+ * state updates here. */
+
+ // Automatically pause and enter low-activity state while inactive,
+ // for example, window minimized.
+ if (!GameActive)
+ SleepGame ();
+
+ if (GamePaused)
+ PauseGame ();
+
+ if (ExitRequested)
+ ConfirmExit ();
+
+ CurrentInputState = ImmediateInputState;
+ OldInputState = CachedInputState;
+ CachedInputState = ImmediateInputState;
+ BeginInputFrame ();
+ NewTime = GetTimeCounter ();
+ if (_gestalt_keys)
+ {
+ _check_gestalt (NewTime);
+ }
+ else
+ {
+ int i, j;
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ _check_for_pulse (&PulsedInputState.key[i][j],
+ &CachedInputState.key[i][j], &OldInputState.key[i][j],
+ &RepeatDelays.key[i][j], &NewTime, &Times.key[i][j]);
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ _check_for_pulse (&PulsedInputState.menu[i],
+ &CachedInputState.menu[i], &OldInputState.menu[i],
+ &RepeatDelays.menu[i], &NewTime, &Times.menu[i]);
+ }
+ }
+
+ if (CurrentInputState.menu[KEY_PAUSE])
+ GamePaused = TRUE;
+
+ if (CurrentInputState.menu[KEY_EXIT])
+ ExitRequested = TRUE;
+
+#if defined(DEBUG) || defined(USE_DEBUG_KEY)
+ if (PulsedInputState.menu[KEY_DEBUG])
+ debugKeyPressedSynchronous ();
+#endif
+}
+
+InputFrameCallback *
+SetInputCallback (InputFrameCallback *callback)
+{
+ InputFrameCallback *old = inputCallback;
+
+ // Replacing an existing callback with another is not a problem,
+ // but currently this should never happen, which is why the assert.
+ assert (!old || !callback);
+ inputCallback = callback;
+
+ return old;
+}
+
+void
+SetMenuRepeatDelay (DWORD min, DWORD max, DWORD step, BOOLEAN gestalt)
+{
+ _min_accel = min;
+ _max_accel = max;
+ _step_accel = step;
+ _gestalt_keys = gestalt;
+ //_clear_menu_state ();
+ ResetKeyRepeat ();
+}
+
+void
+SetDefaultMenuRepeatDelay (void)
+{
+ _min_accel = ACCELERATION_INCREMENT;
+ _max_accel = MENU_REPEAT_DELAY;
+ _step_accel = ACCELERATION_INCREMENT;
+ _gestalt_keys = FALSE;
+ //_clear_menu_state ();
+ ResetKeyRepeat ();
+}
+
+void
+FlushInput (void)
+{
+ TFB_ResetControls ();
+ _clear_menu_state ();
+}
+
+static MENU_SOUND_FLAGS
+MenuKeysToSoundFlags (const CONTROLLER_INPUT_STATE *state)
+{
+ MENU_SOUND_FLAGS soundFlags;
+
+ soundFlags = MENU_SOUND_NONE;
+ if (state->menu[KEY_MENU_UP])
+ soundFlags |= MENU_SOUND_UP;
+ if (state->menu[KEY_MENU_DOWN])
+ soundFlags |= MENU_SOUND_DOWN;
+ if (state->menu[KEY_MENU_LEFT])
+ soundFlags |= MENU_SOUND_LEFT;
+ if (state->menu[KEY_MENU_RIGHT])
+ soundFlags |= MENU_SOUND_RIGHT;
+ if (state->menu[KEY_MENU_SELECT])
+ soundFlags |= MENU_SOUND_SELECT;
+ if (state->menu[KEY_MENU_CANCEL])
+ soundFlags |= MENU_SOUND_CANCEL;
+ if (state->menu[KEY_MENU_SPECIAL])
+ soundFlags |= MENU_SOUND_SPECIAL;
+ if (state->menu[KEY_MENU_PAGE_UP])
+ soundFlags |= MENU_SOUND_PAGEUP;
+ if (state->menu[KEY_MENU_PAGE_DOWN])
+ soundFlags |= MENU_SOUND_PAGEDOWN;
+ if (state->menu[KEY_MENU_DELETE])
+ soundFlags |= MENU_SOUND_DELETE;
+ if (state->menu[KEY_MENU_BACKSPACE])
+ soundFlags |= MENU_SOUND_DELETE;
+
+ return soundFlags;
+}
+
+void
+DoInput (void *pInputState, BOOLEAN resetInput)
+{
+ if (resetInput)
+ FlushInput ();
+
+ do
+ {
+ MENU_SOUND_FLAGS soundFlags;
+ Async_process ();
+ TaskSwitch ();
+
+ UpdateInputState ();
+
+#if DEMO_MODE || CREATE_JOURNAL
+ if (ArrowInput != DemoInput)
+#endif
+ {
+#if CREATE_JOURNAL
+ JournalInput (InputState);
+#endif /* CREATE_JOURNAL */
+ }
+
+ soundFlags = MenuKeysToSoundFlags (&PulsedInputState);
+
+ if (MenuSounds && (soundFlags & (sound_0 | sound_1)))
+ {
+ SOUND S;
+
+ S = MenuSounds;
+ if (soundFlags & sound_1)
+ S = SetAbsSoundIndex (S, MENU_SOUND_SUCCESS);
+
+ PlaySoundEffect (S, 0, NotPositional (), NULL, 0);
+ }
+
+ if (inputCallback)
+ inputCallback ();
+
+ } while (((INPUT_STATE_DESC*)pInputState)->InputFunc (pInputState));
+
+ if (resetInput)
+ FlushInput ();
+}
+
+void
+SetMenuSounds (MENU_SOUND_FLAGS s0, MENU_SOUND_FLAGS s1)
+{
+ sound_0 = s0;
+ sound_1 = s1;
+}
+
+void
+GetMenuSounds (MENU_SOUND_FLAGS *s0, MENU_SOUND_FLAGS *s1)
+{
+ *s0 = sound_0;
+ *s1 = sound_1;
+}
+
+static BATTLE_INPUT_STATE
+ControlInputToBattleInput (const int *keyState)
+{
+ BATTLE_INPUT_STATE InputState = 0;
+
+ if (keyState[KEY_UP])
+ InputState |= BATTLE_THRUST;
+ if (keyState[KEY_LEFT])
+ InputState |= BATTLE_LEFT;
+ if (keyState[KEY_RIGHT])
+ InputState |= BATTLE_RIGHT;
+ if (keyState[KEY_WEAPON])
+ InputState |= BATTLE_WEAPON;
+ if (keyState[KEY_SPECIAL])
+ InputState |= BATTLE_SPECIAL;
+ if (keyState[KEY_ESCAPE])
+ InputState |= BATTLE_ESCAPE;
+ if (keyState[KEY_DOWN])
+ InputState |= BATTLE_DOWN;
+
+ return InputState;
+}
+
+BATTLE_INPUT_STATE
+CurrentInputToBattleInput (COUNT player)
+{
+ return ControlInputToBattleInput(
+ CurrentInputState.key[PlayerControls[player]]);
+}
+
+BATTLE_INPUT_STATE
+PulsedInputToBattleInput (COUNT player)
+{
+ return ControlInputToBattleInput(
+ PulsedInputState.key[PlayerControls[player]]);
+}
+
+BOOLEAN
+AnyButtonPress (BOOLEAN CheckSpecial)
+{
+ int i, j;
+ (void) CheckSpecial; // Ignored
+ UpdateInputState ();
+ for (i = 0; i < NUM_TEMPLATES; i++)
+ {
+ for (j = 0; j < NUM_KEYS; j++)
+ {
+ if (CurrentInputState.key[i][j])
+ return TRUE;
+ }
+ }
+ for (i = 0; i < NUM_MENU_KEYS; i++)
+ {
+ if (CurrentInputState.menu[i])
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOLEAN
+ConfirmExit (void)
+{
+ DWORD old_max_accel, old_min_accel, old_step_accel;
+ BOOLEAN old_gestalt_keys, result;
+
+ old_max_accel = _max_accel;
+ old_min_accel = _min_accel;
+ old_step_accel = _step_accel;
+ old_gestalt_keys = _gestalt_keys;
+
+ SetDefaultMenuRepeatDelay ();
+
+ result = DoConfirmExit ();
+
+ SetMenuRepeatDelay (old_min_accel, old_max_accel, old_step_accel,
+ old_gestalt_keys);
+ return result;
+}
+
diff --git a/src/uqm/gameopt.c b/src/uqm/gameopt.c
new file mode 100644
index 0000000..ba42a3b
--- /dev/null
+++ b/src/uqm/gameopt.c
@@ -0,0 +1,1347 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gameopt.h"
+
+#include "build.h"
+#include "colors.h"
+#include "controls.h"
+#include "starmap.h"
+#include "menustat.h"
+#include "sis.h"
+#include "units.h"
+#include "gamestr.h"
+#include "options.h"
+#include "save.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "util.h"
+#include "libs/graphics/gfx_common.h"
+
+#include <ctype.h>
+
+extern FRAME PlayFrame;
+
+#define MAX_SAVED_GAMES 50
+#define SUMMARY_X_OFFS 14
+#define SUMMARY_SIDE_OFFS 7
+#define SAVES_PER_PAGE 5
+
+#define MAX_NAME_SIZE SIS_NAME_SIZE
+
+static COUNT lastUsedSlot;
+
+static NamingCallback *namingCB;
+
+void
+ConfirmSaveLoad (STAMP *MsgStamp)
+{
+ RECT r, clip_r;
+ TEXT t;
+
+ SetContextFont (StarConFont);
+ GetContextClipRect (&clip_r);
+
+ t.baseline.x = clip_r.extent.width >> 1;
+ t.baseline.y = (clip_r.extent.height >> 1) + 3;
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+ if (MsgStamp)
+ t.pStr = GAME_STRING (SAVEGAME_STRING_BASE + 0);
+ // "Saving . . ."
+ else
+ t.pStr = GAME_STRING (SAVEGAME_STRING_BASE + 1);
+ // "Loading . . ."
+ TextRect (&t, &r, NULL);
+ r.corner.x -= 4;
+ r.corner.y -= 4;
+ r.extent.width += 8;
+ r.extent.height += 8;
+ if (MsgStamp)
+ {
+ *MsgStamp = SaveContextFrame (&r);
+ }
+ DrawStarConBox (&r, 2,
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19),
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F),
+ TRUE, BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x0F));
+ font_DrawText (&t);
+}
+
+enum
+{
+ SAVE_GAME = 0,
+ LOAD_GAME,
+ QUIT_GAME,
+ SETTINGS,
+ EXIT_GAME_MENU,
+};
+
+enum
+{
+ SOUND_ON_SETTING,
+ SOUND_OFF_SETTING,
+ MUSIC_ON_SETTING,
+ MUSIC_OFF_SETTING,
+ CYBORG_OFF_SETTING,
+ CYBORG_NORMAL_SETTING,
+ CYBORG_DOUBLE_SETTING,
+ CYBORG_SUPER_SETTING,
+ CHANGE_CAPTAIN_SETTING,
+ CHANGE_SHIP_SETTING,
+ EXIT_SETTINGS_MENU,
+};
+
+static void
+FeedbackSetting (BYTE which_setting)
+{
+ UNICODE buf[128];
+ const char *tmpstr;
+
+ buf[0] = '\0';
+ // pre-terminate buffer in case snprintf() overflows
+ buf[sizeof (buf) - 1] = '\0';
+
+ switch (which_setting)
+ {
+ case SOUND_ON_SETTING:
+ case SOUND_OFF_SETTING:
+ snprintf (buf, sizeof (buf) - 1, "%s %s",
+ GAME_STRING (OPTION_STRING_BASE + 0),
+ GLOBAL (glob_flags) & SOUND_DISABLED
+ ? GAME_STRING (OPTION_STRING_BASE + 3) :
+ GAME_STRING (OPTION_STRING_BASE + 4));
+ break;
+ case MUSIC_ON_SETTING:
+ case MUSIC_OFF_SETTING:
+ snprintf (buf, sizeof (buf) - 1, "%s %s",
+ GAME_STRING (OPTION_STRING_BASE + 1),
+ GLOBAL (glob_flags) & MUSIC_DISABLED
+ ? GAME_STRING (OPTION_STRING_BASE + 3) :
+ GAME_STRING (OPTION_STRING_BASE + 4));
+ break;
+ case CYBORG_OFF_SETTING:
+ case CYBORG_NORMAL_SETTING:
+ case CYBORG_DOUBLE_SETTING:
+ case CYBORG_SUPER_SETTING:
+ if (optWhichMenu == OPT_PC &&
+ which_setting > CYBORG_NORMAL_SETTING)
+ {
+ if (which_setting == CYBORG_DOUBLE_SETTING)
+ tmpstr = "+";
+ else
+ tmpstr = "++";
+ }
+ else
+ tmpstr = "";
+ snprintf (buf, sizeof (buf) - 1, "%s %s%s",
+ GAME_STRING (OPTION_STRING_BASE + 2),
+ !(GLOBAL (glob_flags) & CYBORG_ENABLED)
+ ? GAME_STRING (OPTION_STRING_BASE + 3) :
+ GAME_STRING (OPTION_STRING_BASE + 4),
+ tmpstr);
+ break;
+ case CHANGE_CAPTAIN_SETTING:
+ case CHANGE_SHIP_SETTING:
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (NAMING_STRING_BASE + 0));
+ break;
+ }
+
+ DrawStatusMessage (buf);
+}
+
+#define DDSHS_NORMAL 0
+#define DDSHS_EDIT 1
+#define DDSHS_BLOCKCUR 2
+
+static const RECT captainNameRect = {
+ /* .corner = */ {
+ /* .x = */ 3,
+ /* .y = */ 10
+ }, /* .extent = */ {
+ /* .width = */ SHIP_NAME_WIDTH - 2,
+ /* .height = */ SHIP_NAME_HEIGHT
+ }
+};
+static const RECT shipNameRect = {
+ /* .corner = */ {
+ /* .x = */ 2,
+ /* .y = */ 20
+ }, /* .extent = */ {
+ /* .width = */ SHIP_NAME_WIDTH,
+ /* .height = */ SHIP_NAME_HEIGHT
+ }
+};
+
+
+static BOOLEAN
+DrawNameString (bool nameCaptain, UNICODE *Str, COUNT CursorPos,
+ COUNT state)
+{
+ RECT r;
+ TEXT lf;
+ Color BackGround, ForeGround;
+ FONT Font;
+
+ {
+ if (nameCaptain)
+ { // Naming the captain
+ Font = TinyFont;
+ r = captainNameRect;
+ lf.baseline.x = r.corner.x + (r.extent.width >> 1) - 1;
+
+ BackGround = BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09);
+ ForeGround = BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B);
+ }
+ else
+ { // Naming the flagship
+ Font = StarConFont;
+ r = shipNameRect;
+ lf.baseline.x = r.corner.x + (r.extent.width >> 1);
+
+ BackGround = BUILD_COLOR (MAKE_RGB15 (0x0F, 0x00, 0x00), 0x2D);
+ ForeGround = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7D);
+ }
+
+ lf.baseline.y = r.corner.y + r.extent.height - 1;
+ lf.align = ALIGN_CENTER;
+ }
+
+ SetContext (StatusContext);
+ SetContextFont (Font);
+ lf.pStr = Str;
+ lf.CharCount = (COUNT)~0;
+
+ if (!(state & DDSHS_EDIT))
+ { // normal state
+ if (nameCaptain)
+ DrawCaptainsName ();
+ else
+ DrawFlagshipName (TRUE);
+ }
+ else
+ { // editing state
+ COUNT i;
+ RECT text_r;
+ BYTE char_deltas[MAX_NAME_SIZE];
+ BYTE *pchar_deltas;
+
+ TextRect (&lf, &text_r, char_deltas);
+ if ((text_r.extent.width + 2) >= r.extent.width)
+ { // the text does not fit the input box size and so
+ // will not fit when displayed later
+ // disallow the change
+ return (FALSE);
+ }
+
+ PreUpdateFlashRect ();
+
+ SetContextForeGroundColor (BackGround);
+ DrawFilledRectangle (&r);
+
+ pchar_deltas = char_deltas;
+ for (i = CursorPos; i > 0; --i)
+ text_r.corner.x += *pchar_deltas++;
+ if (CursorPos < lf.CharCount) /* end of line */
+ --text_r.corner.x;
+
+ if (state & DDSHS_BLOCKCUR)
+ { // Use block cursor for keyboardless systems
+ if (CursorPos == lf.CharCount)
+ { // cursor at end-line -- use insertion point
+ text_r.extent.width = 1;
+ }
+ else if (CursorPos + 1 == lf.CharCount)
+ { // extra pixel for last char margin
+ text_r.extent.width = (SIZE)*pchar_deltas + 2;
+ }
+ else
+ { // normal mid-line char
+ text_r.extent.width = (SIZE)*pchar_deltas + 1;
+ }
+ }
+ else
+ { // Insertion point cursor
+ text_r.extent.width = 1;
+ }
+
+ text_r.corner.y = r.corner.y;
+ text_r.extent.height = r.extent.height;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&text_r);
+
+ SetContextForeGroundColor (ForeGround);
+ font_DrawText (&lf);
+
+ PostUpdateFlashRect ();
+ }
+
+ return (TRUE);
+}
+
+static BOOLEAN
+OnNameChange (TEXTENTRY_STATE *pTES)
+{
+ bool nameCaptain = (bool) pTES->CbParam;
+ COUNT hl = DDSHS_EDIT;
+
+ if (pTES->JoystickMode)
+ hl |= DDSHS_BLOCKCUR;
+
+ return DrawNameString (nameCaptain, pTES->BaseStr, pTES->CursorPos, hl);
+}
+
+static void
+NameCaptainOrShip (bool nameCaptain)
+{
+ UNICODE buf[MAX_NAME_SIZE] = "";
+ TEXTENTRY_STATE tes;
+ UNICODE *Setting;
+
+ SetContext (StatusContext);
+ SetFlashRect (nameCaptain ? &captainNameRect : &shipNameRect);
+
+ DrawNameString (nameCaptain, buf, 0, DDSHS_EDIT);
+
+ DrawStatusMessage (GAME_STRING (NAMING_STRING_BASE + 0));
+
+ if (nameCaptain)
+ {
+ Setting = GLOBAL_SIS (CommanderName);
+ tes.MaxSize = sizeof (GLOBAL_SIS (CommanderName));
+ }
+ else
+ {
+ Setting = GLOBAL_SIS (ShipName);
+ tes.MaxSize = sizeof (GLOBAL_SIS (ShipName));
+ }
+
+ // text entry setup
+ tes.Initialized = FALSE;
+ tes.BaseStr = buf;
+ tes.CursorPos = 0;
+ tes.CbParam = (void*) nameCaptain;
+ tes.ChangeCallback = OnNameChange;
+ tes.FrameCallback = 0;
+
+ if (DoTextEntry (&tes))
+ utf8StringCopy (Setting, tes.MaxSize, buf);
+ else
+ utf8StringCopy (buf, sizeof (buf), Setting);
+
+ SetFlashRect (SFR_MENU_3DO);
+
+ DrawNameString (nameCaptain, buf, 0, DDSHS_NORMAL);
+
+ if (namingCB)
+ namingCB ();
+}
+
+static BOOLEAN
+DrawSaveNameString (UNICODE *Str, COUNT CursorPos, COUNT state, COUNT gameIndex)
+{
+ RECT r;
+ TEXT lf;
+ Color BackGround, ForeGround;
+ FONT Font;
+ UNICODE fullStr[256], dateStr[80];
+
+ DateToString (dateStr, sizeof dateStr, GLOBAL(GameClock.month_index),
+ GLOBAL(GameClock.day_index), GLOBAL(GameClock.year_index));
+ strncat (dateStr, ": ", sizeof(dateStr) - strlen(dateStr) -1);
+ snprintf (fullStr, sizeof fullStr, "%s%s", dateStr, Str);
+
+ SetContextForeGroundColor (BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33));
+ r.extent.width = 15;
+ if (MAX_SAVED_GAMES > 99)
+ r.extent.width += 5;
+ r.extent.height = 11;
+ r.corner.x = 8;
+ r.corner.y = (160 + ((gameIndex % SAVES_PER_PAGE) * 13));
+ DrawRectangle (&r);
+
+ r.extent.width = (204 - SAFE_X);
+ r.corner.x = (30 + SAFE_X);
+ DrawRectangle (&r);
+
+ Font = TinyFont;
+ lf.baseline.x = r.corner.x + 3;
+ lf.baseline.y = r.corner.y + 8;
+
+ BackGround = BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33);
+ ForeGround = BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01);
+
+ lf.align = ALIGN_LEFT;
+
+ SetContextFont (Font);
+ lf.pStr = fullStr;
+ lf.CharCount = (COUNT)~0;
+
+ if (!(state & DDSHS_EDIT))
+ {
+ TEXT t;
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ t.baseline.x = r.corner.x + 3;
+ t.baseline.y = r.corner.y + 8;
+ t.align = ALIGN_LEFT;
+ t.pStr = Str;
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (CAPTAIN_NAME_TEXT_COLOR);
+ font_DrawText (&lf);
+ }
+ else
+ { // editing state
+ COUNT i, FullCursorPos;
+ RECT text_r;
+ BYTE char_deltas[256];
+ BYTE *pchar_deltas;
+
+ TextRect (&lf, &text_r, char_deltas);
+ if ((text_r.extent.width + 2) >= r.extent.width)
+ { // the text does not fit the input box size and so
+ // will not fit when displayed later
+ // disallow the change
+ return (FALSE);
+ }
+
+ PreUpdateFlashRect ();
+
+ SetContextForeGroundColor (BackGround);
+ DrawFilledRectangle (&r);
+
+ pchar_deltas = char_deltas;
+
+ FullCursorPos = CursorPos + strlen(dateStr) - 1;
+ for (i = FullCursorPos; i > 0; --i)
+ text_r.corner.x += *pchar_deltas++;
+
+ if (FullCursorPos < lf.CharCount) /* end of line */
+ --text_r.corner.x;
+
+ if (state & DDSHS_BLOCKCUR)
+ { // Use block cursor for keyboardless systems
+ if (FullCursorPos == lf.CharCount)
+ { // cursor at end-line -- use insertion point
+ text_r.extent.width = 1;
+ }
+ else if (FullCursorPos + 1 == lf.CharCount)
+ { // extra pixel for last char margin
+ text_r.extent.width = (SIZE)*pchar_deltas + 2;
+ }
+ else
+ { // normal mid-line char
+ text_r.extent.width = (SIZE)*pchar_deltas + 1;
+ }
+ }
+ else
+ { // Insertion point cursor
+ text_r.extent.width = 1;
+ }
+
+ text_r.corner.y = r.corner.y;
+ text_r.extent.height = r.extent.height;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&text_r);
+
+ SetContextForeGroundColor (ForeGround);
+ font_DrawText (&lf);
+ PostUpdateFlashRect ();
+ }
+
+ return (TRUE);
+}
+
+static BOOLEAN
+OnSaveNameChange (TEXTENTRY_STATE *pTES)
+{
+ COUNT hl = DDSHS_EDIT;
+ COUNT *gameIndex = pTES->CbParam;
+
+ if (pTES->JoystickMode)
+ hl |= DDSHS_BLOCKCUR;
+
+ return DrawSaveNameString (pTES->BaseStr, pTES->CursorPos, hl, *gameIndex);
+}
+
+static BOOLEAN
+NameSaveGame (COUNT gameIndex, UNICODE *buf)
+{
+ TEXTENTRY_STATE tes;
+ COUNT CursPos = strlen(buf);
+ COUNT *gIndex = HMalloc (sizeof (COUNT));
+ RECT r;
+ *gIndex = gameIndex;
+
+ DrawSaveNameString (buf, CursPos, DDSHS_EDIT, gameIndex);
+
+ tes.MaxSize = SAVE_NAME_SIZE;
+
+ // text entry setup
+ tes.Initialized = FALSE;
+ tes.BaseStr = buf;
+ tes.CursorPos = CursPos;
+ tes.CbParam = gIndex;
+ tes.ChangeCallback = OnSaveNameChange;
+ tes.FrameCallback = 0;
+ r.extent.width = (204 - SAFE_X);
+ r.extent.height = 11;
+ r.corner.x = (30 + SAFE_X);
+ r.corner.y = (160 + ((gameIndex % SAVES_PER_PAGE) * 13));
+ SetFlashRect (&r);
+
+ if (!DoTextEntry (&tes))
+ buf[0] = 0;
+
+ SetFlashRect(NULL);
+
+ DrawSaveNameString (buf, CursPos, DDSHS_NORMAL, gameIndex);
+
+ if (namingCB)
+ namingCB ();
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ HFree (gIndex);
+
+ SetFlashRect (NULL);
+
+ if (tes.Success)
+ return (TRUE);
+ else
+ return (FALSE);
+}
+
+void
+SetNamingCallback (NamingCallback *callback)
+{
+ namingCB = callback;
+}
+
+static BOOLEAN
+DoSettings (MENU_STATE *pMS)
+{
+ BYTE cur_speed;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ cur_speed = (GLOBAL (glob_flags) & COMBAT_SPEED_MASK) >> COMBAT_SPEED_SHIFT;
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL]
+ || (PulsedInputState.menu[KEY_MENU_SELECT]
+ && pMS->CurState == EXIT_SETTINGS_MENU))
+ {
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ switch (pMS->CurState)
+ {
+ case SOUND_ON_SETTING:
+ case SOUND_OFF_SETTING:
+ ToggleSoundEffect ();
+ pMS->CurState ^= 1;
+ DrawMenuStateStrings (PM_SOUND_ON, pMS->CurState);
+ break;
+ case MUSIC_ON_SETTING:
+ case MUSIC_OFF_SETTING:
+ ToggleMusic ();
+ pMS->CurState ^= 1;
+ DrawMenuStateStrings (PM_SOUND_ON, pMS->CurState);
+ break;
+ case CHANGE_CAPTAIN_SETTING:
+ case CHANGE_SHIP_SETTING:
+ NameCaptainOrShip (pMS->CurState == CHANGE_CAPTAIN_SETTING);
+ break;
+ default:
+ if (cur_speed++ < NUM_COMBAT_SPEEDS - 1)
+ GLOBAL (glob_flags) |= CYBORG_ENABLED;
+ else
+ {
+ cur_speed = 0;
+ GLOBAL (glob_flags) &= ~CYBORG_ENABLED;
+ }
+ GLOBAL (glob_flags) =
+ ((GLOBAL (glob_flags) & ~COMBAT_SPEED_MASK)
+ | (cur_speed << COMBAT_SPEED_SHIFT));
+ pMS->CurState = CYBORG_OFF_SETTING + cur_speed;
+ DrawMenuStateStrings (PM_SOUND_ON, pMS->CurState);
+ }
+
+ FeedbackSetting (pMS->CurState);
+ }
+ else if (DoMenuChooser (pMS, PM_SOUND_ON))
+ FeedbackSetting (pMS->CurState);
+
+ return TRUE;
+}
+
+static void
+SettingsMenu (void)
+{
+ MENU_STATE MenuState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.CurState = SOUND_ON_SETTING;
+
+ DrawMenuStateStrings (PM_SOUND_ON, MenuState.CurState);
+ FeedbackSetting (MenuState.CurState);
+
+ MenuState.InputFunc = DoSettings;
+ DoInput (&MenuState, FALSE);
+
+ DrawStatusMessage (NULL);
+}
+
+typedef struct
+{
+ SUMMARY_DESC summary[MAX_SAVED_GAMES];
+ BOOLEAN saving;
+ // TRUE when saving, FALSE when loading
+ BOOLEAN success;
+ // TRUE when load/save succeeded
+ FRAME SummaryFrame;
+
+} PICK_GAME_STATE;
+
+static void
+DrawBlankSavegameDisplay (PICK_GAME_STATE *pickState)
+{
+ STAMP s;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pickState->SummaryFrame,
+ GetFrameCount (pickState->SummaryFrame) - 1);
+ DrawStamp (&s);
+}
+
+static void
+DrawSaveLoad (PICK_GAME_STATE *pickState)
+{
+ STAMP s;
+
+ s.origin.x = SUMMARY_X_OFFS + 1;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pickState->SummaryFrame,
+ GetFrameCount (pickState->SummaryFrame) - 2);
+ if (pickState->saving)
+ s.frame = DecFrameIndex (s.frame);
+ DrawStamp (&s);
+}
+
+static void
+DrawSavegameCargo (SIS_STATE *sisState)
+{
+ COUNT i;
+ STAMP s;
+ TEXT t;
+ UNICODE buf[40];
+ static const Color cargo_color[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x02, 0x0E, 0x13), 0x00),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x19, 0x00, 0x00), 0x00),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x10, 0x10, 0x10), 0x00),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x03, 0x05, 0x1E), 0x00),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x18, 0x00), 0x00),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x1B, 0x00), 0x00),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1E, 0x0D, 0x00), 0x00),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x14, 0x00, 0x14), 0x05),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x19), 0x00),
+ };
+#define ELEMENT_ORG_Y 17
+#define ELEMENT_SPACING_Y 12
+#define ELEMENT_SPACING_X 36
+
+ SetContext (SpaceContext);
+ BatchGraphics ();
+ SetContextFont (StarConFont);
+
+ // setup element icons
+ s.frame = SetAbsFrameIndex (MiscDataFrame,
+ (NUM_SCANDOT_TRANSITIONS << 1) + 3);
+ s.origin.x = 7 + SUMMARY_X_OFFS - SUMMARY_SIDE_OFFS + 3;
+ s.origin.y = ELEMENT_ORG_Y;
+ // setup element amounts
+ t.baseline.x = 33 + SUMMARY_X_OFFS - SUMMARY_SIDE_OFFS + 3;
+ t.baseline.y = ELEMENT_ORG_Y + 3;
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+
+ // draw element icons and amounts
+ for (i = 0; i < NUM_ELEMENT_CATEGORIES; ++i)
+ {
+ if (i == NUM_ELEMENT_CATEGORIES / 2)
+ {
+ s.origin.x += ELEMENT_SPACING_X;
+ s.origin.y = ELEMENT_ORG_Y;
+ t.baseline.x += ELEMENT_SPACING_X;
+ t.baseline.y = ELEMENT_ORG_Y + 3;
+ }
+ // draw element icon
+ DrawStamp (&s);
+ s.frame = SetRelFrameIndex (s.frame, 5);
+ s.origin.y += ELEMENT_SPACING_Y;
+ // print element amount
+ SetContextForeGroundColor (cargo_color[i]);
+ snprintf (buf, sizeof buf, "%u", sisState->ElementAmounts[i]);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += ELEMENT_SPACING_Y;
+ }
+
+ // draw Bio icon
+ s.origin.x = 24 + SUMMARY_X_OFFS - SUMMARY_SIDE_OFFS;
+ s.origin.y = 68;
+ s.frame = SetAbsFrameIndex (s.frame, 68);
+ DrawStamp (&s);
+ // print Bio amount
+ t.baseline.x = 50 + SUMMARY_X_OFFS;
+ t.baseline.y = s.origin.y + 3;
+ SetContextForeGroundColor (cargo_color[i]);
+ snprintf (buf, sizeof buf, "%u", sisState->TotalBioMass);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ UnbatchGraphics ();
+}
+
+static void
+DrawSavegameSummary (PICK_GAME_STATE *pickState, COUNT gameIndex)
+{
+ SUMMARY_DESC *pSD = pickState->summary + gameIndex;
+ RECT r;
+ STAMP s;
+
+ BatchGraphics ();
+
+ if (pSD->year_index == 0)
+ {
+ // Unused save slot, draw 'Empty Game' message.
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pickState->SummaryFrame,
+ GetFrameCount (pickState->SummaryFrame) - 4);
+ DrawStamp (&s);
+ }
+ else
+ {
+ // Game slot used, draw information about save game.
+ COUNT i;
+ RECT OldRect;
+ TEXT t;
+ QUEUE player_q;
+ CONTEXT OldContext;
+ SIS_STATE SaveSS;
+ UNICODE buf[256];
+ POINT starPt;
+
+ // Save the states because we will hack them
+ SaveSS = GlobData.SIS_state;
+ player_q = GLOBAL (built_ship_q);
+
+ OldContext = SetContext (StatusContext);
+ // Hack StatusContext so we can use standard SIS display funcs
+ GetContextClipRect (&OldRect);
+ r.corner.x = SIS_ORG_X + ((SIS_SCREEN_WIDTH - STATUS_WIDTH) >> 1) +
+ SAFE_X - 16 + SUMMARY_X_OFFS;
+// r.corner.x = SIS_ORG_X + ((SIS_SCREEN_WIDTH - STATUS_WIDTH) >> 1);
+ r.corner.y = SIS_ORG_Y;
+ r.extent.width = STATUS_WIDTH;
+ r.extent.height = STATUS_HEIGHT;
+ SetContextClipRect (&r);
+
+ // Hack the states so that we can use standard SIS display funcs
+ GlobData.SIS_state = pSD->SS;
+ InitQueue (&GLOBAL (built_ship_q),
+ MAX_BUILT_SHIPS, sizeof (SHIP_FRAGMENT));
+ for (i = 0; i < pSD->NumShips; ++i)
+ CloneShipFragment (pSD->ShipList[i], &GLOBAL (built_ship_q), 0);
+ DateToString (buf, sizeof buf,
+ pSD->month_index, pSD->day_index, pSD->year_index),
+ ClearSISRect (DRAW_SIS_DISPLAY);
+ DrawStatusMessage (buf);
+ UninitQueue (&GLOBAL (built_ship_q));
+
+ SetContextClipRect (&OldRect);
+
+ SetContext (SpaceContext);
+ // draw devices
+ s.origin.y = 13;
+ for (i = 0; i < 4; ++i)
+ {
+ COUNT j;
+
+ s.origin.x = 140 + SUMMARY_X_OFFS + SUMMARY_SIDE_OFFS;
+ for (j = 0; j < 4; ++j)
+ {
+ COUNT devIndex = (i * 4) + j;
+ if (devIndex < pSD->NumDevices)
+ {
+ s.frame = SetAbsFrameIndex (MiscDataFrame, 77
+ + pSD->DeviceList[devIndex]);
+ DrawStamp (&s);
+ }
+ s.origin.x += 18;
+ }
+ s.origin.y += 18;
+ }
+
+ SetContextFont (StarConFont);
+ t.baseline.x = 173 + SUMMARY_X_OFFS + SUMMARY_SIDE_OFFS;
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ if (pSD->Flags & AFTER_BOMB_INSTALLED)
+ {
+ // draw the bomb and the escape pod
+ s.origin.x = SUMMARY_X_OFFS - SUMMARY_SIDE_OFFS + 6;
+ s.origin.y = 0;
+ s.frame = SetRelFrameIndex (pickState->SummaryFrame, 0);
+ DrawStamp (&s);
+ // draw RU "NO LIMIT"
+ s.origin.x = SUMMARY_X_OFFS + SUMMARY_SIDE_OFFS;
+ s.frame = IncFrameIndex (s.frame);
+ DrawStamp (&s);
+ }
+ else
+ {
+ DrawSavegameCargo (&pSD->SS);
+
+ SetContext (RadarContext);
+ // Hack RadarContext so we can use standard Lander display funcs
+ GetContextClipRect (&OldRect);
+ r.corner.x = SIS_ORG_X + 10 + SUMMARY_X_OFFS - SUMMARY_SIDE_OFFS;
+ r.corner.y = SIS_ORG_Y + 84;
+ r.extent = OldRect.extent;
+ SetContextClipRect (&r);
+ // draw the lander with upgrades
+ InitLander (pSD->Flags | OVERRIDE_LANDER_FLAGS);
+ SetContextClipRect (&OldRect);
+ SetContext (SpaceContext);
+
+ snprintf (buf, sizeof buf, "%u", pSD->SS.ResUnits);
+ t.baseline.y = 102;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x00, 0x10), 0x01));
+ font_DrawText (&t);
+ t.CharCount = (COUNT)~0;
+ }
+ t.baseline.y = 126;
+ snprintf (buf, sizeof buf, "%u",
+ MAKE_WORD (pSD->MCreditLo, pSD->MCreditHi));
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x00, 0x10), 0x01));
+ font_DrawText (&t);
+
+ // print the location
+ t.baseline.x = 6;
+ t.baseline.y = 139 + 6;
+ t.align = ALIGN_LEFT;
+ t.pStr = buf;
+ starPt.x = LOGX_TO_UNIVERSE (pSD->SS.log_x);
+ starPt.y = LOGY_TO_UNIVERSE (pSD->SS.log_y);
+ switch (pSD->Activity)
+ {
+ case IN_LAST_BATTLE:
+ case IN_INTERPLANETARY:
+ case IN_PLANET_ORBIT:
+ case IN_STARBASE:
+ {
+ BYTE QuasiState;
+ STAR_DESC *SDPtr;
+
+ QuasiState = GET_GAME_STATE (ARILOU_SPACE_SIDE);
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, 0);
+ SDPtr = FindStar (NULL, &starPt, 1, 1);
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, QuasiState);
+ if (SDPtr)
+ {
+ GetClusterName (SDPtr, buf);
+ starPt = SDPtr->star_pt;
+ break;
+ }
+ }
+ default:
+ buf[0] = '\0';
+ break;
+ case IN_HYPERSPACE:
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (NAVIGATION_STRING_BASE + 0));
+ break;
+ case IN_QUASISPACE:
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (NAVIGATION_STRING_BASE + 1));
+ break;
+ }
+
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33));
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.align = ALIGN_CENTER;
+ t.baseline.x = SIS_SCREEN_WIDTH - SIS_TITLE_BOX_WIDTH - 4
+ + (SIS_TITLE_WIDTH >> 1);
+ switch (pSD->Activity)
+ {
+ case IN_STARBASE:
+ utf8StringCopy (buf, sizeof (buf), // Starbase
+ GAME_STRING (STARBASE_STRING_BASE));
+ break;
+ case IN_LAST_BATTLE:
+ utf8StringCopy (buf, sizeof (buf), // Sa-Matra
+ GAME_STRING (PLANET_NUMBER_BASE + 32));
+ break;
+ case IN_PLANET_ORBIT:
+ utf8StringCopy (buf, sizeof (buf), pSD->SS.PlanetName);
+ break;
+ default:
+ snprintf (buf, sizeof buf, "%03u.%01u : %03u.%01u",
+ starPt.x / 10, starPt.x % 10,
+ starPt.y / 10, starPt.y % 10);
+ }
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ SetContext (OldContext);
+
+ // Restore the states because we hacked them
+ GLOBAL (built_ship_q) = player_q;
+ GlobData.SIS_state = SaveSS;
+ }
+
+ UnbatchGraphics ();
+}
+
+static void
+DrawGameSelection (PICK_GAME_STATE *pickState, COUNT selSlot)
+{
+ RECT r;
+ TEXT t;
+ COUNT i;
+ COUNT curSlot;
+ UNICODE buf[256];
+ UNICODE buf2[80];
+
+ BatchGraphics ();
+
+ SetContextFont (TinyFont);
+
+ // Erase the selection menu
+ r.extent.width = 240;
+ r.extent.height = 65;
+ r.corner.x = 1;
+ r.corner.y = 160;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ t.align = ALIGN_LEFT;
+
+ // Draw savegame slots info
+ curSlot = selSlot - (selSlot % SAVES_PER_PAGE);
+ for (i = 0; i < SAVES_PER_PAGE && curSlot < MAX_SAVED_GAMES;
+ ++i, ++curSlot)
+ {
+ SUMMARY_DESC *desc = &pickState->summary[curSlot];
+
+ SetContextForeGroundColor ((curSlot == selSlot) ?
+ (BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x1B), 0x33)):
+ (BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)));
+ r.extent.width = 15;
+ if (MAX_SAVED_GAMES > 99)
+ r.extent.width += 5;
+ r.extent.height = 11;
+ r.corner.x = 8;
+ r.corner.y = 160 + (i * 13);
+ DrawRectangle (&r);
+
+ t.baseline.x = r.corner.x + 3;
+ t.baseline.y = r.corner.y + 8;
+ snprintf (buf, sizeof buf, (MAX_SAVED_GAMES > 99) ? "%03u" : "%02u",
+ curSlot);
+ font_DrawText (&t);
+
+ r.extent.width = 204 - SAFE_X;
+ r.corner.x = 30 + SAFE_X;
+ DrawRectangle (&r);
+
+ t.baseline.x = r.corner.x + 3;
+ if (desc->year_index == 0)
+ {
+ utf8StringCopy (buf, sizeof buf,
+ GAME_STRING (SAVEGAME_STRING_BASE + 3)); // "Empty Slot"
+ }
+ else
+ {
+ DateToString (buf2, sizeof buf2, desc->month_index,
+ desc->day_index, desc->year_index);
+ snprintf (buf, sizeof buf, "%s: %s", buf2, desc->SaveName[0] ? desc->SaveName : GAME_STRING (SAVEGAME_STRING_BASE + 4));
+ }
+ font_DrawText (&t);
+ }
+
+ UnbatchGraphics ();
+}
+
+static void
+RedrawPickDisplay (PICK_GAME_STATE *pickState, COUNT selSlot)
+{
+ BatchGraphics ();
+ DrawBlankSavegameDisplay (pickState);
+ DrawSavegameSummary (pickState, selSlot);
+ DrawGameSelection (pickState, selSlot);
+ UnbatchGraphics ();
+}
+
+static void
+LoadGameDescriptions (SUMMARY_DESC *pSD)
+{
+ COUNT i;
+
+ for (i = 0; i < MAX_SAVED_GAMES; ++i, ++pSD)
+ {
+ if (!LoadGame (i, pSD))
+ pSD->year_index = 0;
+ }
+}
+
+static BOOLEAN
+DoPickGame (MENU_STATE *pMS)
+{
+ PICK_GAME_STATE *pickState = pMS->privData;
+ BYTE NewState;
+ SUMMARY_DESC *pSD;
+ DWORD TimeIn = GetTimeCounter ();
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ pickState->success = FALSE;
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ pSD = &pickState->summary[pMS->CurState];
+ if (pickState->saving || pSD->year_index)
+ { // valid slot
+ PlayMenuSound (MENU_SOUND_SUCCESS);
+ pickState->success = TRUE;
+ return FALSE;
+ }
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ else
+ {
+ NewState = pMS->CurState;
+ if (PulsedInputState.menu[KEY_MENU_LEFT]
+ || PulsedInputState.menu[KEY_MENU_PAGE_UP])
+ {
+ if (NewState == 0)
+ NewState = MAX_SAVED_GAMES - 1;
+ else if ((NewState - SAVES_PER_PAGE) > 0)
+ NewState -= SAVES_PER_PAGE;
+ else
+ NewState = 0;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT]
+ || PulsedInputState.menu[KEY_MENU_PAGE_DOWN])
+ {
+ if (NewState == MAX_SAVED_GAMES - 1)
+ NewState = 0;
+ else if ((NewState + SAVES_PER_PAGE) < MAX_SAVED_GAMES - 1)
+ NewState += SAVES_PER_PAGE;
+ else
+ NewState = MAX_SAVED_GAMES - 1;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (NewState == 0)
+ NewState = MAX_SAVED_GAMES - 1;
+ else
+ NewState--;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ if (NewState == MAX_SAVED_GAMES - 1)
+ NewState = 0;
+ else
+ NewState++;
+ }
+
+ if (NewState != pMS->CurState)
+ {
+ pMS->CurState = NewState;
+ SetContext (SpaceContext);
+ RedrawPickDisplay (pickState, pMS->CurState);
+ }
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 30);
+ }
+
+ return TRUE;
+}
+
+static BOOLEAN
+SaveLoadGame (PICK_GAME_STATE *pickState, COUNT gameIndex, BOOLEAN *canceled_by_user)
+{
+ SUMMARY_DESC *desc = pickState->summary + gameIndex;
+ UNICODE nameBuf[256];
+ STAMP saveStamp;
+ BOOLEAN success;
+
+ saveStamp.frame = NULL;
+
+ if (pickState->saving)
+ {
+ // Initialize the save name with whatever name is there already
+ // SAVE_NAME_SIZE is less than 256, so this is safe.
+ strncpy(nameBuf, desc->SaveName, SAVE_NAME_SIZE);
+ nameBuf[SAVE_NAME_SIZE] = 0;
+ if (NameSaveGame (gameIndex, nameBuf))
+ {
+ PlayMenuSound (MENU_SOUND_SUCCESS);
+ ConfirmSaveLoad (pickState->saving ? &saveStamp : NULL);
+ success = SaveGame (gameIndex, desc, nameBuf);
+ }
+ else
+ {
+ success = FALSE;
+ *canceled_by_user = TRUE;
+ }
+ }
+ else
+ {
+ ConfirmSaveLoad (pickState->saving ? &saveStamp : NULL);
+ success = LoadGame (gameIndex, NULL);
+ }
+
+ // TODO: the same should be done for both save and load if we also
+ // display a load problem message
+ if (pickState->saving)
+ { // restore the screen under "SAVING..." message
+ DrawStamp (&saveStamp);
+ }
+
+ DestroyDrawable (ReleaseDrawable (saveStamp.frame));
+
+ return success;
+}
+
+static BOOLEAN
+PickGame (BOOLEAN saving, BOOLEAN fromMainMenu)
+{
+ CONTEXT OldContext;
+ MENU_STATE MenuState;
+ PICK_GAME_STATE pickState;
+ RECT DlgRect;
+ STAMP DlgStamp;
+ TimeCount TimeOut;
+ InputFrameCallback *oldCallback;
+
+ memset (&pickState, 0, sizeof pickState);
+ pickState.saving = saving;
+ pickState.SummaryFrame = SetAbsFrameIndex (PlayFrame, 39);
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.privData = &pickState;
+ // select the last used slot
+ MenuState.CurState = lastUsedSlot;
+
+ TimeOut = FadeMusic (0, ONE_SECOND / 2);
+
+ // Deactivate any background drawing, like planet rotation
+ oldCallback = SetInputCallback (NULL);
+
+ LoadGameDescriptions (pickState.summary);
+
+ OldContext = SetContext (SpaceContext);
+ // Save the current state of the screen for later restoration
+ DlgStamp = SaveContextFrame (NULL);
+ GetContextClipRect (&DlgRect);
+
+ SleepThreadUntil (TimeOut);
+ PauseMusic ();
+ StopSound ();
+ FadeMusic (NORMAL_VOLUME, 0);
+
+ // draw the current savegame and fade in
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+ RedrawPickDisplay (&pickState, MenuState.CurState);
+ DrawSaveLoad (&pickState);
+
+ if (fromMainMenu)
+ {
+ UnbatchGraphics ();
+ FadeScreen (FadeAllToColor, ONE_SECOND / 2);
+ }
+ else
+ {
+ RECT ctxRect;
+
+ GetContextClipRect (&ctxRect);
+ ScreenTransition (3, &ctxRect);
+ UnbatchGraphics ();
+ }
+
+ SetMenuSounds (MENU_SOUND_ARROWS | MENU_SOUND_PAGEUP | MENU_SOUND_PAGEDOWN,
+ 0);
+ MenuState.InputFunc = DoPickGame;
+
+ // Save/load retry loop
+ while (1)
+ {
+ BOOLEAN canceled_by_user = FALSE;
+
+ pickState.success = FALSE;
+ DoInput (&MenuState, TRUE);
+ if (!pickState.success)
+ break; // canceled
+
+ lastUsedSlot = MenuState.CurState;
+
+ if (SaveLoadGame (&pickState, MenuState.CurState, &canceled_by_user))
+ break; // all good
+
+ // something broke
+ if (saving && !canceled_by_user)
+ SaveProblem ();
+ // TODO: Shouldn't we have a Problem() equivalent for Load too?
+
+ // reload and redraw everything
+ LoadGameDescriptions (pickState.summary);
+ RedrawPickDisplay (&pickState, MenuState.CurState);
+ }
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ if (pickState.success && !saving)
+ { // Load succeeded, signal up the chain
+ GLOBAL (CurrentActivity) |= CHECK_LOAD;
+ }
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT) &&
+ (saving || (!pickState.success && !fromMainMenu)))
+ { // Restore previous screen
+ SetTransitionSource (&DlgRect);
+ BatchGraphics ();
+ DrawStamp (&DlgStamp);
+ ScreenTransition (3, &DlgRect);
+ UnbatchGraphics ();
+ }
+
+ DestroyDrawable (ReleaseDrawable (DlgStamp.frame));
+
+ SetContext (OldContext);
+
+ ResumeMusic ();
+
+ // Reactivate any background drawing, like planet rotation
+ SetInputCallback (oldCallback);
+
+ return pickState.success;
+}
+
+static BOOLEAN
+DoGameOptions (MENU_STATE *pMS)
+{
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL]
+ || (PulsedInputState.menu[KEY_MENU_SELECT]
+ && pMS->CurState == EXIT_GAME_MENU))
+ {
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ switch (pMS->CurState)
+ {
+ case SAVE_GAME:
+ case LOAD_GAME:
+ SetFlashRect (NULL);
+ if (PickGame (pMS->CurState == SAVE_GAME, FALSE))
+ return FALSE;
+ SetFlashRect (SFR_MENU_3DO);
+ break;
+ case QUIT_GAME:
+ if (ConfirmExit ())
+ return FALSE;
+ break;
+ case SETTINGS:
+ SettingsMenu ();
+ DrawMenuStateStrings (PM_SAVE_GAME, pMS->CurState);
+ break;
+ }
+ }
+ else
+ DoMenuChooser (pMS, PM_SAVE_GAME);
+
+ return TRUE;
+}
+
+// Returns TRUE when the owner menu should continue
+BOOLEAN
+GameOptions (void)
+{
+ MENU_STATE MenuState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ if (LastActivity == CHECK_LOAD)
+ { // Selected LOAD from main menu
+ BOOLEAN success;
+
+ DrawMenuStateStrings (PM_SAVE_GAME, LOAD_GAME);
+ success = PickGame (FALSE, TRUE);
+ if (!success)
+ { // Selected LOAD from main menu, and canceled
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ }
+ return FALSE;
+ }
+
+ MenuState.CurState = SAVE_GAME;
+ DrawMenuStateStrings (PM_SAVE_GAME, MenuState.CurState);
+
+ SetFlashRect (SFR_MENU_3DO);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ MenuState.InputFunc = DoGameOptions;
+ DoInput (&MenuState, TRUE);
+
+ SetFlashRect (NULL);
+
+ return !(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD));
+}
diff --git a/src/uqm/gameopt.h b/src/uqm/gameopt.h
new file mode 100644
index 0000000..1e14906
--- /dev/null
+++ b/src/uqm/gameopt.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef UQM_GAMEOPT_H_
+#define UQM_GAMEOPT_H_
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void ConfirmSaveLoad (STAMP *MsgStamp);
+extern BOOLEAN GameOptions (void);
+
+typedef void (NamingCallback) (void);
+extern void SetNamingCallback (NamingCallback *);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_GAMEOPT_H_ */
diff --git a/src/uqm/gamestr.h b/src/uqm/gamestr.h
new file mode 100644
index 0000000..60f7843
--- /dev/null
+++ b/src/uqm/gamestr.h
@@ -0,0 +1,93 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file contains definitions relating to using strings specific to
+ * the game. libs/strlib.h is for the string library.
+ */
+
+#ifndef UQM_GAMESTR_H_
+#define UQM_GAMESTR_H_
+
+
+#include "libs/strlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define STAR_STRING_COUNT 133
+#define DEVICE_STRING_COUNT 29
+#define CARGO_STRING_COUNT 10
+#define ELEMENTS_STRING_COUNT 133
+#define SCAN_STRING_COUNT 56
+#define STAR_NUMBER_COUNT 14
+#define PLANET_NUMBER_COUNT 33
+#define MONTHS_STRING_COUNT 12
+#define FEEDBACK_STRING_COUNT 2
+#define STARBASE_STRING_COUNT 5
+#define ENCOUNTER_STRING_COUNT 8
+#define NAVIGATION_STRING_COUNT 6
+#define NAMING_STRING_COUNT 4
+#define MELEE_STRING_COUNT 9
+#define SAVEGAME_STRING_COUNT 5
+#define OPTION_STRING_COUNT 5
+#define QUITMENU_STRING_COUNT 3
+#define STATUS_STRING_COUNT 6
+#define FLAGSHIP_STRING_COUNT 13
+#define ORBITSCAN_STRING_COUNT 19
+#define MAINMENU_STRING_COUNT 55
+#define NETMELEE_STRING_COUNT 19
+
+enum {
+ STAR_STRING_BASE = 0,
+ DEVICE_STRING_BASE = STAR_STRING_BASE + STAR_STRING_COUNT,
+ CARGO_STRING_BASE = DEVICE_STRING_BASE + DEVICE_STRING_COUNT,
+ ELEMENTS_STRING_BASE = CARGO_STRING_BASE + CARGO_STRING_COUNT,
+ SCAN_STRING_BASE = ELEMENTS_STRING_BASE + ELEMENTS_STRING_COUNT,
+ STAR_NUMBER_BASE = SCAN_STRING_BASE + SCAN_STRING_COUNT,
+ PLANET_NUMBER_BASE = STAR_NUMBER_BASE + STAR_NUMBER_COUNT,
+ MONTHS_STRING_BASE = PLANET_NUMBER_BASE + PLANET_NUMBER_COUNT,
+ FEEDBACK_STRING_BASE = MONTHS_STRING_BASE + MONTHS_STRING_COUNT,
+ STARBASE_STRING_BASE = FEEDBACK_STRING_BASE + FEEDBACK_STRING_COUNT,
+ ENCOUNTER_STRING_BASE = STARBASE_STRING_BASE + STARBASE_STRING_COUNT,
+ NAVIGATION_STRING_BASE = ENCOUNTER_STRING_BASE + ENCOUNTER_STRING_COUNT,
+ NAMING_STRING_BASE = NAVIGATION_STRING_BASE + NAVIGATION_STRING_COUNT,
+ MELEE_STRING_BASE = NAMING_STRING_BASE + NAMING_STRING_COUNT,
+ SAVEGAME_STRING_BASE = MELEE_STRING_BASE + MELEE_STRING_COUNT,
+ OPTION_STRING_BASE = SAVEGAME_STRING_BASE + SAVEGAME_STRING_COUNT,
+ QUITMENU_STRING_BASE = OPTION_STRING_BASE + OPTION_STRING_COUNT,
+ STATUS_STRING_BASE = QUITMENU_STRING_BASE + QUITMENU_STRING_COUNT,
+ FLAGSHIP_STRING_BASE = STATUS_STRING_BASE + STATUS_STRING_COUNT,
+ ORBITSCAN_STRING_BASE = FLAGSHIP_STRING_BASE + FLAGSHIP_STRING_COUNT,
+ MAINMENU_STRING_BASE = ORBITSCAN_STRING_BASE + ORBITSCAN_STRING_COUNT,
+ NETMELEE_STRING_BASE = MAINMENU_STRING_BASE + MAINMENU_STRING_COUNT,
+
+ GAMESTR_COUNT = NETMELEE_STRING_BASE + NETMELEE_STRING_COUNT
+};
+
+
+#define GAME_STRING(i) ((UNICODE *)GetStringAddress (SetAbsStringTableIndex (GameStrings, (i))))
+
+extern STRING GameStrings;
+
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_GAMESTR_H_ */
diff --git a/src/uqm/gendef.c b/src/uqm/gendef.c
new file mode 100644
index 0000000..6a01377
--- /dev/null
+++ b/src/uqm/gendef.c
@@ -0,0 +1,137 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gendef.h"
+#include "planets/generate.h"
+
+
+extern GenerateFunctions generateDefaultFunctions;
+
+extern GenerateFunctions generateAndrosynthFunctions;
+extern GenerateFunctions generateBurvixeseFunctions;
+extern GenerateFunctions generateChmmrFunctions;
+extern GenerateFunctions generateColonyFunctions;
+extern GenerateFunctions generateDruugeFunctions;
+extern GenerateFunctions generateIlwrathFunctions;
+extern GenerateFunctions generateMelnormeFunctions;
+extern GenerateFunctions generateMyconFunctions;
+extern GenerateFunctions generateOrzFunctions;
+extern GenerateFunctions generatePkunkFunctions;
+extern GenerateFunctions generateRainbowWorldFunctions;
+extern GenerateFunctions generateSaMatraFunctions;
+extern GenerateFunctions generateShofixtiFunctions;
+extern GenerateFunctions generateSlylandroFunctions;
+extern GenerateFunctions generateSolFunctions;
+extern GenerateFunctions generateSpathiFunctions;
+extern GenerateFunctions generateSupoxFunctions;
+extern GenerateFunctions generateSyreenFunctions;
+extern GenerateFunctions generateTalkingPetFunctions;
+extern GenerateFunctions generateThraddashFunctions;
+extern GenerateFunctions generateTrapFunctions;
+extern GenerateFunctions generateUtwigFunctions;
+extern GenerateFunctions generateVaultFunctions;
+extern GenerateFunctions generateVuxFunctions;
+extern GenerateFunctions generateWreckFunctions;
+extern GenerateFunctions generateYehatFunctions;
+extern GenerateFunctions generateZoqFotPikFunctions;
+extern GenerateFunctions generateZoqFotPikScoutFunctions;
+
+
+const GenerateFunctions *
+getGenerateFunctions (BYTE Index)
+{
+ switch (Index)
+ {
+ case SOL_DEFINED:
+ return &generateSolFunctions;
+ case SHOFIXTI_DEFINED:
+ return &generateShofixtiFunctions;
+ case START_COLONY_DEFINED:
+ return &generateColonyFunctions;
+ case SPATHI_DEFINED:
+ return &generateSpathiFunctions;
+ case MELNORME0_DEFINED:
+ case MELNORME1_DEFINED:
+ case MELNORME2_DEFINED:
+ case MELNORME3_DEFINED:
+ case MELNORME4_DEFINED:
+ case MELNORME5_DEFINED:
+ case MELNORME6_DEFINED:
+ case MELNORME7_DEFINED:
+ case MELNORME8_DEFINED:
+ return &generateMelnormeFunctions;
+ case TALKING_PET_DEFINED:
+ return &generateTalkingPetFunctions;
+ case CHMMR_DEFINED:
+ return &generateChmmrFunctions;
+ case SYREEN_DEFINED:
+ return &generateSyreenFunctions;
+ case MYCON_TRAP_DEFINED:
+ return &generateTrapFunctions;
+ case BURVIXESE_DEFINED:
+ return &generateBurvixeseFunctions;
+ case SLYLANDRO_DEFINED:
+ return &generateSlylandroFunctions;
+ case DRUUGE_DEFINED:
+ return &generateDruugeFunctions;
+ case BOMB_DEFINED:
+ case UTWIG_DEFINED:
+ return &generateUtwigFunctions;
+ case AQUA_HELIX_DEFINED:
+ case THRADD_DEFINED:
+ return &generateThraddashFunctions;
+ case SUN_DEVICE_DEFINED:
+ case MYCON_DEFINED:
+ case EGG_CASE0_DEFINED:
+ case EGG_CASE1_DEFINED:
+ case EGG_CASE2_DEFINED:
+ return &generateMyconFunctions;
+ case ANDROSYNTH_DEFINED:
+ return &generateAndrosynthFunctions;
+ case TAALO_PROTECTOR_DEFINED:
+ case ORZ_DEFINED:
+ return &generateOrzFunctions;
+ case SHIP_VAULT_DEFINED:
+ return &generateVaultFunctions;
+ case URQUAN_WRECK_DEFINED:
+ return &generateWreckFunctions;
+ case MAIDENS_DEFINED:
+ case VUX_BEAST_DEFINED:
+ case VUX_DEFINED:
+ return &generateVuxFunctions;
+ case SAMATRA_DEFINED:
+ return &generateSaMatraFunctions;
+ case ZOQFOT_DEFINED:
+ return &generateZoqFotPikFunctions;
+ case ZOQ_SCOUT_DEFINED:
+ return &generateZoqFotPikScoutFunctions;
+ case YEHAT_DEFINED:
+ return &generateYehatFunctions;
+ case PKUNK_DEFINED:
+ return &generatePkunkFunctions;
+ case SUPOX_DEFINED:
+ return &generateSupoxFunctions;
+ case RAINBOW_DEFINED:
+ return &generateRainbowWorldFunctions;
+ case ILWRATH_DEFINED:
+ return &generateIlwrathFunctions;
+ default:
+ return &generateDefaultFunctions;
+ }
+}
+
diff --git a/src/uqm/gendef.h b/src/uqm/gendef.h
new file mode 100644
index 0000000..b5a292a
--- /dev/null
+++ b/src/uqm/gendef.h
@@ -0,0 +1,71 @@
+#ifndef GENDEF_H
+#define GENDEF_H
+
+#include "planets/generate.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+const GenerateFunctions *getGenerateFunctions (BYTE Index);
+
+enum
+{
+ SOL_DEFINED = 1,
+ SHOFIXTI_DEFINED,
+ MAIDENS_DEFINED,
+ START_COLONY_DEFINED,
+ SPATHI_DEFINED,
+ ZOQFOT_DEFINED,
+
+ MELNORME0_DEFINED,
+ MELNORME1_DEFINED,
+ MELNORME2_DEFINED,
+ MELNORME3_DEFINED,
+ MELNORME4_DEFINED,
+ MELNORME5_DEFINED,
+ MELNORME6_DEFINED,
+ MELNORME7_DEFINED,
+ MELNORME8_DEFINED,
+
+ TALKING_PET_DEFINED,
+ CHMMR_DEFINED,
+ SYREEN_DEFINED,
+ BURVIXESE_DEFINED,
+ SLYLANDRO_DEFINED,
+ DRUUGE_DEFINED,
+ BOMB_DEFINED,
+ AQUA_HELIX_DEFINED,
+ SUN_DEVICE_DEFINED,
+ TAALO_PROTECTOR_DEFINED,
+ SHIP_VAULT_DEFINED,
+ URQUAN_WRECK_DEFINED,
+ VUX_BEAST_DEFINED,
+ SAMATRA_DEFINED,
+ ZOQ_SCOUT_DEFINED,
+ MYCON_DEFINED,
+ EGG_CASE0_DEFINED,
+ EGG_CASE1_DEFINED,
+ EGG_CASE2_DEFINED,
+ PKUNK_DEFINED,
+ UTWIG_DEFINED,
+ SUPOX_DEFINED,
+ YEHAT_DEFINED,
+ VUX_DEFINED,
+ ORZ_DEFINED,
+ THRADD_DEFINED,
+ RAINBOW_DEFINED,
+ ILWRATH_DEFINED,
+ ANDROSYNTH_DEFINED,
+ MYCON_TRAP_DEFINED
+};
+
+#define UMGAH_DEFINED TALKING_PET_DEFINED
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* GENDEF_H */
+
diff --git a/src/uqm/getchar.c b/src/uqm/getchar.c
new file mode 100644
index 0000000..3f1f6ed
--- /dev/null
+++ b/src/uqm/getchar.c
@@ -0,0 +1,442 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "port.h"
+#include "controls.h"
+#include "libs/inplib.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+#include "globdata.h"
+#include "sounds.h"
+#include "settings.h"
+#include "resinst.h"
+#include "nameref.h"
+
+
+// TODO: This may be better done with UniChar at the cost of a tiny bit
+// of overhead to convert UniChar back to UTF8 string. This overhead
+// will probably be offset by removal of looped string-compare overhead ;)
+struct joy_char
+{
+ unsigned char len;
+ char enc[7];
+ // 1+7 is a nice round number
+};
+
+static int
+ReadOneChar (joy_char_t *ch, const UNICODE *str)
+{
+ UNICODE *next = skipUTF8Chars (str, 1);
+ int len = next - str;
+ ch->len = len;
+ memcpy (ch->enc, str, len);
+ ch->enc[len] = '\0'; // string term
+
+ return len;
+}
+
+static joy_char_t *
+LoadJoystickAlpha (STRING String, int *count)
+{
+ UNICODE *str;
+ int c;
+ int i;
+ joy_char_t *chars;
+ UNICODE *cur;
+
+ *count = 0;
+ str = GetStringAddress (String);
+ if (!str)
+ return 0;
+
+ c = utf8StringCount (str);
+ chars = HMalloc (c * sizeof (*chars));
+ if (!chars)
+ return 0;
+
+ for (i = 0, cur = str; i < c; ++i)
+ {
+ int len = ReadOneChar (chars + i, cur);
+ cur += len;
+ }
+
+ *count = c;
+ return chars;
+}
+
+static int
+JoyCharFindIn (const joy_char_t *ch, const joy_char_t *set,
+ int setsize)
+{
+ int i;
+
+ for (i = 0; i < setsize && strcmp (set[i].enc, ch->enc) != 0; ++i)
+ ;
+
+ return (i < setsize) ? i : -1;
+}
+
+static int
+JoyCharIsLower (const joy_char_t *ch, TEXTENTRY_STATE *pTES)
+{
+ return 0 <= JoyCharFindIn (ch, pTES->JoyLower, pTES->JoyRegLength);
+}
+
+static void
+JoyCharSwitchReg (joy_char_t *ch, const joy_char_t *from,
+ const joy_char_t *to, int regsize)
+{
+ int i = JoyCharFindIn (ch, from, regsize);
+ if (i >= 0)
+ *ch = to[i];
+}
+
+static void
+JoyCharToUpper (joy_char_t *outch, const joy_char_t *ch,
+ TEXTENTRY_STATE *pTES)
+{
+ *outch = *ch;
+ JoyCharSwitchReg (outch, pTES->JoyLower, pTES->JoyUpper,
+ pTES->JoyRegLength);
+}
+
+static void
+JoyCharToLower (joy_char_t *outch, const joy_char_t *ch,
+ TEXTENTRY_STATE *pTES)
+{
+ *outch = *ch;
+ JoyCharSwitchReg (outch, pTES->JoyUpper, pTES->JoyLower,
+ pTES->JoyRegLength);
+}
+
+BOOLEAN
+DoTextEntry (TEXTENTRY_STATE *pTES)
+{
+ UniChar ch;
+ UNICODE *pStr;
+ UNICODE *CacheInsPt;
+ int CacheCursorPos;
+ int len;
+ BOOLEAN changed = FALSE;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return (FALSE);
+
+ if (!pTES->Initialized)
+ { // init basic vars
+ int lwlen;
+
+ pTES->InputFunc = DoTextEntry;
+ pTES->Success = FALSE;
+ pTES->Initialized = TRUE;
+ pTES->JoystickMode = FALSE;
+ pTES->UpperRegister = TRUE;
+
+ // init insertion point
+ if ((size_t)pTES->CursorPos > utf8StringCount (pTES->BaseStr))
+ pTES->CursorPos = utf8StringCount (pTES->BaseStr);
+ pTES->InsPt = skipUTF8Chars (pTES->BaseStr, pTES->CursorPos);
+
+ // load joystick alphabet
+ pTES->JoyAlphaString = CaptureStringTable (
+ LoadStringTable (JOYSTICK_ALPHA_STRTAB));
+ pTES->JoyAlpha = LoadJoystickAlpha (
+ SetAbsStringTableIndex (pTES->JoyAlphaString, 0),
+ &pTES->JoyAlphaLength);
+ pTES->JoyUpper = LoadJoystickAlpha (
+ SetAbsStringTableIndex (pTES->JoyAlphaString, 1),
+ &pTES->JoyRegLength);
+ pTES->JoyLower = LoadJoystickAlpha (
+ SetAbsStringTableIndex (pTES->JoyAlphaString, 2),
+ &lwlen);
+ if (lwlen != pTES->JoyRegLength)
+ {
+ if (lwlen < pTES->JoyRegLength)
+ pTES->JoyRegLength = lwlen;
+ log_add (log_Warning, "Warning: Joystick upper-lower registers"
+ " size mismatch; using the smallest subset (%d)",
+ pTES->JoyRegLength);
+ }
+
+ pTES->CacheStr = HMalloc (pTES->MaxSize * sizeof (*pTES->CacheStr));
+
+ EnterCharacterMode ();
+ DoInput (pTES, TRUE);
+ ExitCharacterMode ();
+
+ if (pTES->CacheStr)
+ HFree (pTES->CacheStr);
+ if (pTES->JoyLower)
+ HFree (pTES->JoyLower);
+ if (pTES->JoyUpper)
+ HFree (pTES->JoyUpper);
+ if (pTES->JoyAlpha)
+ HFree (pTES->JoyAlpha);
+ DestroyStringTable ( ReleaseStringTable (pTES->JoyAlphaString));
+
+ return pTES->Success;
+ }
+
+ pStr = pTES->InsPt;
+ len = strlen (pStr);
+ // save a copy of string
+ CacheInsPt = pTES->InsPt;
+ CacheCursorPos = pTES->CursorPos;
+ memcpy (pTES->CacheStr, pTES->BaseStr, pTES->MaxSize);
+
+ // process the pending character buffer
+ ch = GetNextCharacter ();
+ if (!ch && PulsedInputState.menu[KEY_MENU_ANY])
+ { // keyboard repeat, but only when buffer empty
+ ch = GetLastCharacter ();
+ }
+ while (ch)
+ {
+ UNICODE chbuf[8];
+ int chsize;
+
+ pTES->JoystickMode = FALSE;
+
+ chsize = getStringFromChar (chbuf, sizeof (chbuf), ch);
+ if (UniChar_isPrint (ch) && chsize > 0)
+ {
+ if (pStr + len - pTES->BaseStr + chsize < pTES->MaxSize)
+ { // insert character, when fits
+ memmove (pStr + chsize, pStr, len + 1);
+ memcpy (pStr, chbuf, chsize);
+ pStr += chsize;
+ ++pTES->CursorPos;
+ changed = TRUE;
+ }
+ else
+ { // does not fit
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ ch = GetNextCharacter ();
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_DELETE])
+ {
+ if (len)
+ {
+ joy_char_t ch;
+
+ ReadOneChar (&ch, pStr);
+ memmove (pStr, pStr + ch.len, len - ch.len + 1);
+ len -= ch.len;
+ changed = TRUE;
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_BACKSPACE])
+ {
+ if (pStr > pTES->BaseStr)
+ {
+ UNICODE *prev = skipUTF8Chars (pTES->BaseStr,
+ pTES->CursorPos - 1);
+
+ memmove (prev, pStr, len + 1);
+ pStr = prev;
+ --pTES->CursorPos;
+ changed = TRUE;
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ if (pStr > pTES->BaseStr)
+ {
+ UNICODE *prev = skipUTF8Chars (pTES->BaseStr,
+ pTES->CursorPos - 1);
+
+ pStr = prev;
+ len += (prev - pStr);
+ --pTES->CursorPos;
+ changed = TRUE;
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ if (len > 0)
+ {
+ joy_char_t ch;
+
+ ReadOneChar (&ch, pStr);
+ pStr += ch.len;
+ len -= ch.len;
+ ++pTES->CursorPos;
+ changed = TRUE;
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_HOME])
+ {
+ if (pStr > pTES->BaseStr)
+ {
+ pStr = pTES->BaseStr;
+ len = strlen (pStr);
+ pTES->CursorPos = 0;
+ changed = TRUE;
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_END])
+ {
+ if (len > 0)
+ {
+ pTES->CursorPos += utf8StringCount (pStr);
+ pStr += len;
+ len = 0;
+ changed = TRUE;
+ }
+ }
+
+ if (pTES->JoyAlpha && (
+ PulsedInputState.menu[KEY_MENU_UP] ||
+ PulsedInputState.menu[KEY_MENU_DOWN] ||
+ PulsedInputState.menu[KEY_MENU_PAGE_UP] ||
+ PulsedInputState.menu[KEY_MENU_PAGE_DOWN]) )
+ { // do joystick text
+ joy_char_t ch;
+ joy_char_t newch;
+ joy_char_t cmpch;
+ int i;
+ BOOLEAN curCharUpper;
+
+ pTES->JoystickMode = TRUE;
+
+ if (len)
+ { // changing an existing character
+ ReadOneChar (&ch, pStr);
+ curCharUpper = !JoyCharIsLower (&ch, pTES);
+ }
+ else
+ { // adding a new character
+ ch = pTES->JoyAlpha[0];
+ // new characters will have case determined by the
+ // currently selected register
+ curCharUpper = pTES->UpperRegister;
+ }
+
+ newch = ch;
+ JoyCharToUpper (&cmpch, &ch, pTES);
+
+ // find current char in the alphabet
+ i = JoyCharFindIn (&cmpch, pTES->JoyAlpha, pTES->JoyAlphaLength);
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ --i;
+ if (i < 0)
+ i = pTES->JoyAlphaLength - 1;
+ newch = pTES->JoyAlpha[i];
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ ++i;
+ if (i >= pTES->JoyAlphaLength)
+ i = 0;
+ newch = pTES->JoyAlpha[i];
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_PAGE_UP] ||
+ PulsedInputState.menu[KEY_MENU_PAGE_DOWN])
+ {
+ if (len)
+ { // single char change
+ if (!curCharUpper)
+ JoyCharToUpper (&newch, &newch, pTES);
+ else
+ JoyCharToLower (&newch, &newch, pTES);
+ }
+ else
+ { // register change
+ pTES->UpperRegister = !pTES->UpperRegister;
+ }
+ }
+ else
+ { // check register
+ if (curCharUpper)
+ JoyCharToUpper (&newch, &newch, pTES);
+ else
+ JoyCharToLower (&newch, &newch, pTES);
+ }
+
+ if (strcmp (newch.enc, ch.enc) != 0)
+ { // new char is different, put it in
+ if (len)
+ { // change current -- this is messy with utf8
+ int l = len - ch.len;
+ if (pStr + l - pTES->BaseStr + newch.len < pTES->MaxSize)
+ {
+ // adjust other chars if necessary
+ if (newch.len != ch.len)
+ memmove (pStr + newch.len, pStr + ch.len, l + 1);
+
+ memcpy (pStr, newch.enc, newch.len);
+ len = l + newch.len;
+ changed = TRUE;
+ }
+ }
+ else
+ { // append
+ if (pStr + len - pTES->BaseStr + newch.len < pTES->MaxSize)
+ {
+ memcpy (pStr, newch.enc, newch.len);
+ pStr[newch.len] = '\0';
+ len += newch.len;
+ changed = TRUE;
+ }
+ else
+ { // does not fit
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ }
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_SELECT])
+ { // done entering
+ pTES->Success = TRUE;
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_EDIT_CANCEL])
+ { // canceled entering
+ pTES->Success = FALSE;
+ return FALSE;
+ }
+
+ pTES->InsPt = pStr;
+
+ if (changed && pTES->ChangeCallback)
+ {
+ if (!pTES->ChangeCallback (pTES))
+ { // changes not accepted - revert
+ memcpy (pTES->BaseStr, pTES->CacheStr, pTES->MaxSize);
+ pTES->InsPt = CacheInsPt;
+ pTES->CursorPos = CacheCursorPos;
+
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+
+ if (pTES->FrameCallback)
+ return pTES->FrameCallback (pTES);
+ else
+ SleepThread (ONE_SECOND / 30);
+
+ return TRUE;
+}
+
diff --git a/src/uqm/globdata.c b/src/uqm/globdata.c
new file mode 100644
index 0000000..ff9edc2
--- /dev/null
+++ b/src/uqm/globdata.c
@@ -0,0 +1,511 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "globdata.h"
+
+#include "coderes.h"
+#include "encount.h"
+#include "starmap.h"
+#include "master.h"
+#include "setup.h"
+#include "units.h"
+#include "hyper.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "build.h"
+#include "state.h"
+#include "grpinfo.h"
+#include "gamestr.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#ifdef STATE_DEBUG
+# include "libs/log.h"
+#endif
+
+
+static void CreateRadar (void);
+
+CONTEXT RadarContext;
+FRAME PlayFrame;
+
+GLOBDATA GlobData;
+
+
+BYTE
+getGameState (BYTE *state, int startBit, int endBit)
+{
+ return (BYTE) (((startBit >> 3) == (endBit >> 3)
+ ? (state[startBit >> 3] >> (startBit & 7))
+ : ((state[startBit >> 3] >> (startBit & 7))
+ | (state[endBit >> 3]
+ << (endBit - startBit - (endBit & 7)))))
+ & ((1 << (endBit - startBit + 1)) - 1));
+}
+
+void
+setGameState (BYTE *state, int startBit, int endBit, BYTE val
+#ifdef STATE_DEBUG
+ , const char *name
+#endif
+)
+{
+ state[startBit >> 3] =
+ (state[startBit >> 3]
+ & (BYTE) ~(((1 << (endBit - startBit + 1)) - 1) << (startBit & 7)))
+ | (BYTE)((val) << (startBit & 7));
+
+ if ((startBit >> 3) < (endBit >> 3)) {
+ state[endBit >> 3] =
+ (state[endBit >> 3]
+ & (BYTE)~((1 << ((endBit & 7) + 1)) - 1))
+ | (BYTE)((val) >> (endBit - startBit - (endBit & 7)));
+ }
+#ifdef STATE_DEBUG
+ log_add (log_Debug, "State '%s' set to %d.", name, (int)val);
+#endif
+}
+
+DWORD
+getGameState32 (BYTE *state, int startBit)
+{
+ DWORD v;
+ int shift;
+
+ for (v = 0, shift = 0; shift < 32; shift += 8, startBit += 8)
+ {
+ v |= getGameState (state, startBit, startBit + 7) << shift;
+ }
+
+ return v;
+}
+
+void
+setGameState32 (BYTE *state, int startBit, DWORD val
+#ifdef STATE_DEBUG
+ , const char *name
+#endif
+)
+{
+ DWORD v = val;
+ int i;
+
+ for (i = 0; i < 4; ++i, v >>= 8, startBit += 8)
+ {
+ setGameState (state, startBit, startBit + 7, v & 0xff
+#ifdef STATE_DEBUG
+ , "(ignored)"
+#endif
+ );
+ }
+
+#ifdef STATE_DEBUG
+ log_add (log_Debug, "State '%s' set to %u.", name, (unsigned)val);
+#endif
+}
+
+void
+copyGameState (BYTE *dest, DWORD target, BYTE *src, DWORD begin, DWORD end)
+{
+ while (begin < end)
+ {
+ BYTE b;
+ DWORD delta = 7;
+ if (begin + delta > end)
+ delta = end - begin;
+ b = getGameState (src, begin, begin + delta);
+ setGameState (dest, target, target + delta, b);
+ begin += 8;
+ target += 8;
+ }
+}
+
+static void
+CreateRadar (void)
+{
+ if (RadarContext == 0)
+ {
+ RECT r;
+ CONTEXT OldContext;
+
+ RadarContext = CreateContext ("RadarContext");
+ OldContext = SetContext (RadarContext);
+ SetContextFGFrame (Screen);
+ r.corner.x = RADAR_X;
+ r.corner.y = RADAR_Y;
+ r.extent.width = RADAR_WIDTH;
+ r.extent.height = RADAR_HEIGHT;
+ SetContextClipRect (&r);
+ SetContext (OldContext);
+ }
+}
+
+BOOLEAN
+LoadSC2Data (void)
+{
+ if (FlagStatFrame == 0)
+ {
+ FlagStatFrame = CaptureDrawable (
+ LoadGraphic (FLAGSTAT_MASK_PMAP_ANIM));
+ if (FlagStatFrame == NULL)
+ return FALSE;
+
+ MiscDataFrame = CaptureDrawable (
+ LoadGraphic (MISCDATA_MASK_PMAP_ANIM));
+ if (MiscDataFrame == NULL)
+ return FALSE;
+
+ FontGradFrame = CaptureDrawable (
+ LoadGraphic (FONTGRAD_PMAP_ANIM));
+ }
+
+ CreateRadar ();
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE)
+ {
+ GLOBAL (ShipStamp.origin.x) =
+ GLOBAL (ShipStamp.origin.y) = -1;
+ }
+
+ return TRUE;
+}
+
+static void
+copyFleetInfo (FLEET_INFO *dst, SHIP_INFO *src, FLEET_STUFF *fleet)
+{
+ // other leading fields are irrelevant
+ dst->crew_level = src->crew_level;
+ dst->max_crew = src->max_crew;
+ dst->max_energy = src->max_energy;
+
+ dst->race_strings = src->race_strings;
+ dst->icons = src->icons;
+ dst->melee_icon = src->melee_icon;
+
+ dst->actual_strength = fleet->strength;
+ dst->known_loc = fleet->known_loc;
+}
+
+BOOLEAN
+InitGameStructures (void)
+{
+ COUNT i;
+
+ InitGlobData ();
+
+ PlayFrame = CaptureDrawable (LoadGraphic (PLAYMENU_ANIM));
+
+ {
+ COUNT num_ships;
+ SPECIES_ID s_id = ARILOU_ID;
+
+ num_ships = KOHR_AH_ID - s_id + 1
+ + 2; /* Yehat Rebels and Ur-Quan probe */
+
+ InitQueue (&GLOBAL (avail_race_q), num_ships, sizeof (FLEET_INFO));
+ for (i = 0; i < num_ships; ++i)
+ {
+ SPECIES_ID ship_ref;
+ HFLEETINFO hFleet;
+ FLEET_INFO *FleetPtr;
+
+ if (i < num_ships - 2)
+ ship_ref = s_id++;
+ else if (i == num_ships - 2)
+ ship_ref = YEHAT_ID;
+ else /* (i == num_ships - 1) */
+ ship_ref = UR_QUAN_PROBE_ID;
+
+ hFleet = AllocLink (&GLOBAL (avail_race_q));
+ if (!hFleet)
+ continue;
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ FleetPtr->SpeciesID = ship_ref;
+
+ if (i < num_ships - 1)
+ {
+ HMASTERSHIP hMasterShip;
+ MASTER_SHIP_INFO *MasterPtr;
+
+ hMasterShip = FindMasterShip (ship_ref);
+ MasterPtr = LockMasterShip (&master_q, hMasterShip);
+ // Grab a copy of loaded icons and strings (not owned)
+ copyFleetInfo (FleetPtr, &MasterPtr->ShipInfo,
+ &MasterPtr->Fleet);
+ UnlockMasterShip (&master_q, hMasterShip);
+ }
+ else
+ {
+ // Ur-Quan probe.
+ RACE_DESC *RDPtr = load_ship (FleetPtr->SpeciesID,
+ FALSE);
+ if (RDPtr)
+ { // Grab a copy of loaded icons and strings
+ copyFleetInfo (FleetPtr, &RDPtr->ship_info,
+ &RDPtr->fleet);
+ // avail_race_q owns these resources now
+ free_ship (RDPtr, FALSE, FALSE);
+ }
+ }
+
+ FleetPtr->allied_state = BAD_GUY;
+ FleetPtr->known_strength = 0;
+ FleetPtr->loc = FleetPtr->known_loc;
+ // XXX: Hack: Rebel special case
+ if (i == YEHAT_REBEL_SHIP)
+ FleetPtr->actual_strength = 0;
+ FleetPtr->growth = 0;
+ FleetPtr->growth_fract = 0;
+ FleetPtr->growth_err_term = 255 >> 1;
+ FleetPtr->days_left = 0;
+ FleetPtr->func_index = ~0;
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ PutQueue (&GLOBAL (avail_race_q), hFleet);
+ }
+ }
+
+ InitSISContexts ();
+ LoadSC2Data ();
+
+ InitPlanetInfo ();
+ InitGroupInfo (TRUE);
+
+ GLOBAL (glob_flags) = 0;
+
+ GLOBAL (ElementWorth[COMMON]) = 1;
+ GLOBAL_SIS (ElementAmounts[COMMON]) = 0;
+ GLOBAL (ElementWorth[CORROSIVE]) = 2;
+ GLOBAL_SIS (ElementAmounts[CORROSIVE]) = 0;
+ GLOBAL (ElementWorth[BASE_METAL]) = 3;
+ GLOBAL_SIS (ElementAmounts[BASE_METAL]) = 0;
+ GLOBAL (ElementWorth[NOBLE]) = 4;
+ GLOBAL_SIS (ElementAmounts[NOBLE]) = 0;
+ GLOBAL (ElementWorth[RARE_EARTH]) = 5;
+ GLOBAL_SIS (ElementAmounts[RARE_EARTH]) = 0;
+ GLOBAL (ElementWorth[PRECIOUS]) = 6;
+ GLOBAL_SIS (ElementAmounts[PRECIOUS]) = 0;
+ GLOBAL (ElementWorth[RADIOACTIVE]) = 8;
+ GLOBAL_SIS (ElementAmounts[RADIOACTIVE]) = 0;
+ GLOBAL (ElementWorth[EXOTIC]) = 25;
+ GLOBAL_SIS (ElementAmounts[EXOTIC]) = 0;
+
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ GLOBAL_SIS (DriveSlots[i]) = EMPTY_SLOT + 0;
+ GLOBAL_SIS (DriveSlots[5]) =
+ GLOBAL_SIS (DriveSlots[6]) = FUSION_THRUSTER;
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ GLOBAL_SIS (JetSlots[i]) = EMPTY_SLOT + 1;
+ GLOBAL_SIS (JetSlots[0]) =
+ GLOBAL_SIS (JetSlots[6]) = TURNING_JETS;
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ GLOBAL_SIS (ModuleSlots[i]) = EMPTY_SLOT + 2;
+ GLOBAL_SIS (ModuleSlots[15]) = GUN_WEAPON;
+ GLOBAL_SIS (ModuleSlots[2]) = CREW_POD;
+ GLOBAL_SIS (CrewEnlisted) = CREW_POD_CAPACITY;
+ GLOBAL_SIS (ModuleSlots[8]) = STORAGE_BAY;
+ GLOBAL_SIS (ModuleSlots[1]) = FUEL_TANK;
+ GLOBAL_SIS (FuelOnBoard) = 10 * FUEL_TANK_SCALE;
+
+ InitQueue (&GLOBAL (built_ship_q),
+ MAX_BUILT_SHIPS, sizeof (SHIP_FRAGMENT));
+ InitQueue (&GLOBAL (npc_built_ship_q), MAX_SHIPS_PER_SIDE,
+ sizeof (SHIP_FRAGMENT));
+ InitQueue (&GLOBAL (ip_group_q), MAX_BATTLE_GROUPS,
+ sizeof (IP_GROUP));
+ InitQueue (&GLOBAL (encounter_q), MAX_ENCOUNTERS, sizeof (ENCOUNTER));
+
+ GLOBAL (CurrentActivity) = IN_INTERPLANETARY | START_INTERPLANETARY;
+
+ GLOBAL_SIS (ResUnits) = 0;
+ GLOBAL (CrewCost) = 3;
+ GLOBAL (FuelCost) = 20;
+ GLOBAL (ModuleCost[PLANET_LANDER]) = 500 / MODULE_COST_SCALE;
+ GLOBAL (ModuleCost[FUSION_THRUSTER]) = 500 / MODULE_COST_SCALE;
+ GLOBAL (ModuleCost[TURNING_JETS]) = 500 / MODULE_COST_SCALE;
+ GLOBAL (ModuleCost[CREW_POD]) = 2000 / MODULE_COST_SCALE;
+ GLOBAL (ModuleCost[STORAGE_BAY]) = 750 / MODULE_COST_SCALE;
+ GLOBAL (ModuleCost[FUEL_TANK]) = 500 / MODULE_COST_SCALE;
+ GLOBAL (ModuleCost[DYNAMO_UNIT]) = 2000 / MODULE_COST_SCALE;
+ GLOBAL (ModuleCost[GUN_WEAPON]) = 2000 / MODULE_COST_SCALE;
+
+ GLOBAL_SIS (NumLanders) = 1;
+
+ utf8StringCopy (GLOBAL_SIS (ShipName), sizeof (GLOBAL_SIS (ShipName)),
+ GAME_STRING (NAMING_STRING_BASE + 2));
+ utf8StringCopy (GLOBAL_SIS (CommanderName),
+ sizeof (GLOBAL_SIS (CommanderName)),
+ GAME_STRING (NAMING_STRING_BASE + 3));
+
+ SetRaceAllied (HUMAN_SHIP, TRUE);
+ CloneShipFragment (HUMAN_SHIP, &GLOBAL (built_ship_q), 0);
+
+ GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (SOL_X);
+ GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (SOL_Y);
+ CurStarDescPtr = 0;
+ GLOBAL (autopilot.x) = ~0;
+ GLOBAL (autopilot.y) = ~0;
+
+ return (TRUE);
+}
+
+void
+FreeSC2Data (void)
+{
+ DestroyContext (RadarContext);
+ RadarContext = 0;
+ DestroyDrawable (ReleaseDrawable (FontGradFrame));
+ FontGradFrame = 0;
+ DestroyDrawable (ReleaseDrawable (MiscDataFrame));
+ MiscDataFrame = 0;
+ DestroyDrawable (ReleaseDrawable (FlagStatFrame));
+ FlagStatFrame = 0;
+}
+
+void
+UninitGameStructures (void)
+{
+ HFLEETINFO hStarShip;
+
+ UninitQueue (&GLOBAL (encounter_q));
+ UninitQueue (&GLOBAL (ip_group_q));
+ UninitQueue (&GLOBAL (npc_built_ship_q));
+ UninitQueue (&GLOBAL (built_ship_q));
+ UninitGroupInfo ();
+ UninitPlanetInfo ();
+
+// FreeSC2Data ();
+
+ // The only resources avail_race_q owns are the Ur-Quan probe's
+ // so free them now
+ hStarShip = GetTailLink (&GLOBAL (avail_race_q));
+ if (hStarShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ DestroyDrawable (ReleaseDrawable (FleetPtr->melee_icon));
+ DestroyDrawable (ReleaseDrawable (FleetPtr->icons));
+ DestroyStringTable (ReleaseStringTable (FleetPtr->race_strings));
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ UninitQueue (&GLOBAL (avail_race_q));
+
+ DestroyDrawable (ReleaseDrawable (PlayFrame));
+ PlayFrame = 0;
+}
+
+void
+InitGlobData (void)
+{
+ COUNT i;
+
+ i = GLOBAL (glob_flags);
+ memset (&GlobData, 0, sizeof (GlobData));
+ GLOBAL (glob_flags) = (BYTE)i;
+
+ GLOBAL (DisplayArray) = DisplayArray;
+}
+
+
+BOOLEAN
+inFullGame (void)
+{
+ ACTIVITY act = LOBYTE (GLOBAL (CurrentActivity));
+ return (act == IN_LAST_BATTLE || act == IN_ENCOUNTER ||
+ act == IN_HYPERSPACE || act == IN_INTERPLANETARY ||
+ act == WON_LAST_BATTLE);
+}
+
+BOOLEAN
+inSuperMelee (void)
+{
+ return (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE);
+ // TODO: && !inMainMenu ()
+}
+
+#if 0
+BOOLEAN
+inBattle (void)
+{
+ // TODO: IN_BATTLE is also set while in HyperSpace/QuasiSpace.
+ return ((GLOBAL (CurrentActivity) & IN_BATTLE) != 0);
+}
+#endif
+
+#if 0
+// Disabled for now as there are similar functions in uqm/planets/planets.h
+// Pre: inFullGame()
+BOOLEAN
+inInterPlanetary (void)
+{
+ assert (inFullGame ());
+ return (pSolarSysState != NULL);
+}
+
+// Pre: inFullGame()
+BOOLEAN
+inSolarSystem (void)
+{
+ assert (inFullGame ());
+ return (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY);
+}
+
+// Pre: inFullGame()
+BOOLEAN
+inOrbit (void)
+{
+ assert (inFullGame ());
+ return (pSolarSysState != NULL) &&
+ (pSolarSysState->pOrbitalDesc != NULL);
+}
+#endif
+
+// In HyperSpace or QuasiSpace
+// Pre: inFullGame()
+BOOLEAN
+inHQSpace (void)
+{
+ //assert (inFullGame ());
+ return (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE);
+ // IN_HYPERSPACE is also set for QuasiSpace
+}
+
+// In HyperSpace
+// Pre: inFullGame()
+BOOLEAN
+inHyperSpace (void)
+{
+ //assert (inFullGame ());
+ return (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE) &&
+ (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1);
+ // IN_HYPERSPACE is also set for QuasiSpace
+}
+
+// In QuasiSpace
+// Pre: inFullGame()
+BOOLEAN
+inQuasiSpace (void)
+{
+ //assert (inFullGame ());
+ return (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE) &&
+ (GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1);
+ // IN_HYPERSPACE is also set for QuasiSpace
+}
+
diff --git a/src/uqm/globdata.h b/src/uqm/globdata.h
new file mode 100644
index 0000000..216cadf
--- /dev/null
+++ b/src/uqm/globdata.h
@@ -0,0 +1,1059 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_GLOBDATA_H_
+#define UQM_GLOBDATA_H_
+
+#include "clock.h"
+#include "libs/gfxlib.h"
+#include "libs/reslib.h"
+#include "libs/sndlib.h"
+#include "sis.h"
+#include "velocity.h"
+#include "commanim.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// general numbers-speech generator info
+// should accomodate most common base-10 languages
+// many languages require various plural forms
+// for digit names like "hundred"
+// possibly needs reworking for others
+typedef struct
+{
+ // an array of these structs must be in ascending remainder order
+ // terminate the array with Divider == 0
+
+ // digit divider, i.e. 1, 10, 100, etc.
+ int Divider;
+ // maximum remainder for this name
+ // name will be used if Number % Divider <= MaxRemainder
+ int MaxRemainder;
+ // string table index for this name
+ // i.e. "hundred" in English
+ COUNT StrIndex;
+} SPEECH_DIGITNAME;
+
+typedef struct
+{
+ // digit divider, i.e. 1, 10, 100, etc.
+ int Divider;
+ // digit sub, i.e. 10 for teens
+ // subtracted from the value to get an index into StrDigits
+ int Subtrahend;
+ // ptr to 10 indices for this digit
+ // index is string table ptr when > 0
+ // is invalid (should not happen) or
+ // is a a 'skip digit' indicator when == 0
+ // StrDigits can be NULL, in which case
+ // the value is interpreted recursively
+ COUNT *StrDigits;
+ // digit Names, can be NULL, in which case
+ // CommonNameIndex is used
+ SPEECH_DIGITNAME *Names;
+ // common digit name string table index
+ // i.e. "hundred" in English
+ COUNT CommonNameIndex;
+} SPEECH_DIGIT;
+
+// this accomodates up to "billions" in english
+#define MAX_SPEECH_DIGITS 7
+
+typedef struct
+{
+ // slots used in Digits array
+ COUNT NumDigits;
+ // slots for each digit in numbers
+ // teens is exception
+ // 0-9, 10-19, ..20-90, ..100-900, etc.
+ SPEECH_DIGIT Digits[MAX_SPEECH_DIGITS];
+} NUMBER_SPEECH_DESC;
+typedef const NUMBER_SPEECH_DESC *NUMBER_SPEECH;
+
+typedef DWORD LDAS_FLAGS;
+#define LDASF_NONE ((LDAS_FLAGS) 0 )
+#define LDASF_USE_ALTERNATE ((LDAS_FLAGS)(1 << 0))
+
+typedef struct
+{
+ void (*init_encounter_func) (void);
+ /* Called when entering communications */
+ void (*post_encounter_func) (void);
+ /* Called when leaving communications or combat normally */
+ COUNT (*uninit_encounter_func) (void);
+ /* Called when encounter is done for cleanup */
+
+ RESOURCE AlienFrameRes;
+ RESOURCE AlienFontRes;
+ Color AlienTextFColor, AlienTextBColor;
+ POINT AlienTextBaseline;
+ COUNT AlienTextWidth;
+ TEXT_ALIGN AlienTextAlign;
+ TEXT_VALIGN AlienTextValign;
+ RESOURCE AlienColorMapRes;
+ RESOURCE AlienSongRes;
+ RESOURCE AlienAltSongRes;
+ LDAS_FLAGS AlienSongFlags;
+
+ RESOURCE ConversationPhrasesRes;
+
+ COUNT NumAnimations;
+ ANIMATION_DESC AlienAmbientArray[MAX_ANIMATIONS];
+
+ // Transition animation to/from talking state;
+ // the first frame is neutral (sort of like YOYO_ANIM)
+ ANIMATION_DESC AlienTransitionDesc;
+ // Talking animation, like RANDOM_ANIM, except random frames
+ // always alternate with a neutral frame;
+ // the first frame is neutral
+ ANIMATION_DESC AlienTalkDesc;
+
+ NUMBER_SPEECH AlienNumberSpeech;
+
+ FRAME AlienFrame;
+ FONT AlienFont;
+ COLORMAP AlienColorMap;
+ MUSIC_REF AlienSong;
+ STRING ConversationPhrases;
+
+} LOCDATA;
+
+enum
+{
+ PORTAL_SPAWNER_DEVICE = 0,
+ TALKING_PET_DEVICE,
+ UTWIG_BOMB_DEVICE,
+ SUN_EFFICIENCY_DEVICE,
+ ROSY_SPHERE_DEVICE,
+ AQUA_HELIX_DEVICE,
+ CLEAR_SPINDLE_DEVICE,
+ ULTRON_0_DEVICE,
+ ULTRON_1_DEVICE,
+ ULTRON_2_DEVICE,
+ ULTRON_3_DEVICE,
+ MAIDENS_DEVICE,
+ UMGAH_HYPERWAVE_DEVICE,
+ BURVIX_HYPERWAVE_DEVICE,
+ DATA_PLATE_1_DEVICE,
+ DATA_PLATE_2_DEVICE,
+ DATA_PLATE_3_DEVICE,
+ TAALO_PROTECTOR_DEVICE,
+ EGG_CASING0_DEVICE,
+ EGG_CASING1_DEVICE,
+ EGG_CASING2_DEVICE,
+ SYREEN_SHUTTLE_DEVICE,
+ VUX_BEAST_DEVICE,
+ DESTRUCT_CODE_DEVICE,
+ URQUAN_WARP_DEVICE,
+ ARTIFACT_2_DEVICE,
+ ARTIFACT_3_DEVICE,
+ LUNAR_BASE_DEVICE,
+
+ NUM_DEVICES
+};
+
+#define YEARS_TO_KOHRAH_VICTORY 4
+
+#define START_GAME_STATE enum {
+#define ADD_GAME_STATE(SName,NumBits) SName, END_##SName = SName + NumBits - 1,
+#define END_GAME_STATE NUM_GAME_STATE_BITS };
+
+START_GAME_STATE
+ /* Shofixti states */
+ ADD_GAME_STATE (SHOFIXTI_VISITS, 3)
+ ADD_GAME_STATE (SHOFIXTI_STACK1, 2)
+ ADD_GAME_STATE (SHOFIXTI_STACK2, 3)
+ ADD_GAME_STATE (SHOFIXTI_STACK3, 2)
+ ADD_GAME_STATE (SHOFIXTI_KIA, 1)
+ ADD_GAME_STATE (SHOFIXTI_BRO_KIA, 1)
+ ADD_GAME_STATE (SHOFIXTI_RECRUITED, 1)
+
+ ADD_GAME_STATE (SHOFIXTI_MAIDENS, 1) /* Did you find the babes yet? */
+ ADD_GAME_STATE (MAIDENS_ON_SHIP, 1)
+ ADD_GAME_STATE (BATTLE_SEGUE, 1)
+ /* Set to 0 in init_xxx_comm() if communications directly
+ * follows an encounter.
+ * Set to 1 in init_xxx_comm() if the player gets to decide
+ * whether to attack or talk.
+ * Set to 1 in communication when battle follows the
+ * communication. It is still valid when uninit_xxx_comm() gets
+ * called after combat or communication.
+ */
+ ADD_GAME_STATE (PLANETARY_LANDING, 1)
+ ADD_GAME_STATE (PLANETARY_CHANGE, 1)
+ /* Flag set to 1 when the planet information for the current
+ * world is changed since it was last saved to the starinfo.dat
+ * file. Set when picking up bio, mineral, or energy nodes.
+ * When there's no current world, it should be 0.
+ */
+
+ /* Spathi states */
+ ADD_GAME_STATE (SPATHI_VISITS, 3)
+ ADD_GAME_STATE (SPATHI_HOME_VISITS, 3)
+ ADD_GAME_STATE (FOUND_PLUTO_SPATHI, 2)
+ /* 0 - Haven't met Fwiffo.
+ * 1 - Met Fwiffo on Pluto, now talking to him.
+ * 2 - Met Fwiffo on Pluto, after dialog.
+ * 3 - Met Fwiffo, and have reported to the Safe Ones on
+ * the Spathi moon that he was either killed, or that
+ * you have him on board.
+ */
+ ADD_GAME_STATE (SPATHI_SHIELDED_SELVES, 1)
+ ADD_GAME_STATE (SPATHI_CREATURES_EXAMINED, 1)
+ ADD_GAME_STATE (SPATHI_CREATURES_ELIMINATED, 1)
+ ADD_GAME_STATE (UMGAH_BROADCASTERS, 1)
+ ADD_GAME_STATE (SPATHI_MANNER, 2)
+ ADD_GAME_STATE (SPATHI_QUEST, 1)
+ ADD_GAME_STATE (LIED_ABOUT_CREATURES, 2)
+ ADD_GAME_STATE (SPATHI_PARTY, 1)
+ ADD_GAME_STATE (KNOW_SPATHI_PASSWORD, 1)
+
+ ADD_GAME_STATE (ILWRATH_HOME_VISITS, 3)
+ ADD_GAME_STATE (ILWRATH_CHMMR_VISITS, 1)
+
+ ADD_GAME_STATE (ARILOU_SPACE, 1)
+ /* 0 if the periodically opening QuasiSpace portal is
+ * closed or closing.
+ * 1 if the periodically opening QuasiSpace portal is
+ * open or opening.
+ */
+ ADD_GAME_STATE (ARILOU_SPACE_SIDE, 2)
+ /* 0 if in HyperSpace and not just emerged from the periodically
+ * opening QuasiSpace portal.
+ * 1 if in HyperSpace and just emerged from the periodically
+ * QuasiSpace portal (still on the portal).
+ * 2 if in QuasiSpace and just emerged from the periodically
+ * opening portal (still on the portal).
+ * 3 if in QuasiSpace and not just emerged from the
+ * periodically opening portal.
+ */
+ ADD_GAME_STATE (ARILOU_SPACE_COUNTER, 4)
+ /* Keeps track of how far the periodically opening QuasiSpace
+ * portal is open. (This determines the image)
+ * 0 <= ARILOU_SPACE_COUNTER <= 9
+ * 0 means totally closed.
+ * 9 means completely open.
+ */
+
+ ADD_GAME_STATE (LANDER_SHIELDS, 4)
+
+ ADD_GAME_STATE (MET_MELNORME, 1)
+ ADD_GAME_STATE (MELNORME_RESCUE_REFUSED, 1)
+ ADD_GAME_STATE (MELNORME_RESCUE_COUNT, 3)
+ ADD_GAME_STATE (TRADED_WITH_MELNORME, 1)
+ ADD_GAME_STATE (WHY_MELNORME_PURPLE, 1)
+ ADD_GAME_STATE (MELNORME_CREDIT0, 8)
+ ADD_GAME_STATE (MELNORME_CREDIT1, 8)
+ ADD_GAME_STATE (MELNORME_BUSINESS_COUNT, 2)
+ ADD_GAME_STATE (MELNORME_YACK_STACK0, 2)
+ ADD_GAME_STATE (MELNORME_YACK_STACK1, 2)
+ ADD_GAME_STATE (MELNORME_YACK_STACK2, 4)
+ ADD_GAME_STATE (MELNORME_YACK_STACK3, 3)
+ ADD_GAME_STATE (MELNORME_YACK_STACK4, 2)
+ ADD_GAME_STATE (WHY_MELNORME_BLUE, 1)
+ ADD_GAME_STATE (MELNORME_ANGER, 2)
+ ADD_GAME_STATE (MELNORME_MIFFED_COUNT, 2)
+ ADD_GAME_STATE (MELNORME_PISSED_COUNT, 2)
+ ADD_GAME_STATE (MELNORME_HATE_COUNT, 2)
+
+ ADD_GAME_STATE (PROBE_MESSAGE_DELIVERED, 1)
+ ADD_GAME_STATE (PROBE_ILWRATH_ENCOUNTER, 1)
+
+ ADD_GAME_STATE (STARBASE_AVAILABLE, 1)
+ ADD_GAME_STATE (STARBASE_VISITED, 1)
+ ADD_GAME_STATE (RADIOACTIVES_PROVIDED, 1)
+ ADD_GAME_STATE (LANDERS_LOST, 1)
+ ADD_GAME_STATE (GIVEN_FUEL_BEFORE, 1)
+
+ ADD_GAME_STATE (AWARE_OF_SAMATRA, 1)
+ ADD_GAME_STATE (YEHAT_CAVALRY_ARRIVED, 1)
+ ADD_GAME_STATE (URQUAN_MESSED_UP, 1)
+
+ ADD_GAME_STATE (MOONBASE_DESTROYED, 1)
+ ADD_GAME_STATE (WILL_DESTROY_BASE, 1)
+
+ ADD_GAME_STATE (ARTIFACT_2_ON_SHIP, 1)
+ ADD_GAME_STATE (ARTIFACT_3_ON_SHIP, 1)
+
+ ADD_GAME_STATE (KOHR_AH_KILLED_ALL, 1)
+
+ ADD_GAME_STATE (STARBASE_YACK_STACK1, 1)
+
+ ADD_GAME_STATE (DISCUSSED_PORTAL_SPAWNER, 1)
+ ADD_GAME_STATE (DISCUSSED_TALKING_PET, 1)
+ ADD_GAME_STATE (DISCUSSED_UTWIG_BOMB, 1)
+ ADD_GAME_STATE (DISCUSSED_SUN_EFFICIENCY, 1)
+ ADD_GAME_STATE (DISCUSSED_ROSY_SPHERE, 1)
+ ADD_GAME_STATE (DISCUSSED_AQUA_HELIX, 1)
+ ADD_GAME_STATE (DISCUSSED_CLEAR_SPINDLE, 1)
+ ADD_GAME_STATE (DISCUSSED_ULTRON, 1)
+ ADD_GAME_STATE (DISCUSSED_MAIDENS, 1)
+ ADD_GAME_STATE (DISCUSSED_UMGAH_HYPERWAVE, 1)
+ ADD_GAME_STATE (DISCUSSED_BURVIX_HYPERWAVE, 1)
+ ADD_GAME_STATE (SYREEN_WANT_PROOF, 1)
+ ADD_GAME_STATE (PLAYER_HAVING_SEX, 1)
+ ADD_GAME_STATE (MET_ARILOU, 1)
+ ADD_GAME_STATE (DISCUSSED_TAALO_PROTECTOR, 1)
+ ADD_GAME_STATE (DISCUSSED_EGG_CASING0, 1)
+ ADD_GAME_STATE (DISCUSSED_EGG_CASING1, 1)
+ ADD_GAME_STATE (DISCUSSED_EGG_CASING2, 1)
+ ADD_GAME_STATE (DISCUSSED_SYREEN_SHUTTLE, 1)
+ ADD_GAME_STATE (DISCUSSED_VUX_BEAST, 1)
+ ADD_GAME_STATE (DISCUSSED_DESTRUCT_CODE, 1)
+ ADD_GAME_STATE (DISCUSSED_URQUAN_WARP, 1)
+ ADD_GAME_STATE (DISCUSSED_ARTIFACT_2, 1)
+ ADD_GAME_STATE (DISCUSSED_ARTIFACT_3, 1)
+
+ ADD_GAME_STATE (ATTACKED_DRUUGE, 1)
+
+ ADD_GAME_STATE (NEW_ALLIANCE_NAME, 2)
+
+ ADD_GAME_STATE (PORTAL_COUNTER, 4)
+ /* Set to 1 when the player opens a QuasiSpace portal.
+ * It will then be increased to 10, at which time
+ * the portal is completely open. (This determines the image).
+ */
+
+ ADD_GAME_STATE (BURVIXESE_BROADCASTERS, 1)
+ ADD_GAME_STATE (BURV_BROADCASTERS_ON_SHIP, 1)
+
+ ADD_GAME_STATE (UTWIG_BOMB, 1)
+ ADD_GAME_STATE (UTWIG_BOMB_ON_SHIP, 1)
+
+ ADD_GAME_STATE (AQUA_HELIX, 1)
+ ADD_GAME_STATE (AQUA_HELIX_ON_SHIP, 1)
+
+ ADD_GAME_STATE (SUN_DEVICE, 1)
+ ADD_GAME_STATE (SUN_DEVICE_ON_SHIP, 1)
+
+ ADD_GAME_STATE (TAALO_PROTECTOR, 1)
+ ADD_GAME_STATE (TAALO_PROTECTOR_ON_SHIP, 1)
+
+ ADD_GAME_STATE (SHIP_VAULT_UNLOCKED, 1)
+ ADD_GAME_STATE (SYREEN_SHUTTLE, 1)
+
+ ADD_GAME_STATE (PORTAL_KEY, 1)
+ ADD_GAME_STATE (PORTAL_KEY_ON_SHIP, 1)
+
+ ADD_GAME_STATE (VUX_BEAST, 1)
+ ADD_GAME_STATE (VUX_BEAST_ON_SHIP, 1)
+
+ ADD_GAME_STATE (TALKING_PET, 1)
+ ADD_GAME_STATE (TALKING_PET_ON_SHIP, 1)
+
+ ADD_GAME_STATE (MOONBASE_ON_SHIP, 1)
+
+ ADD_GAME_STATE (KOHR_AH_FRENZY, 1)
+ ADD_GAME_STATE (KOHR_AH_VISITS, 2)
+ ADD_GAME_STATE (KOHR_AH_BYES, 1)
+
+ ADD_GAME_STATE (SLYLANDRO_HOME_VISITS, 3)
+ ADD_GAME_STATE (DESTRUCT_CODE_ON_SHIP, 1)
+
+ ADD_GAME_STATE (ILWRATH_VISITS, 3)
+ ADD_GAME_STATE (ILWRATH_DECEIVED, 1)
+ ADD_GAME_STATE (FLAGSHIP_CLOAKED, 1)
+
+ ADD_GAME_STATE (MYCON_VISITS, 3)
+ ADD_GAME_STATE (MYCON_HOME_VISITS, 3)
+ ADD_GAME_STATE (MYCON_AMBUSH, 1)
+ ADD_GAME_STATE (MYCON_FELL_FOR_AMBUSH, 1)
+ /* Set to 1 when the Mycon have been told about Organon
+ * and are moving towards it.
+ */
+
+ ADD_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 8)
+ /* This state seems to be used to distinguish between different
+ * places where one may have an conversation with an alien.
+ * Like home world, other world, space.
+ * Why this needs 8 bits I don't know. Only specific
+ * combinations of bits seem to be used (0, 1, or all bits).
+ * A closer investigation is desirable. - SvdB
+ * Bit 4 is set when initiating communication with the Ilwrath
+ * homeworld by means of a HyperWave Broadcaster.
+ * Bit 5 is set when initiating communication with an Ilwrath
+ * ship by means of a HyperWave Broadcaster.
+ * All bits are cleared when communication is over.
+ */
+
+ ADD_GAME_STATE (ORZ_VISITS, 3)
+ ADD_GAME_STATE (TAALO_VISITS, 3)
+ ADD_GAME_STATE (ORZ_MANNER, 2)
+
+ ADD_GAME_STATE (PROBE_EXHIBITED_BUG, 1)
+ ADD_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 1)
+
+ ADD_GAME_STATE (URQUAN_VISITS, 3)
+ ADD_GAME_STATE (PLAYER_HYPNOTIZED, 1)
+
+ ADD_GAME_STATE (VUX_VISITS, 3)
+ ADD_GAME_STATE (VUX_HOME_VISITS, 3)
+ ADD_GAME_STATE (ZEX_VISITS, 3)
+ ADD_GAME_STATE (ZEX_IS_DEAD, 1)
+ ADD_GAME_STATE (KNOW_ZEX_WANTS_MONSTER, 1)
+
+ ADD_GAME_STATE (UTWIG_VISITS, 3)
+ ADD_GAME_STATE (UTWIG_HOME_VISITS, 3)
+ ADD_GAME_STATE (BOMB_VISITS, 3)
+ ADD_GAME_STATE (ULTRON_CONDITION, 3)
+ /* 0 if the Supox still have the Ultron
+ * 1 if the Captain has the Ultron, completely broken
+ * 2 if the Captain has the Ultron, with 1 fix
+ * 3 if the Captain has the Ultron, with 2 fixes
+ * 4 if the Captain has the Ultron, completely restored
+ * 5 if the Ultron has been returned to the Utwig
+ */
+ ADD_GAME_STATE (UTWIG_HAVE_ULTRON, 1)
+ ADD_GAME_STATE (BOMB_UNPROTECTED, 1)
+
+ ADD_GAME_STATE (TAALO_UNPROTECTED, 1)
+
+ ADD_GAME_STATE (TALKING_PET_VISITS, 3)
+ ADD_GAME_STATE (TALKING_PET_HOME_VISITS, 3)
+ ADD_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES, 1)
+ /* The Umgah have come under the influence of the Talking Pet */
+ ADD_GAME_STATE (KNOW_UMGAH_ZOMBIES, 1)
+ /* The Captain is aware that something is up with the Umgah */
+
+ ADD_GAME_STATE (ARILOU_VISITS, 3)
+ ADD_GAME_STATE (ARILOU_HOME_VISITS, 3)
+ ADD_GAME_STATE (KNOW_ARILOU_WANT_WRECK, 1)
+ ADD_GAME_STATE (ARILOU_CHECKED_UMGAH, 2)
+ ADD_GAME_STATE (PORTAL_SPAWNER, 1)
+ ADD_GAME_STATE (PORTAL_SPAWNER_ON_SHIP, 1)
+
+ ADD_GAME_STATE (UMGAH_VISITS, 3)
+ ADD_GAME_STATE (UMGAH_HOME_VISITS, 3)
+ ADD_GAME_STATE (MET_NORMAL_UMGAH, 1)
+
+ ADD_GAME_STATE (SYREEN_HOME_VISITS, 3)
+ ADD_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP, 1)
+ ADD_GAME_STATE (KNOW_SYREEN_VAULT, 1)
+
+ ADD_GAME_STATE (EGG_CASE0_ON_SHIP, 1)
+ ADD_GAME_STATE (SUN_DEVICE_UNGUARDED, 1)
+
+ ADD_GAME_STATE (ROSY_SPHERE_ON_SHIP, 1)
+ /* The Rosy Sphere is aboard the flagship, i.e. It has been
+ * acquired from the Druuge, but not yet inserted in the broken
+ * Ultron. cf. ROSY_SPHERE */
+
+ ADD_GAME_STATE (CHMMR_HOME_VISITS, 3)
+ ADD_GAME_STATE (CHMMR_EMERGING, 1)
+ ADD_GAME_STATE (CHMMR_UNLEASHED, 1)
+ ADD_GAME_STATE (CHMMR_BOMB_STATE, 2)
+ /* 0 - Nothing is known about the Precursor Bomb.
+ * 1 - The captain knows from the Chmmr that some extremely
+ * powerful weapon is needed to destroy the Sa-Matra.
+ * 2 - Installation of the precursor bomb has started.
+ * 3 - Left the starbase after installation of the Precursor bomb.
+ */
+
+ ADD_GAME_STATE (DRUUGE_DISCLAIMER, 1)
+
+ ADD_GAME_STATE (YEHAT_VISITS, 3)
+ ADD_GAME_STATE (YEHAT_REBEL_VISITS, 3)
+ ADD_GAME_STATE (YEHAT_HOME_VISITS, 3)
+ ADD_GAME_STATE (YEHAT_CIVIL_WAR, 1)
+ ADD_GAME_STATE (YEHAT_ABSORBED_PKUNK, 1)
+ ADD_GAME_STATE (YEHAT_SHIP_MONTH, 4)
+ ADD_GAME_STATE (YEHAT_SHIP_DAY, 5)
+ ADD_GAME_STATE (YEHAT_SHIP_YEAR, 5)
+
+ ADD_GAME_STATE (CLEAR_SPINDLE, 1)
+ ADD_GAME_STATE (PKUNK_VISITS, 3)
+ ADD_GAME_STATE (PKUNK_HOME_VISITS, 3)
+ ADD_GAME_STATE (PKUNK_SHIP_MONTH, 4)
+ /* The month in PKUNK_SHIP_YEAR that new ships are available
+ * from the Pkunk. */
+ ADD_GAME_STATE (PKUNK_SHIP_DAY, 5)
+ /* The day of the month in PKUNK_SHIP_MONTH in PKUNK_SHIP_YEAR
+ * that new ships are available. */
+ ADD_GAME_STATE (PKUNK_SHIP_YEAR, 5)
+ /* The year that new ships are available from the Pkunk
+ * (stored as an offset from the year the game starts). */
+ ADD_GAME_STATE (PKUNK_MISSION, 3)
+
+ ADD_GAME_STATE (SUPOX_VISITS, 3)
+ ADD_GAME_STATE (SUPOX_HOME_VISITS, 3)
+
+ ADD_GAME_STATE (THRADD_VISITS, 3)
+ ADD_GAME_STATE (THRADD_HOME_VISITS, 3)
+ ADD_GAME_STATE (HELIX_VISITS, 3)
+ ADD_GAME_STATE (HELIX_UNPROTECTED, 1)
+ ADD_GAME_STATE (THRADD_CULTURE, 2)
+ ADD_GAME_STATE (THRADD_MISSION, 3)
+ /* 0 if the Thraddash fleet hasn't left the Thraddash home world.
+ * 1 if the Thraddash are heading towards Kohr-Ah territory.
+ * 2 if the Thraddash are fighting the Kohr-Ah.
+ * 3 if the Thraddash are returning from Kohr-Ah territory.
+ * 4 if the Thraddash fleet is back at the Thraddash home world.
+ */
+
+ ADD_GAME_STATE (DRUUGE_VISITS, 3)
+ ADD_GAME_STATE (DRUUGE_HOME_VISITS, 3)
+ ADD_GAME_STATE (ROSY_SPHERE, 1)
+ /* The play has or has had the Rosy Sphere.
+ * cf. ROSY_SHERE_ON_SHIP */
+ ADD_GAME_STATE (SCANNED_MAIDENS, 1)
+ ADD_GAME_STATE (SCANNED_FRAGMENTS, 1)
+ ADD_GAME_STATE (SCANNED_CASTER, 1)
+ ADD_GAME_STATE (SCANNED_SPAWNER, 1)
+ ADD_GAME_STATE (SCANNED_ULTRON, 1)
+
+ ADD_GAME_STATE (ZOQFOT_INFO, 2)
+ ADD_GAME_STATE (ZOQFOT_HOSTILE, 1)
+ ADD_GAME_STATE (ZOQFOT_HOME_VISITS, 3)
+ ADD_GAME_STATE (MET_ZOQFOT, 1)
+ ADD_GAME_STATE (ZOQFOT_DISTRESS, 2)
+ /* 0 if the Zoq-Fot-Pik aren't in distress
+ * 1 if the Zoq-Fot-Pik are under attack by the Kohr-Ah
+ * 2 if the Zoq-Fot-Pik have been destroyed because of this
+ * attack (not by the Kohr-Ah final victory cleansing)
+ */
+
+ ADD_GAME_STATE (EGG_CASE1_ON_SHIP, 1)
+ ADD_GAME_STATE (EGG_CASE2_ON_SHIP, 1)
+ ADD_GAME_STATE (MYCON_SUN_VISITS, 3)
+ ADD_GAME_STATE (ORZ_HOME_VISITS, 3)
+
+ ADD_GAME_STATE (MELNORME_FUEL_PROCEDURE, 1)
+ ADD_GAME_STATE (MELNORME_TECH_PROCEDURE, 1)
+ ADD_GAME_STATE (MELNORME_INFO_PROCEDURE, 1)
+
+ ADD_GAME_STATE (MELNORME_TECH_STACK, 4)
+ /* MELNORME_TECH_STACK is now unused */
+ ADD_GAME_STATE (MELNORME_EVENTS_INFO_STACK, 5)
+ ADD_GAME_STATE (MELNORME_ALIEN_INFO_STACK, 5)
+ ADD_GAME_STATE (MELNORME_HISTORY_INFO_STACK, 5)
+
+ ADD_GAME_STATE (RAINBOW_WORLD0, 8)
+ /* Low byte of a bit array, one bit per rainbow world.
+ * Each bit is set if the rainbow world has been visited.
+ * The lowest bit is for the first star in the star_array
+ * with RAINBOW_DEFINED, and so on.
+ */
+ ADD_GAME_STATE (RAINBOW_WORLD1, 2)
+ /* High 2 bits of the bit array of which RAINBOW_WORLD0
+ * is the low byte.
+ */
+ ADD_GAME_STATE (MELNORME_RAINBOW_COUNT, 4)
+ /* The number of rainbow world locations sold to the Melnorme. */
+
+ ADD_GAME_STATE (USED_BROADCASTER, 1)
+ ADD_GAME_STATE (BROADCASTER_RESPONSE, 1)
+
+ ADD_GAME_STATE (IMPROVED_LANDER_SPEED, 1)
+ ADD_GAME_STATE (IMPROVED_LANDER_CARGO, 1)
+ ADD_GAME_STATE (IMPROVED_LANDER_SHOT, 1)
+
+ ADD_GAME_STATE (MET_ORZ_BEFORE, 1)
+ ADD_GAME_STATE (YEHAT_REBEL_TOLD_PKUNK, 1)
+ ADD_GAME_STATE (PLAYER_HAD_SEX, 1)
+ ADD_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP, 1)
+
+ ADD_GAME_STATE (LIGHT_MINERAL_LOAD, 3)
+ ADD_GAME_STATE (MEDIUM_MINERAL_LOAD, 3)
+ ADD_GAME_STATE (HEAVY_MINERAL_LOAD, 3)
+
+ ADD_GAME_STATE (STARBASE_BULLETS0, 8)
+ ADD_GAME_STATE (STARBASE_BULLETS1, 8)
+ ADD_GAME_STATE (STARBASE_BULLETS2, 8)
+ ADD_GAME_STATE (STARBASE_BULLETS3, 8)
+
+ ADD_GAME_STATE (STARBASE_MONTH, 4)
+ ADD_GAME_STATE (STARBASE_DAY, 5)
+
+ ADD_GAME_STATE (CREW_SOLD_TO_DRUUGE0, 8)
+ ADD_GAME_STATE (CREW_PURCHASED0, 8)
+ ADD_GAME_STATE (CREW_PURCHASED1, 8)
+
+ ADD_GAME_STATE (URQUAN_PROTECTING_SAMATRA, 1)
+
+#define THRADDASH_BODY_THRESHOLD 25
+ ADD_GAME_STATE (THRADDASH_BODY_COUNT, 5)
+
+ ADD_GAME_STATE (UTWIG_SUPOX_MISSION, 3)
+ /* 0 if the Utwig and Supox fleet haven't left their home world.
+ * 1 if the U&S are on their way towards the Kohr-Ah
+ * 2 if the U&S are fighting the Kohr-Ah (first 80 days)
+ * 3 does not occur
+ * 4 if the U&S are fighting the Kohr-Ah (second 80 days)
+ * 5 if the U&S are returning home.
+ * 6 if the U&S are back at their home world.
+ */
+ ADD_GAME_STATE (SPATHI_INFO, 3)
+
+ ADD_GAME_STATE (ILWRATH_INFO, 2)
+ ADD_GAME_STATE (ILWRATH_GODS_SPOKEN, 4)
+ ADD_GAME_STATE (ILWRATH_WORSHIP, 2)
+ ADD_GAME_STATE (ILWRATH_FIGHT_THRADDASH, 1)
+
+ ADD_GAME_STATE (READY_TO_CONFUSE_URQUAN, 1)
+ ADD_GAME_STATE (URQUAN_HYPNO_VISITS, 1)
+ ADD_GAME_STATE (MENTIONED_PET_COMPULSION, 1)
+ ADD_GAME_STATE (URQUAN_INFO, 2)
+ ADD_GAME_STATE (KNOW_URQUAN_STORY, 2)
+
+ ADD_GAME_STATE (MYCON_INFO, 4)
+ ADD_GAME_STATE (MYCON_RAMBLE, 5)
+ ADD_GAME_STATE (KNOW_ABOUT_SHATTERED, 2)
+ /* 0 if the player doesn't known about shattered worlds
+ * 1 if the player has encountered a shattered world
+ * 2 if the player knows that shatterred worlds are caused
+ * by Mycon deep children.
+ * 3 if the player has told the Syreen that Mycon Deep Children
+ * cause shattered worlds. Proof doesn't have to be presented
+ * yet at this time.
+ */
+ ADD_GAME_STATE (MYCON_INSULTS, 3)
+ ADD_GAME_STATE (MYCON_KNOW_AMBUSH, 1)
+ /* Set to 1 when the Mycon have been butchered at Organon,
+ * just before the remaining Mycon head back home.
+ */
+
+ ADD_GAME_STATE (SYREEN_INFO, 2)
+ ADD_GAME_STATE (KNOW_SYREEN_WORLD_SHATTERED, 1)
+ ADD_GAME_STATE (SYREEN_KNOW_ABOUT_MYCON, 1)
+
+ ADD_GAME_STATE (TALKING_PET_INFO, 3)
+ ADD_GAME_STATE (TALKING_PET_SUGGESTIONS, 3)
+ ADD_GAME_STATE (LEARNED_TALKING_PET, 1)
+ ADD_GAME_STATE (DNYARRI_LIED, 1)
+ /* Set when the Talking Pet tells you his version of their
+ * race's history with the Ur-Quan.
+ * Cleared once you confront him about this lie.
+ */
+ ADD_GAME_STATE (SHIP_TO_COMPEL, 1)
+
+ ADD_GAME_STATE (ORZ_GENERAL_INFO, 2)
+ ADD_GAME_STATE (ORZ_PERSONAL_INFO, 3)
+ ADD_GAME_STATE (ORZ_ANDRO_STATE, 2)
+ ADD_GAME_STATE (REFUSED_ORZ_ALLIANCE, 1)
+
+ ADD_GAME_STATE (PKUNK_MANNER, 2)
+ /* 0 not met the Pkunk
+ * 1 fought the Pkunk, but relations are still salvagable.
+ * 2 hostile relations with the Pkunk, no way back.
+ * 3 friendly relations with the Pkunk
+ */
+ ADD_GAME_STATE (PKUNK_ON_THE_MOVE, 1)
+ ADD_GAME_STATE (PKUNK_FLEET, 2)
+ ADD_GAME_STATE (PKUNK_MIGRATE, 2)
+ ADD_GAME_STATE (PKUNK_RETURN, 1)
+ ADD_GAME_STATE (PKUNK_WORRY, 2)
+ ADD_GAME_STATE (PKUNK_INFO, 3)
+ ADD_GAME_STATE (PKUNK_WAR, 2)
+ ADD_GAME_STATE (PKUNK_FORTUNE, 3)
+ ADD_GAME_STATE (PKUNK_MIGRATE_VISITS, 3)
+ ADD_GAME_STATE (PKUNK_REASONS, 4)
+ ADD_GAME_STATE (PKUNK_SWITCH, 1)
+ ADD_GAME_STATE (PKUNK_SENSE_VICTOR, 1)
+
+ ADD_GAME_STATE (KOHR_AH_REASONS, 2)
+ ADD_GAME_STATE (KOHR_AH_PLEAD, 2)
+ ADD_GAME_STATE (KOHR_AH_INFO, 2)
+ ADD_GAME_STATE (KNOW_KOHR_AH_STORY, 2)
+ ADD_GAME_STATE (KOHR_AH_SENSES_EVIL, 1)
+ ADD_GAME_STATE (URQUAN_SENSES_EVIL, 1)
+
+ ADD_GAME_STATE (SLYLANDRO_PROBE_VISITS, 3)
+ ADD_GAME_STATE (SLYLANDRO_PROBE_THREAT, 2)
+ ADD_GAME_STATE (SLYLANDRO_PROBE_WRONG, 2)
+ ADD_GAME_STATE (SLYLANDRO_PROBE_ID, 2)
+ ADD_GAME_STATE (SLYLANDRO_PROBE_INFO, 2)
+ ADD_GAME_STATE (SLYLANDRO_PROBE_EXIT, 2)
+
+ ADD_GAME_STATE (UMGAH_HOSTILE, 1)
+ ADD_GAME_STATE (UMGAH_EVIL_BLOBBIES, 1)
+ ADD_GAME_STATE (UMGAH_MENTIONED_TRICKS, 2)
+
+ ADD_GAME_STATE (BOMB_CARRIER, 1)
+ /* 0 when the flagship is not in battle, or it doesn't have the
+ * enhanced precursor bomb installed.
+ * 1 when the flagship is in battle and the bomb is installed.
+ * This determines whether you can flee (if the warp escape unit
+ * is installed at all), and whether taking the ship into the
+ * Sa-Matra defense structure will trigger the end of the game.
+ */
+
+ ADD_GAME_STATE (THRADD_MANNER, 1)
+ ADD_GAME_STATE (THRADD_INTRO, 2)
+ ADD_GAME_STATE (THRADD_DEMEANOR, 3)
+ ADD_GAME_STATE (THRADD_INFO, 2)
+ ADD_GAME_STATE (THRADD_BODY_LEVEL, 2)
+ ADD_GAME_STATE (THRADD_MISSION_VISITS, 1)
+ ADD_GAME_STATE (THRADD_STACK_1, 3)
+ ADD_GAME_STATE (THRADD_HOSTILE_STACK_2, 1)
+ ADD_GAME_STATE (THRADD_HOSTILE_STACK_3, 1)
+ ADD_GAME_STATE (THRADD_HOSTILE_STACK_4, 1)
+ ADD_GAME_STATE (THRADD_HOSTILE_STACK_5, 1)
+
+ ADD_GAME_STATE (CHMMR_STACK, 2)
+
+ ADD_GAME_STATE (ARILOU_MANNER, 2)
+ ADD_GAME_STATE (NO_PORTAL_VISITS, 1)
+ ADD_GAME_STATE (ARILOU_STACK_1, 2)
+ ADD_GAME_STATE (ARILOU_STACK_2, 1)
+ ADD_GAME_STATE (ARILOU_STACK_3, 2)
+ ADD_GAME_STATE (ARILOU_STACK_4, 1)
+ ADD_GAME_STATE (ARILOU_STACK_5, 2)
+ ADD_GAME_STATE (ARILOU_INFO, 2)
+ ADD_GAME_STATE (ARILOU_HINTS, 2)
+
+ ADD_GAME_STATE (DRUUGE_MANNER, 1)
+ ADD_GAME_STATE (DRUUGE_SPACE_INFO, 2)
+ ADD_GAME_STATE (DRUUGE_HOME_INFO, 2)
+ ADD_GAME_STATE (DRUUGE_SALVAGE, 1)
+ ADD_GAME_STATE (KNOW_DRUUGE_SLAVERS, 2)
+ ADD_GAME_STATE (FRAGMENTS_BOUGHT, 2)
+
+ ADD_GAME_STATE (ZEX_STACK_1, 2)
+ ADD_GAME_STATE (ZEX_STACK_2, 2)
+ ADD_GAME_STATE (ZEX_STACK_3, 2)
+
+ ADD_GAME_STATE (VUX_INFO, 2)
+ ADD_GAME_STATE (VUX_STACK_1, 4)
+ ADD_GAME_STATE (VUX_STACK_2, 2)
+ ADD_GAME_STATE (VUX_STACK_3, 2)
+ ADD_GAME_STATE (VUX_STACK_4, 2)
+
+ ADD_GAME_STATE (SHOFIXTI_STACK4, 2)
+
+ ADD_GAME_STATE (YEHAT_REBEL_INFO, 3)
+ ADD_GAME_STATE (YEHAT_ROYALIST_INFO, 1)
+ ADD_GAME_STATE (YEHAT_ROYALIST_TOLD_PKUNK, 1)
+ ADD_GAME_STATE (NO_YEHAT_ALLY_HOME, 1)
+ ADD_GAME_STATE (NO_YEHAT_HELP_HOME, 1)
+ ADD_GAME_STATE (NO_YEHAT_INFO, 1)
+ ADD_GAME_STATE (NO_YEHAT_ALLY_SPACE, 2)
+ ADD_GAME_STATE (NO_YEHAT_HELP_SPACE, 2)
+
+ ADD_GAME_STATE (ZOQFOT_KNOW_MASK, 4)
+
+ ADD_GAME_STATE (SUPOX_HOSTILE, 1)
+ ADD_GAME_STATE (SUPOX_INFO, 1)
+ ADD_GAME_STATE (SUPOX_WAR_NEWS, 2)
+ ADD_GAME_STATE (SUPOX_ULTRON_HELP, 1)
+ ADD_GAME_STATE (SUPOX_STACK1, 3)
+ ADD_GAME_STATE (SUPOX_STACK2, 2)
+
+ ADD_GAME_STATE (UTWIG_HOSTILE, 1)
+ ADD_GAME_STATE (UTWIG_INFO, 1)
+ ADD_GAME_STATE (UTWIG_WAR_NEWS, 2)
+ ADD_GAME_STATE (UTWIG_STACK1, 3)
+ ADD_GAME_STATE (UTWIG_STACK2, 2)
+ ADD_GAME_STATE (BOMB_INFO, 1)
+ ADD_GAME_STATE (BOMB_STACK1, 2)
+ ADD_GAME_STATE (BOMB_STACK2, 2)
+
+ ADD_GAME_STATE (SLYLANDRO_KNOW_BROKEN, 1)
+ ADD_GAME_STATE (PLAYER_KNOWS_PROBE, 1)
+ ADD_GAME_STATE (PLAYER_KNOWS_PROGRAM, 1)
+ ADD_GAME_STATE (PLAYER_KNOWS_EFFECTS, 1)
+ ADD_GAME_STATE (PLAYER_KNOWS_PRIORITY, 1)
+ ADD_GAME_STATE (SLYLANDRO_STACK1, 3)
+ ADD_GAME_STATE (SLYLANDRO_STACK2, 1)
+ ADD_GAME_STATE (SLYLANDRO_STACK3, 2)
+ ADD_GAME_STATE (SLYLANDRO_STACK4, 2)
+ ADD_GAME_STATE (SLYLANDRO_STACK5, 1)
+ ADD_GAME_STATE (SLYLANDRO_STACK6, 1)
+ ADD_GAME_STATE (SLYLANDRO_STACK7, 2)
+ ADD_GAME_STATE (SLYLANDRO_STACK8, 2)
+ ADD_GAME_STATE (SLYLANDRO_STACK9, 2)
+ ADD_GAME_STATE (SLYLANDRO_KNOW_EARTH, 1)
+ ADD_GAME_STATE (SLYLANDRO_KNOW_EXPLORE, 1)
+ ADD_GAME_STATE (SLYLANDRO_KNOW_GATHER, 1)
+ ADD_GAME_STATE (SLYLANDRO_KNOW_URQUAN, 2)
+ ADD_GAME_STATE (RECALL_VISITS, 2)
+
+ ADD_GAME_STATE (SLYLANDRO_MULTIPLIER, 3)
+ ADD_GAME_STATE (KNOW_SPATHI_QUEST, 1)
+ ADD_GAME_STATE (KNOW_SPATHI_EVIL, 1)
+
+ ADD_GAME_STATE (BATTLE_PLANET, 8)
+ ADD_GAME_STATE (ESCAPE_COUNTER, 8)
+
+ ADD_GAME_STATE (CREW_SOLD_TO_DRUUGE1, 8)
+ ADD_GAME_STATE (PKUNK_DONE_WAR, 1)
+
+ ADD_GAME_STATE (SYREEN_STACK0, 2)
+ ADD_GAME_STATE (SYREEN_STACK1, 2)
+ ADD_GAME_STATE (SYREEN_STACK2, 2)
+
+ ADD_GAME_STATE (REFUSED_ULTRON_AT_BOMB, 1)
+ ADD_GAME_STATE (NO_TRICK_AT_SUN, 1)
+
+ ADD_GAME_STATE (SPATHI_STACK0, 2)
+ ADD_GAME_STATE (SPATHI_STACK1, 1)
+ ADD_GAME_STATE (SPATHI_STACK2, 1)
+
+ ADD_GAME_STATE (ORZ_STACK0, 1)
+ ADD_GAME_STATE (ORZ_STACK1, 1)
+
+/* These state bits are actually offsets into defgrp.dat. They really
+ * shouldn't be part of the serialized Game State array! --MCM */
+ ADD_GAME_STATE (SHOFIXTI_GRPOFFS0, 8)
+ ADD_GAME_STATE (SHOFIXTI_GRPOFFS1, 8)
+ ADD_GAME_STATE (SHOFIXTI_GRPOFFS2, 8)
+ ADD_GAME_STATE (SHOFIXTI_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (ZOQFOT_GRPOFFS0, 8)
+ ADD_GAME_STATE (ZOQFOT_GRPOFFS1, 8)
+ ADD_GAME_STATE (ZOQFOT_GRPOFFS2, 8)
+ ADD_GAME_STATE (ZOQFOT_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME0_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME0_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME0_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME0_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME1_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME1_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME1_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME1_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME2_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME2_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME2_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME2_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME3_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME3_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME3_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME3_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME4_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME4_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME4_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME4_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME5_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME5_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME5_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME5_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME6_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME6_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME6_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME6_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME7_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME7_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME7_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME7_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (MELNORME8_GRPOFFS0, 8)
+ ADD_GAME_STATE (MELNORME8_GRPOFFS1, 8)
+ ADD_GAME_STATE (MELNORME8_GRPOFFS2, 8)
+ ADD_GAME_STATE (MELNORME8_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS0, 8)
+ ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS1, 8)
+ ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS2, 8)
+ ADD_GAME_STATE (URQUAN_PROBE_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (COLONY_GRPOFFS0, 8)
+ ADD_GAME_STATE (COLONY_GRPOFFS1, 8)
+ ADD_GAME_STATE (COLONY_GRPOFFS2, 8)
+ ADD_GAME_STATE (COLONY_GRPOFFS3, 8)
+
+ ADD_GAME_STATE (SAMATRA_GRPOFFS0, 8)
+ ADD_GAME_STATE (SAMATRA_GRPOFFS1, 8)
+ ADD_GAME_STATE (SAMATRA_GRPOFFS2, 8)
+ ADD_GAME_STATE (SAMATRA_GRPOFFS3, 8)
+
+END_GAME_STATE
+
+// Values for GAME_STATE.glob_flags:
+#define COMBAT_SPEED_SHIFT 6
+#define COMBAT_SPEED_MASK (((1 << 2) - 1) << COMBAT_SPEED_SHIFT)
+#define NUM_COMBAT_SPEEDS 4
+#define MUSIC_DISABLED (1 << 3)
+#define SOUND_DISABLED (1 << 4)
+#define CYBORG_ENABLED (1 << 5)
+
+enum
+{
+ SUPER_MELEE = 0, /* Is also used while in the main menu */
+ IN_LAST_BATTLE,
+ IN_ENCOUNTER,
+ IN_HYPERSPACE /* in HyperSpace or QuasiSpace */,
+ IN_INTERPLANETARY,
+ WON_LAST_BATTLE,
+
+ /* The following three are only used when displaying save game
+ * summaries */
+ IN_QUASISPACE,
+ IN_PLANET_ORBIT,
+ IN_STARBASE,
+
+ CHECK_PAUSE = MAKE_WORD (0, (1 << 0)),
+ IN_BATTLE = MAKE_WORD (0, (1 << 1)),
+ /* Is also set while in HyperSpace/QuasiSpace */
+ START_ENCOUNTER = MAKE_WORD (0, (1 << 2)),
+ START_INTERPLANETARY = MAKE_WORD (0, (1 << 3)),
+ CHECK_LOAD = MAKE_WORD (0, (1 << 4)),
+ CHECK_RESTART = MAKE_WORD (0, (1 << 5)),
+ CHECK_ABORT = MAKE_WORD (0, (1 << 6)),
+};
+typedef UWORD ACTIVITY;
+
+typedef struct
+{
+ BYTE glob_flags;
+ // See above for the meaning of the bits.
+
+ BYTE CrewCost, FuelCost;
+ BYTE ModuleCost[NUM_MODULES];
+ BYTE ElementWorth[NUM_ELEMENT_CATEGORIES];
+
+ PRIMITIVE *DisplayArray;
+ ACTIVITY CurrentActivity;
+
+ CLOCK_STATE GameClock;
+
+ POINT autopilot;
+ POINT ip_location;
+ STAMP ShipStamp;
+ UWORD ShipFacing;
+ BYTE ip_planet;
+ BYTE in_orbit;
+ VELOCITY_DESC velocity;
+
+ DWORD BattleGroupRef;
+ QUEUE avail_race_q;
+ /* List of all the races in the game with information
+ * about their ships, and what player knows about their
+ * fleet, center of SoI, status, etc.
+ * queue element is FLEET_INFO */
+ QUEUE npc_built_ship_q;
+ /* Non-player-character list of ships (during encounter)
+ * queue element is SHIP_FRAGMENT */
+ QUEUE ip_group_q;
+ /* List of groups present in solarsys (during IP);
+ * queue element is IP_GROUP */
+ QUEUE encounter_q;
+ /* List of HyperSpace encounters (black globes);
+ * queue element is ENCOUNTER */
+ QUEUE built_ship_q;
+ /* List of SIS escort ships;
+ * queue element is SHIP_FRAGMENT */
+
+ BYTE GameState[(NUM_GAME_STATE_BITS + 7) >> 3];
+} GAME_STATE;
+
+typedef struct
+{
+ SIS_STATE SIS_state;
+ GAME_STATE Game_state;
+} GLOBDATA;
+
+extern GLOBDATA GlobData;
+#define GLOBAL(f) GlobData.Game_state.f
+#define GLOBAL_SIS(f) GlobData.SIS_state.f
+
+#define MAX_ENCOUNTERS 16
+#define MAX_BATTLE_GROUPS 32
+
+/* DEFGRP enumeration. These identify scripted TrueSpace encounters
+ * more consistently than offsets into the DEFGRPINFO_FILE state
+ * file. */
+enum {
+ DEFGRP_NONE,
+ DEFGRP_SHOFIXTI,
+ DEFGRP_ZOQFOT,
+ DEFGRP_MELNORME0,
+ DEFGRP_MELNORME1,
+ DEFGRP_MELNORME2,
+ DEFGRP_MELNORME3,
+ DEFGRP_MELNORME4,
+ DEFGRP_MELNORME5,
+ DEFGRP_MELNORME6,
+ DEFGRP_MELNORME7,
+ DEFGRP_MELNORME8,
+ DEFGRP_URQUAN_PROBE,
+ DEFGRP_COLONY,
+ DEFGRP_SAMATRA,
+ NUM_DEFGRPS
+};
+
+//#define STATE_DEBUG
+
+extern BYTE getGameState (BYTE *state, int startBit, int endBit);
+extern void setGameState (BYTE *state, int startBit, int endBit, BYTE val
+#ifdef STATE_DEBUG
+ , const char *name
+#endif
+ );
+extern void copyGameState (BYTE *dest, DWORD target, BYTE *src, DWORD begin, DWORD end);
+
+#define GET_GAME_STATE(SName) getGameState (GLOBAL(GameState), (SName), (END_##SName))
+#ifdef STATE_DEBUG
+# define SET_GAME_STATE(SName, val) \
+ setGameState (GLOBAL(GameState), (SName), (END_##SName), (val), #SName)
+#else
+# define SET_GAME_STATE(SName, val) \
+ setGameState (GLOBAL(GameState), (SName), (END_##SName), (val))
+#endif
+
+extern DWORD getGameState32 (BYTE *state, int startBit);
+extern void setGameState32 (BYTE *state, int startBit, DWORD val
+#ifdef STATE_DEBUG
+ , const char *name
+#endif
+ );
+
+#define GET_GAME_STATE_32(SName) getGameState32 (GLOBAL(GameState), (SName))
+#ifdef STATE_DEBUG
+# define SET_GAME_STATE_32(SName, val) \
+ setGameState32 (GLOBAL(GameState), (SName), (val), #SName)
+#else
+# define SET_GAME_STATE_32(SName, val) \
+ setGameState32 (GLOBAL(GameState), (SName), (val))
+#endif
+
+
+extern CONTEXT RadarContext;
+
+extern void FreeSC2Data (void);
+extern BOOLEAN LoadSC2Data (void);
+
+extern void InitGlobData (void);
+
+BOOLEAN inFullGame (void);
+BOOLEAN inSuperMelee (void);
+//BOOLEAN inBattle (void);
+//BOOLEAN inInterPlanetary (void);
+//BOOLEAN inSolarSystem (void);
+//BOOLEAN inOrbit (void);
+BOOLEAN inHQSpace (void);
+BOOLEAN inHyperSpace (void);
+BOOLEAN inQuasiSpace (void);
+
+extern BOOLEAN InitGameStructures (void);
+extern void UninitGameStructures (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_GLOBDATA_H_ */
diff --git a/src/uqm/gravity.c b/src/uqm/gravity.c
new file mode 100644
index 0000000..5a7899c
--- /dev/null
+++ b/src/uqm/gravity.c
@@ -0,0 +1,200 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "collide.h"
+#include "races.h"
+#include "units.h"
+#include "libs/log.h"
+
+//#define DEBUG_GRAVITY
+
+BOOLEAN
+CalculateGravity (ELEMENT *ElementPtr)
+{
+ BOOLEAN retval, HasGravity;
+ HELEMENT hTestElement, hSuccElement;
+
+ retval = FALSE;
+ HasGravity = (BOOLEAN)(CollidingElement (ElementPtr)
+ && GRAVITY_MASS (ElementPtr->mass_points + 1));
+ for (hTestElement = GetHeadElement ();
+ hTestElement != 0; hTestElement = hSuccElement)
+ {
+ BOOLEAN TestHasGravity;
+ ELEMENT *TestElementPtr;
+
+ LockElement (hTestElement, &TestElementPtr);
+ if (TestElementPtr != ElementPtr
+ && CollidingElement (TestElementPtr)
+ && (TestHasGravity =
+ GRAVITY_MASS (TestElementPtr->mass_points + 1)) != HasGravity)
+ {
+ COUNT abs_dx, abs_dy;
+ SIZE dx, dy;
+
+ if (!(ElementPtr->state_flags & PRE_PROCESS))
+ {
+ dx = ElementPtr->current.location.x
+ - TestElementPtr->current.location.x;
+ dy = ElementPtr->current.location.y
+ - TestElementPtr->current.location.y;
+ }
+ else
+ {
+ dx = ElementPtr->next.location.x
+ - TestElementPtr->next.location.x;
+ dy = ElementPtr->next.location.y
+ - TestElementPtr->next.location.y;
+ }
+#ifdef DEBUG_GRAVITY
+ if (TestElementPtr->state_flags & PLAYER_SHIP)
+ {
+ log_add (log_Debug, "CalculateGravity:");
+ log_add (log_Debug, "\tdx = %d, dy = %d", dx, dy);
+ }
+#endif /* DEBUG_GRAVITY */
+ dx = WRAP_DELTA_X (dx);
+ dy = WRAP_DELTA_Y (dy);
+#ifdef DEBUG_GRAVITY
+ if (TestElementPtr->state_flags & PLAYER_SHIP)
+ log_add (log_Debug, "\twrap_dx = %d, wrap_dy = %d", dx, dy);
+#endif /* DEBUG_GRAVITY */
+ abs_dx = dx >= 0 ? dx : -dx;
+ abs_dy = dy >= 0 ? dy : -dy;
+ abs_dx = WORLD_TO_DISPLAY (abs_dx);
+ abs_dy = WORLD_TO_DISPLAY (abs_dy);
+#ifdef DEBUG_GRAVITY
+ if (TestElementPtr->state_flags & PLAYER_SHIP)
+ log_add (log_Debug, "\tdisplay_dx = %d, display_dy = %d",
+ abs_dx, abs_dy);
+#endif /* DEBUG_GRAVITY */
+ if (abs_dx <= GRAVITY_THRESHOLD
+ && abs_dy <= GRAVITY_THRESHOLD)
+ {
+ DWORD dist_squared;
+
+ dist_squared = (DWORD)(abs_dx * abs_dx)
+ + (DWORD)(abs_dy * abs_dy);
+ if (dist_squared <= (DWORD)(GRAVITY_THRESHOLD
+ * GRAVITY_THRESHOLD))
+ {
+#ifdef NEVER
+ COUNT magnitude;
+
+#define DIFUSE_GRAVITY 175
+ dist_squared += (DWORD)abs_dx * (DIFUSE_GRAVITY << 1)
+ + (DWORD)abs_dy * (DIFUSE_GRAVITY << 1)
+ + ((DWORD)(DIFUSE_GRAVITY * DIFUSE_GRAVITY) << 1);
+ if ((magnitude = (COUNT)((DWORD)(GRAVITY_THRESHOLD
+ * GRAVITY_THRESHOLD) / dist_squared)) == 0)
+ magnitude = 1;
+
+#define MAX_MAGNITUDE 6
+ else if (magnitude > MAX_MAGNITUDE)
+ magnitude = MAX_MAGNITUDE;
+ log_add (log_Debug, "magnitude = %u", magnitude);
+#endif /* NEVER */
+
+#ifdef DEBUG_GRAVITY
+ if (TestElementPtr->state_flags & PLAYER_SHIP)
+ log_add (log_Debug, "dist_squared = %lu", dist_squared);
+#endif /* DEBUG_GRAVITY */
+ if (TestHasGravity)
+ {
+ retval = TRUE;
+ UnlockElement (hTestElement);
+ break;
+ }
+ else
+ {
+ COUNT angle;
+
+ angle = ARCTAN (dx, dy);
+ DeltaVelocityComponents (&TestElementPtr->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (1)),
+ SINE (angle, WORLD_TO_VELOCITY (1)));
+ if (TestElementPtr->state_flags & PLAYER_SHIP)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (TestElementPtr, &StarShipPtr);
+ StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED;
+ StarShipPtr->cur_status_flags |= SHIP_IN_GRAVITY_WELL;
+ }
+ }
+ }
+ }
+ }
+
+ hSuccElement = GetSuccElement (TestElementPtr);
+ UnlockElement (hTestElement);
+ }
+
+ return (retval);
+}
+
+BOOLEAN
+TimeSpaceMatterConflict (ELEMENT *ElementPtr)
+{
+ HELEMENT hTestElement, hSuccElement;
+ INTERSECT_CONTROL ElementControl;
+
+ ElementControl.IntersectStamp.origin.x =
+ WORLD_TO_DISPLAY (ElementPtr->current.location.x);
+ ElementControl.IntersectStamp.origin.y =
+ WORLD_TO_DISPLAY (ElementPtr->current.location.y);
+ ElementControl.IntersectStamp.frame =
+ SetEquFrameIndex (ElementPtr->current.image.farray[0],
+ ElementPtr->current.image.frame);
+ ElementControl.EndPoint = ElementControl.IntersectStamp.origin;
+ for (hTestElement = GetHeadElement ();
+ hTestElement != 0; hTestElement = hSuccElement)
+ {
+ ELEMENT *TestElementPtr;
+
+ LockElement (hTestElement, &TestElementPtr);
+ hSuccElement = GetSuccElement (TestElementPtr);
+ if (TestElementPtr != ElementPtr
+ && (CollidingElement (TestElementPtr)
+ /* ship in transition */
+ || (TestElementPtr->state_flags & PLAYER_SHIP)))
+ {
+ INTERSECT_CONTROL TestElementControl;
+
+ TestElementControl.IntersectStamp.origin.x =
+ WORLD_TO_DISPLAY (TestElementPtr->current.location.x);
+ TestElementControl.IntersectStamp.origin.y =
+ WORLD_TO_DISPLAY (TestElementPtr->current.location.y);
+ TestElementControl.IntersectStamp.frame =
+ SetEquFrameIndex (TestElementPtr->current.image.farray[0],
+ TestElementPtr->current.image.frame);
+ TestElementControl.EndPoint = TestElementControl.IntersectStamp.origin;
+ if (DrawablesIntersect (&ElementControl,
+ &TestElementControl, MAX_TIME_VALUE))
+ {
+ UnlockElement (hTestElement);
+
+ break;
+ }
+ }
+ UnlockElement (hTestElement);
+ }
+
+ return (hTestElement != 0 ? TRUE : FALSE);
+}
+
diff --git a/src/uqm/grpinfo.c b/src/uqm/grpinfo.c
new file mode 100644
index 0000000..43f1b22
--- /dev/null
+++ b/src/uqm/grpinfo.c
@@ -0,0 +1,865 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "build.h"
+#include "starmap.h"
+#include "gendef.h"
+#include "libs/file.h"
+#include "globdata.h"
+#include "intel.h"
+#include "state.h"
+#include "grpintrn.h"
+
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+static BYTE LastEncGroup;
+ // Last encountered group, saved into state files
+
+void
+ReadGroupHeader (GAME_STATE_FILE *fp, GROUP_HEADER *pGH)
+{
+ sread_8 (fp, &pGH->NumGroups);
+ sread_8 (fp, &pGH->day_index);
+ sread_8 (fp, &pGH->month_index);
+ sread_8 (fp, NULL); /* padding */
+ sread_16 (fp, &pGH->star_index);
+ sread_16 (fp, &pGH->year_index);
+ sread_a32 (fp, pGH->GroupOffset, NUM_SAVED_BATTLE_GROUPS + 1);
+}
+
+void
+WriteGroupHeader (GAME_STATE_FILE *fp, const GROUP_HEADER *pGH)
+{
+ swrite_8 (fp, pGH->NumGroups);
+ swrite_8 (fp, pGH->day_index);
+ swrite_8 (fp, pGH->month_index);
+ swrite_8 (fp, 0); /* padding */
+ swrite_16 (fp, pGH->star_index);
+ swrite_16 (fp, pGH->year_index);
+ swrite_a32 (fp, pGH->GroupOffset, NUM_SAVED_BATTLE_GROUPS + 1);
+}
+
+void
+ReadShipFragment (GAME_STATE_FILE *fp, SHIP_FRAGMENT *FragPtr)
+{
+ BYTE tmpb;
+
+ sread_16 (fp, NULL); /* unused: was which_side */
+ sread_8 (fp, &FragPtr->captains_name_index);
+ sread_8 (fp, NULL); /* padding; for savegame compat */
+ sread_16 (fp, NULL); /* unused: was ship_flags */
+ sread_8 (fp, &FragPtr->race_id);
+ sread_8 (fp, &FragPtr->index);
+ // XXX: reading crew as BYTE to maintain savegame compatibility
+ sread_8 (fp, &tmpb);
+ FragPtr->crew_level = tmpb;
+ sread_8 (fp, &tmpb);
+ FragPtr->max_crew = tmpb;
+ sread_8 (fp, &FragPtr->energy_level);
+ sread_8 (fp, &FragPtr->max_energy);
+ sread_16 (fp, NULL); /* unused; was loc.x */
+ sread_16 (fp, NULL); /* unused; was loc.y */
+}
+
+void
+WriteShipFragment (GAME_STATE_FILE *fp, const SHIP_FRAGMENT *FragPtr)
+{
+ swrite_16 (fp, 0); /* unused: was which_side */
+ swrite_8 (fp, FragPtr->captains_name_index);
+ swrite_8 (fp, 0); /* padding; for savegame compat */
+ swrite_16 (fp, 0); /* unused: was ship_flags */
+ swrite_8 (fp, FragPtr->race_id);
+ swrite_8 (fp, FragPtr->index);
+ // XXX: writing crew as BYTE to maintain savegame compatibility
+ swrite_8 (fp, FragPtr->crew_level);
+ swrite_8 (fp, FragPtr->max_crew);
+ swrite_8 (fp, FragPtr->energy_level);
+ swrite_8 (fp, FragPtr->max_energy);
+ swrite_16 (fp, 0); /* unused; was loc.x */
+ swrite_16 (fp, 0); /* unused; was loc.y */
+}
+
+void
+ReadIpGroup (GAME_STATE_FILE *fp, IP_GROUP *GroupPtr)
+{
+ BYTE tmpb;
+
+ sread_16 (fp, NULL); /* unused; was which_side */
+ sread_8 (fp, NULL); /* unused; was captains_name_index */
+ sread_8 (fp, NULL); /* padding; for savegame compat */
+ sread_16 (fp, &GroupPtr->group_counter);
+ sread_8 (fp, &GroupPtr->race_id);
+ sread_8 (fp, &tmpb); /* was var2 */
+ GroupPtr->sys_loc = LONIBBLE (tmpb);
+ GroupPtr->task = HINIBBLE (tmpb);
+ sread_8 (fp, &GroupPtr->in_system); /* was crew_level */
+ sread_8 (fp, NULL); /* unused; was max_crew */
+ sread_8 (fp, &tmpb); /* was energy_level */
+ GroupPtr->dest_loc = LONIBBLE (tmpb);
+ GroupPtr->orbit_pos = HINIBBLE (tmpb);
+ sread_8 (fp, &GroupPtr->group_id); /* was max_energy */
+ sread_16s(fp, &GroupPtr->loc.x);
+ sread_16s(fp, &GroupPtr->loc.y);
+}
+
+void
+WriteIpGroup (GAME_STATE_FILE *fp, const IP_GROUP *GroupPtr)
+{
+ swrite_16 (fp, 0); /* unused; was which_side */
+ swrite_8 (fp, 0); /* unused; was captains_name_index */
+ swrite_8 (fp, 0); /* padding; for savegame compat */
+ swrite_16 (fp, GroupPtr->group_counter);
+ swrite_8 (fp, GroupPtr->race_id);
+ assert (GroupPtr->sys_loc < 0x10 && GroupPtr->task < 0x10);
+ swrite_8 (fp, MAKE_BYTE (GroupPtr->sys_loc, GroupPtr->task));
+ /* was var2 */
+ swrite_8 (fp, GroupPtr->in_system); /* was crew_level */
+ swrite_8 (fp, 0); /* unused; was max_crew */
+ assert (GroupPtr->dest_loc < 0x10 && GroupPtr->orbit_pos < 0x10);
+ swrite_8 (fp, MAKE_BYTE (GroupPtr->dest_loc, GroupPtr->orbit_pos));
+ /* was energy_level */
+ swrite_8 (fp, GroupPtr->group_id); /* was max_energy */
+ swrite_16 (fp, GroupPtr->loc.x);
+ swrite_16 (fp, GroupPtr->loc.y);
+}
+
+void
+InitGroupInfo (BOOLEAN FirstTime)
+{
+ GAME_STATE_FILE *fp;
+
+ assert (NUM_SAVED_BATTLE_GROUPS >= MAX_BATTLE_GROUPS);
+
+ fp = OpenStateFile (RANDGRPINFO_FILE, "wb");
+ if (fp)
+ {
+ GROUP_HEADER GH;
+
+ memset (&GH, 0, sizeof (GH));
+ GH.star_index = (COUNT)~0;
+ WriteGroupHeader (fp, &GH);
+ CloseStateFile (fp);
+ }
+
+ if (FirstTime && (fp = OpenStateFile (DEFGRPINFO_FILE, "wb")))
+ {
+ // Group headers cannot start with offset 0 in 'defined' group
+ // info file, so bump it (because offset 0 is reserved to
+ // indicate the 'random' group info file).
+ swrite_8 (fp, 0);
+ CloseStateFile (fp);
+ }
+}
+
+void
+UninitGroupInfo (void)
+{
+ DeleteStateFile (DEFGRPINFO_FILE);
+ DeleteStateFile (RANDGRPINFO_FILE);
+}
+
+HIPGROUP
+BuildGroup (QUEUE *pDstQueue, BYTE race_id)
+{
+ HFLEETINFO hFleet;
+ FLEET_INFO *TemplatePtr;
+ HLINK hGroup;
+ IP_GROUP *GroupPtr;
+
+ assert (GetLinkSize (pDstQueue) == sizeof (IP_GROUP));
+
+ hFleet = GetStarShipFromIndex (&GLOBAL (avail_race_q), race_id);
+ if (!hFleet)
+ return 0;
+
+ hGroup = AllocLink (pDstQueue);
+ if (!hGroup)
+ return 0;
+
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ GroupPtr = LockIpGroup (pDstQueue, hGroup);
+ memset (GroupPtr, 0, GetLinkSize (pDstQueue));
+ GroupPtr->race_id = race_id;
+ GroupPtr->melee_icon = TemplatePtr->melee_icon;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ UnlockIpGroup (pDstQueue, hGroup);
+ PutQueue (pDstQueue, hGroup);
+
+ return hGroup;
+}
+
+void
+BuildGroups (void)
+{
+ BYTE Index;
+ BYTE BestIndex = 0;
+ COUNT BestPercent = 0;
+ POINT universe;
+ HFLEETINFO hFleet, hNextFleet;
+ BYTE HomeWorld[] =
+ {
+ 0, /* ARILOU_SHIP */
+ 0, /* CHMMR_SHIP */
+ 0, /* HUMAN_SHIP */
+ ORZ_DEFINED, /* ORZ_SHIP */
+ PKUNK_DEFINED, /* PKUNK_SHIP */
+ 0, /* SHOFIXTI_SHIP */
+ SPATHI_DEFINED, /* SPATHI_SHIP */
+ SUPOX_DEFINED, /* SUPOX_SHIP */
+ THRADD_DEFINED, /* THRADDASH_SHIP */
+ UTWIG_DEFINED, /* UTWIG_SHIP */
+ VUX_DEFINED, /* VUX_SHIP */
+ YEHAT_DEFINED, /* YEHAT_SHIP */
+ 0, /* MELNORME_SHIP */
+ DRUUGE_DEFINED, /* DRUUGE_SHIP */
+ ILWRATH_DEFINED, /* ILWRATH_SHIP */
+ MYCON_DEFINED, /* MYCON_SHIP */
+ 0, /* SLYLANDRO_SHIP */
+ UMGAH_DEFINED, /* UMGAH_SHIP */
+ 0, /* URQUAN_SHIP */
+ ZOQFOT_DEFINED, /* ZOQFOTPIK_SHIP */
+
+ 0, /* SYREEN_SHIP */
+ 0, /* BLACK_URQUAN_SHIP */
+ 0, /* YEHAT_REBEL_SHIP */
+ };
+ BYTE EncounterPercent[] =
+ {
+ RACE_INTERPLANETARY_PERCENT
+ };
+
+ EncounterPercent[SLYLANDRO_SHIP] *= GET_GAME_STATE (SLYLANDRO_MULTIPLIER);
+ Index = GET_GAME_STATE (UTWIG_SUPOX_MISSION);
+ if (Index > 1 && Index < 5)
+ {
+ // When the Utwig and Supox are on their mission, there won't be
+ // new battle groups generated for the system.
+ // Note that old groups may still exist (in which case this function
+ // would not even be called), but those expire after spending a week
+ // outside of the star system, or when a different star system is
+ // entered.
+ HomeWorld[UTWIG_SHIP] = 0;
+ HomeWorld[SUPOX_SHIP] = 0;
+ }
+
+ universe = CurStarDescPtr->star_pt;
+ for (hFleet = GetHeadLink (&GLOBAL (avail_race_q)), Index = 0;
+ hFleet; hFleet = hNextFleet, ++Index)
+ {
+ COUNT i, encounter_radius;
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ hNextFleet = _GetSuccLink (FleetPtr);
+
+ if ((encounter_radius = FleetPtr->actual_strength)
+ && (i = EncounterPercent[Index]))
+ {
+ SIZE dx, dy;
+ DWORD d_squared;
+ BYTE race_enc;
+
+ race_enc = HomeWorld[Index];
+ if (race_enc && CurStarDescPtr->Index == race_enc)
+ { // In general, there are always ships at the Homeworld for
+ // the races specified in HomeWorld[] array.
+ BestIndex = Index;
+ BestPercent = 70;
+ if (race_enc == SPATHI_DEFINED || race_enc == SUPOX_DEFINED)
+ BestPercent = 2;
+ // Terminate the loop!
+ hNextFleet = 0;
+
+ goto FoundHome;
+ }
+
+ if (encounter_radius == INFINITE_RADIUS)
+ encounter_radius = (MAX_X_UNIVERSE + 1) << 1;
+ else
+ encounter_radius =
+ (encounter_radius * SPHERE_RADIUS_INCREMENT) >> 1;
+ dx = universe.x - FleetPtr->loc.x;
+ if (dx < 0)
+ dx = -dx;
+ dy = universe.y - FleetPtr->loc.y;
+ if (dy < 0)
+ dy = -dy;
+ if ((COUNT)dx < encounter_radius
+ && (COUNT)dy < encounter_radius
+ && (d_squared = (DWORD)dx * dx + (DWORD)dy * dy) <
+ (DWORD)encounter_radius * encounter_radius)
+ {
+ DWORD rand_val;
+
+ // EncounterPercent is only used in practice for the Slylandro
+ // Probes, for the rest of races the chance of encounter is
+ // calced directly below from the distance to the Homeworld
+ if (FleetPtr->actual_strength != INFINITE_RADIUS)
+ {
+ i = 70 - (COUNT)((DWORD)square_root (d_squared)
+ * 60L / encounter_radius);
+ }
+
+ rand_val = TFB_Random ();
+ if ((int)(LOWORD (rand_val) % 100) < (int)i
+ && (BestPercent == 0
+ || (HIWORD (rand_val) % (i + BestPercent)) < i))
+ {
+ if (FleetPtr->actual_strength == INFINITE_RADIUS)
+ { // The prevailing encounter chance is hereby limitted
+ // to 4% for races with infinite SoI (currently, it
+ // is only the Slylandro Probes)
+ i = 4;
+ }
+
+ BestPercent = i;
+ BestIndex = Index;
+ }
+ }
+ }
+
+FoundHome:
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hFleet);
+ }
+
+ if (BestPercent)
+ {
+ BYTE which_group, num_groups;
+ BYTE EncounterMakeup[] =
+ {
+ RACE_ENCOUNTER_MAKEUP
+ };
+
+ which_group = 0;
+ num_groups = ((COUNT)TFB_Random () % (BestPercent >> 1)) + 1;
+ if (num_groups > MAX_BATTLE_GROUPS)
+ num_groups = MAX_BATTLE_GROUPS;
+ else if (num_groups < 5
+ && (Index = HomeWorld[BestIndex])
+ && CurStarDescPtr->Index == Index)
+ num_groups = 5;
+ do
+ {
+ for (Index = HINIBBLE (EncounterMakeup[BestIndex]); Index;
+ --Index)
+ {
+ if (Index <= LONIBBLE (EncounterMakeup[BestIndex])
+ || (COUNT)TFB_Random () % 100 < 50)
+ CloneShipFragment (BestIndex,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+
+ PutGroupInfo (GROUPS_RANDOM, ++which_group);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ } while (--num_groups);
+ }
+
+ GetGroupInfo (GROUPS_RANDOM, GROUP_INIT_IP);
+}
+
+static void
+FlushGroupInfo (GROUP_HEADER* pGH, DWORD offset, BYTE which_group, GAME_STATE_FILE *fp)
+{
+ if (which_group == GROUP_LIST)
+ {
+ HIPGROUP hGroup, hNextGroup;
+
+ /* If the group list was never written before, add it */
+ if (pGH->GroupOffset[0] == 0)
+ pGH->GroupOffset[0] = LengthStateFile (fp);
+
+ // XXX: npc_built_ship_q must be empty because the wipe-out
+ // procedure is actually the writing of the npc_built_ship_q
+ // out as the group in question
+ assert (!GetHeadLink (&GLOBAL (npc_built_ship_q)));
+
+ /* Weed out the groups that left the system first */
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ hGroup; hGroup = hNextGroup)
+ {
+ BYTE in_system;
+ BYTE group_id;
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+ in_system = GroupPtr->in_system;
+ group_id = GroupPtr->group_id;
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+
+ if (!in_system)
+ {
+ // The following 'if' is needed because GROUP_LIST is only
+ // ever flushed to RANDGRPINFO_FILE, but the current group
+ // may need to be updated in the DEFGRPINFO_FILE as well.
+ // In that case, PutGroupInfo() will update the correct file.
+ if (GLOBAL (BattleGroupRef))
+ PutGroupInfo (GLOBAL (BattleGroupRef), group_id);
+ else
+ FlushGroupInfo (pGH, GROUPS_RANDOM, group_id, fp);
+ // This will also wipe the group out in the RANDGRPINFO_FILE
+ pGH->GroupOffset[group_id] = 0;
+ RemoveQueue (&GLOBAL (ip_group_q), hGroup);
+ FreeIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+ }
+ }
+ else if (which_group > pGH->NumGroups)
+ { /* Group not present yet -- add it */
+ pGH->NumGroups = which_group;
+ pGH->GroupOffset[which_group] = LengthStateFile (fp);
+ }
+
+ SeekStateFile (fp, offset, SEEK_SET);
+ WriteGroupHeader (fp, pGH);
+
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, "1)FlushGroupInfo(%lu): WG = %u(%lu), NG = %u, "
+ "SI = %u", offset, which_group, pGH->GroupOffset[which_group],
+ pGH->NumGroups, pGH->star_index);
+#endif /* DEBUG_GROUPS */
+
+ if (which_group == GROUP_LIST)
+ {
+ /* Write out ip_group_q as group 0 */
+ HIPGROUP hGroup, hNextGroup;
+ BYTE NumGroups = CountLinks (&GLOBAL (ip_group_q));
+
+ SeekStateFile (fp, pGH->GroupOffset[0], SEEK_SET);
+ swrite_8 (fp, LastEncGroup);
+ swrite_8 (fp, NumGroups);
+
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ for ( ; NumGroups; --NumGroups, hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ swrite_8 (fp, GroupPtr->race_id);
+
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, "F) type %u, loc %u<%d, %d>, task 0x%02x:%u",
+ GroupPtr->race_id,
+ GET_GROUP_LOC (GroupPtr),
+ GroupPtr->loc.x,
+ GroupPtr->loc.y,
+ GET_GROUP_MISSION (GroupPtr),
+ GET_GROUP_DEST (GroupPtr));
+#endif /* DEBUG_GROUPS */
+
+ WriteIpGroup (fp, GroupPtr);
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+ }
+ else
+ {
+ /* Write out npc_built_ship_q as 'which_group' group */
+ HSHIPFRAG hStarShip, hNextShip;
+ BYTE NumShips = CountLinks (&GLOBAL (npc_built_ship_q));
+ BYTE RaceType = 0;
+
+ hStarShip = GetHeadLink (&GLOBAL (npc_built_ship_q));
+ if (NumShips > 0)
+ {
+ SHIP_FRAGMENT *FragPtr;
+
+ /* The first ship in a group defines the alien race */
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ RaceType = FragPtr->race_id;
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+
+ SeekStateFile (fp, pGH->GroupOffset[which_group], SEEK_SET);
+ swrite_8 (fp, RaceType);
+ swrite_8 (fp, NumShips);
+
+ for ( ; NumShips; --NumShips, hStarShip = hNextShip)
+ {
+ SHIP_FRAGMENT *FragPtr;
+
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (FragPtr);
+
+ swrite_8 (fp, FragPtr->race_id);
+ WriteShipFragment (fp, FragPtr);
+
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+ }
+}
+
+BOOLEAN
+GetGroupInfo (DWORD offset, BYTE which_group)
+{
+ GAME_STATE_FILE *fp;
+ GROUP_HEADER GH;
+
+ if (offset != GROUPS_RANDOM && which_group != GROUP_LIST)
+ fp = OpenStateFile (DEFGRPINFO_FILE, "r+b");
+ else
+ fp = OpenStateFile (RANDGRPINFO_FILE, "r+b");
+
+ if (!fp)
+ return FALSE;
+
+ SeekStateFile (fp, offset, SEEK_SET);
+ ReadGroupHeader (fp, &GH);
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, "GetGroupInfo(%lu): %u(%lu) out of %u", offset,
+ which_group, GH.GroupOffset[which_group], GH.NumGroups);
+#endif /* DEBUG_GROUPS */
+
+ if (which_group == GROUP_INIT_IP)
+ {
+ COUNT month_index, day_index, year_index;
+
+ ReinitQueue (&GLOBAL (ip_group_q));
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, "%u == %u", GH.star_index,
+ (COUNT)(CurStarDescPtr - star_array));
+#endif /* DEBUG_GROUPS */
+
+ /* Check if the requested groups are valid for this star system
+ * and if they are still current (not expired) */
+ day_index = GH.day_index;
+ month_index = GH.month_index;
+ year_index = GH.year_index;
+ if (offset == GROUPS_RANDOM
+ && (GH.star_index != (COUNT)(CurStarDescPtr - star_array)
+ || !ValidateEvent (ABSOLUTE_EVENT, &month_index, &day_index,
+ &year_index)))
+ {
+#ifdef DEBUG_GROUPS
+ if (GH.star_index == CurStarDescPtr - star_array)
+ log_add (log_Debug, "GetGroupInfo: battle groups out of "
+ "date %u/%u/%u!", month_index, day_index,
+ year_index);
+#endif /* DEBUG_GROUPS */
+
+ CloseStateFile (fp);
+ /* Erase random groups (out of date) */
+ fp = OpenStateFile (RANDGRPINFO_FILE, "wb");
+ memset (&GH, 0, sizeof (GH));
+ GH.star_index = (COUNT)~0;
+ WriteGroupHeader (fp, &GH);
+ CloseStateFile (fp);
+
+ return FALSE;
+ }
+
+ /* Read IP groups into ip_group_q and send them on their missions */
+ for (which_group = 1; which_group <= GH.NumGroups; ++which_group)
+ {
+ BYTE task, group_loc;
+ DWORD rand_val;
+ BYTE RaceType;
+ BYTE NumShips;
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ if (GH.GroupOffset[which_group] == 0)
+ continue;
+
+ SeekStateFile (fp, GH.GroupOffset[which_group], SEEK_SET);
+ sread_8 (fp, &RaceType);
+ sread_8 (fp, &NumShips);
+ if (!NumShips)
+ continue; /* group is dead */
+
+ hGroup = BuildGroup (&GLOBAL (ip_group_q), RaceType);
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ GroupPtr->group_id = which_group;
+ GroupPtr->in_system = 1;
+
+ rand_val = TFB_Random ();
+ task = (BYTE)(LOBYTE (LOWORD (rand_val)) % ON_STATION);
+ if (task == FLEE)
+ task = ON_STATION;
+ GroupPtr->orbit_pos = NORMALIZE_FACING (
+ LOBYTE (HIWORD (rand_val)));
+
+ group_loc = pSolarSysState->SunDesc[0].NumPlanets;
+ if (group_loc == 1 && task == EXPLORE)
+ task = IN_ORBIT;
+ else
+ group_loc = (BYTE)((HIBYTE (LOWORD (rand_val)) % group_loc) + 1);
+ GroupPtr->dest_loc = group_loc;
+ rand_val = TFB_Random ();
+ GroupPtr->loc.x = (LOWORD (rand_val) % 10000) - 5000;
+ GroupPtr->loc.y = (HIWORD (rand_val) % 10000) - 5000;
+ GroupPtr->group_counter = 0;
+ if (task == EXPLORE)
+ {
+ GroupPtr->group_counter = ((COUNT)TFB_Random () %
+ MAX_REVOLUTIONS) << FACING_SHIFT;
+ }
+ else if (task == ON_STATION)
+ {
+ COUNT angle;
+ POINT org;
+
+ org = planetOuterLocation (group_loc - 1);
+ angle = FACING_TO_ANGLE (GroupPtr->orbit_pos + 1);
+ GroupPtr->loc.x = org.x + COSINE (angle, STATION_RADIUS);
+ GroupPtr->loc.y = org.y + SINE (angle, STATION_RADIUS);
+ group_loc = 0;
+ }
+
+ GroupPtr->task = task;
+ GroupPtr->sys_loc = group_loc;
+
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, "battle group %u(0x%04x) strength "
+ "%u, type %u, loc %u<%d, %d>, task %u",
+ which_group,
+ hGroup,
+ NumShips,
+ RaceType,
+ group_loc,
+ GroupPtr->loc.x,
+ GroupPtr->loc.y,
+ task);
+#endif /* DEBUG_GROUPS */
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+
+ if (offset != GROUPS_RANDOM)
+ InitGroupInfo (FALSE); /* Wipe out random battle groups */
+ else if (ValidateEvent (ABSOLUTE_EVENT, /* still fresh */
+ &month_index, &day_index, &year_index))
+ {
+ CloseStateFile (fp);
+ return TRUE;
+ }
+
+ CloseStateFile (fp);
+ return (GetHeadLink (&GLOBAL (ip_group_q)) != 0);
+ }
+
+ if (!GH.GroupOffset[which_group])
+ {
+ /* Group not present */
+ CloseStateFile (fp);
+ return FALSE;
+ }
+
+
+ if (which_group == GROUP_LIST)
+ {
+ BYTE NumGroups;
+ COUNT ShipsLeftInLEG;
+
+ // XXX: Hack: First, save the state of last encountered group, if any.
+ // The assumption here is that we read the group list immediately
+ // after an IP encounter, and npc_built_ship_q contains whatever
+ // ships are left in the encountered group (can be none).
+ ShipsLeftInLEG = CountLinks (&GLOBAL (npc_built_ship_q));
+
+ SeekStateFile (fp, GH.GroupOffset[0], SEEK_SET);
+ sread_8 (fp, &LastEncGroup);
+
+ if (LastEncGroup)
+ {
+ // The following 'if' is needed because GROUP_LIST is only
+ // ever read from RANDGRPINFO_FILE, but the LastEncGroup
+ // may need to be updated in the DEFGRPINFO_FILE as well.
+ // In that case, PutGroupInfo() will update the correct file.
+ if (GLOBAL (BattleGroupRef))
+ PutGroupInfo (GLOBAL (BattleGroupRef), LastEncGroup);
+ else
+ FlushGroupInfo (&GH, offset, LastEncGroup, fp);
+ }
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+
+ /* Read group 0 into ip_group_q */
+ ReinitQueue (&GLOBAL (ip_group_q));
+ /* Need a seek because Put/Flush has moved the file ptr */
+ SeekStateFile (fp, GH.GroupOffset[0] + 1, SEEK_SET);
+ sread_8 (fp, &NumGroups);
+
+ while (NumGroups--)
+ {
+ BYTE group_id;
+ BYTE RaceType;
+ HSHIPFRAG hGroup;
+ IP_GROUP *GroupPtr;
+
+ sread_8 (fp, &RaceType);
+
+ hGroup = BuildGroup (&GLOBAL (ip_group_q), RaceType);
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ ReadIpGroup (fp, GroupPtr);
+ group_id = GroupPtr->group_id;
+
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, "G) type %u, loc %u<%d, %d>, task 0x%02x:%u",
+ RaceType,
+ GroupPtr->sys_loc,
+ GroupPtr->loc.x,
+ GroupPtr->loc.y,
+ GroupPtr->task,
+ GroupPtr->dest_loc);
+#endif /* DEBUG_GROUPS */
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+
+ if (group_id == LastEncGroup && !ShipsLeftInLEG)
+ {
+ /* No ships left in the last encountered group, remove it */
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, " -- REMOVING");
+#endif /* DEBUG_GROUPS */
+ RemoveQueue (&GLOBAL (ip_group_q), hGroup);
+ FreeIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+ }
+
+ CloseStateFile (fp);
+ return (GetHeadLink (&GLOBAL (ip_group_q)) != 0);
+ }
+ else
+ {
+ /* Read 'which_group' group into npc_built_ship_q */
+ BYTE NumShips;
+
+ // XXX: Hack: The assumption here is that we only read the makeup
+ // of a particular group when initializing an encounter, which
+ // makes this group 'last encountered'. Also the state of all
+ // groups is saved here. This may make working with savegames
+ // harder in the future, as special care will have to be taken
+ // when loading a game into an encounter.
+ LastEncGroup = which_group;
+ // The following 'if' is needed because GROUP_LIST is only
+ // ever written to RANDGRPINFO_FILE, but the group we are reading
+ // may be in the DEFGRPINFO_FILE as well.
+ // In that case, PutGroupInfo() will update the correct file.
+ // Always calling PutGroupInfo() here would also be acceptable now.
+ if (offset != GROUPS_RANDOM)
+ PutGroupInfo (GROUPS_RANDOM, GROUP_LIST);
+ else
+ FlushGroupInfo (&GH, GROUPS_RANDOM, GROUP_LIST, fp);
+ ReinitQueue (&GLOBAL (ip_group_q));
+
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ // skip RaceType
+ SeekStateFile (fp, GH.GroupOffset[which_group] + 1, SEEK_SET);
+ sread_8 (fp, &NumShips);
+
+ while (NumShips--)
+ {
+ BYTE RaceType;
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+
+ sread_8 (fp, &RaceType);
+
+ hStarShip = CloneShipFragment (RaceType,
+ &GLOBAL (npc_built_ship_q), 0);
+
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ ReadShipFragment (fp, FragPtr);
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+
+ CloseStateFile (fp);
+ return (GetHeadLink (&GLOBAL (npc_built_ship_q)) != 0);
+ }
+}
+
+DWORD
+PutGroupInfo (DWORD offset, BYTE which_group)
+{
+ GAME_STATE_FILE *fp;
+ GROUP_HEADER GH;
+
+ if (offset != GROUPS_RANDOM && which_group != GROUP_LIST)
+ fp = OpenStateFile (DEFGRPINFO_FILE, "r+b");
+ else
+ fp = OpenStateFile (RANDGRPINFO_FILE, "r+b");
+
+ if (!fp)
+ return offset;
+
+ if (offset == GROUPS_ADD_NEW)
+ {
+ offset = LengthStateFile (fp);
+ SeekStateFile (fp, offset, SEEK_SET);
+ memset (&GH, 0, sizeof (GH));
+ GH.star_index = (COUNT)~0;
+ WriteGroupHeader (fp, &GH);
+ }
+
+ // XXX: This is a bit dangerous. The assumption here is that we are
+ // only called to write GROUP_LIST in the GROUPS_RANDOM context,
+ // which is true right now and in which case we would seek to 0 anyway.
+ // The latter also makes guarding the seek with
+ // 'if (which_group != GROUP_LIST)' moot.
+ if (which_group != GROUP_LIST)
+ {
+ SeekStateFile (fp, offset, SEEK_SET);
+ if (which_group == GROUP_SAVE_IP)
+ {
+ LastEncGroup = 0;
+ which_group = GROUP_LIST;
+ }
+ }
+ ReadGroupHeader (fp, &GH);
+
+#ifdef NEVER
+ // XXX: this appears to be a remnant of a slightly different group info
+ // expiration mechanism. Nowadays, the 'defined' groups never expire,
+ // and the dead 'random' groups stay in the file with NumShips==0 until
+ // the entire 'random' group header expires.
+ if (GetHeadLink (&GLOBAL (npc_built_ship_q)) || GH.GroupOffset[0] == 0)
+#endif /* NEVER */
+ {
+ COUNT month_index, day_index, year_index;
+
+ /* The groups in this system are good for the next 7 days */
+ month_index = 0;
+ day_index = 7;
+ year_index = 0;
+ ValidateEvent (RELATIVE_EVENT, &month_index, &day_index, &year_index);
+ GH.day_index = (BYTE)day_index;
+ GH.month_index = (BYTE)month_index;
+ GH.year_index = year_index;
+ }
+ GH.star_index = CurStarDescPtr - star_array;
+
+#ifdef DEBUG_GROUPS
+ log_add (log_Debug, "PutGroupInfo(%lu): %u out of %u -- %u/%u/%u",
+ offset, which_group, GH.NumGroups,
+ GH.month_index, GH.day_index, GH.year_index);
+#endif /* DEBUG_GROUPS */
+
+ FlushGroupInfo (&GH, offset, which_group, fp);
+
+ CloseStateFile (fp);
+
+ return (offset);
+}
+
diff --git a/src/uqm/grpinfo.h b/src/uqm/grpinfo.h
new file mode 100644
index 0000000..65286aa
--- /dev/null
+++ b/src/uqm/grpinfo.h
@@ -0,0 +1,93 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_GRPINFO_H_
+#define UQM_GRPINFO_H_
+
+#include "port.h"
+#include "libs/compiler.h"
+#include "displist.h"
+#include "libs/gfxlib.h"
+ // for POINT
+#include <assert.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// XXX: Needed to maintain savegame compatibility
+#define NUM_SAVED_BATTLE_GROUPS 64
+
+typedef HLINK HIPGROUP;
+
+typedef struct
+{
+ // LINK elements; must be first
+ HIPGROUP pred;
+ HIPGROUP succ;
+
+ UWORD group_counter;
+ BYTE race_id;
+ BYTE sys_loc;
+ BYTE task; // AKA mission
+ BYTE in_system;
+ // a simple != 0 flag
+ // In older savegames this will be >1, because
+ // CloneShipFragment was used to spawn groups,
+ // and it set this to crew_level values
+
+ BYTE dest_loc;
+ BYTE orbit_pos;
+ /* Also: saved prev dest_loc before intercept call,
+ * restored to dest_loc on all-clear */
+ BYTE group_id;
+ POINT loc;
+
+ FRAME melee_icon;
+} IP_GROUP;
+
+enum
+{
+ IN_ORBIT = 0,
+ EXPLORE,
+ FLEE,
+ ON_STATION,
+
+ IGNORE_FLAGSHIP = 1 << 2,
+ REFORM_GROUP = 1 << 3
+};
+#define MAX_REVOLUTIONS 5
+
+#define STATION_RADIUS 1600
+#define ORBIT_RADIUS 2400
+
+static inline IP_GROUP *
+LockIpGroup (const QUEUE *pq, HIPGROUP h)
+{
+ assert (GetLinkSize (pq) == sizeof (IP_GROUP));
+ return (IP_GROUP *) LockLink (pq, h);
+}
+
+#define UnlockIpGroup(pq, h) UnlockLink (pq, h)
+#define FreeIpGroup(pq, h) FreeLink (pq, h)
+
+extern HIPGROUP BuildGroup (QUEUE *pDstQueue, BYTE race_id);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_GRPINFO_H_ */
diff --git a/src/uqm/grpintrn.h b/src/uqm/grpintrn.h
new file mode 100644
index 0000000..d2136a5
--- /dev/null
+++ b/src/uqm/grpintrn.h
@@ -0,0 +1,56 @@
+#ifndef _GRPINTRN_H
+#define _GRPINTRN_H
+
+// For IPGROUP
+#include "grpinfo.h"
+
+// For SHIP_FRAGMENT
+#include "races.h"
+
+// For GAME_STATE_FILE
+#include "state.h"
+
+//#define DEBUG_GROUPS
+
+// A group header describes battle groups present in a star system. There is
+// at most 1 group header per system.
+// 'Random' group info file (RANDGRPINFO_FILE) always contains only one
+// group header record, which describes the last-visited star system,
+// (which may be the current system). Thus the randomly generated groups
+// are valid for 7 days (set in PutGroupInfo) after the player leaves
+// the system, or until the player enters another star system.
+typedef struct
+{
+ BYTE NumGroups;
+ BYTE day_index, month_index;
+ COUNT star_index, year_index;
+ // day_index, month_index, year_index specify when
+ // random groups expire (if you were to leave the system
+ // by going to HSpace and stay there till such time)
+ // star_index is the index of a star this group header
+ // applies to; ~0 means uninited
+ DWORD GroupOffset[NUM_SAVED_BATTLE_GROUPS + 1];
+ // Absolute offsets of group definitions in a state file
+ // Group 0 is a list of groups present in solarsys
+ // (RANDGRPINFO_FILE only)
+ // Groups 1..max are definitions of actual battle groups
+ // containing ship makeup and status
+
+ // Each group has the following format:
+ // 1 byte, RaceType (LastEncGroup in Group 0)
+ // 1 byte, NumShips (NumGroups in Group 0)
+ // Ships follow:
+ // 1 byte, RaceType
+ // 16 bytes, part of SHIP_FRAGMENT struct
+ // (part of IP_GROUP struct in Group 0)
+
+} GROUP_HEADER;
+
+void ReadGroupHeader (GAME_STATE_FILE *fp, GROUP_HEADER *pGH);
+void WriteGroupHeader (GAME_STATE_FILE *fp, const GROUP_HEADER *pGH);
+void ReadShipFragment (GAME_STATE_FILE *fp, SHIP_FRAGMENT *FragPtr);
+void WriteShipFragment (GAME_STATE_FILE *fp, const SHIP_FRAGMENT *FragPtr);
+void ReadIpGroup (GAME_STATE_FILE *fp, IP_GROUP *GroupPtr);
+void WriteIpGroup (GAME_STATE_FILE *fp, const IP_GROUP *GroupPtr);
+
+#endif
diff --git a/src/uqm/hyper.c b/src/uqm/hyper.c
new file mode 100644
index 0000000..9a9b9f4
--- /dev/null
+++ b/src/uqm/hyper.c
@@ -0,0 +1,1747 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "hyper.h"
+
+#include "build.h"
+#include "collide.h"
+#include "colors.h"
+#include "controls.h"
+#include "gameopt.h"
+#include "menustat.h"
+ // for DrawMenuStateStrings()
+#include "encount.h"
+#include "starmap.h"
+#include "ship.h"
+#include "shipcont.h"
+#include "process.h"
+#include "globdata.h"
+#include "sis.h"
+#include "units.h"
+#include "init.h"
+#include "nameref.h"
+#include "resinst.h"
+#include "setup.h"
+#include "sounds.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+
+
+#define XOFFS ((RADAR_SCAN_WIDTH + (UNIT_SCREEN_WIDTH << 2)) >> 1)
+#define YOFFS ((RADAR_SCAN_HEIGHT + (UNIT_SCREEN_HEIGHT << 2)) >> 1)
+
+static FRAME hyperstars[3];
+static COLORMAP hypercmaps[2];
+static BYTE fuel_ticks;
+static COUNT hyper_dx, hyper_dy, hyper_extra;
+
+// HyperspaceMenu() items
+enum HyperMenuItems
+{
+ // XXX: Must match the enum in menustat.h
+ STARMAP = 1,
+ EQUIP_DEVICE,
+ CARGO,
+ ROSTER,
+ GAME_MENU,
+ NAVIGATION,
+};
+
+
+void
+MoveSIS (SIZE *pdx, SIZE *pdy)
+{
+ SIZE new_dx, new_dy;
+
+ new_dx = *pdx;
+ GLOBAL_SIS (log_x) -= new_dx;
+ if (GLOBAL_SIS (log_x) < 0)
+ {
+ new_dx += (SIZE)GLOBAL_SIS (log_x);
+ GLOBAL_SIS (log_x) = 0;
+ }
+ else if (GLOBAL_SIS (log_x) > MAX_X_LOGICAL)
+ {
+ new_dx += (SIZE)(GLOBAL_SIS (log_x) - MAX_X_LOGICAL);
+ GLOBAL_SIS (log_x) = MAX_X_LOGICAL;
+ }
+
+ new_dy = *pdy;
+ GLOBAL_SIS (log_y) -= new_dy;
+ if (GLOBAL_SIS (log_y) < 0)
+ {
+ new_dy += (SIZE)GLOBAL_SIS (log_y);
+ GLOBAL_SIS (log_y) = 0;
+ }
+ else if (GLOBAL_SIS (log_y) > MAX_Y_LOGICAL)
+ {
+ new_dy += (SIZE)(GLOBAL_SIS (log_y) - MAX_Y_LOGICAL);
+ GLOBAL_SIS (log_y) = MAX_Y_LOGICAL;
+ }
+
+ if (new_dx != *pdx || new_dy != *pdy)
+ {
+ HELEMENT hElement, hNextElement;
+
+ *pdx = new_dx;
+ *pdy = new_dy;
+
+ for (hElement = GetTailElement ();
+ hElement != 0; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+
+ if (!(ElementPtr->state_flags & PLAYER_SHIP))
+ hNextElement = GetPredElement (ElementPtr);
+ else
+ {
+ ElementPtr->next.location.x = (LOG_SPACE_WIDTH >> 1) - new_dx;
+ ElementPtr->next.location.y = (LOG_SPACE_HEIGHT >> 1) - new_dy;
+ hNextElement = 0;
+ }
+
+ UnlockElement (hElement);
+ }
+ }
+
+ if (GLOBAL_SIS (FuelOnBoard) && GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ COUNT cur_fuel_ticks;
+ COUNT hyper_dist;
+ DWORD adj_dx, adj_dy;
+
+ if (new_dx < 0)
+ new_dx = -new_dx;
+ hyper_dx += new_dx;
+ if (new_dy < 0)
+ new_dy = -new_dy;
+ hyper_dy += new_dy;
+
+ /* These macros are also used in the fuel estimate on the starmap. */
+ adj_dx = LOGX_TO_UNIVERSE(16 * hyper_dx);
+ adj_dy = MAX_Y_UNIVERSE - LOGY_TO_UNIVERSE(16 * hyper_dy);
+
+ hyper_dist = square_root (adj_dx * adj_dx + adj_dy * adj_dy)
+ + hyper_extra;
+ cur_fuel_ticks = hyper_dist >> 4;
+
+ if (cur_fuel_ticks > (COUNT)fuel_ticks)
+ {
+#ifndef TESTING
+ DeltaSISGauges (0, fuel_ticks - cur_fuel_ticks, 0);
+#endif /* TESTING */
+ if (cur_fuel_ticks > 0x00FF)
+ {
+ hyper_dx = 0;
+ hyper_extra = hyper_dist & ((1 << 4) - 1);
+ hyper_dy = 0;
+ cur_fuel_ticks = 0;
+ }
+
+ fuel_ticks = (BYTE)cur_fuel_ticks;
+ }
+ }
+}
+
+void
+check_hyperspace_encounter (void)
+{
+ BYTE Type;
+ POINT universe;
+ HFLEETINFO hStarShip, hNextShip;
+ COUNT EncounterPercent[] =
+ {
+ RACE_HYPERSPACE_PERCENT
+ };
+
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q)), Type = 0;
+ hStarShip && (GLOBAL (CurrentActivity) & IN_BATTLE);
+ hStarShip = hNextShip, ++Type)
+ {
+ COUNT encounter_radius;
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ encounter_radius = FleetPtr->actual_strength;
+ if (encounter_radius)
+ {
+ BYTE encounter_flags;
+ SIZE dx, dy;
+ COUNT percent;
+ HENCOUNTER hEncounter;
+ ENCOUNTER *EncounterPtr;
+
+ encounter_flags = 0;
+ percent = EncounterPercent[Type];
+
+ if (encounter_radius != INFINITE_RADIUS)
+ {
+ encounter_radius =
+ (encounter_radius * SPHERE_RADIUS_INCREMENT) >> 1;
+ }
+ else /* encounter_radius == infinity */
+ {
+ HENCOUNTER hNextEncounter;
+
+ encounter_radius = (MAX_X_UNIVERSE + 1) << 1;
+ if (Type == SLYLANDRO_SHIP)
+ {
+ encounter_flags = ONE_SHOT_ENCOUNTER;
+ if (!GET_GAME_STATE (STARBASE_AVAILABLE))
+ percent = 100;
+ else
+ percent *= GET_GAME_STATE (SLYLANDRO_MULTIPLIER);
+ }
+ else if (Type == MELNORME_SHIP
+ && (GLOBAL_SIS (FuelOnBoard) == 0
+ || GET_GAME_STATE (USED_BROADCASTER))
+ && GET_GAME_STATE (MELNORME_ANGER) < 3)
+ {
+ if (!GET_GAME_STATE (USED_BROADCASTER))
+ percent = 30;
+ else
+ percent = 100;
+ encounter_flags = ONE_SHOT_ENCOUNTER;
+ }
+
+ // There can be only one! (of either Slylandro or Melnorme)
+ for (hEncounter = GetHeadEncounter ();
+ hEncounter; hEncounter = hNextEncounter)
+ {
+ LockEncounter (hEncounter, &EncounterPtr);
+ hNextEncounter = GetSuccEncounter (EncounterPtr);
+ if (EncounterPtr->race_id == Type)
+ {
+ percent = 0;
+ hNextEncounter = 0;
+ }
+ UnlockEncounter (hEncounter);
+ }
+
+
+ if (percent == 100 && Type == MELNORME_SHIP)
+ {
+ SET_GAME_STATE (BROADCASTER_RESPONSE, 1);
+ }
+ }
+
+ dx = universe.x - FleetPtr->loc.x;
+ if (dx < 0)
+ dx = -dx;
+ dy = universe.y - FleetPtr->loc.y;
+ if (dy < 0)
+ dy = -dy;
+ if ((COUNT)dx < encounter_radius
+ && (COUNT)dy < encounter_radius
+ && (DWORD)dx * dx + (DWORD)dy * dy <
+ (DWORD)encounter_radius * encounter_radius
+ && ((COUNT)TFB_Random () % 100) < percent)
+ {
+ // Ship spawned for encounter.
+ hEncounter = AllocEncounter ();
+ if (hEncounter)
+ {
+ LockEncounter (hEncounter, &EncounterPtr);
+ memset (EncounterPtr, 0, sizeof (*EncounterPtr));
+ EncounterPtr->origin = FleetPtr->loc;
+ EncounterPtr->radius = encounter_radius;
+ EncounterPtr->flags = encounter_flags;
+ EncounterPtr->race_id = Type;
+ UnlockEncounter (hEncounter);
+
+ PutEncounter (hEncounter);
+ }
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ SET_GAME_STATE (USED_BROADCASTER, 0);
+}
+
+void
+FreeHyperData (void)
+{
+ DestroyDrawable (ReleaseDrawable (hyperstars[0]));
+ hyperstars[0] = 0;
+ DestroyDrawable (ReleaseDrawable (hyperstars[1]));
+ hyperstars[1] = 0;
+ DestroyDrawable (ReleaseDrawable (hyperstars[2]));
+ hyperstars[2] = 0;
+
+ DestroyColorMap (ReleaseColorMap (hypercmaps[0]));
+ hypercmaps[0] = 0;
+ DestroyColorMap (ReleaseColorMap (hypercmaps[1]));
+ hypercmaps[1] = 0;
+}
+
+static void
+LoadHyperData (void)
+{
+ if (hyperstars[0] == 0)
+ {
+ hyperstars[0] = CaptureDrawable (
+ LoadGraphic (AMBIENT_MASK_PMAP_ANIM));
+ hyperstars[1] = CaptureDrawable (
+ LoadGraphic (HYPERSTARS_MASK_PMAP_ANIM));
+ hypercmaps[0] = CaptureColorMap (LoadColorMap (HYPER_COLOR_TAB));
+
+ hyperstars[2] = CaptureDrawable (
+ LoadGraphic (ARISPACE_MASK_PMAP_ANIM));
+ hypercmaps[1] = CaptureColorMap (LoadColorMap (ARISPACE_COLOR_TAB));
+ }
+}
+
+BOOLEAN
+LoadHyperspace (void)
+{
+ hyper_dx = 0;
+ hyper_dy = 0;
+ hyper_extra = 0;
+ fuel_ticks = 1;
+
+ GLOBAL (ShipStamp.origin.x) = -MAX_X_UNIVERSE;
+ GLOBAL (ShipStamp.origin.y) = -MAX_Y_UNIVERSE;
+
+ LoadHyperData ();
+ {
+ FRAME F;
+
+ F = hyperstars[0];
+ hyperstars[0] = stars_in_space;
+ stars_in_space = F;
+ }
+
+ if (!(LastActivity & CHECK_LOAD))
+ RepairSISBorder ();
+ else
+ {
+ if (LOBYTE (LastActivity) == 0)
+ {
+ DrawSISFrame ();
+ }
+ else
+ {
+ ClearSISRect (DRAW_SIS_DISPLAY);
+ RepairSISBorder ();
+ }
+ }
+ if (!(GLOBAL (autopilot.x) != ~0 && GLOBAL (autopilot.y) != ~0))
+ {
+ DrawSISMessage (NULL);
+ }
+
+ SetContext (RadarContext);
+ SetContextBackGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x0E, 0x00), 0x6C));
+
+ SetContext (SpaceContext);
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ SetContextBackGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x07, 0x00, 0x00), 0x2F));
+ SetColorMap (GetColorMapAddress (hypercmaps[0]));
+ }
+ else
+ {
+ SetContextBackGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1A, 0x00), 0x2F));
+ SetColorMap (GetColorMapAddress (hypercmaps[1]));
+ SET_GAME_STATE (USED_BROADCASTER, 0);
+ SET_GAME_STATE (BROADCASTER_RESPONSE, 0);
+ }
+// ClearDrawable ();
+
+ ClearSISRect (CLEAR_SIS_RADAR);
+
+ return TRUE;
+}
+
+BOOLEAN
+FreeHyperspace (void)
+{
+ {
+ FRAME F;
+
+ F = hyperstars[0];
+ hyperstars[0] = stars_in_space;
+ stars_in_space = F;
+ }
+// FreeHyperData ();
+
+ return TRUE;
+}
+
+static void
+ElementToUniverse (ELEMENT *ElementPtr, POINT *pPt)
+{
+ SDWORD log_x, log_y;
+
+ log_x = GLOBAL_SIS (log_x)
+ + (ElementPtr->next.location.x - (LOG_SPACE_WIDTH >> 1));
+ log_y = GLOBAL_SIS (log_y)
+ + (ElementPtr->next.location.y - (LOG_SPACE_HEIGHT >> 1));
+ pPt->x = LOGX_TO_UNIVERSE (log_x);
+ pPt->y = LOGY_TO_UNIVERSE (log_y);
+}
+
+static void
+cleanup_hyperspace (void)
+{
+ HENCOUNTER hEncounter, hNextEncounter;
+
+ for (hEncounter = GetHeadEncounter ();
+ hEncounter != 0; hEncounter = hNextEncounter)
+ {
+ ENCOUNTER *EncounterPtr;
+
+ LockEncounter (hEncounter, &EncounterPtr);
+ hNextEncounter = GetSuccEncounter (EncounterPtr);
+ if (EncounterPtr->hElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (EncounterPtr->hElement, &ElementPtr);
+
+ if (ElementPtr->hTarget)
+ { // This is the encounter that collided with flagship
+ // Move the encounter to the head of the queue so that
+ // comm.c:RaceCommunication() gets the right one.
+ RemoveEncounter (hEncounter);
+ InsertEncounter (hEncounter, GetHeadEncounter ());
+ }
+
+ UnlockElement (EncounterPtr->hElement);
+ }
+ EncounterPtr->hElement = 0;
+ UnlockEncounter (hEncounter);
+ }
+}
+
+typedef enum
+{
+ RANDOM_ENCOUNTER_TRANSITION,
+ INTERPLANETARY_TRANSITION,
+ ARILOU_SPACE_TRANSITION
+} TRANSITION_TYPE;
+
+static void
+InterplanetaryTransition (ELEMENT *ElementPtr)
+{
+ GLOBAL (ip_planet) = 0;
+ GLOBAL (in_orbit) = 0;
+ GLOBAL (ShipFacing) = 0; /* Not reentering the system */
+ SET_GAME_STATE (USED_BROADCASTER, 0);
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ // Enter a solar system from HyperSpace.
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (ESCAPE_COUNTER, 0);
+ }
+ else
+ {
+ POINT pt;
+
+ GLOBAL (autopilot.x) = ~0;
+ GLOBAL (autopilot.y) = ~0;
+
+ ElementToUniverse (ElementPtr, &pt);
+ CurStarDescPtr = FindStar (NULL, &pt, 5, 5);
+ if (CurStarDescPtr->star_pt.x == ARILOU_HOME_X
+ && CurStarDescPtr->star_pt.y == ARILOU_HOME_Y)
+ {
+ // Meet the Arilou.
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ }
+ else
+ {
+ // Transition from QuasiSpace to HyperSpace through
+ // one of the permanent portals.
+ COUNT index;
+ const POINT portal_pt[] = QUASISPACE_PORTALS_HYPERSPACE_ENDPOINTS;
+
+ index = CurStarDescPtr - &star_array[NUM_SOLAR_SYSTEMS + 1];
+ GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (portal_pt[index].x);
+ GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (portal_pt[index].y);
+
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, 0);
+ }
+ }
+}
+
+/* Enter QuasiSpace from HyperSpace by any portal, or HyperSpace from
+ * QuasiSpace through the periodically opening portal.
+ */
+static void
+ArilouSpaceTransition (void)
+{
+ GLOBAL (ShipFacing) = 0; /* Not reentering the system */
+ SET_GAME_STATE (USED_BROADCASTER, 0);
+ GLOBAL (autopilot.x) = ~0;
+ GLOBAL (autopilot.y) = ~0;
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ // From HyperSpace to QuasiSpace.
+ GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (QUASI_SPACE_X);
+ GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (QUASI_SPACE_Y);
+ if (GET_GAME_STATE (PORTAL_COUNTER) == 0)
+ {
+ // Periodically appearing portal.
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, 3);
+ }
+ else
+ {
+ // Player-induced portal.
+ SET_GAME_STATE (PORTAL_COUNTER, 0);
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, 3);
+ }
+ }
+ else
+ {
+ // From QuasiSpace to HyperSpace through the periodically appearing
+ // portal.
+ GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (ARILOU_SPACE_X);
+ GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (ARILOU_SPACE_Y);
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, 0);
+ }
+}
+
+static void
+unhyper_transition (ELEMENT *ElementPtr)
+{
+ COUNT frame_index;
+
+ ElementPtr->state_flags |= CHANGING;
+
+ frame_index = GetFrameIndex (ElementPtr->current.image.frame);
+ if (frame_index == 0)
+ frame_index += ANGLE_TO_FACING (FULL_CIRCLE);
+ else if (frame_index < ANGLE_TO_FACING (FULL_CIRCLE))
+ frame_index = NORMALIZE_FACING (frame_index + 1);
+ else if (++frame_index == GetFrameCount (ElementPtr->current.image.frame))
+ {
+ cleanup_hyperspace ();
+
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ switch ((TRANSITION_TYPE) ElementPtr->turn_wait)
+ {
+ case RANDOM_ENCOUNTER_TRANSITION:
+ SaveSisHyperState ();
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ break;
+ case INTERPLANETARY_TRANSITION:
+ InterplanetaryTransition (ElementPtr);
+ break;
+ case ARILOU_SPACE_TRANSITION:
+ ArilouSpaceTransition ();
+ break;
+ }
+
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM);
+ return;
+ }
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame, frame_index);
+}
+
+static void
+init_transition (ELEMENT *ElementPtr0, ELEMENT *ElementPtr1,
+ TRANSITION_TYPE which_transition)
+{
+ SIZE dx, dy;
+ SIZE num_turns;
+ STARSHIP *StarShipPtr;
+
+ dx = WORLD_TO_VELOCITY (ElementPtr0->next.location.x
+ - ElementPtr1->next.location.x);
+ dy = WORLD_TO_VELOCITY (ElementPtr0->next.location.y
+ - ElementPtr1->next.location.y);
+
+ ElementPtr1->state_flags |= NONSOLID;
+ ElementPtr1->preprocess_func = unhyper_transition;
+ ElementPtr1->postprocess_func = NULL;
+ ElementPtr1->turn_wait = (BYTE) which_transition;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ num_turns = GetFrameCount (ElementPtr1->next.image.frame)
+ - ANGLE_TO_FACING (FULL_CIRCLE)
+ + NORMALIZE_FACING (ANGLE_TO_FACING (FULL_CIRCLE)
+ - StarShipPtr->ShipFacing);
+ if (num_turns == 0)
+ num_turns = 1;
+
+ SetVelocityComponents (&ElementPtr1->velocity,
+ dx / num_turns, dy / num_turns);
+}
+
+BOOLEAN
+hyper_transition (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ if (LastActivity & CHECK_LOAD)
+ {
+ LastActivity &= ~CHECK_LOAD;
+
+ ElementPtr->current = ElementPtr->next;
+ SetUpElement (ElementPtr);
+
+ ElementPtr->state_flags |= DEFY_PHYSICS;
+
+ return FALSE;
+ }
+ else
+ {
+ ElementPtr->preprocess_func =
+ (void (*) (struct element *ElementPtr)) hyper_transition;
+ ElementPtr->postprocess_func = NULL;
+ ElementPtr->state_flags |= NONSOLID;
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ GetFrameCount (ElementPtr->current.image.frame) - 1);
+ }
+ }
+ else
+ {
+ COUNT frame_index;
+
+ frame_index = GetFrameIndex (ElementPtr->current.image.frame);
+ if (frame_index-- <= ANGLE_TO_FACING (FULL_CIRCLE))
+ {
+ STARSHIP *StarShipPtr;
+
+ if (frame_index == ANGLE_TO_FACING (FULL_CIRCLE) - 1)
+ frame_index = 0;
+ else
+ frame_index = NORMALIZE_FACING (frame_index);
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (frame_index == StarShipPtr->ShipFacing)
+ {
+ ElementPtr->preprocess_func = ship_preprocess;
+ ElementPtr->postprocess_func = ship_postprocess;
+ ElementPtr->state_flags &= ~NONSOLID;
+ }
+ }
+
+ ElementPtr->state_flags |= CHANGING;
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ frame_index);
+
+ if (!(ElementPtr->state_flags & NONSOLID))
+ {
+ ElementPtr->current = ElementPtr->next;
+ SetUpElement (ElementPtr);
+
+ ElementPtr->state_flags |= DEFY_PHYSICS;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+hyper_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && GET_GAME_STATE (PORTAL_COUNTER) == 0)
+ {
+ SIZE dx, dy;
+ POINT pt;
+ STAR_DESC *SDPtr;
+ STARSHIP *StarShipPtr;
+
+ ElementToUniverse (ElementPtr0, &pt);
+
+ SDPtr = FindStar (NULL, &pt, 5, 5);
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ GetCurrentVelocityComponents (&ElementPtr1->velocity, &dx, &dy);
+ if (SDPtr == CurStarDescPtr
+ || (ElementPtr1->state_flags & APPEARING)
+ || !(dx || dy || (StarShipPtr->cur_status_flags
+ & (LEFT | RIGHT | THRUST | WEAPON | SPECIAL))))
+ {
+ CurStarDescPtr = SDPtr;
+ ElementPtr0->state_flags |= DEFY_PHYSICS | COLLISION;
+ }
+ else if ((GLOBAL (CurrentActivity) & IN_BATTLE)
+ && (GLOBAL (autopilot.x) == ~0
+ || GLOBAL (autopilot.y) == ~0
+ || (GLOBAL (autopilot.x) == SDPtr->star_pt.x
+ && GLOBAL (autopilot.y) == SDPtr->star_pt.y)))
+ {
+ CurStarDescPtr = SDPtr;
+ ElementPtr0->state_flags |= COLLISION;
+
+ init_transition (ElementPtr0, ElementPtr1,
+ INTERPLANETARY_TRANSITION);
+ }
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+hyper_death (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & DEFY_PHYSICS)
+ && (GLOBAL (CurrentActivity) & IN_BATTLE))
+ CurStarDescPtr = 0;
+}
+
+static void
+arilou_space_death (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & DEFY_PHYSICS)
+ || GET_GAME_STATE (ARILOU_SPACE_COUNTER) == 0)
+ {
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, 0);
+ }
+ else
+ {
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, 3);
+ }
+ }
+}
+
+static void
+arilou_space_collision (ELEMENT *ElementPtr0,
+ POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ COUNT which_side;
+
+ if (!(ElementPtr1->state_flags & PLAYER_SHIP))
+ return;
+
+ which_side = GET_GAME_STATE (ARILOU_SPACE_SIDE);
+ if (which_side == 0 || which_side == 3)
+ {
+ if (ElementPtr1->state_flags & DEFY_PHYSICS)
+ {
+ SET_GAME_STATE (ARILOU_SPACE_SIDE, which_side ^ 1);
+ }
+ else
+ {
+ init_transition (ElementPtr0, ElementPtr1,
+ ARILOU_SPACE_TRANSITION);
+ }
+ }
+
+ ElementPtr0->state_flags |= DEFY_PHYSICS | COLLISION;
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static HELEMENT
+AllocHyperElement (const POINT *elem_pt)
+{
+ HELEMENT hHyperSpaceElement;
+
+ hHyperSpaceElement = AllocElement ();
+ if (hHyperSpaceElement)
+ {
+ ELEMENT *HyperSpaceElementPtr;
+
+ LockElement (hHyperSpaceElement, &HyperSpaceElementPtr);
+ HyperSpaceElementPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ HyperSpaceElementPtr->state_flags = CHANGING | FINITE_LIFE;
+ HyperSpaceElementPtr->life_span = 1;
+ HyperSpaceElementPtr->mass_points = 1;
+
+ {
+ long lx, ly;
+
+ lx = UNIVERSE_TO_LOGX (elem_pt->x)
+ + (LOG_SPACE_WIDTH >> 1) - GLOBAL_SIS (log_x);
+ HyperSpaceElementPtr->current.location.x = WRAP_X (lx);
+
+ ly = UNIVERSE_TO_LOGY (elem_pt->y)
+ + (LOG_SPACE_HEIGHT >> 1) - GLOBAL_SIS (log_y);
+ HyperSpaceElementPtr->current.location.y = WRAP_Y (ly);
+ }
+
+ SetPrimType (&DisplayArray[HyperSpaceElementPtr->PrimIndex],
+ STAMP_PRIM);
+ HyperSpaceElementPtr->current.image.farray =
+ &hyperstars[1 + (GET_GAME_STATE (ARILOU_SPACE_SIDE) >> 1)];
+
+ UnlockElement (hHyperSpaceElement);
+ }
+
+ return hHyperSpaceElement;
+}
+
+static void
+AddAmbientElement (void)
+{
+ HELEMENT hHyperSpaceElement;
+
+ hHyperSpaceElement = AllocElement ();
+ if (hHyperSpaceElement)
+ {
+ SIZE dx, dy;
+ DWORD rand_val;
+ ELEMENT *HyperSpaceElementPtr;
+
+ LockElement (hHyperSpaceElement, &HyperSpaceElementPtr);
+ HyperSpaceElementPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ HyperSpaceElementPtr->state_flags =
+ APPEARING | FINITE_LIFE | NONSOLID;
+ SetPrimType (&DisplayArray[HyperSpaceElementPtr->PrimIndex],
+ STAMP_PRIM);
+ HyperSpaceElementPtr->preprocess_func = animation_preprocess;
+
+ rand_val = TFB_Random ();
+ dy = LOWORD (rand_val);
+ dx = (SIZE)(LOBYTE (dy) % SPACE_WIDTH) - (SPACE_WIDTH >> 1);
+ dy = (SIZE)(HIBYTE (dy) % SPACE_HEIGHT) - (SPACE_HEIGHT >> 1);
+ HyperSpaceElementPtr->current.location.x = (LOG_SPACE_WIDTH >> 1)
+ + DISPLAY_TO_WORLD (dx);
+ HyperSpaceElementPtr->current.location.y = (LOG_SPACE_HEIGHT >> 1)
+ + DISPLAY_TO_WORLD (dy);
+ HyperSpaceElementPtr->current.image.farray = &stars_in_space;
+
+ if (HIWORD (rand_val) & 7)
+ {
+ HyperSpaceElementPtr->life_span = 14;
+ HyperSpaceElementPtr->current.image.frame = stars_in_space;
+ }
+ else
+ {
+ HyperSpaceElementPtr->life_span = 12;
+ HyperSpaceElementPtr->current.image.frame =
+ SetAbsFrameIndex (stars_in_space, 14);
+ }
+
+ UnlockElement (hHyperSpaceElement);
+
+ InsertElement (hHyperSpaceElement, GetHeadElement ());
+ }
+}
+
+#define NUM_VORTEX_TRANSITIONS 9
+#define VORTEX_WAIT 1
+
+static void
+encounter_transition (ELEMENT *ElementPtr)
+{
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->life_span = 1;
+ if (ElementPtr->turn_wait)
+ {
+ --ElementPtr->turn_wait;
+ }
+ else
+ {
+ FRAME f;
+
+ if (ElementPtr->hit_points)
+ {
+ f = DecFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->next.image.frame = f;
+ }
+ else
+ {
+ f = IncFrameIndex (ElementPtr->current.image.frame);
+ if (f != ElementPtr->current.image.farray[0])
+ ElementPtr->next.image.frame = f;
+ else
+ ElementPtr->death_func = NULL;
+ }
+
+ ElementPtr->turn_wait = VORTEX_WAIT;
+ }
+}
+
+static HELEMENT
+getSisElement (void)
+{
+ HSTARSHIP hSis;
+ HELEMENT hShip;
+ STARSHIP *StarShipPtr;
+
+ hSis = GetHeadLink (&race_q[RPG_PLAYER_NUM]);
+ if (!hSis)
+ return NULL;
+
+ StarShipPtr = LockStarShip (&race_q[RPG_PLAYER_NUM], hSis);
+ hShip = StarShipPtr->hShip;
+ UnlockStarShip (&race_q[RPG_PLAYER_NUM], hSis);
+
+#ifdef DEBUG
+ {
+ ELEMENT *ElementPtr;
+ LockElement (hShip, &ElementPtr);
+ assert (ElementPtr->state_flags & PLAYER_SHIP);
+ UnlockElement (hShip);
+ }
+#endif
+
+ return hShip;
+}
+
+static void
+encounter_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ HENCOUNTER hEncounter;
+ HENCOUNTER hNextEncounter;
+
+ if (!(ElementPtr1->state_flags & PLAYER_SHIP)
+ || !(GLOBAL (CurrentActivity) & IN_BATTLE))
+ return;
+
+ init_transition (ElementPtr0, ElementPtr1, RANDOM_ENCOUNTER_TRANSITION);
+
+ for (hEncounter = GetHeadEncounter ();
+ hEncounter != 0; hEncounter = hNextEncounter)
+ {
+ ENCOUNTER *EncounterPtr;
+
+ LockEncounter (hEncounter, &EncounterPtr);
+ hNextEncounter = GetSuccEncounter (EncounterPtr);
+ if (EncounterPtr->hElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (EncounterPtr->hElement, &ElementPtr);
+ ElementPtr->state_flags |= NONSOLID | IGNORE_SIMILAR;
+ UnlockElement (EncounterPtr->hElement);
+ }
+ UnlockEncounter (hEncounter);
+ }
+
+ // Mark this element as collided with flagship
+ // XXX: We could simply set hTarget to 1 or to ElementPtr1,
+ // but that would be too hacky ;)
+ ElementPtr0->hTarget = getSisElement ();
+ ZeroVelocityComponents (&ElementPtr0->velocity);
+
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static HELEMENT
+AddEncounterElement (ENCOUNTER *EncounterPtr, POINT *puniverse)
+{
+ BOOLEAN NewEncounter;
+ HELEMENT hElement;
+ POINT enc_pt;
+
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) >= 2)
+ return 0;
+
+ if (EncounterPtr->flags & ENCOUNTER_REFORMING)
+ {
+ EncounterPtr->flags &= ~ENCOUNTER_REFORMING;
+
+ EncounterPtr->transition_state = 100;
+ if ((EncounterPtr->flags & ONE_SHOT_ENCOUNTER)
+ || EncounterPtr->num_ships == 0)
+ return 0;
+ }
+
+ if (EncounterPtr->num_ships)
+ {
+ NewEncounter = FALSE;
+ enc_pt = EncounterPtr->loc_pt;
+ }
+ else
+ {
+ BYTE Type;
+ SIZE dx, dy;
+ COUNT i;
+ COUNT NumShips;
+ DWORD radius_squared;
+ BYTE EncounterMakeup[] =
+ {
+ RACE_ENCOUNTER_MAKEUP
+ };
+
+ NewEncounter = TRUE;
+
+ radius_squared = (DWORD)EncounterPtr->radius * EncounterPtr->radius;
+
+ Type = EncounterPtr->race_id;
+ NumShips = LONIBBLE (EncounterMakeup[Type]);
+ for (i = HINIBBLE (EncounterMakeup[Type]) - NumShips; i; --i)
+ {
+ if ((COUNT)TFB_Random () % 100 < 50)
+ ++NumShips;
+ }
+
+ if (NumShips > MAX_HYPER_SHIPS)
+ NumShips = MAX_HYPER_SHIPS;
+
+ EncounterPtr->num_ships = NumShips;
+ for (i = 0; i < NumShips; ++i)
+ {
+ BRIEF_SHIP_INFO *BSIPtr = &EncounterPtr->ShipList[i];
+ HFLEETINFO hStarShip =
+ GetStarShipFromIndex (&GLOBAL (avail_race_q), Type);
+ FLEET_INFO *FleetPtr =
+ LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ BSIPtr->race_id = Type;
+ BSIPtr->crew_level = FleetPtr->crew_level;
+ BSIPtr->max_crew = FleetPtr->max_crew;
+ BSIPtr->max_energy = FleetPtr->max_energy;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ do
+ {
+ DWORD rand_val;
+
+ rand_val = TFB_Random ();
+
+ enc_pt.x = puniverse->x
+ + (LOWORD (rand_val) % (XOFFS << 1)) - XOFFS;
+ if (enc_pt.x < 0)
+ enc_pt.x = 0;
+ else if (enc_pt.x > MAX_X_UNIVERSE)
+ enc_pt.x = MAX_X_UNIVERSE;
+ enc_pt.y = puniverse->y
+ + (HIWORD (rand_val) % (YOFFS << 1)) - YOFFS;
+ if (enc_pt.y < 0)
+ enc_pt.y = 0;
+ else if (enc_pt.y > MAX_Y_UNIVERSE)
+ enc_pt.y = MAX_Y_UNIVERSE;
+
+ dx = enc_pt.x - EncounterPtr->origin.x;
+ dy = enc_pt.y - EncounterPtr->origin.y;
+ } while ((DWORD)((long)dx * dx + (long)dy * dy) > radius_squared);
+
+ EncounterPtr->loc_pt = enc_pt;
+ EncounterPtr->log_x = UNIVERSE_TO_LOGX (enc_pt.x);
+ EncounterPtr->log_y = UNIVERSE_TO_LOGY (enc_pt.y);
+ }
+
+ hElement = AllocHyperElement (&enc_pt);
+ if (hElement)
+ {
+ SIZE i;
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+
+ i = EncounterPtr->transition_state;
+ if (i || NewEncounter)
+ {
+ if (i < 0)
+ {
+ i = -i;
+ ElementPtr->hit_points = 1;
+ }
+ if (i == 0 || i > NUM_VORTEX_TRANSITIONS)
+ i = NUM_VORTEX_TRANSITIONS;
+
+ ElementPtr->current.image.frame = SetRelFrameIndex (
+ ElementPtr->current.image.farray[0], -i);
+ ElementPtr->death_func = encounter_transition;
+ }
+ else
+ {
+ ElementPtr->current.image.frame =
+ DecFrameIndex (ElementPtr->current.image.farray[0]);
+ }
+
+ ElementPtr->turn_wait = VORTEX_WAIT;
+ ElementPtr->preprocess_func = NULL;
+ ElementPtr->postprocess_func = NULL;
+ ElementPtr->collision_func = encounter_collision;
+
+ SetUpElement (ElementPtr);
+
+ ElementPtr->IntersectControl.IntersectStamp.frame =
+ DecFrameIndex (stars_in_space);
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM);
+ ElementPtr->state_flags |= NONSOLID | IGNORE_VELOCITY;
+
+ UnlockElement (hElement);
+
+ InsertElement (hElement, GetTailElement ());
+ }
+
+ EncounterPtr->hElement = hElement;
+ return hElement;
+}
+
+#define GRID_OFFSET 200
+
+static void
+DrawHyperGrid (COORD ux, COORD uy, COORD ox, COORD oy)
+{
+ COORD sx, sy, ex, ey;
+ RECT r;
+
+ ClearDrawable ();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x10, 0x00), 0x6B));
+
+ sx = ux - (RADAR_SCAN_WIDTH >> 1);
+ if (sx < 0)
+ sx = 0;
+ else
+ sx -= sx % GRID_OFFSET;
+ ex = ux + (RADAR_SCAN_WIDTH >> 1);
+ if (ex > MAX_X_UNIVERSE + 1)
+ ex = MAX_X_UNIVERSE + 1;
+
+ sy = uy - (RADAR_SCAN_HEIGHT >> 1);
+ if (sy < 0)
+ sy = 0;
+ else
+ sy -= sy % GRID_OFFSET;
+ ey = uy + (RADAR_SCAN_HEIGHT >> 1);
+ if (ey > MAX_Y_UNIVERSE + 1)
+ ey = MAX_Y_UNIVERSE + 1;
+
+ r.corner.y = (COORD) ((long)(MAX_Y_UNIVERSE - ey)
+ * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy;
+ r.extent.width = 1;
+ r.extent.height = ((COORD) ((long)(MAX_Y_UNIVERSE - sy)
+ * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy) - r.corner.y + 1;
+ for (ux = sx; ux <= ex; ux += GRID_OFFSET)
+ {
+ r.corner.x = (COORD) ((long)ux * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox;
+ DrawFilledRectangle (&r);
+ }
+
+ r.corner.x = (COORD) ((long)sx * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox;
+ r.extent.width = ((COORD) ((long)ex * RADAR_WIDTH / RADAR_SCAN_WIDTH)
+ - ox) - r.corner.x + 1;
+ r.extent.height = 1;
+ for (uy = sy; uy <= ey; uy += GRID_OFFSET)
+ {
+ r.corner.y = (COORD)((long)(MAX_Y_UNIVERSE - uy)
+ * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy;
+ DrawFilledRectangle (&r);
+ }
+}
+
+// Returns false iff the encounter is to be removed.
+static bool
+ProcessEncounter (ENCOUNTER *EncounterPtr, POINT *puniverse,
+ COORD ox, COORD oy, STAMP *stamp)
+{
+ ELEMENT *ElementPtr;
+ COORD ex, ey;
+
+ if (EncounterPtr->hElement == 0
+ && AddEncounterElement (EncounterPtr, puniverse) == 0)
+ return false;
+
+ LockElement (EncounterPtr->hElement, &ElementPtr);
+
+ if (ElementPtr->death_func)
+ {
+ if (EncounterPtr->transition_state && ElementPtr->turn_wait == 0)
+ {
+ --EncounterPtr->transition_state;
+ if (EncounterPtr->transition_state >= NUM_VORTEX_TRANSITIONS)
+ ++ElementPtr->turn_wait;
+ else if (EncounterPtr->transition_state ==
+ -NUM_VORTEX_TRANSITIONS)
+ {
+ ElementPtr->death_func = NULL;
+ UnlockElement (EncounterPtr->hElement);
+ return false;
+ }
+ else
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex],
+ STAMP_PRIM);
+ }
+ }
+ else
+ {
+ SIZE delta_x, delta_y;
+ COUNT encounter_radius;
+
+ ElementPtr->life_span = 1;
+ GetNextVelocityComponents (&ElementPtr->velocity,
+ &delta_x, &delta_y, 1);
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else if (!ElementPtr->hTarget)
+ { // This is an encounter that did not collide with flagship
+ // The colliding encounter does not move
+ COUNT cur_facing, delta_facing;
+
+ cur_facing = ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity));
+ delta_facing = NORMALIZE_FACING (cur_facing - ANGLE_TO_FACING (
+ ARCTAN (puniverse->x - EncounterPtr->loc_pt.x,
+ puniverse->y - EncounterPtr->loc_pt.y)));
+ if (delta_facing || (delta_x == 0 && delta_y == 0))
+ {
+ SIZE speed;
+ const SIZE RaceHyperSpeed[] =
+ {
+ RACE_HYPER_SPEED
+ };
+
+#define ENCOUNTER_TRACK_WAIT 3
+ speed = RaceHyperSpeed[EncounterPtr->race_id];
+ if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
+ --cur_facing;
+ else
+ ++cur_facing;
+ if (NORMALIZE_FACING (delta_facing + ANGLE_TO_FACING (OCTANT))
+ > ANGLE_TO_FACING (QUADRANT))
+ {
+ if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
+ --cur_facing;
+ else
+ ++cur_facing;
+ speed >>= 1;
+ }
+ cur_facing = FACING_TO_ANGLE (cur_facing);
+ SetVelocityComponents (&ElementPtr->velocity,
+ COSINE (cur_facing, speed), SINE (cur_facing, speed));
+ GetNextVelocityComponents (&ElementPtr->velocity,
+ &delta_x, &delta_y, 1);
+
+ ElementPtr->thrust_wait = ENCOUNTER_TRACK_WAIT;
+ }
+ }
+ EncounterPtr->log_x += delta_x;
+ EncounterPtr->log_y -= delta_y;
+ EncounterPtr->loc_pt.x = LOGX_TO_UNIVERSE (EncounterPtr->log_x);
+ EncounterPtr->loc_pt.y = LOGY_TO_UNIVERSE (EncounterPtr->log_y);
+
+ encounter_radius = EncounterPtr->radius + (GRID_OFFSET >> 1);
+ delta_x = EncounterPtr->loc_pt.x - EncounterPtr->origin.x;
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ delta_y = EncounterPtr->loc_pt.y - EncounterPtr->origin.y;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ if ((COUNT)delta_x >= encounter_radius
+ || (COUNT)delta_y >= encounter_radius
+ || (DWORD)delta_x * delta_x + (DWORD)delta_y * delta_y >=
+ (DWORD)encounter_radius * encounter_radius)
+ {
+ // Encounter globe traveled outside the SoI and now disappears
+ ElementPtr->state_flags |= NONSOLID;
+ ElementPtr->life_span = 0;
+
+ if (EncounterPtr->transition_state == 0)
+ {
+ ElementPtr->death_func = encounter_transition;
+ EncounterPtr->transition_state = -1;
+ ElementPtr->hit_points = 1;
+ }
+ else
+ {
+ ElementPtr->death_func = NULL;
+ UnlockElement (EncounterPtr->hElement);
+ return false;
+ }
+ }
+ }
+
+ ex = EncounterPtr->loc_pt.x;
+ ey = EncounterPtr->loc_pt.y;
+ if (ex - puniverse->x >= -UNIT_SCREEN_WIDTH
+ && ex - puniverse->x <= UNIT_SCREEN_WIDTH
+ && ey - puniverse->y >= -UNIT_SCREEN_HEIGHT
+ && ey - puniverse->y <= UNIT_SCREEN_HEIGHT)
+ {
+ ElementPtr->next.location.x =
+ (SIZE)(EncounterPtr->log_x - GLOBAL_SIS (log_x))
+ + (LOG_SPACE_WIDTH >> 1);
+ ElementPtr->next.location.y =
+ (SIZE)(EncounterPtr->log_y - GLOBAL_SIS (log_y))
+ + (LOG_SPACE_HEIGHT >> 1);
+ if ((ElementPtr->state_flags & NONSOLID)
+ && EncounterPtr->transition_state == 0)
+ {
+ ElementPtr->current.location = ElementPtr->next.location;
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex],
+ STAMP_PRIM);
+ if (ElementPtr->death_func == 0)
+ {
+ InitIntersectStartPoint (ElementPtr);
+ ElementPtr->state_flags &= ~NONSOLID;
+ }
+ }
+ }
+ else
+ {
+ ElementPtr->state_flags |= NONSOLID;
+ if (ex - puniverse->x < -XOFFS || ex - puniverse->x > XOFFS
+ || ey - puniverse->y < -YOFFS || ey - puniverse->y > YOFFS)
+ {
+ ElementPtr->life_span = 0;
+ ElementPtr->death_func = NULL;
+ UnlockElement (EncounterPtr->hElement);
+ return false;
+ }
+
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM);
+ }
+
+ UnlockElement (EncounterPtr->hElement);
+
+ stamp->origin.x = (COORD)((long)ex * RADAR_WIDTH / RADAR_SCAN_WIDTH) - ox;
+ stamp->origin.y = (COORD)((long)(MAX_Y_UNIVERSE - ey) * RADAR_HEIGHT
+ / RADAR_SCAN_HEIGHT) - oy;
+ DrawStamp (stamp);
+
+ return true;
+}
+
+static void
+ProcessEncounters (POINT *puniverse, COORD ox, COORD oy)
+{
+ STAMP stamp;
+ HENCOUNTER hEncounter, hNextEncounter;
+
+ stamp.frame = SetAbsFrameIndex (stars_in_space, 91);
+ for (hEncounter = GetHeadEncounter ();
+ hEncounter; hEncounter = hNextEncounter)
+ {
+ ENCOUNTER *EncounterPtr;
+
+ LockEncounter (hEncounter, &EncounterPtr);
+ hNextEncounter = GetSuccEncounter (EncounterPtr);
+
+ if (!ProcessEncounter (EncounterPtr, puniverse, ox, oy, &stamp))
+ {
+ UnlockEncounter (hEncounter);
+ RemoveEncounter (hEncounter);
+ FreeEncounter (hEncounter);
+ continue;
+ }
+
+ UnlockEncounter (hEncounter);
+ }
+}
+
+void
+SeedUniverse (void)
+{
+ COORD ox, oy;
+ COORD sx, sy, ex, ey;
+ SWORD portalCounter, arilouSpaceCounter, arilouSpaceSide;
+ POINT universe;
+ FRAME blip_frame;
+ STAMP s;
+ STAR_DESC *SDPtr;
+ HELEMENT hHyperSpaceElement;
+ ELEMENT *HyperSpaceElementPtr;
+
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+
+ blip_frame = SetAbsFrameIndex (stars_in_space, 90);
+
+ SetContext (RadarContext);
+ BatchGraphics ();
+
+ ox = (COORD)((long)universe.x * RADAR_WIDTH / RADAR_SCAN_WIDTH)
+ - (RADAR_WIDTH >> 1);
+ oy = (COORD)((long)(MAX_Y_UNIVERSE - universe.y)
+ * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - (RADAR_HEIGHT >> 1);
+
+ ex = (COORD)((long)GLOBAL (ShipStamp.origin.x)
+ * RADAR_WIDTH / RADAR_SCAN_WIDTH) - (RADAR_WIDTH >> 1);
+ ey = (COORD)((long)(MAX_Y_UNIVERSE - GLOBAL (ShipStamp.origin.y))
+ * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - (RADAR_HEIGHT >> 1);
+
+ arilouSpaceCounter = GET_GAME_STATE (ARILOU_SPACE_COUNTER);
+ arilouSpaceSide = GET_GAME_STATE (ARILOU_SPACE_SIDE);
+
+// if (ox != ex || oy != ey)
+ {
+ DrawHyperGrid (universe.x, universe.y, ox, oy);
+
+ {
+ SDPtr = 0;
+ while ((SDPtr = FindStar (SDPtr, &universe, XOFFS, YOFFS)))
+ {
+ BYTE star_type;
+
+ ex = SDPtr->star_pt.x;
+ ey = SDPtr->star_pt.y;
+ star_type = STAR_TYPE (SDPtr->Type);
+ if (arilouSpaceSide >= 2 &&
+ ex == ARILOU_HOME_X && ey == ARILOU_HOME_Y)
+ star_type = SUPER_GIANT_STAR;
+
+ s.origin.x = (COORD)((long)ex * RADAR_WIDTH
+ / RADAR_SCAN_WIDTH) - ox;
+ s.origin.y = (COORD)((long)(MAX_Y_UNIVERSE - ey)
+ * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy;
+ s.frame = SetRelFrameIndex (blip_frame,
+ star_type + 2);
+ DrawStamp (&s);
+ }
+ }
+ }
+
+ portalCounter = GET_GAME_STATE (PORTAL_COUNTER);
+ if (portalCounter || arilouSpaceCounter)
+ {
+ COUNT i;
+ STAR_DESC SD[2];
+ // This array is filled with the STAR_DESC's of
+ // QuasiSpace portals that need to be taken into account.
+ // i is set to the number of active portals (max 2).
+
+ i = 0;
+ if (portalCounter)
+ {
+ // A player-created QuasiSpace portal is opening.
+ static POINT portal_pt;
+
+ SD[i].Index = ((portalCounter - 1) >> 1) + 18;
+ if (portalCounter == 1)
+ portal_pt = universe;
+ SD[i].star_pt = portal_pt;
+ ++i;
+
+ if (++portalCounter == (10 + 1))
+ portalCounter = (9 + 1);
+
+ SET_GAME_STATE (PORTAL_COUNTER, portalCounter);
+ }
+
+ if (arilouSpaceCounter)
+ {
+ // The periodically appearing QuasiSpace portal is open.
+ SD[i].Index = arilouSpaceCounter >> 1;
+ if (arilouSpaceSide <= 1)
+ {
+ // The player is in HyperSpace
+ SD[i].Index += 18;
+ SD[i].star_pt.x = ARILOU_SPACE_X;
+ SD[i].star_pt.y = ARILOU_SPACE_Y;
+ }
+ else
+ {
+ // The player is in QuasiSpace
+ SD[i].star_pt.x = QUASI_SPACE_X;
+ SD[i].star_pt.y = QUASI_SPACE_Y;
+ }
+ ++i;
+ }
+
+ // Process the i portals from SD.
+ do
+ {
+ --i;
+ sx = SD[i].star_pt.x - universe.x + XOFFS;
+ sy = SD[i].star_pt.y - universe.y + YOFFS;
+ if (sx < 0 || sy < 0 || sx >= (XOFFS << 1) || sy >= (YOFFS << 1))
+ continue;
+
+ ex = SD[i].star_pt.x;
+ ey = SD[i].star_pt.y;
+ s.origin.x = (COORD)((long)ex * RADAR_WIDTH / RADAR_SCAN_WIDTH)
+ - ox;
+ s.origin.y = (COORD)((long)(MAX_Y_UNIVERSE - ey)
+ * RADAR_HEIGHT / RADAR_SCAN_HEIGHT) - oy;
+ s.frame = SetAbsFrameIndex (stars_in_space, 95);
+ DrawStamp (&s);
+
+ ex -= universe.x;
+ if (ex < 0)
+ ex = -ex;
+ ey -= universe.y;
+ if (ey < 0)
+ ey = -ey;
+
+ if (ex > (XOFFS / NUM_RADAR_SCREENS)
+ || ey > (YOFFS / NUM_RADAR_SCREENS))
+ continue;
+
+ hHyperSpaceElement = AllocHyperElement (&SD[i].star_pt);
+ if (hHyperSpaceElement == 0)
+ continue;
+
+ LockElement (hHyperSpaceElement, &HyperSpaceElementPtr);
+ HyperSpaceElementPtr->current.image.frame = SetAbsFrameIndex (
+ hyperstars[1 + (GET_GAME_STATE (ARILOU_SPACE_SIDE) >> 1)],
+ SD[i].Index);
+ HyperSpaceElementPtr->preprocess_func = NULL;
+ HyperSpaceElementPtr->postprocess_func = NULL;
+ HyperSpaceElementPtr->collision_func = arilou_space_collision;
+
+ SetUpElement (HyperSpaceElementPtr);
+
+ if (arilouSpaceSide == 1 || arilouSpaceSide == 2)
+ HyperSpaceElementPtr->death_func = arilou_space_death;
+ else
+ {
+ HyperSpaceElementPtr->death_func = NULL;
+ HyperSpaceElementPtr->IntersectControl.IntersectStamp.frame =
+ DecFrameIndex (stars_in_space);
+ }
+
+ UnlockElement (hHyperSpaceElement);
+
+ InsertElement (hHyperSpaceElement, GetHeadElement ());
+ } while (i);
+ }
+
+ {
+ SDPtr = 0;
+ while ((SDPtr = FindStar (SDPtr, &universe, XOFFS, YOFFS)))
+ {
+ BYTE star_type;
+
+ ex = SDPtr->star_pt.x - universe.x;
+ if (ex < 0)
+ ex = -ex;
+ ey = SDPtr->star_pt.y - universe.y;
+ if (ey < 0)
+ ey = -ey;
+ if (ex > (XOFFS / NUM_RADAR_SCREENS)
+ || ey > (YOFFS / NUM_RADAR_SCREENS))
+ continue;
+
+ hHyperSpaceElement = AllocHyperElement (&SDPtr->star_pt);
+ if (hHyperSpaceElement == 0)
+ continue;
+
+ star_type = SDPtr->Type;
+
+ LockElement (hHyperSpaceElement, &HyperSpaceElementPtr);
+ HyperSpaceElementPtr->current.image.frame = SetAbsFrameIndex (
+ hyperstars[1 + (GET_GAME_STATE (ARILOU_SPACE_SIDE) >> 1)],
+ STAR_TYPE (star_type) * NUM_STAR_COLORS
+ + STAR_COLOR (star_type));
+ HyperSpaceElementPtr->preprocess_func = NULL;
+ HyperSpaceElementPtr->postprocess_func = NULL;
+ HyperSpaceElementPtr->collision_func = hyper_collision;
+
+ SetUpElement (HyperSpaceElementPtr);
+
+ if (SDPtr == CurStarDescPtr
+ && GET_GAME_STATE (PORTAL_COUNTER) == 0)
+ HyperSpaceElementPtr->death_func = hyper_death;
+ else
+ {
+ HyperSpaceElementPtr->death_func = NULL;
+ HyperSpaceElementPtr->IntersectControl.IntersectStamp.frame =
+ DecFrameIndex (stars_in_space);
+ }
+ UnlockElement (hHyperSpaceElement);
+
+ InsertElement (hHyperSpaceElement, GetHeadElement ());
+ }
+ ProcessEncounters (&universe, ox, oy);
+ }
+
+ s.origin.x = RADAR_WIDTH >> 1;
+ s.origin.y = RADAR_HEIGHT >> 1;
+ s.frame = blip_frame;
+ DrawStamp (&s);
+
+ {
+ // draws borders to mini-map
+
+ RECT r;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0E, 0x0E, 0x0E), 0x00));
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = RADAR_WIDTH - 1;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.extent.width = 1;
+ r.extent.height = RADAR_HEIGHT - 1;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x06, 0x06, 0x06), 0x00));
+ r.corner.x = RADAR_WIDTH - 1;
+ r.corner.y = 1;
+ r.extent.height = RADAR_HEIGHT - 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = 1;
+ r.corner.y = RADAR_HEIGHT - 1;
+ r.extent.width = RADAR_WIDTH - 2;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x00));
+ r.corner.x = 0;
+ DrawPoint (&r.corner);
+ r.corner.x = RADAR_WIDTH - 1;
+ r.corner.y = 0;
+ DrawPoint (&r.corner);
+ }
+
+ UnbatchGraphics ();
+
+ SetContext (StatusContext);
+
+ if (!(LOWORD (TFB_Random ()) & 7))
+ AddAmbientElement ();
+
+ if (universe.x != GLOBAL (ShipStamp.origin.x)
+ || universe.y != GLOBAL (ShipStamp.origin.y))
+ {
+ GLOBAL (ShipStamp.origin) = universe;
+ DrawHyperCoords (universe);
+ }
+}
+
+static BOOLEAN
+DoHyperspaceMenu (MENU_STATE *pMS)
+{
+ BOOLEAN select = PulsedInputState.menu[KEY_MENU_SELECT];
+ BOOLEAN handled;
+
+ if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
+ return FALSE;
+
+ handled = DoMenuChooser (pMS, PM_STARMAP);
+ if (handled)
+ return TRUE;
+
+ if (!select)
+ return TRUE;
+
+ SetFlashRect (NULL);
+
+ switch (pMS->CurState)
+ {
+ case EQUIP_DEVICE:
+ select = DevicesMenu ();
+ if (GET_GAME_STATE (PORTAL_COUNTER))
+ { // A player-induced portal to QuasiSpace is opening
+ return FALSE;
+ }
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ { // Selected Talking Pet, going into conversation
+ return FALSE;
+ }
+ break;
+ case CARGO:
+ CargoMenu ();
+ break;
+ case ROSTER:
+ select = RosterMenu ();
+ break;
+ case GAME_MENU:
+ if (!GameOptions ())
+ return FALSE; // abort or load
+ break;
+ case STARMAP:
+ StarMap ();
+ return FALSE;
+ case NAVIGATION:
+ return FALSE;
+ }
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (select)
+ { // 3DO menu jumps to NAVIGATE after a successful submenu run
+ if (optWhichMenu != OPT_PC)
+ pMS->CurState = NAVIGATION;
+ DrawMenuStateStrings (PM_STARMAP, pMS->CurState);
+ }
+ SetFlashRect (SFR_MENU_3DO);
+ }
+
+ return TRUE;
+}
+
+void
+HyperspaceMenu (void)
+{
+ Color OldColor;
+ CONTEXT OldContext;
+ MENU_STATE MenuState;
+
+UnbatchGraphics ();
+
+ OldContext = SetContext (SpaceContext);
+ OldColor = SetContextBackGroundColor (BLACK_COLOR);
+
+
+ memset (&MenuState, 0, sizeof (MenuState));
+ MenuState.InputFunc = DoHyperspaceMenu;
+ MenuState.Initialized = TRUE;
+ MenuState.CurState = STARMAP;
+
+ DrawMenuStateStrings (PM_STARMAP, STARMAP);
+ SetFlashRect (SFR_MENU_3DO);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ DoInput (&MenuState, TRUE);
+
+ SetFlashRect (NULL);
+
+ SetContext (SpaceContext);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ ClearSISRect (CLEAR_SIS_RADAR);
+ WaitForNoInput (ONE_SECOND / 2, FALSE);
+ }
+
+ SetContextBackGroundColor (OldColor);
+ SetContext (OldContext);
+ if (!(GLOBAL (CurrentActivity) & IN_BATTLE))
+ cleanup_hyperspace ();
+
+BatchGraphics ();
+}
+
+void
+SaveSisHyperState (void)
+{
+ HELEMENT hSisElement;
+ ELEMENT *ElementPtr;
+ STARSHIP *StarShipPtr;
+
+ // Update 'GLOBAL (ShipFacing)' to the direction the flagship is facing
+ hSisElement = getSisElement ();
+ if (!hSisElement)
+ { // Happens when saving a game from Hyperspace encounter screen
+ return;
+ }
+ //if (ElementPtr->state_flags & PLAYER_SHIP)
+ LockElement (hSisElement, &ElementPtr);
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ // XXX: Solar system reentry test depends on ShipFacing != 0
+ GLOBAL (ShipFacing) = StarShipPtr->ShipFacing + 1;
+ UnlockElement (hSisElement);
+}
+
diff --git a/src/uqm/hyper.h b/src/uqm/hyper.h
new file mode 100644
index 0000000..b53c324
--- /dev/null
+++ b/src/uqm/hyper.h
@@ -0,0 +1,90 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_HYPER_H_
+#define UQM_HYPER_H_
+
+#include "element.h"
+#include "units.h"
+ // for UNIT_SCREEN_WIDTH/HEIGHT
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define NUM_RADAR_SCREENS 12
+
+#define RADAR_SCAN_WIDTH (UNIT_SCREEN_WIDTH * NUM_RADAR_SCREENS)
+#define RADAR_SCAN_HEIGHT (UNIT_SCREEN_HEIGHT * NUM_RADAR_SCREENS)
+
+// Hyperspace coordinates of the naturally occuring portal into QuasiSpace
+#define ARILOU_SPACE_X 438
+#define ARILOU_SPACE_Y 6372
+
+// QuasiSpace coordinates of the same portal
+#define QUASI_SPACE_X 5000
+#define QUASI_SPACE_Y 5000
+
+// QuasiSpace coordinates of the Arilou home world
+#define ARILOU_HOME_X (QUASI_SPACE_X + ((RADAR_SCAN_WIDTH >> 1) * 3))
+#define ARILOU_HOME_Y (QUASI_SPACE_Y + ((RADAR_SCAN_HEIGHT >> 1) * 3))
+
+// HyperSpace coordinates of the locations where the QuasiSpace portals
+// take you.
+#define QUASISPACE_PORTALS_HYPERSPACE_ENDPOINTS \
+ { \
+ {4091, 7748}, \
+ {3184, 4906}, \
+ {9211, 6104}, \
+ {5673, 1207}, \
+ {1910, 926}, \
+ {8607, 151}, \
+ { 50, 1647}, \
+ {6117, 4131}, \
+ {5658, 9712}, \
+ {2302, 3988}, \
+ { 112, 9409}, \
+ {7752, 8906}, \
+ { 368, 6332}, \
+ {9735, 3153}, \
+ {5850, 6213}, \
+ }
+
+// Hyperspace coordinates of the Sol system
+// Should be the same as in plandata.c
+#define SOL_X 1752
+#define SOL_Y 1450
+
+
+extern BOOLEAN LoadHyperspace (void);
+extern BOOLEAN FreeHyperspace (void);
+extern void SeedUniverse (void);
+extern void MoveSIS (SIZE *pdx, SIZE *pdy);
+
+extern void FreeHyperData (void);
+extern void check_hyperspace_encounter (void);
+extern BOOLEAN hyper_transition (ELEMENT *ElementPtr);
+
+extern void HyperspaceMenu (void);
+extern void SaveSisHyperState (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_HYPER_H_ */
diff --git a/src/uqm/ifontres.h b/src/uqm/ifontres.h
new file mode 100644
index 0000000..4d9f0a8
--- /dev/null
+++ b/src/uqm/ifontres.h
@@ -0,0 +1,12 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define LANDER_FONT "font.lander"
+#define MICRO_FONT "font.micro"
+#define PLAYER_FONT "font.player"
+#define PT13AA_FONT "credits.font.pt13"
+#define PT17AA_FONT "credits.font.pt17"
+#define PT45AA_FONT "credits.font.pt45"
+#define STARCON_FONT "font.starcon"
+#define TINY_FONT "font.tiny"
diff --git a/src/uqm/igfxres.h b/src/uqm/igfxres.h
new file mode 100644
index 0000000..e31726d
--- /dev/null
+++ b/src/uqm/igfxres.h
@@ -0,0 +1,274 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ACTIVITY_ANIM "graphics.activity"
+#define AMBIENT_MASK_PMAP_ANIM "graphics.ambient"
+#define AQUA_MASK_PMAP_ANIM "graphics.aquahelix"
+#define ARISPACE_MASK_PMAP_ANIM "graphics.quasispace"
+#define ASTEROID_BIG_MASK_PMAP_ANIM "graphics.asteroid.large"
+#define ASTEROID_MED_MASK_PMAP_ANIM "graphics.asteroid.medium"
+#define ASTEROID_SML_MASK_PMAP_ANIM "graphics.asteroid.small"
+#define BLAST_BIG_MASK_PMAP_ANIM "graphics.blast.large"
+#define BLAST_MED_MASK_PMAP_ANIM "graphics.blast.medium"
+#define BLAST_SML_MASK_PMAP_ANIM "graphics.blast.small"
+#define BOMB_MASK_PMAP_ANIM "graphics.utwigbomb"
+#define BOOM_BIG_MASK_PMAP_ANIM "graphics.boom.large"
+#define BOOM_MED_MASK_PMAP_ANIM "graphics.boom.medium"
+#define BOOM_SML_MASK_PMAP_ANIM "graphics.boom.small"
+#define BURV_BCS_MASK_PMAP_ANIM "graphics.burvixcaster"
+#define CANNISTER_MASK_PMAP_ANIM "graphics.lifecan"
+#define CREDITS_BACK_ANIM "credits.background"
+#define EARTH_MASK_ANIM "graphics.earthmask"
+#define EGG_CASE_MASK_PMAP_ANIM "graphics.eggcase"
+#define FLAGSTAT_MASK_PMAP_ANIM "graphics.flagshipstatus"
+#define FONTGRAD_PMAP_ANIM "graphics.fontgradient"
+#define HYPERSTARS_MASK_PMAP_ANIM "graphics.hyperstars"
+#define IPBKGND_MASK_PMAP_ANIM "graphics.orbitbackground"
+#define LANDER_FONTEFF_PMAP_ANIM "graphics.landerfonteffect"
+#define LANDER_LAUNCH_MASK_PMAP_ANIM "graphics.landerlaunch"
+#define LANDER_MASK_PMAP_ANIM "graphics.lander"
+#define LANDER_RETURN_MASK_PMAP_ANIM "graphics.landerreturn"
+#define LANDER_SHIELD_MASK_ANIM "graphics.landershield"
+#define LAVA_MASK_PMAP_ANIM "graphics.lavaspot"
+#define LIFE00_MASK_PMAP_ANIM "graphics.life.0"
+#define LIFE01_MASK_PMAP_ANIM "graphics.life.1"
+#define LIFE02_MASK_PMAP_ANIM "graphics.life.2"
+#define LIFE03_MASK_PMAP_ANIM "graphics.life.3"
+#define LIFE04_MASK_PMAP_ANIM "graphics.life.4"
+#define LIFE05_MASK_PMAP_ANIM "graphics.life.5"
+#define LIFE06_MASK_PMAP_ANIM "graphics.life.6"
+#define LIFE07_MASK_PMAP_ANIM "graphics.life.7"
+#define LIFE08_MASK_PMAP_ANIM "graphics.life.8"
+#define LIFE09_MASK_PMAP_ANIM "graphics.life.9"
+#define LIFE10_MASK_PMAP_ANIM "graphics.life.10"
+#define LIFE11_MASK_PMAP_ANIM "graphics.life.11"
+#define LIFE12_MASK_PMAP_ANIM "graphics.life.12"
+#define LIFE13_MASK_PMAP_ANIM "graphics.life.13"
+#define LIFE14_MASK_PMAP_ANIM "graphics.life.14"
+#define LIFE15_MASK_PMAP_ANIM "graphics.life.15"
+#define LIFE16_MASK_PMAP_ANIM "graphics.life.16"
+#define LIFE17_MASK_PMAP_ANIM "graphics.life.17"
+#define LIFE18_MASK_PMAP_ANIM "graphics.life.18"
+#define LIFE19_MASK_PMAP_ANIM "graphics.life.19"
+#define LIFE20_MASK_PMAP_ANIM "graphics.life.20"
+#define LIFE21_MASK_PMAP_ANIM "graphics.life.21"
+#define LIFE22_MASK_PMAP_ANIM "graphics.life.22"
+#define LIFE23_MASK_PMAP_ANIM "graphics.life.23"
+#define LIFE24_MASK_PMAP_ANIM "graphics.life.24"
+#define LIFE25_MASK_PMAP_ANIM "graphics.life.25"
+#define LIGHTNING_MASK_ANIM "graphics.lightning"
+#define MAIDENS_MASK_PMAP_ANIM "graphics.maidens"
+#define MELEE_PICK_MASK_PMAP_ANIM "graphics.meleepickship"
+#define MELEE_SCREEN_PMAP_ANIM "graphics.meleemenu"
+#define MENUBKG_PMAP_ANIM "graphics.setupmenu"
+#define MISCDATA_MASK_PMAP_ANIM "graphics.miscdata"
+#define MODULES_PMAP_ANIM "graphics.modulesmenu"
+#define MOONBASE_MASK_PMAP_ANIM "graphics.moonbase"
+#define ORBENTER_PMAP_ANIM "graphics.orbitenter"
+#define ORBIT_VIEW_ANIM "graphics.orbview"
+#define ORBPLAN_MASK_PMAP_ANIM "graphics.planets"
+#define OUTFIT_PMAP_ANIM "graphics.outfit"
+#define PLANET00_BIG_MASK_PMAP_ANIM "planet.oolite.large"
+#define PLANET00_MED_MASK_PMAP_ANIM "planet.oolite.medium"
+#define PLANET00_SML_MASK_PMAP_ANIM "planet.oolite.small"
+#define PLANET01_BIG_MASK_PMAP_ANIM "planet.yttric.large"
+#define PLANET01_MED_MASK_PMAP_ANIM "planet.yttric.medium"
+#define PLANET01_SML_MASK_PMAP_ANIM "planet.yttric.small"
+#define PLANET02_BIG_MASK_PMAP_ANIM "planet.quasidegenerate.large"
+#define PLANET02_MED_MASK_PMAP_ANIM "planet.quasidegenerate.medium"
+#define PLANET02_SML_MASK_PMAP_ANIM "planet.quasidegenerate.small"
+#define PLANET03_BIG_MASK_PMAP_ANIM "planet.lanthanide.large"
+#define PLANET03_MED_MASK_PMAP_ANIM "planet.lanthanide.medium"
+#define PLANET03_SML_MASK_PMAP_ANIM "planet.lanthanide.small"
+#define PLANET04_BIG_MASK_PMAP_ANIM "planet.treasure.large"
+#define PLANET04_MED_MASK_PMAP_ANIM "planet.treasure.medium"
+#define PLANET04_SML_MASK_PMAP_ANIM "planet.treasure.small"
+#define PLANET05_BIG_MASK_PMAP_ANIM "planet.urea.large"
+#define PLANET05_MED_MASK_PMAP_ANIM "planet.urea.medium"
+#define PLANET05_SML_MASK_PMAP_ANIM "planet.urea.small"
+#define PLANET06_BIG_MASK_PMAP_ANIM "planet.metal.large"
+#define PLANET06_MED_MASK_PMAP_ANIM "planet.metal.medium"
+#define PLANET06_SML_MASK_PMAP_ANIM "planet.metal.small"
+#define PLANET07_BIG_MASK_PMAP_ANIM "planet.radioactive.large"
+#define PLANET07_MED_MASK_PMAP_ANIM "planet.radioactive.medium"
+#define PLANET07_SML_MASK_PMAP_ANIM "planet.radioactive.small"
+#define PLANET08_BIG_MASK_PMAP_ANIM "planet.opalescent.large"
+#define PLANET08_MED_MASK_PMAP_ANIM "planet.opalescent.medium"
+#define PLANET08_SML_MASK_PMAP_ANIM "planet.opalescent.small"
+#define PLANET09_BIG_MASK_PMAP_ANIM "planet.cyanic.large"
+#define PLANET09_MED_MASK_PMAP_ANIM "planet.cyanic.medium"
+#define PLANET09_SML_MASK_PMAP_ANIM "planet.cyanic.small"
+#define PLANET10_BIG_MASK_PMAP_ANIM "planet.acid.large"
+#define PLANET10_MED_MASK_PMAP_ANIM "planet.acid.medium"
+#define PLANET10_SML_MASK_PMAP_ANIM "planet.acid.small"
+#define PLANET11_BIG_MASK_PMAP_ANIM "planet.alkali.large"
+#define PLANET11_MED_MASK_PMAP_ANIM "planet.alkali.medium"
+#define PLANET11_SML_MASK_PMAP_ANIM "planet.alkali.small"
+#define PLANET12_BIG_MASK_PMAP_ANIM "planet.halide.large"
+#define PLANET12_MED_MASK_PMAP_ANIM "planet.halide.medium"
+#define PLANET12_SML_MASK_PMAP_ANIM "planet.halide.small"
+#define PLANET13_BIG_MASK_PMAP_ANIM "planet.green.large"
+#define PLANET13_MED_MASK_PMAP_ANIM "planet.green.medium"
+#define PLANET13_SML_MASK_PMAP_ANIM "planet.green.small"
+#define PLANET14_BIG_MASK_PMAP_ANIM "planet.copper.large"
+#define PLANET14_MED_MASK_PMAP_ANIM "planet.copper.medium"
+#define PLANET14_SML_MASK_PMAP_ANIM "planet.copper.small"
+#define PLANET15_BIG_MASK_PMAP_ANIM "planet.carbide.large"
+#define PLANET15_MED_MASK_PMAP_ANIM "planet.carbide.medium"
+#define PLANET15_SML_MASK_PMAP_ANIM "planet.carbide.small"
+#define PLANET16_BIG_MASK_PMAP_ANIM "planet.ultramarine.large"
+#define PLANET16_MED_MASK_PMAP_ANIM "planet.ultramarine.medium"
+#define PLANET16_SML_MASK_PMAP_ANIM "planet.ultramarine.small"
+#define PLANET17_BIG_MASK_PMAP_ANIM "planet.noble.large"
+#define PLANET17_MED_MASK_PMAP_ANIM "planet.noble.medium"
+#define PLANET17_SML_MASK_PMAP_ANIM "planet.noble.small"
+#define PLANET18_BIG_MASK_PMAP_ANIM "planet.azure.large"
+#define PLANET18_MED_MASK_PMAP_ANIM "planet.azure.medium"
+#define PLANET18_SML_MASK_PMAP_ANIM "planet.azure.small"
+#define PLANET19_BIG_MASK_PMAP_ANIM "planet.chondrite.large"
+#define PLANET19_MED_MASK_PMAP_ANIM "planet.chondrite.medium"
+#define PLANET19_SML_MASK_PMAP_ANIM "planet.chondrite.small"
+#define PLANET20_BIG_MASK_PMAP_ANIM "planet.purple.large"
+#define PLANET20_MED_MASK_PMAP_ANIM "planet.purple.medium"
+#define PLANET20_SML_MASK_PMAP_ANIM "planet.purple.small"
+#define PLANET21_BIG_MASK_PMAP_ANIM "planet.superdense.large"
+#define PLANET21_MED_MASK_PMAP_ANIM "planet.superdense.medium"
+#define PLANET21_SML_MASK_PMAP_ANIM "planet.superdense.small"
+#define PLANET22_BIG_MASK_PMAP_ANIM "planet.pellucid.large"
+#define PLANET22_MED_MASK_PMAP_ANIM "planet.pellucid.medium"
+#define PLANET22_SML_MASK_PMAP_ANIM "planet.pellucid.small"
+#define PLANET23_BIG_MASK_PMAP_ANIM "planet.dust.large"
+#define PLANET23_MED_MASK_PMAP_ANIM "planet.dust.medium"
+#define PLANET23_SML_MASK_PMAP_ANIM "planet.dust.small"
+#define PLANET24_BIG_MASK_PMAP_ANIM "planet.crimson.large"
+#define PLANET24_MED_MASK_PMAP_ANIM "planet.crimson.medium"
+#define PLANET24_SML_MASK_PMAP_ANIM "planet.crimson.small"
+#define PLANET25_BIG_MASK_PMAP_ANIM "planet.cimmerian.large"
+#define PLANET25_MED_MASK_PMAP_ANIM "planet.cimmerian.medium"
+#define PLANET25_SML_MASK_PMAP_ANIM "planet.cimmerian.small"
+#define PLANET26_BIG_MASK_PMAP_ANIM "planet.infrared.large"
+#define PLANET26_MED_MASK_PMAP_ANIM "planet.infrared.medium"
+#define PLANET26_SML_MASK_PMAP_ANIM "planet.infrared.small"
+#define PLANET27_BIG_MASK_PMAP_ANIM "planet.selenic.large"
+#define PLANET27_MED_MASK_PMAP_ANIM "planet.selenic.medium"
+#define PLANET27_SML_MASK_PMAP_ANIM "planet.selenic.small"
+#define PLANET28_BIG_MASK_PMAP_ANIM "planet.auric.large"
+#define PLANET28_MED_MASK_PMAP_ANIM "planet.auric.medium"
+#define PLANET28_SML_MASK_PMAP_ANIM "planet.auric.small"
+#define PLANET29_BIG_MASK_PMAP_ANIM "planet.fluorescent.large"
+#define PLANET29_MED_MASK_PMAP_ANIM "planet.fluorescent.medium"
+#define PLANET29_SML_MASK_PMAP_ANIM "planet.fluorescent.small"
+#define PLANET30_BIG_MASK_PMAP_ANIM "planet.ultraviolet.large"
+#define PLANET30_MED_MASK_PMAP_ANIM "planet.ultraviolet.medium"
+#define PLANET30_SML_MASK_PMAP_ANIM "planet.ultraviolet.small"
+#define PLANET31_BIG_MASK_PMAP_ANIM "planet.plutonic.large"
+#define PLANET31_MED_MASK_PMAP_ANIM "planet.plutonic.medium"
+#define PLANET31_SML_MASK_PMAP_ANIM "planet.plutonic.small"
+#define PLANET32_BIG_MASK_PMAP_ANIM "planet.rainbow.large"
+#define PLANET32_MED_MASK_PMAP_ANIM "planet.rainbow.medium"
+#define PLANET32_SML_MASK_PMAP_ANIM "planet.rainbow.small"
+#define PLANET33_BIG_MASK_PMAP_ANIM "planet.shattered.large"
+#define PLANET33_MED_MASK_PMAP_ANIM "planet.shattered.medium"
+#define PLANET33_SML_MASK_PMAP_ANIM "planet.shattered.small"
+#define PLANET34_BIG_MASK_PMAP_ANIM "planet.sapphire.large"
+#define PLANET34_MED_MASK_PMAP_ANIM "planet.sapphire.medium"
+#define PLANET34_SML_MASK_PMAP_ANIM "planet.sapphire.small"
+#define PLANET35_BIG_MASK_PMAP_ANIM "planet.organic.large"
+#define PLANET35_MED_MASK_PMAP_ANIM "planet.organic.medium"
+#define PLANET35_SML_MASK_PMAP_ANIM "planet.organic.small"
+#define PLANET36_BIG_MASK_PMAP_ANIM "planet.xenolithic.large"
+#define PLANET36_MED_MASK_PMAP_ANIM "planet.xenolithic.medium"
+#define PLANET36_SML_MASK_PMAP_ANIM "planet.xenolithic.small"
+#define PLANET37_BIG_MASK_PMAP_ANIM "planet.redux.large"
+#define PLANET37_MED_MASK_PMAP_ANIM "planet.redux.medium"
+#define PLANET37_SML_MASK_PMAP_ANIM "planet.redux.small"
+#define PLANET38_BIG_MASK_PMAP_ANIM "planet.primordial.large"
+#define PLANET38_MED_MASK_PMAP_ANIM "planet.primordial.medium"
+#define PLANET38_SML_MASK_PMAP_ANIM "planet.primordial.small"
+#define PLANET39_BIG_MASK_PMAP_ANIM "planet.emerald.large"
+#define PLANET39_MED_MASK_PMAP_ANIM "planet.emerald.medium"
+#define PLANET39_SML_MASK_PMAP_ANIM "planet.emerald.small"
+#define PLANET40_BIG_MASK_PMAP_ANIM "planet.chlorine.large"
+#define PLANET40_MED_MASK_PMAP_ANIM "planet.chlorine.medium"
+#define PLANET40_SML_MASK_PMAP_ANIM "planet.chlorine.small"
+#define PLANET41_BIG_MASK_PMAP_ANIM "planet.magnetic.large"
+#define PLANET41_MED_MASK_PMAP_ANIM "planet.magnetic.medium"
+#define PLANET41_SML_MASK_PMAP_ANIM "planet.magnetic.small"
+#define PLANET42_BIG_MASK_PMAP_ANIM "planet.water.large"
+#define PLANET42_MED_MASK_PMAP_ANIM "planet.water.medium"
+#define PLANET42_SML_MASK_PMAP_ANIM "planet.water.small"
+#define PLANET43_BIG_MASK_PMAP_ANIM "planet.telluric.large"
+#define PLANET43_MED_MASK_PMAP_ANIM "planet.telluric.medium"
+#define PLANET43_SML_MASK_PMAP_ANIM "planet.telluric.small"
+#define PLANET44_BIG_MASK_PMAP_ANIM "planet.hydrocarbon.large"
+#define PLANET44_MED_MASK_PMAP_ANIM "planet.hydrocarbon.medium"
+#define PLANET44_SML_MASK_PMAP_ANIM "planet.hydrocarbon.small"
+#define PLANET45_BIG_MASK_PMAP_ANIM "planet.iodine.large"
+#define PLANET45_MED_MASK_PMAP_ANIM "planet.iodine.medium"
+#define PLANET45_SML_MASK_PMAP_ANIM "planet.iodine.small"
+#define PLANET46_BIG_MASK_PMAP_ANIM "planet.vinylogous.large"
+#define PLANET46_MED_MASK_PMAP_ANIM "planet.vinylogous.medium"
+#define PLANET46_SML_MASK_PMAP_ANIM "planet.vinylogous.small"
+#define PLANET47_BIG_MASK_PMAP_ANIM "planet.ruby.large"
+#define PLANET47_MED_MASK_PMAP_ANIM "planet.ruby.medium"
+#define PLANET47_SML_MASK_PMAP_ANIM "planet.ruby.small"
+#define PLANET48_BIG_MASK_PMAP_ANIM "planet.magma.large"
+#define PLANET48_MED_MASK_PMAP_ANIM "planet.magma.medium"
+#define PLANET48_SML_MASK_PMAP_ANIM "planet.magma.small"
+#define PLANET49_BIG_MASK_PMAP_ANIM "planet.maroon.large"
+#define PLANET49_MED_MASK_PMAP_ANIM "planet.maroon.medium"
+#define PLANET49_SML_MASK_PMAP_ANIM "planet.maroon.small"
+#define PLANET50_BIG_MASK_PMAP_ANIM "planet.bluegas.large"
+#define PLANET50_MED_MASK_PMAP_ANIM "planet.bluegas.medium"
+#define PLANET50_SML_MASK_PMAP_ANIM "planet.bluegas.small"
+#define PLANET51_BIG_MASK_PMAP_ANIM "planet.cyangas.large"
+#define PLANET51_MED_MASK_PMAP_ANIM "planet.cyangas.medium"
+#define PLANET51_SML_MASK_PMAP_ANIM "planet.cyangas.small"
+#define PLANET52_BIG_MASK_PMAP_ANIM "planet.greengas.large"
+#define PLANET52_MED_MASK_PMAP_ANIM "planet.greengas.medium"
+#define PLANET52_SML_MASK_PMAP_ANIM "planet.greengas.small"
+#define PLANET53_BIG_MASK_PMAP_ANIM "planet.greygas.large"
+#define PLANET53_MED_MASK_PMAP_ANIM "planet.greygas.medium"
+#define PLANET53_SML_MASK_PMAP_ANIM "planet.greygas.small"
+#define PLANET54_BIG_MASK_PMAP_ANIM "planet.orangegas.large"
+#define PLANET54_MED_MASK_PMAP_ANIM "planet.orangegas.medium"
+#define PLANET54_SML_MASK_PMAP_ANIM "planet.orangegas.small"
+#define PLANET55_BIG_MASK_PMAP_ANIM "planet.purplegas.large"
+#define PLANET55_MED_MASK_PMAP_ANIM "planet.purplegas.medium"
+#define PLANET55_SML_MASK_PMAP_ANIM "planet.purplegas.small"
+#define PLANET56_BIG_MASK_PMAP_ANIM "planet.redgas.large"
+#define PLANET56_MED_MASK_PMAP_ANIM "planet.redgas.medium"
+#define PLANET56_SML_MASK_PMAP_ANIM "planet.redgas.small"
+#define PLANET57_BIG_MASK_PMAP_ANIM "planet.violetgas.large"
+#define PLANET57_MED_MASK_PMAP_ANIM "planet.violetgas.medium"
+#define PLANET57_SML_MASK_PMAP_ANIM "planet.violetgas.small"
+#define PLANET58_BIG_MASK_PMAP_ANIM "planet.yellowgas.large"
+#define PLANET58_MED_MASK_PMAP_ANIM "planet.yellowgas.medium"
+#define PLANET58_SML_MASK_PMAP_ANIM "planet.yellowgas.small"
+#define PLAYMENU_ANIM "graphics.playmenu"
+#define QUAKE_MASK_PMAP_ANIM "graphics.quake"
+#define RESTART_PMAP_ANIM "graphics.newgame"
+#define RUINS_MASK_PMAP_ANIM "graphics.ruins"
+#define SAMATRA_BIG_MASK_PMAP_ANIM "planet.samatra.large"
+#define SC2_PICK_PMAP_ANIM "graphics.pickship"
+#define SEGUE_PMAP_ANIM "graphics.segue"
+#define SHIELDED_BIG_MASK_PMAP_ANIM "planet.slaveshield.large"
+#define SHIELDED_MED_MASK_PMAP_ANIM "planet.slaveshield.medium"
+#define SHIELDED_SML_MASK_PMAP_ANIM "planet.slaveshield.small"
+#define SHIPYARD_PMAP_ANIM "graphics.shipyard"
+#define SISBLU_MASK_ANIM "graphics.blueprints"
+#define SISIP_MASK_PMAP_ANIM "graphics.flagship"
+#define SISMODS_MASK_PMAP_ANIM "graphics.outfitmodules"
+#define SISSKEL_MASK_PMAP_ANIM "graphics.flagshipskeleton"
+#define SPAPLUTO_MASK_PMAP_ANIM "graphics.fwiffo"
+#define STARBASE_ANIM "graphics.starbase"
+#define STAR_MASK_PMAP_ANIM "graphics.stars"
+#define STATUS_MASK_PMAP_ANIM "graphics.status"
+#define SUN_DEVICE_MASK_PMAP_ANIM "graphics.sundevice"
+#define SUN_MASK_PMAP_ANIM "graphics.truespacesun"
+#define TAALO_DEVICE_MASK_PMAP_ANIM "graphics.taalodevice"
+#define TITLE_ANIM "graphics.title"
+#define UMGAH_BCS_MASK_PMAP_ANIM "graphics.umgahcaster"
+#define VAULT_MASK_PMAP_ANIM "graphics.syreenvault"
+#define WRECK_MASK_PMAP_ANIM "graphics.urquanwreck"
diff --git a/src/uqm/ikey_con.h b/src/uqm/ikey_con.h
new file mode 100644
index 0000000..e780e2c
--- /dev/null
+++ b/src/uqm/ikey_con.h
@@ -0,0 +1,2 @@
+// This is a dead resource file.
+
diff --git a/src/uqm/imusicre.h b/src/uqm/imusicre.h
new file mode 100644
index 0000000..87d3ca4
--- /dev/null
+++ b/src/uqm/imusicre.h
@@ -0,0 +1,20 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define BATTLE_MUSIC "music.battle"
+#define CREDITS_MUSIC "music.credits"
+#define HYPERSPACE_MUSIC "music.hyperspace"
+#define IP_MUSIC "music.space"
+#define MAINMENU_MUSIC "music.mainmenu"
+#define MELEE_MUSIC "music.meleemenu"
+#define ORBIT1_MUSIC "music.orbit1"
+#define ORBIT2_MUSIC "music.orbit2"
+#define ORBIT3_MUSIC "music.orbit3"
+#define ORBIT4_MUSIC "music.orbit4"
+#define ORBIT5_MUSIC "music.orbit5"
+#define OUTFIT_MUSIC "music.outfit"
+#define QUASISPACE_MUSIC "music.quasispace"
+#define REDALERT_MUSIC "music.redalert"
+#define SHIPYARD_MUSIC "music.shipyard"
+#define STARBASE_MUSIC "music.starbase"
diff --git a/src/uqm/init.c b/src/uqm/init.c
new file mode 100644
index 0000000..7831d08
--- /dev/null
+++ b/src/uqm/init.c
@@ -0,0 +1,351 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "build.h"
+#include "colors.h"
+#include "cons_res.h"
+#include "races.h"
+#include "element.h"
+#include "tactrans.h"
+#include "pickship.h"
+#include "process.h"
+#include "globdata.h"
+#include "encount.h"
+#include "hyper.h"
+#include "init.h"
+#include "port.h"
+#include "resinst.h"
+#include "libs/reslib.h"
+#include "nameref.h"
+#include "setup.h"
+#include "units.h"
+
+
+FRAME stars_in_space;
+FRAME asteroid[NUM_VIEWS];
+FRAME blast[NUM_VIEWS];
+FRAME explosion[NUM_VIEWS];
+
+
+BOOLEAN
+load_animation (FRAME *pixarray, RESOURCE big_res, RESOURCE med_res, RESOURCE
+ sml_res)
+{
+ DRAWABLE d;
+
+ d = LoadGraphic (big_res);
+ if (!d)
+ return FALSE;
+ pixarray[0] = CaptureDrawable (d);
+
+ if (med_res != NULL_RESOURCE)
+ {
+ d = LoadGraphic (med_res);
+ if (!d)
+ return FALSE;
+ }
+ pixarray[1] = CaptureDrawable (d);
+
+ if (sml_res != NULL_RESOURCE)
+ {
+ d = LoadGraphic (sml_res);
+ if (!d)
+ return FALSE;
+ }
+ pixarray[2] = CaptureDrawable (d);
+
+ return TRUE;
+}
+
+/* Warning: Some ships (such as the Umgah) will alias their pixarrays,
+ so we need to track to make sure that we do not double-free. */
+BOOLEAN
+free_image (FRAME *pixarray)
+{
+ BOOLEAN retval;
+ COUNT i, j;
+ void *already_freed[NUM_VIEWS];
+
+ retval = TRUE;
+ for (i = 0; i < NUM_VIEWS; ++i)
+ {
+ if (pixarray[i] != NULL)
+ {
+ BOOLEAN ok = TRUE;
+ for (j = 0; j < i; j++)
+ {
+ if (already_freed[j] == pixarray[i])
+ {
+ ok = FALSE;
+ break;
+ }
+ }
+ if (ok)
+ {
+ if (!DestroyDrawable (ReleaseDrawable (pixarray[i])))
+ retval = FALSE;
+ }
+ already_freed[i] = pixarray[i];
+ pixarray[i] = NULL;
+ }
+ }
+
+ return (retval);
+}
+
+static BYTE space_ini_cnt;
+
+BOOLEAN
+InitSpace (void)
+{
+ if (space_ini_cnt++ == 0
+ && LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ {
+ stars_in_space = CaptureDrawable (
+ LoadGraphic (STAR_MASK_PMAP_ANIM));
+ if (stars_in_space == NULL)
+ return FALSE;
+
+ if (!load_animation (explosion,
+ BOOM_BIG_MASK_PMAP_ANIM,
+ BOOM_MED_MASK_PMAP_ANIM,
+ BOOM_SML_MASK_PMAP_ANIM))
+ return FALSE;
+
+ if (!load_animation (blast,
+ BLAST_BIG_MASK_PMAP_ANIM,
+ BLAST_MED_MASK_PMAP_ANIM,
+ BLAST_SML_MASK_PMAP_ANIM))
+ return FALSE;
+
+ if (!load_animation (asteroid,
+ ASTEROID_BIG_MASK_PMAP_ANIM,
+ ASTEROID_MED_MASK_PMAP_ANIM,
+ ASTEROID_SML_MASK_PMAP_ANIM))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void
+UninitSpace (void)
+{
+ if (space_ini_cnt && --space_ini_cnt == 0)
+ {
+ free_image (blast);
+ free_image (explosion);
+ free_image (asteroid);
+
+ DestroyDrawable (ReleaseDrawable (stars_in_space));
+ stars_in_space = 0;
+ }
+}
+
+static HSTARSHIP
+BuildSIS (void)
+{
+ HSTARSHIP hStarShip;
+ STARSHIP *StarShipPtr;
+
+ hStarShip = Build (&race_q[0], SIS_SHIP_ID);
+ if (!hStarShip)
+ return 0;
+ StarShipPtr = LockStarShip (&race_q[0], hStarShip);
+ StarShipPtr->playerNr = RPG_PLAYER_NUM;
+ StarShipPtr->captains_name_index = 0;
+ UnlockStarShip (&race_q[0], hStarShip);
+
+ return hStarShip;
+}
+
+SIZE
+InitShips (void)
+{
+ SIZE num_ships;
+
+ InitSpace ();
+
+ SetContext (StatusContext);
+ SetContext (SpaceContext);
+
+ InitDisplayList ();
+ InitGalaxy ();
+
+ if (inHQSpace ())
+ {
+ ReinitQueue (&race_q[0]);
+ ReinitQueue (&race_q[1]);
+
+ BuildSIS ();
+ LoadHyperspace ();
+
+ num_ships = 1;
+ }
+ else
+ {
+ COUNT i;
+ RECT r;
+
+ SetContextFGFrame (Screen);
+ r.corner.x = SAFE_X;
+ r.corner.y = SAFE_Y;
+ r.extent.width = SPACE_WIDTH;
+ r.extent.height = SPACE_HEIGHT;
+ SetContextClipRect (&r);
+
+ SetContextBackGroundColor (BLACK_COLOR);
+ {
+ CONTEXT OldContext;
+
+ OldContext = SetContext (ScreenContext);
+
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+
+ SetContext (OldContext);
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ free_gravity_well ();
+ else
+ {
+#define NUM_ASTEROIDS 5
+ for (i = 0; i < NUM_ASTEROIDS; ++i)
+ spawn_asteroid (NULL);
+#define NUM_PLANETS 1
+ for (i = 0; i < NUM_PLANETS; ++i)
+ spawn_planet ();
+ }
+
+ num_ships = NUM_SIDES;
+ }
+
+ // FlushInput ();
+
+ return (num_ships);
+}
+
+// Count the crew elements in the display list.
+static COUNT
+CountCrewElements (void)
+{
+ COUNT result;
+ HELEMENT hElement, hNextElement;
+
+ result = 0;
+ for (hElement = GetHeadElement ();
+ hElement != 0; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+ if (ElementPtr->state_flags & CREW_OBJECT)
+ ++result;
+
+ UnlockElement (hElement);
+ }
+
+ return result;
+}
+
+void
+UninitShips (void)
+{
+ COUNT crew_retrieved;
+ int i;
+ HELEMENT hElement, hNextElement;
+ STARSHIP *SPtr[NUM_PLAYERS];
+
+ StopSound ();
+
+ UninitSpace ();
+
+ for (i = 0; i < NUM_PLAYERS; ++i)
+ SPtr[i] = 0;
+
+ // Count the crew floating in space.
+ crew_retrieved = CountCrewElements();
+
+ for (hElement = GetHeadElement ();
+ hElement != 0; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+ if ((ElementPtr->state_flags & PLAYER_SHIP)
+ || ElementPtr->death_func == new_ship)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ // There should only be one ship left in battle.
+ // He gets the crew still floating in space.
+ if (StarShipPtr->RaceDescPtr->ship_info.crew_level)
+ {
+ if (crew_retrieved >=
+ StarShipPtr->RaceDescPtr->ship_info.max_crew -
+ StarShipPtr->RaceDescPtr->ship_info.crew_level)
+ StarShipPtr->RaceDescPtr->ship_info.crew_level =
+ StarShipPtr->RaceDescPtr->ship_info.max_crew;
+ else
+ StarShipPtr->RaceDescPtr->ship_info.crew_level +=
+ crew_retrieved;
+ }
+
+ /* Record crew left after battle */
+ StarShipPtr->crew_level =
+ StarShipPtr->RaceDescPtr->ship_info.crew_level;
+ SPtr[StarShipPtr->playerNr] = StarShipPtr;
+ free_ship (StarShipPtr->RaceDescPtr, TRUE, TRUE);
+ StarShipPtr->RaceDescPtr = 0;
+ }
+ UnlockElement (hElement);
+ }
+
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER
+ && !(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ // Encounter battle in full game.
+ // Record the crew left in the last ship standing. The crew left
+ // is first recorded into STARSHIP.crew_level just a few lines
+ // above here.
+ for (i = NUM_PLAYERS - 1; i >= 0; --i)
+ {
+ if (SPtr[i] && !FleetIsInfinite (i))
+ UpdateShipFragCrew (SPtr[i]);
+ }
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != IN_ENCOUNTER)
+ {
+ // Remove any ships left from the race queue.
+ for (i = 0; i < NUM_PLAYERS; i++)
+ ReinitQueue (&race_q[i]);
+
+ if (inHQSpace ())
+ FreeHyperspace ();
+ }
+}
+
+
diff --git a/src/uqm/init.h b/src/uqm/init.h
new file mode 100644
index 0000000..69867f0
--- /dev/null
+++ b/src/uqm/init.h
@@ -0,0 +1,46 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_INIT_H_
+#define UQM_INIT_H_
+
+#include "libs/gfxlib.h"
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define NUM_PLAYERS 2
+#define NUM_SIDES 2
+
+extern FRAME stars_in_space;
+
+extern BOOLEAN InitSpace (void);
+extern void UninitSpace (void);
+
+extern SIZE InitShips (void);
+extern void UninitShips (void);
+
+extern BOOLEAN load_animation (FRAME *pixarray, RESOURCE big_res,
+ RESOURCE med_res, RESOURCE sml_res);
+extern BOOLEAN free_image (FRAME *pixarray);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_INIT_H_ */
diff --git a/src/uqm/intel.c b/src/uqm/intel.c
new file mode 100644
index 0000000..eb58736
--- /dev/null
+++ b/src/uqm/intel.c
@@ -0,0 +1,76 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "intel.h"
+
+#include "battlecontrols.h"
+#include "controls.h"
+#include "globdata.h"
+#include "setup.h"
+#include "libs/log.h"
+
+#include <stdio.h>
+
+
+BATTLE_INPUT_STATE
+computer_intelligence (ComputerInputContext *context, STARSHIP *StarShipPtr)
+{
+ BATTLE_INPUT_STATE InputState;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ return 0;
+
+ if (StarShipPtr)
+ {
+ // Selecting the next action for in battle.
+ if (StarShipPtr->control & CYBORG_CONTROL)
+ {
+ InputState = tactical_intelligence (context, StarShipPtr);
+
+ // Allow a player to warp-escape in cyborg mode
+ if (StarShipPtr->playerNr == RPG_PLAYER_NUM)
+ InputState |= CurrentInputToBattleInput (
+ context->playerNr) & BATTLE_ESCAPE;
+ }
+ else
+ InputState = CurrentInputToBattleInput (context->playerNr);
+ }
+ else if (!(PlayerControl[context->playerNr] & PSYTRON_CONTROL))
+ InputState = 0;
+ else
+ {
+ switch (LOBYTE (GLOBAL (CurrentActivity)))
+ {
+ case SUPER_MELEE:
+ {
+ SleepThread (ONE_SECOND >> 1);
+ InputState = BATTLE_WEAPON; /* pick a random ship */
+ break;
+ }
+ default:
+ // Should not happen. Satisfying compiler.
+ log_add (log_Warning, "Warning: Unexpected state in "
+ "computer_intelligence().");
+ InputState = 0;
+ break;
+ }
+ }
+ return InputState;
+}
+
+
diff --git a/src/uqm/intel.h b/src/uqm/intel.h
new file mode 100644
index 0000000..6e63e51
--- /dev/null
+++ b/src/uqm/intel.h
@@ -0,0 +1,85 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_INTEL_H_
+#define UQM_INTEL_H_
+
+#include "battlecontrols.h"
+#include "controls.h"
+#include "element.h"
+#include "races.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define MANEUVERABILITY(pi) ((pi)->ManeuverabilityIndex)
+#define WEAPON_RANGE(pi) ((pi)->WeaponRange)
+
+#define WORLD_TO_TURN(d) ((d)>>6)
+
+#define CLOSE_RANGE_WEAPON DISPLAY_TO_WORLD (50)
+#define LONG_RANGE_WEAPON DISPLAY_TO_WORLD (1000)
+#define FAST_SHIP 150
+#define MEDIUM_SHIP 45
+#define SLOW_SHIP 25
+
+enum
+{
+ ENEMY_SHIP_INDEX = 0,
+ CREW_OBJECT_INDEX,
+ ENEMY_WEAPON_INDEX,
+ GRAVITY_MASS_INDEX,
+ FIRST_EMPTY_INDEX
+};
+
+extern BATTLE_INPUT_STATE computer_intelligence (
+ ComputerInputContext *context, STARSHIP *StarShipPtr);
+extern BATTLE_INPUT_STATE tactical_intelligence (
+ ComputerInputContext *context, STARSHIP *StarShipPtr);
+extern void ship_intelligence (ELEMENT *ShipPtr,
+ EVALUATE_DESC *ObjectsOfConcern, COUNT ConcernCounter);
+extern BOOLEAN ship_weapons (ELEMENT *ShipPtr, ELEMENT *OtherPtr,
+ COUNT margin_of_error);
+
+extern void Pursue (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr);
+extern void Entice (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr);
+extern void Avoid (ELEMENT *ShipPtr, EVALUATE_DESC *EvalDescPtr);
+extern BOOLEAN TurnShip (ELEMENT *ShipPtr, COUNT angle);
+extern BOOLEAN ThrustShip (ELEMENT *ShipPtr, COUNT angle);
+
+
+#define HUMAN_CONTROL (BYTE)(1 << 0)
+#define CYBORG_CONTROL (BYTE)(1 << 1)
+ // The computer fights the battles.
+#define PSYTRON_CONTROL (BYTE)(1 << 2)
+ // The computer selects the ships to fight with.
+#define NETWORK_CONTROL (BYTE)(1 << 3)
+#define COMPUTER_CONTROL (CYBORG_CONTROL | PSYTRON_CONTROL)
+#define CONTROL_MASK (HUMAN_CONTROL | COMPUTER_CONTROL | NETWORK_CONTROL)
+
+#define STANDARD_RATING (BYTE)(1 << 4)
+#define GOOD_RATING (BYTE)(1 << 5)
+#define AWESOME_RATING (BYTE)(1 << 6)
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_INTEL_H_ */
diff --git a/src/uqm/intro.c b/src/uqm/intro.c
new file mode 100644
index 0000000..092c428
--- /dev/null
+++ b/src/uqm/intro.c
@@ -0,0 +1,875 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "controls.h"
+#include "options.h"
+#include "settings.h"
+#include "globdata.h"
+#include "sis.h"
+#include "setup.h"
+#include "sounds.h"
+#include "colors.h"
+#include "fmv.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/sound/sound.h"
+#include "libs/vidlib.h"
+#include "libs/log.h"
+
+#include <ctype.h>
+
+static BOOLEAN ShowSlidePresentation (STRING PresStr);
+
+typedef struct
+{
+ /* standard state required by DoInput */
+ BOOLEAN (*InputFunc) (void *pInputState);
+
+ /* Presentation state */
+ TimeCount StartTime;
+ TimeCount LastSyncTime;
+ TimeCount TimeOut;
+ int TimeOutOnSkip;
+ STRING SlideShow;
+#define MAX_FONTS 5
+ FONT Fonts[MAX_FONTS];
+ FRAME Frame;
+ MUSIC_REF MusicRef;
+ BOOLEAN Batched;
+ FRAME SisFrame;
+ FRAME RotatedFrame;
+ int LastDrawKind;
+ int LastAngle;
+ COUNT OperIndex;
+ Color TextFadeColor;
+ Color TextColor;
+ Color TextBackColor;
+ int TextVPos;
+ int TextEffect;
+ RECT clip_r;
+ RECT tfade_r;
+#define MAX_TEXT_LINES 15
+ TEXT TextLines[MAX_TEXT_LINES];
+ COUNT LinesCount;
+ char Buffer[512];
+ int MovieFrame;
+ int MovieEndFrame;
+ int InterframeDelay;
+
+} PRESENTATION_INPUT_STATE;
+
+typedef struct {
+ /* standard state required by DoInput */
+ BOOLEAN (*InputFunc) (void *pInputState);
+
+ /* Spinanim state */
+ STAMP anim;
+ TimeCount last_time;
+ int debounce;
+} SPINANIM_INPUT_STATE;
+
+typedef struct
+{
+ // standard state required by DoInput
+ BOOLEAN (*InputFunc) (void *pInputState);
+
+ LEGACY_VIDEO_REF CurVideo;
+
+} VIDEO_INPUT_STATE;
+
+static BOOLEAN DoPresentation (void *pIS);
+
+static BOOLEAN
+ParseColorString (const char *Src, Color* pColor)
+{
+ unsigned clr;
+ if (1 != sscanf (Src, "%x", &clr))
+ return FALSE;
+
+ *pColor = BUILD_COLOR_RGBA (
+ (clr >> 16) & 0xff, (clr >> 8) & 0xff, clr & 0xff, 0);
+ return TRUE;
+}
+
+static BOOLEAN
+DoFadeScreen (PRESENTATION_INPUT_STATE* pPIS, const char *Src, BYTE FadeType)
+{
+ int msecs;
+ if (1 == sscanf (Src, "%d", &msecs))
+ {
+ pPIS->TimeOut = FadeScreen (FadeType, msecs * ONE_SECOND / 1000)
+ + ONE_SECOND / 10;
+ pPIS->TimeOutOnSkip = FALSE;
+ }
+ return TRUE;
+}
+
+static void
+DrawTextEffect (TEXT *pText, Color Fore, Color Back, int Effect)
+{
+ if (Effect == 'T')
+ {
+ font_DrawTracedText (pText, Fore, Back);
+ }
+ else
+ {
+ SetContextForeGroundColor (Fore);
+ font_DrawText (pText);
+ }
+}
+
+static COUNT
+ParseTextLines (TEXT *Lines, COUNT MaxLines, char* Buffer)
+{
+ COUNT i;
+ const char* pEnd = Buffer + strlen (Buffer);
+
+ for (i = 0; i < MaxLines && Buffer < pEnd; ++i, ++Lines)
+ {
+ char* pTerm = strchr (Buffer, '\n');
+ if (!pTerm)
+ pTerm = Buffer + strlen (Buffer);
+ *pTerm = '\0'; /* terminate string */
+ Lines->pStr = Buffer;
+ Lines->CharCount = ~0;
+ Buffer = pTerm + 1;
+ }
+ return i;
+}
+
+static void
+Present_BatchGraphics (PRESENTATION_INPUT_STATE* pPIS)
+{
+ if (!pPIS->Batched)
+ {
+ pPIS->Batched = TRUE;
+ BatchGraphics ();
+ }
+}
+
+static void
+Present_UnbatchGraphics (PRESENTATION_INPUT_STATE* pPIS, BOOLEAN bYield)
+{
+ if (pPIS->Batched)
+ {
+ UnbatchGraphics ();
+ pPIS->Batched = FALSE;
+ if (bYield)
+ TaskSwitch ();
+ }
+}
+
+static void
+Present_GenerateSIS (PRESENTATION_INPUT_STATE* pPIS)
+{
+#define MODULE_YOFS_P (-79)
+#define DRIVE_TOP_Y_P (DRIVE_TOP_Y + MODULE_YOFS_P)
+#define JET_TOP_Y_P (JET_TOP_Y + MODULE_YOFS_P)
+#define MODULE_TOP_Y_P (MODULE_TOP_Y + MODULE_YOFS_P)
+ CONTEXT OldContext;
+ FRAME SisFrame;
+ FRAME ModuleFrame;
+ FRAME SkelFrame;
+ STAMP s;
+ RECT r;
+ HOT_SPOT hs;
+ int slot;
+ COUNT piece;
+ Color SisBack;
+
+ OldContext = SetContext (OffScreenContext);
+
+ SkelFrame = CaptureDrawable (LoadGraphic (SISSKEL_MASK_PMAP_ANIM));
+ ModuleFrame = CaptureDrawable (LoadGraphic (SISMODS_MASK_PMAP_ANIM));
+
+ GetFrameRect (SkelFrame, &r);
+ SisFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, r.extent.width, r.extent.height, 1
+ ));
+ SetContextFGFrame (SisFrame);
+ SetContextClipRect (NULL);
+ SisBack = BUILD_COLOR (MAKE_RGB15 (0x01, 0x01, 0x01), 0x07);
+ SetContextBackGroundColor (SisBack);
+ ClearDrawable ();
+ SetFrameTransparentColor (SisFrame, SisBack);
+
+ s.frame = SetAbsFrameIndex (SkelFrame, 0);
+ s.origin.x = 0;
+ s.origin.y = 0;
+ DrawStamp (&s);
+
+ for (slot = 0; slot < NUM_DRIVE_SLOTS; ++slot)
+ {
+ piece = GLOBAL_SIS (DriveSlots[slot]);
+ if (piece < EMPTY_SLOT)
+ {
+ s.origin.x = DRIVE_TOP_X;
+ s.origin.y = DRIVE_TOP_Y_P;
+ s.origin.x += slot * SHIP_PIECE_OFFSET;
+ s.frame = SetAbsFrameIndex (ModuleFrame, piece);
+ DrawStamp (&s);
+ }
+ }
+ for (slot = 0; slot < NUM_JET_SLOTS; ++slot)
+ {
+ piece = GLOBAL_SIS (JetSlots[slot]);
+ if (piece < EMPTY_SLOT)
+ {
+ s.origin.x = JET_TOP_X;
+ s.origin.y = JET_TOP_Y_P;
+ s.origin.x += slot * SHIP_PIECE_OFFSET;
+ s.frame = SetAbsFrameIndex (ModuleFrame, piece);
+ DrawStamp (&s);
+ }
+ }
+ for (slot = 0; slot < NUM_MODULE_SLOTS; ++slot)
+ {
+ piece = GLOBAL_SIS (ModuleSlots[slot]);
+ if (piece < EMPTY_SLOT)
+ {
+ s.origin.x = MODULE_TOP_X;
+ s.origin.y = MODULE_TOP_Y_P;
+ s.origin.x += slot * SHIP_PIECE_OFFSET;
+ s.frame = SetAbsFrameIndex (ModuleFrame, piece);
+ DrawStamp (&s);
+ }
+ }
+
+ DestroyDrawable (ReleaseDrawable (SkelFrame));
+ DestroyDrawable (ReleaseDrawable (ModuleFrame));
+
+ hs.x = r.extent.width / 2;
+ hs.y = r.extent.height / 2;
+ SetFrameHot (SisFrame, hs);
+
+ SetContext (OldContext);
+ FlushGraphics ();
+
+ pPIS->SisFrame = SisFrame;
+}
+
+static void
+Present_DrawMovieFrame (PRESENTATION_INPUT_STATE* pPIS)
+{
+ STAMP s;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pPIS->Frame, pPIS->MovieFrame);
+ DrawStamp (&s);
+}
+
+static BOOLEAN
+ShowPresentationFile (const char *name)
+{
+ STRING pres = CaptureStringTable (LoadStringTableFile (contentDir, name));
+ BOOLEAN result = ShowSlidePresentation (pres);
+ DestroyStringTable (ReleaseStringTable (pres));
+ return result;
+}
+
+static BOOLEAN
+DoPresentation (void *pIS)
+{
+ PRESENTATION_INPUT_STATE* pPIS = (PRESENTATION_INPUT_STATE*) pIS;
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL]
+ || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ return FALSE; /* abort requested - we are done */
+
+ if (pPIS->TimeOut)
+ {
+ TimeCount Delay = ONE_SECOND / 84;
+
+ if (GetTimeCounter () >= pPIS->TimeOut)
+ {
+ if (pPIS->MovieFrame >= 0)
+ { /* Movie mode */
+ Present_DrawMovieFrame (pPIS);
+ ++pPIS->MovieFrame;
+ if (pPIS->MovieFrame > pPIS->MovieEndFrame)
+ pPIS->MovieFrame = -1; /* movie is done */
+ Delay = pPIS->InterframeDelay;
+ }
+ else
+ { /* time elapsed - continue normal ops */
+ pPIS->TimeOut = 0;
+ return TRUE;
+ }
+ }
+
+ if (pPIS->TimeOutOnSkip &&
+ (PulsedInputState.menu[KEY_MENU_SELECT]
+ || PulsedInputState.menu[KEY_MENU_SPECIAL]
+ || PulsedInputState.menu[KEY_MENU_RIGHT]) )
+ { /* skip requested - continue normal ops */
+ pPIS->TimeOut = 0;
+ pPIS->MovieFrame = -1; /* abort any movie in progress */
+ return TRUE;
+ }
+
+ SleepThread (Delay);
+ return TRUE;
+ }
+
+ while (pPIS->OperIndex < GetStringTableCount (pPIS->SlideShow))
+ {
+ char Opcode[16];
+ char *pStr = GetStringAddress (pPIS->SlideShow);
+
+ pPIS->OperIndex++;
+ pPIS->SlideShow = SetRelStringTableIndex (pPIS->SlideShow, 1);
+
+ if (!pStr)
+ continue;
+ if (1 != sscanf (pStr, "%15s", Opcode))
+ continue;
+ pStr += strlen (Opcode);
+ if (*pStr != '\0')
+ ++pStr;
+ strupr (Opcode);
+
+ if (strcmp (Opcode, "DIMS") == 0)
+ { /* set dimensions */
+ int w, h;
+ if (2 == sscanf (pStr, "%d %d", &w, &h))
+ {
+ pPIS->clip_r.extent.width = w;
+ pPIS->clip_r.extent.height = h;
+ /* center on screen */
+ pPIS->clip_r.corner.x = (SCREEN_WIDTH - w) / 2;
+ pPIS->clip_r.corner.y = (SCREEN_HEIGHT - h) / 2;
+ SetContextClipRect (&pPIS->clip_r);
+ }
+ }
+ else if (strcmp (Opcode, "FONT") == 0)
+ { /* set and/or load a font */
+ int index;
+ FONT *pFont;
+
+ assert (sizeof (pPIS->Buffer) >= 256);
+
+ pPIS->Buffer[0] = '\0';
+ if (1 > sscanf (pStr, "%d %255[^\n]", &index, pPIS->Buffer) ||
+ index < 0 || index >= MAX_FONTS)
+ {
+ log_add (log_Warning, "Bad FONT command '%s'", pStr);
+ continue;
+ }
+ pFont = &pPIS->Fonts[index];
+
+ if (pPIS->Buffer[0])
+ { /* asked to load a font */
+ if (*pFont)
+ DestroyFont (*pFont);
+ *pFont = LoadFontFile (pPIS->Buffer);
+ }
+
+ SetContextFont (*pFont);
+ }
+ else if (strcmp (Opcode, "ANI") == 0)
+ { /* set ani */
+ utf8StringCopy (pPIS->Buffer, sizeof (pPIS->Buffer), pStr);
+ if (pPIS->Frame)
+ DestroyDrawable (ReleaseDrawable (pPIS->Frame));
+ pPIS->Frame = CaptureDrawable (LoadGraphicFile (pPIS->Buffer));
+ }
+ else if (strcmp (Opcode, "MUSIC") == 0)
+ { /* set music */
+ utf8StringCopy (pPIS->Buffer, sizeof (pPIS->Buffer), pStr);
+ if (pPIS->MusicRef)
+ {
+ StopMusic ();
+ DestroyMusic (pPIS->MusicRef);
+ }
+ pPIS->MusicRef = LoadMusicFile (pPIS->Buffer);
+ PlayMusic (pPIS->MusicRef, FALSE, 1);
+ }
+ else if (strcmp (Opcode, "WAIT") == 0)
+ { /* wait */
+ int msecs;
+ Present_UnbatchGraphics (pPIS, TRUE);
+ if (1 == sscanf (pStr, "%d", &msecs))
+ {
+ pPIS->TimeOut = GetTimeCounter ()
+ + msecs * ONE_SECOND / 1000;
+ pPIS->TimeOutOnSkip = TRUE;
+ return TRUE;
+ }
+ }
+ else if (strcmp (Opcode, "SYNC") == 0)
+ { /* absolute time-sync */
+ int msecs;
+ Present_UnbatchGraphics (pPIS, TRUE);
+ if (1 == sscanf (pStr, "%d", &msecs))
+ {
+ pPIS->LastSyncTime = pPIS->StartTime
+ + msecs * ONE_SECOND / 1000;
+ pPIS->TimeOut = pPIS->LastSyncTime;
+ pPIS->TimeOutOnSkip = FALSE;
+ return TRUE;
+ }
+ }
+ else if (strcmp (Opcode, "RESYNC") == 0)
+ { /* flush and update absolute sync point */
+ pPIS->LastSyncTime = pPIS->StartTime = GetTimeCounter ();
+ }
+ else if (strcmp (Opcode, "DSYNC") == 0)
+ { /* delta time-sync; from the last absolute sync */
+ int msecs;
+ Present_UnbatchGraphics (pPIS, TRUE);
+ if (1 == sscanf (pStr, "%d", &msecs))
+ {
+ pPIS->TimeOut = pPIS->LastSyncTime
+ + msecs * ONE_SECOND / 1000;
+ pPIS->TimeOutOnSkip = FALSE;
+ return TRUE;
+ }
+ }
+ else if (strcmp (Opcode, "TC") == 0)
+ { /* text fore color */
+ ParseColorString (pStr, &pPIS->TextColor);
+ }
+ else if (strcmp (Opcode, "TBC") == 0)
+ { /* text back color */
+ ParseColorString (pStr, &pPIS->TextBackColor);
+ }
+ else if (strcmp (Opcode, "TFC") == 0)
+ { /* text fade color */
+ ParseColorString (pStr, &pPIS->TextFadeColor);
+ }
+ else if (strcmp (Opcode, "TVA") == 0)
+ { /* text vertical align */
+ pPIS->TextVPos = toupper (*pStr);
+ }
+ else if (strcmp (Opcode, "TE") == 0)
+ { /* text vertical align */
+ pPIS->TextEffect = toupper (*pStr);
+ }
+ else if (strcmp (Opcode, "TEXT") == 0)
+ { /* simple text draw */
+ int x, y;
+
+ assert (sizeof (pPIS->Buffer) >= 256);
+
+ if (3 == sscanf (pStr, "%d %d %255[^\n]", &x, &y, pPIS->Buffer))
+ {
+ TEXT t;
+
+ t.align = ALIGN_CENTER;
+ t.pStr = pPIS->Buffer;
+ t.CharCount = (COUNT)~0;
+ t.baseline.x = x;
+ t.baseline.y = y;
+ DrawTextEffect (&t, pPIS->TextColor, pPIS->TextBackColor,
+ pPIS->TextEffect);
+ }
+ }
+ else if (strcmp (Opcode, "TFI") == 0)
+ { /* text fade-in */
+ SIZE leading;
+ COUNT i;
+ COORD y;
+
+ utf8StringCopy (pPIS->Buffer, sizeof (pPIS->Buffer), pStr);
+ pPIS->LinesCount = ParseTextLines (pPIS->TextLines,
+ MAX_TEXT_LINES, pPIS->Buffer);
+
+ Present_UnbatchGraphics (pPIS, TRUE);
+
+ GetContextFontLeading (&leading);
+
+ switch (pPIS->TextVPos)
+ {
+ case 'T': /* top */
+ y = leading;
+ break;
+ case 'M': /* middle */
+ y = (pPIS->clip_r.extent.height
+ - pPIS->LinesCount * leading) / 2;
+ break;
+ default: /* bottom */
+ y = pPIS->clip_r.extent.height - pPIS->LinesCount * leading;
+ }
+ pPIS->tfade_r = pPIS->clip_r;
+ pPIS->tfade_r.corner.y += y - leading;
+ pPIS->tfade_r.extent.height = (pPIS->LinesCount + 1) * leading;
+ for (i = 0; i < pPIS->LinesCount; ++i, y += leading)
+ {
+ pPIS->TextLines[i].align = ALIGN_CENTER;
+ pPIS->TextLines[i].baseline.x = SCREEN_WIDTH / 2;
+ pPIS->TextLines[i].baseline.y = y;
+ }
+
+ for (i = 0; i < pPIS->LinesCount; ++i)
+ DrawTextEffect (pPIS->TextLines + i, pPIS->TextFadeColor,
+ pPIS->TextFadeColor, pPIS->TextEffect);
+
+ /* do transition */
+ SetTransitionSource (&pPIS->tfade_r);
+ BatchGraphics ();
+ for (i = 0; i < pPIS->LinesCount; ++i)
+ DrawTextEffect (pPIS->TextLines + i, pPIS->TextColor,
+ pPIS->TextBackColor, pPIS->TextEffect);
+ ScreenTransition (3, &pPIS->tfade_r);
+ UnbatchGraphics ();
+
+ }
+ else if (strcmp (Opcode, "TFO") == 0)
+ { /* text fade-out */
+ COUNT i;
+
+ Present_UnbatchGraphics (pPIS, TRUE);
+
+ /* do transition */
+ SetTransitionSource (&pPIS->tfade_r);
+ BatchGraphics ();
+ for (i = 0; i < pPIS->LinesCount; ++i)
+ DrawTextEffect (pPIS->TextLines + i, pPIS->TextFadeColor,
+ pPIS->TextFadeColor, pPIS->TextEffect);
+ ScreenTransition (3, &pPIS->tfade_r);
+ UnbatchGraphics ();
+ }
+ else if (strcmp (Opcode, "SAVEBG") == 0)
+ { /* save background */
+ TFB_DrawScreen_Copy (&pPIS->clip_r,
+ TFB_SCREEN_MAIN, TFB_SCREEN_EXTRA);
+ }
+ else if (strcmp (Opcode, "RESTBG") == 0)
+ { /* restore background */
+ TFB_DrawScreen_Copy (&pPIS->clip_r,
+ TFB_SCREEN_EXTRA, TFB_SCREEN_MAIN);
+ }
+ else if (strcmp (Opcode, "DRAW") == 0)
+ { /* draw a graphic */
+#define PRES_DRAW_INDEX 0
+#define PRES_DRAW_SIS 1
+ int cargs;
+ int draw_what;
+ int index = 0;
+ int x, y;
+ int scale;
+ int angle;
+ int scale_mode;
+ char ImgName[16];
+ int old_scale, old_mode;
+ STAMP s;
+
+ if (1 == sscanf (pStr, "%15s", ImgName)
+ && strcmp (strupr (ImgName), "SIS") == 0)
+ {
+ draw_what = PRES_DRAW_SIS;
+ scale_mode = TFB_SCALE_NEAREST;
+ cargs = sscanf (pStr, "%*s %d %d %d %d",
+ &x, &y, &scale, &angle) + 1;
+ }
+ else
+ {
+ draw_what = PRES_DRAW_INDEX;
+ scale_mode = TFB_SCALE_BILINEAR;
+ cargs = sscanf (pStr, "%d %d %d %d %d",
+ &index, &x, &y, &scale, &angle);
+ }
+
+ if (cargs < 1)
+ {
+ log_add (log_Warning, "Bad DRAW command '%s'", pStr);
+ continue;
+ }
+ if (cargs < 5)
+ angle = 0;
+ if (cargs < 4)
+ scale = GSCALE_IDENTITY;
+ if (cargs < 3)
+ {
+ x = 0;
+ y = 0;
+ }
+
+ s.frame = NULL;
+ if (draw_what == PRES_DRAW_INDEX)
+ { /* draw stamp by index */
+ s.frame = SetAbsFrameIndex (pPIS->Frame, (COUNT)index);
+ }
+ else if (draw_what == PRES_DRAW_SIS)
+ { /* draw dynamic SIS image with player's modules */
+ if (!pPIS->SisFrame)
+ Present_GenerateSIS (pPIS);
+
+ s.frame = SetAbsFrameIndex (pPIS->SisFrame, 0);
+ }
+ if (angle != 0)
+ {
+ if (angle != pPIS->LastAngle
+ || draw_what != pPIS->LastDrawKind)
+ {
+ DestroyDrawable (ReleaseDrawable (pPIS->RotatedFrame));
+ pPIS->RotatedFrame = CaptureDrawable (
+ RotateFrame (s.frame, -angle));
+ pPIS->LastAngle = angle;
+ pPIS->LastDrawKind = draw_what;
+ }
+ s.frame = pPIS->RotatedFrame;
+ }
+ s.origin.x = x;
+ s.origin.y = y;
+ old_mode = SetGraphicScaleMode (scale_mode);
+ old_scale = SetGraphicScale (scale);
+ DrawStamp (&s);
+ SetGraphicScale (old_scale);
+ SetGraphicScaleMode (old_mode);
+ }
+ else if (strcmp (Opcode, "BATCH") == 0)
+ { /* batch graphics */
+ Present_BatchGraphics (pPIS);
+ }
+ else if (strcmp (Opcode, "UNBATCH") == 0)
+ { /* unbatch graphics */
+ Present_UnbatchGraphics (pPIS, FALSE);
+ }
+ else if (strcmp (Opcode, "FTC") == 0)
+ { /* fade to color */
+ Present_UnbatchGraphics (pPIS, TRUE);
+ return DoFadeScreen (pPIS, pStr, FadeAllToColor);
+ }
+ else if (strcmp (Opcode, "FTB") == 0)
+ { /* fade to black */
+ Present_UnbatchGraphics (pPIS, TRUE);
+ return DoFadeScreen (pPIS, pStr, FadeAllToBlack);
+ }
+ else if (strcmp (Opcode, "FTW") == 0)
+ { /* fade to white */
+ Present_UnbatchGraphics (pPIS, TRUE);
+ return DoFadeScreen (pPIS, pStr, FadeAllToWhite);
+ }
+ else if (strcmp (Opcode, "CLS") == 0)
+ { /* clear screen */
+ Present_UnbatchGraphics (pPIS, TRUE);
+
+ ClearDrawable ();
+ }
+ else if (strcmp (Opcode, "CALL") == 0)
+ { /* call another script */
+ Present_UnbatchGraphics (pPIS, TRUE);
+
+ utf8StringCopy (pPIS->Buffer, sizeof (pPIS->Buffer), pStr);
+ ShowPresentationFile (pPIS->Buffer);
+ }
+ else if (strcmp (Opcode, "LINE") == 0)
+ {
+ int x1, x2, y1, y2;
+ if (4 == sscanf (pStr, "%d %d %d %d", &x1, &y1, &x2, &y2))
+ {
+ LINE l;
+
+ l.first.x = x1;
+ l.first.y = y1;
+ l.second.x = x2;
+ l.second.y = y2;
+
+ SetContextForeGroundColor (pPIS->TextColor);
+ DrawLine (&l);
+ }
+ else
+ {
+ log_add (log_Warning, "Bad LINE command '%s'", pStr);
+ }
+ }
+ else if (strcmp (Opcode, "MOVIE") == 0)
+ {
+ int fps, from, to;
+
+ if (3 == sscanf (pStr, "%d %d %d", &fps, &from, &to) &&
+ fps > 0 && from >= 0 && to >= 0 && to >= from)
+ {
+ Present_UnbatchGraphics (pPIS, TRUE);
+
+ pPIS->MovieFrame = from;
+ pPIS->MovieEndFrame = to;
+ pPIS->InterframeDelay = ONE_SECOND / fps;
+
+ pPIS->TimeOut = GetTimeCounter ();
+ pPIS->TimeOutOnSkip = TRUE;
+ return TRUE;
+ }
+ else
+ {
+ log_add (log_Warning, "Bad MOVIE command '%s'", pStr);
+ }
+ }
+ else if (strcmp (Opcode, "NOOP") == 0)
+ { /* no operation - must be a comment in script */
+ /* do nothing */
+ }
+ }
+ /* we are all done */
+ return FALSE;
+}
+
+static BOOLEAN
+ShowSlidePresentation (STRING PresStr)
+{
+ CONTEXT OldContext;
+ FONT OldFont;
+ RECT OldRect;
+ PRESENTATION_INPUT_STATE pis;
+ int i;
+
+ memset (&pis, 0, sizeof(pis));
+ pis.SlideShow = PresStr;
+ if (!pis.SlideShow)
+ return FALSE;
+ pis.SlideShow = SetAbsStringTableIndex (pis.SlideShow, 0);
+ pis.OperIndex = 0;
+
+ OldContext = SetContext (ScreenContext);
+ GetContextClipRect (&OldRect);
+ OldFont = SetContextFont (NULL);
+ SetContextBackGroundColor (BLACK_COLOR);
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ pis.InputFunc = DoPresentation;
+ pis.LastDrawKind = -1;
+ pis.TextVPos = 'B';
+ pis.MovieFrame = -1;
+ pis.StartTime = GetTimeCounter ();
+ pis.LastSyncTime = pis.StartTime;
+ DoInput (&pis, TRUE);
+
+ SleepThreadUntil (FadeMusic (0, ONE_SECOND));
+ StopMusic ();
+ FadeMusic (NORMAL_VOLUME, 0);
+
+ DestroyMusic (pis.MusicRef);
+ DestroyDrawable (ReleaseDrawable (pis.RotatedFrame));
+ DestroyDrawable (ReleaseDrawable (pis.Frame));
+ for (i = 0; i < MAX_FONTS; ++i)
+ DestroyFont (pis.Fonts[i]);
+
+ SetContextFont (OldFont);
+ SetContextClipRect (&OldRect);
+ SetContext (OldContext);
+
+ return TRUE;
+}
+
+static BOOLEAN
+DoVideoInput (void *pIS)
+{
+ VIDEO_INPUT_STATE* pVIS = (VIDEO_INPUT_STATE*) pIS;
+
+ if (!PlayingLegacyVideo (pVIS->CurVideo))
+ { // Video probably finished
+ return FALSE;
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_SELECT]
+ || PulsedInputState.menu[KEY_MENU_CANCEL]
+ || PulsedInputState.menu[KEY_MENU_SPECIAL]
+ || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ { // abort movie
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT]
+ || PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ SDWORD newpos = VidGetPosition ();
+ if (PulsedInputState.menu[KEY_MENU_LEFT])
+ newpos -= 2000;
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ newpos += 1000;
+ if (newpos < 0)
+ newpos = 0;
+
+ VidSeek (newpos);
+ }
+ else
+ {
+ if (!VidProcessFrame ())
+ return FALSE;
+
+ SleepThread (ONE_SECOND / 40);
+ }
+
+ return TRUE;
+}
+
+static void
+FadeClearScreen (void)
+{
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+
+ // clear the screen with black
+ SetContext (ScreenContext);
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+
+ FadeScreen (FadeAllToColor, 0);
+}
+
+static BOOLEAN
+ShowLegacyVideo (LEGACY_VIDEO vid)
+{
+ VIDEO_INPUT_STATE vis;
+ LEGACY_VIDEO_REF ref;
+
+ FadeClearScreen ();
+
+ ref = PlayLegacyVideo (vid);
+ if (!ref)
+ return FALSE;
+
+ vis.InputFunc = DoVideoInput;
+ vis.CurVideo = ref;
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ DoInput (&vis, TRUE);
+
+ StopLegacyVideo (ref);
+ FadeClearScreen ();
+
+ return TRUE;
+}
+
+BOOLEAN
+ShowPresentation (RESOURCE res)
+{
+ const char *resType = res_GetResourceType (res);
+ if (!resType)
+ {
+ return FALSE;
+ }
+ if (!strcmp (resType, "STRTAB"))
+ {
+ STRING pres = CaptureStringTable (LoadStringTable (res));
+ BOOLEAN result = ShowSlidePresentation (pres);
+ DestroyStringTable (ReleaseStringTable (pres));
+ return result;
+ }
+ else if (!strcmp (resType, "3DOVID"))
+ {
+ LEGACY_VIDEO vid = LoadLegacyVideoInstance (res);
+ BOOLEAN result = ShowLegacyVideo (vid);
+ DestroyLegacyVideo (vid);
+ return result;
+ }
+
+ log_add (log_Warning, "Tried to present '%s', of non-presentable type '%s'", res, resType);
+ return FALSE;
+}
diff --git a/src/uqm/ipdisp.c b/src/uqm/ipdisp.c
new file mode 100644
index 0000000..09a6fb8
--- /dev/null
+++ b/src/uqm/ipdisp.c
@@ -0,0 +1,777 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "ipdisp.h"
+
+#include "collide.h"
+#include "globdata.h"
+#include "init.h"
+#include "races.h"
+#include "process.h"
+#include "grpinfo.h"
+#include "encount.h"
+ // for EncounterGroup, EncounterRace
+#include "libs/mathlib.h"
+
+
+void
+NotifyOthers (COUNT which_race, BYTE target_loc)
+{
+ HSHIPFRAG hGroup, hNextGroup;
+
+ // NOTE: "Others" includes the group causing the notification too.
+
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ hGroup; hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ if (GroupPtr->race_id != which_race)
+ {
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ continue;
+ }
+
+ if (target_loc == IPNL_INTERCEPT_PLAYER)
+ {
+ GroupPtr->task &= ~IGNORE_FLAGSHIP;
+ // XXX: orbit_pos is abused here to store the previous
+ // group destination, before the intercept task.
+ // Returned to dest_loc below.
+ GroupPtr->orbit_pos = GroupPtr->dest_loc;
+ GroupPtr->dest_loc = IPNL_INTERCEPT_PLAYER;
+ }
+ else if (target_loc == IPNL_ALL_CLEAR)
+ {
+ GroupPtr->task |= IGNORE_FLAGSHIP;
+
+ if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ { // The group was intercepting, so send it back where it came
+ // XXX: orbit_pos was abused to store the previous
+ // group destination, before the intercept task.
+ GroupPtr->dest_loc = GroupPtr->orbit_pos;
+ GroupPtr->orbit_pos = NORMALIZE_FACING (TFB_Random ());
+#ifdef OLD
+ GroupPtr->dest_loc = (BYTE)(((COUNT)TFB_Random ()
+ % pSolarSysState->SunDesc[0].NumPlanets) + 1);
+#endif /* OLD */
+ }
+ // If the group wasn't intercepting, it will just continue
+ // going about its business.
+
+ if (!(GroupPtr->task & REFORM_GROUP))
+ {
+ if ((GroupPtr->task & ~IGNORE_FLAGSHIP) != EXPLORE)
+ GroupPtr->group_counter = 0;
+ else
+ GroupPtr->group_counter = ((COUNT) TFB_Random ()
+ % MAX_REVOLUTIONS) << FACING_SHIFT;
+ }
+ }
+ else
+ { // Send the group to the location.
+ // XXX: There is currently no use of such notify that I know of.
+ GroupPtr->task |= IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = target_loc;
+ }
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+}
+
+static SIZE
+zoomRadiusForLocation (BYTE location)
+{
+ if (location == 0)
+ { // In outer system view; use current zoom radius
+ return pSolarSysState->SunDesc[0].radius;
+ }
+ else
+ { // In inner system view; always max zoom
+ return MAX_ZOOM_RADIUS;
+ }
+}
+
+static inline void
+adjustDeltaVforZoom (SIZE zoom, SIZE *dx, SIZE *dy)
+{
+ if (zoom == MIN_ZOOM_RADIUS)
+ {
+ *dx >>= 2;
+ *dy >>= 2;
+ }
+ else if (zoom < MAX_ZOOM_RADIUS)
+ {
+ *dx >>= 1;
+ *dy >>= 1;
+ }
+}
+
+static BYTE
+getFlagshipLocation (void)
+{
+ if (!playerInInnerSystem ())
+ return 0;
+ else
+ return 1 + planetIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
+}
+
+static void
+ip_group_preprocess (ELEMENT *ElementPtr)
+{
+#define TRACK_WAIT 5
+ BYTE task;
+ BYTE target_loc, group_loc, flagship_loc;
+ SIZE radius;
+ POINT dest_pt;
+ SIZE vdx, vdy;
+ ELEMENT *EPtr;
+ IP_GROUP *GroupPtr;
+
+ EPtr = ElementPtr;
+ EPtr->state_flags &= ~(DISAPPEARING | NONSOLID); // "I'm not quite dead"
+ ++EPtr->life_span; // so that it will 'die' again next time
+
+ GetElementStarShip (EPtr, &GroupPtr);
+ group_loc = GroupPtr->sys_loc; // save old location
+ DisplayArray[EPtr->PrimIndex].Object.Point = GroupPtr->loc;
+
+ radius = zoomRadiusForLocation (group_loc);
+ dest_pt = locationToDisplay (GroupPtr->loc, radius);
+ EPtr->current.location.x = DISPLAY_TO_WORLD (dest_pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ EPtr->current.location.y = DISPLAY_TO_WORLD (dest_pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ InitIntersectStartPoint (EPtr);
+
+ flagship_loc = getFlagshipLocation ();
+
+ task = GroupPtr->task;
+
+ if ((task & REFORM_GROUP) && --GroupPtr->group_counter == 0)
+ { // Finished reforming the group
+ task &= ~REFORM_GROUP;
+ GroupPtr->task = task;
+ if ((task & ~IGNORE_FLAGSHIP) != EXPLORE)
+ GroupPtr->group_counter = 0;
+ else
+ GroupPtr->group_counter = ((COUNT)TFB_Random ()
+ % MAX_REVOLUTIONS) << FACING_SHIFT;
+ }
+
+ // If fleeing *and* ignoring flagship
+ if ((task & ~(IGNORE_FLAGSHIP | REFORM_GROUP)) == FLEE
+ && (task & IGNORE_FLAGSHIP))
+ { // Make fleeing groups non-collidable
+ EPtr->state_flags |= NONSOLID;
+ }
+
+ target_loc = GroupPtr->dest_loc;
+ if (!(task & (IGNORE_FLAGSHIP | REFORM_GROUP)))
+ {
+ if (target_loc == IPNL_INTERCEPT_PLAYER && task != FLEE)
+ {
+ /* if intercepting flagship */
+ target_loc = flagship_loc;
+ if (EPtr->thrust_wait > TRACK_WAIT)
+ {
+ EPtr->thrust_wait = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+ }
+ }
+ else if (group_loc == flagship_loc)
+ {
+ long detect_dist;
+
+ detect_dist = 1200;
+ if (group_loc != 0) /* if in planetary views */
+ {
+ detect_dist *= (MAX_ZOOM_RADIUS / MIN_ZOOM_RADIUS);
+ if (GroupPtr->race_id == URQUAN_DRONE_SHIP)
+ detect_dist <<= 1;
+ }
+ vdx = GLOBAL (ip_location.x) - GroupPtr->loc.x;
+ vdy = GLOBAL (ip_location.y) - GroupPtr->loc.y;
+ if ((long)vdx * vdx
+ + (long)vdy * vdy < (long)detect_dist * detect_dist)
+ {
+ EPtr->thrust_wait = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+
+ NotifyOthers (GroupPtr->race_id, IPNL_INTERCEPT_PLAYER);
+ task = GroupPtr->task;
+ target_loc = GroupPtr->dest_loc;
+ if (target_loc == IPNL_INTERCEPT_PLAYER)
+ target_loc = flagship_loc;
+ }
+ }
+ }
+
+ GetCurrentVelocityComponents (&EPtr->velocity, &vdx, &vdy);
+
+ task &= ~IGNORE_FLAGSHIP;
+#ifdef NEVER
+ if (task <= FLEE || (task == ON_STATION && GroupPtr->dest_loc == 0))
+#else
+ if (task <= ON_STATION)
+#endif /* NEVER */
+ {
+ BOOLEAN Transition;
+ SIZE dx, dy;
+ SIZE delta_x, delta_y;
+ COUNT angle;
+
+ Transition = FALSE;
+ if (task == FLEE)
+ {
+ dest_pt.x = GroupPtr->loc.x << 1;
+ dest_pt.y = GroupPtr->loc.y << 1;
+ }
+ else if (((task != ON_STATION ||
+ GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ && group_loc == target_loc)
+ || (task == ON_STATION &&
+ GroupPtr->dest_loc != IPNL_INTERCEPT_PLAYER
+ && group_loc == 0))
+ {
+ if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ dest_pt = GLOBAL (ip_location);
+ else
+ {
+ COUNT orbit_dist;
+ POINT org;
+
+ if (task != ON_STATION)
+ {
+ orbit_dist = ORBIT_RADIUS;
+ org.x = org.y = 0;
+ }
+ else
+ {
+ orbit_dist = STATION_RADIUS;
+ org = planetOuterLocation (target_loc - 1);
+ }
+
+ angle = FACING_TO_ANGLE (GroupPtr->orbit_pos + 1);
+ dest_pt.x = org.x + COSINE (angle, orbit_dist);
+ dest_pt.y = org.y + SINE (angle, orbit_dist);
+ if (GroupPtr->loc.x == dest_pt.x
+ && GroupPtr->loc.y == dest_pt.y)
+ {
+ BYTE next_loc;
+
+ GroupPtr->orbit_pos = NORMALIZE_FACING (
+ ANGLE_TO_FACING (angle));
+ angle += FACING_TO_ANGLE (1);
+ dest_pt.x = org.x + COSINE (angle, orbit_dist);
+ dest_pt.y = org.y + SINE (angle, orbit_dist);
+
+ EPtr->thrust_wait = (BYTE)~0;
+ if (GroupPtr->group_counter)
+ --GroupPtr->group_counter;
+ else if (task == EXPLORE
+ && (next_loc = (BYTE)(((COUNT)TFB_Random ()
+ % pSolarSysState->SunDesc[0].NumPlanets)
+ + 1)) != target_loc)
+ {
+ EPtr->thrust_wait = 0;
+ target_loc = next_loc;
+ GroupPtr->dest_loc = next_loc;
+ }
+ }
+ }
+ }
+ else if (group_loc == 0)
+ {
+ if (GroupPtr->dest_loc == IPNL_INTERCEPT_PLAYER)
+ dest_pt = pSolarSysState->SunDesc[0].location;
+ else
+ dest_pt = planetOuterLocation (target_loc - 1);
+ }
+ else
+ {
+ if (task == ON_STATION)
+ target_loc = 0;
+
+ dest_pt.x = GroupPtr->loc.x << 1;
+ dest_pt.y = GroupPtr->loc.y << 1;
+ }
+
+ delta_x = dest_pt.x - GroupPtr->loc.x;
+ delta_y = dest_pt.y - GroupPtr->loc.y;
+ angle = ARCTAN (delta_x, delta_y);
+
+ if (EPtr->thrust_wait && EPtr->thrust_wait != (BYTE)~0)
+ --EPtr->thrust_wait;
+ else if ((vdx == 0 && vdy == 0)
+ || angle != GetVelocityTravelAngle (&EPtr->velocity))
+ {
+ SIZE speed;
+
+ if (EPtr->thrust_wait &&
+ GroupPtr->dest_loc != IPNL_INTERCEPT_PLAYER)
+ {
+#define ORBIT_SPEED 60
+ speed = ORBIT_SPEED;
+ if (task == ON_STATION)
+ speed >>= 1;
+ }
+ else
+ {
+ SIZE RaceIPSpeed[] =
+ {
+ RACE_IP_SPEED
+ };
+
+ speed = RaceIPSpeed[GroupPtr->race_id];
+ EPtr->thrust_wait = TRACK_WAIT;
+ }
+
+ vdx = COSINE (angle, speed);
+ vdy = SINE (angle, speed);
+ SetVelocityComponents (&EPtr->velocity, vdx, vdy);
+ }
+
+ dx = vdx;
+ dy = vdy;
+ if (group_loc == target_loc)
+ {
+ if (target_loc == 0)
+ {
+ if (task == FLEE)
+ goto CheckGetAway;
+ }
+ else if (target_loc == GroupPtr->dest_loc)
+ {
+PartialRevolution:
+ if ((long)((COUNT)(dx * dx) + (COUNT)(dy * dy))
+ >= (long)delta_x * delta_x + (long)delta_y * delta_y)
+ {
+ GroupPtr->loc = dest_pt;
+ vdx = 0;
+ vdy = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+ }
+ }
+ }
+ else
+ {
+ if (group_loc == 0)
+ { // In outer system
+ adjustDeltaVforZoom (radius, &dx, &dy);
+
+ if (task == ON_STATION && GroupPtr->dest_loc)
+ goto PartialRevolution;
+ else if ((long)((COUNT)(dx * dx) + (COUNT)(dy * dy))
+ >= (long)delta_x * delta_x + (long)delta_y * delta_y)
+ Transition = TRUE;
+ }
+ else
+ { // In inner system; also leaving outer CheckGetAway hack
+CheckGetAway:
+ dest_pt = locationToDisplay (GroupPtr->loc, radius);
+ if (dest_pt.x < 0
+ || dest_pt.x >= SIS_SCREEN_WIDTH
+ || dest_pt.y < 0
+ || dest_pt.y >= SIS_SCREEN_HEIGHT)
+ Transition = TRUE;
+ }
+
+ if (Transition)
+ {
+ /* no collisions during transition */
+ EPtr->state_flags |= NONSOLID;
+
+ vdx = 0;
+ vdy = 0;
+ ZeroVelocityComponents (&EPtr->velocity);
+ if (group_loc != 0)
+ {
+ GroupPtr->loc = planetOuterLocation (group_loc - 1);
+ group_loc = 0;
+ GroupPtr->sys_loc = 0;
+ }
+ else if (target_loc == 0)
+ {
+ /* Group completely left the star system */
+ EPtr->life_span = 0;
+ EPtr->state_flags |= DISAPPEARING | NONSOLID;
+ GroupPtr->in_system = 0;
+ return;
+ }
+ else
+ {
+ POINT entryPt;
+
+ if (target_loc == GroupPtr->dest_loc)
+ {
+ GroupPtr->orbit_pos = NORMALIZE_FACING (
+ ANGLE_TO_FACING (angle + HALF_CIRCLE));
+ GroupPtr->group_counter =
+ ((COUNT)TFB_Random () % MAX_REVOLUTIONS)
+ << FACING_SHIFT;
+ }
+ // The group enters inner system exactly on the edge of a
+ // circle with radius = 9/16 * window-dim, which is
+ // different from how the flagship enters, but similar
+ // in the way that the group will never show up in any
+ // of the corners.
+ entryPt.x = (SIS_SCREEN_WIDTH >> 1) - COSINE (angle,
+ SIS_SCREEN_WIDTH * 9 / 16);
+ entryPt.y = (SIS_SCREEN_HEIGHT >> 1) - SINE (angle,
+ SIS_SCREEN_HEIGHT * 9 / 16);
+ GroupPtr->loc = displayToLocation (entryPt,
+ MAX_ZOOM_RADIUS);
+ group_loc = target_loc;
+ GroupPtr->sys_loc = target_loc;
+ }
+ }
+ }
+ }
+
+ radius = zoomRadiusForLocation (group_loc);
+ adjustDeltaVforZoom (radius, &vdx, &vdy);
+ GroupPtr->loc.x += vdx;
+ GroupPtr->loc.y += vdy;
+
+ dest_pt = locationToDisplay (GroupPtr->loc, radius);
+ EPtr->next.location.x = DISPLAY_TO_WORLD (dest_pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ EPtr->next.location.y = DISPLAY_TO_WORLD (dest_pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ // Don't draw the group if it's not at flagship location,
+ // or flash the group while it's reforming
+ if (group_loc != flagship_loc
+ || ((task & REFORM_GROUP)
+ && (GroupPtr->group_counter & 1)))
+ {
+ SetPrimType (&DisplayArray[EPtr->PrimIndex], NO_PRIM);
+ EPtr->state_flags |= NONSOLID;
+ }
+ else
+ {
+ SetPrimType (&DisplayArray[EPtr->PrimIndex], STAMP_PRIM);
+ if (task & REFORM_GROUP)
+ EPtr->state_flags |= NONSOLID;
+ }
+
+ EPtr->state_flags |= CHANGING;
+}
+
+static void
+flag_ship_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ return; // ignore the rest of the collisions
+
+ if (!(ElementPtr1->state_flags & COLLISION))
+ { // The other element's collision has not been processed yet
+ // Defer starting the encounter until it is.
+ ElementPtr0->state_flags |= COLLISION | NONSOLID;
+ }
+ else
+ { // Both element's collisions have now been processed
+ ElementPtr1->state_flags &= ~COLLISION;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+ip_group_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ IP_GROUP *GroupPtr;
+ void *OtherPtr;
+
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ return; // ignore the rest of the collisions
+
+ GetElementStarShip (ElementPtr0, &GroupPtr);
+ GetElementStarShip (ElementPtr1, &OtherPtr);
+ if (OtherPtr)
+ { // Collision with another group
+ // Prevent the groups from coalescing into a single ship icon
+ if ((ElementPtr0->state_flags & COLLISION)
+ || (ElementPtr1->current.location.x == ElementPtr1->next.location.x
+ && ElementPtr1->current.location.y == ElementPtr1->next.location.y))
+ {
+ ElementPtr0->state_flags &= ~COLLISION;
+ }
+ else
+ {
+ ElementPtr1->state_flags |= COLLISION;
+
+ GroupPtr->loc = DisplayArray[ElementPtr0->PrimIndex].Object.Point;
+ ElementPtr0->next.location = ElementPtr0->current.location;
+ InitIntersectEndPoint (ElementPtr0);
+ }
+ }
+ else // if (!OtherPtr)
+ { // Collision with a flagship
+ EncounterGroup = GroupPtr->group_id;
+
+ GroupPtr->task |= REFORM_GROUP;
+ GroupPtr->group_counter = 100;
+ // Send "all clear" for the time being. After the encounter, if
+ // the player battles the group, the "intercept" notify will be
+ // resent.
+ NotifyOthers (GroupPtr->race_id, IPNL_ALL_CLEAR);
+
+ if (!(ElementPtr1->state_flags & COLLISION))
+ { // The other element's collision has not been processed yet
+ // Defer starting the encounter until it is.
+ ElementPtr0->state_flags |= COLLISION | NONSOLID;
+ }
+ else
+ { // Both element's collisions have now been processed
+ ElementPtr1->state_flags &= ~COLLISION;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ }
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+spawn_ip_group (IP_GROUP *GroupPtr)
+{
+ HELEMENT hIPSHIPElement;
+
+ hIPSHIPElement = AllocElement ();
+ if (hIPSHIPElement)
+ {
+ ELEMENT *IPSHIPElementPtr;
+
+ LockElement (hIPSHIPElement, &IPSHIPElementPtr);
+ // Must have mass_points for collisions to work
+ IPSHIPElementPtr->mass_points = 1;
+ IPSHIPElementPtr->hit_points = 1;
+ IPSHIPElementPtr->state_flags =
+ CHANGING | FINITE_LIFE | IGNORE_VELOCITY;
+
+ SetPrimType (&DisplayArray[IPSHIPElementPtr->PrimIndex], STAMP_PRIM);
+ // XXX: Hack: farray points to FRAME[3] and given FRAME
+ IPSHIPElementPtr->current.image.farray = &GroupPtr->melee_icon;
+ IPSHIPElementPtr->current.image.frame = SetAbsFrameIndex (
+ GroupPtr->melee_icon, 1);
+ /* preprocessing has a side effect
+ * we wish to avoid. So death_func
+ * is used instead, but will achieve
+ * same result without the side
+ * effect (InitIntersectFrame)
+ */
+ IPSHIPElementPtr->death_func = ip_group_preprocess;
+ IPSHIPElementPtr->collision_func = ip_group_collision;
+
+ {
+ SIZE radius;
+ POINT pt;
+
+ radius = zoomRadiusForLocation (GroupPtr->sys_loc);
+ pt = locationToDisplay (GroupPtr->loc, radius);
+
+ IPSHIPElementPtr->current.location.x =
+ DISPLAY_TO_WORLD (pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ IPSHIPElementPtr->current.location.y =
+ DISPLAY_TO_WORLD (pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+ }
+
+ SetElementStarShip (IPSHIPElementPtr, GroupPtr);
+
+ SetUpElement (IPSHIPElementPtr);
+ IPSHIPElementPtr->IntersectControl.IntersectStamp.frame =
+ DecFrameIndex (stars_in_space);
+
+ UnlockElement (hIPSHIPElement);
+
+ PutElement (hIPSHIPElement);
+ }
+}
+
+#define FLIP_WAIT 42
+
+static void
+flag_ship_preprocess (ELEMENT *ElementPtr)
+{
+ if (--ElementPtr->thrust_wait == 0)
+ /* juggle list after flagship */
+ {
+ HELEMENT hSuccElement;
+
+ if ((hSuccElement = GetSuccElement (ElementPtr))
+ && hSuccElement != GetTailElement ())
+ {
+ HELEMENT hPredElement;
+ ELEMENT *TailPtr;
+
+ LockElement (GetTailElement (), &TailPtr);
+ hPredElement = _GetPredLink (TailPtr);
+ UnlockElement (GetTailElement ());
+
+ RemoveElement (hSuccElement);
+ PutElement (hSuccElement);
+ }
+
+ ElementPtr->thrust_wait = FLIP_WAIT;
+ }
+
+ {
+ BYTE flagship_loc, ec;
+ SIZE vdx, vdy, radius;
+ POINT pt;
+
+ GetCurrentVelocityComponents (&GLOBAL (velocity), &vdx, &vdy);
+
+ flagship_loc = getFlagshipLocation ();
+ radius = zoomRadiusForLocation (flagship_loc);
+ adjustDeltaVforZoom (radius, &vdx, &vdy);
+
+ pt = locationToDisplay (GLOBAL (ip_location), radius);
+ ElementPtr->current.location.x = DISPLAY_TO_WORLD (pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ ElementPtr->current.location.y = DISPLAY_TO_WORLD (pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+ InitIntersectStartPoint (ElementPtr);
+
+ GLOBAL (ip_location.x) += vdx;
+ GLOBAL (ip_location.y) += vdy;
+
+ pt = locationToDisplay (GLOBAL (ip_location), radius);
+ ElementPtr->next.location.x = DISPLAY_TO_WORLD (pt.x)
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ ElementPtr->next.location.y = DISPLAY_TO_WORLD (pt.y)
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ GLOBAL (ShipStamp.origin) = pt;
+ ElementPtr->next.image.frame = GLOBAL (ShipStamp.frame);
+
+ if (ElementPtr->sys_loc == flagship_loc)
+ {
+ if (ElementPtr->state_flags & NONSOLID)
+ ElementPtr->state_flags &= ~NONSOLID;
+ }
+ else /* no collisions during transition */
+ {
+ ElementPtr->state_flags |= NONSOLID;
+ ElementPtr->sys_loc = flagship_loc;
+ }
+
+ if ((ec = GET_GAME_STATE (ESCAPE_COUNTER))
+ && !(GLOBAL (CurrentActivity) & START_ENCOUNTER))
+ {
+ ElementPtr->state_flags |= NONSOLID;
+
+ --ec;
+ SET_GAME_STATE (ESCAPE_COUNTER, ec);
+ }
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static void
+spawn_flag_ship (void)
+{
+ HELEMENT hFlagShipElement;
+
+ hFlagShipElement = AllocElement ();
+ if (hFlagShipElement)
+ {
+ ELEMENT *FlagShipElementPtr;
+
+ LockElement (hFlagShipElement, &FlagShipElementPtr);
+ FlagShipElementPtr->hit_points = 1;
+ // Must have mass_points for collisions to work
+ FlagShipElementPtr->mass_points = 1;
+ FlagShipElementPtr->sys_loc = getFlagshipLocation ();
+ FlagShipElementPtr->state_flags = APPEARING | IGNORE_VELOCITY;
+ if (GET_GAME_STATE (ESCAPE_COUNTER))
+ FlagShipElementPtr->state_flags |= NONSOLID;
+ FlagShipElementPtr->life_span = NORMAL_LIFE;
+ FlagShipElementPtr->thrust_wait = FLIP_WAIT;
+ SetPrimType (&DisplayArray[FlagShipElementPtr->PrimIndex], STAMP_PRIM);
+ FlagShipElementPtr->current.image.farray =
+ &GLOBAL (ShipStamp.frame);
+ FlagShipElementPtr->current.image.frame =
+ GLOBAL (ShipStamp.frame);
+ FlagShipElementPtr->preprocess_func = flag_ship_preprocess;
+ FlagShipElementPtr->collision_func = flag_ship_collision;
+
+ FlagShipElementPtr->current.location.x =
+ DISPLAY_TO_WORLD (GLOBAL (ShipStamp.origin.x))
+ + (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> (MAX_REDUCTION + 1));
+ FlagShipElementPtr->current.location.y =
+ DISPLAY_TO_WORLD (GLOBAL (ShipStamp.origin.y))
+ + (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> (MAX_REDUCTION + 1));
+
+ UnlockElement (hFlagShipElement);
+
+ PutElement (hFlagShipElement);
+ }
+}
+
+void
+DoMissions (void)
+{
+ HSHIPFRAG hGroup, hNextGroup;
+
+ spawn_flag_ship ();
+
+ if (EncounterRace >= 0)
+ { // There was a battle. Call in reinforcements.
+ NotifyOthers (EncounterRace, IPNL_INTERCEPT_PLAYER);
+ EncounterRace = -1;
+ }
+
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ hGroup; hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ if (GroupPtr->in_system)
+ spawn_ip_group (GroupPtr);
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+}
+
diff --git a/src/uqm/ipdisp.h b/src/uqm/ipdisp.h
new file mode 100644
index 0000000..6b910ca
--- /dev/null
+++ b/src/uqm/ipdisp.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_IPDISP_H_INCL_
+#define UQM_IPDISP_H_INCL_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void NotifyOthers (COUNT which_race, BYTE target_loc);
+// Special target locations for NotifyOthers()
+#define IPNL_INTERCEPT_PLAYER 0
+#define IPNL_ALL_CLEAR ((BYTE)-1)
+
+extern void DoMissions (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_IPDISP_H_INCL_ */
diff --git a/src/uqm/isndres.h b/src/uqm/isndres.h
new file mode 100644
index 0000000..6de89e6
--- /dev/null
+++ b/src/uqm/isndres.h
@@ -0,0 +1,7 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define GAME_SOUNDS "sounds.battle"
+#define LANDER_SOUNDS "sounds.lander"
+#define MENU_SOUNDS "sounds.menu"
diff --git a/src/uqm/istrtab.h b/src/uqm/istrtab.h
new file mode 100644
index 0000000..503b495
--- /dev/null
+++ b/src/uqm/istrtab.h
@@ -0,0 +1,154 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ACID_COLOR_TAB "planet.acid.colortable"
+#define ACID_XLAT_TAB "planet.acid.translatetable"
+#define ALKALI_COLOR_TAB "planet.alkali.colortable"
+#define ALKALI_XLAT_TAB "planet.alkali.translatetable"
+#define ANDROSYNTH_RUINS_STRTAB "text.androsynthruins"
+#define AQUA_STRTAB "text.aquahelix"
+#define ARISPACE_COLOR_TAB "colortable.quasispace"
+#define AURIC_COLOR_TAB "planet.auric.colortable"
+#define AURIC_XLAT_TAB "planet.auric.translatetable"
+#define AZURE_COLOR_TAB "planet.azure.colortable"
+#define AZURE_XLAT_TAB "planet.azure.translatetable"
+#define BEAST_STRTAB "text.vuxbeast"
+#define BLU_GAS_COLOR_TAB "planet.bluegas.colortable"
+#define BLU_GAS_XLAT_TAB "planet.bluegas.translatetable"
+#define BOMB_STRTAB "text.utwigbomb"
+#define BURV_BCS_STRTAB "text.burvixcaster"
+#define BURV_RUINS_STRTAB "text.burvixeseruins"
+#define CARBIDE_COLOR_TAB "planet.carbide.colortable"
+#define CARBIDE_XLAT_TAB "planet.carbide.translatetable"
+#define CHLORINE_COLOR_TAB "planet.chlorine.colortable"
+#define CHLORINE_XLAT_TAB "planet.chlorine.translatetable"
+#define CHMMR_BASE_STRTAB "text.chmmrbase"
+#define CHONDRITE_COLOR_TAB "planet.chondrite.colortable"
+#define CHONDRITE_XLAT_TAB "planet.chondrite.translatetable"
+#define CIMMERIAN_COLOR_TAB "planet.cimmerian.colortable"
+#define CIMMERIAN_XLAT_TAB "planet.cimmerian.translatetable"
+#define COPPER_COLOR_TAB "planet.copper.colortable"
+#define COPPER_XLAT_TAB "planet.copper.translatetable"
+#define CREDITS_STRTAB "credits.credits"
+#define CRIMSON_COLOR_TAB "planet.crimson.colortable"
+#define CRIMSON_XLAT_TAB "planet.crimson.translatetable"
+#define CYANIC_COLOR_TAB "planet.cyanic.colortable"
+#define CYANIC_XLAT_TAB "planet.cyanic.translatetable"
+#define CYA_GAS_COLOR_TAB "planet.cyangas.colortable"
+#define CYA_GAS_XLAT_TAB "planet.cyangas.translatetable"
+#define DRUUGE_RUINS_STRTAB "text.sphere"
+#define DUST_COLOR_TAB "planet.dust.colortable"
+#define DUST_XLAT_TAB "planet.dust.translatetable"
+#define EGG_CASE_STRTAB "text.eggcase"
+#define EMERALD_COLOR_TAB "planet.emerald.colortable"
+#define EMERALD_XLAT_TAB "planet.emerald.translatetable"
+#define FINALPRES_STRTAB "slides.ending"
+#define FLUORESCENT_COLOR_TAB "planet.fluorescent.colortable"
+#define FLUORESCENT_XLAT_TAB "planet.fluorescent.translatetable"
+#define GREEN_COLOR_TAB "planet.green.colortable"
+#define GREEN_XLAT_TAB "planet.green.translatetable"
+#define GRN_GAS_COLOR_TAB "planet.greengas.colortable"
+#define GRN_GAS_XLAT_TAB "planet.greengas.translatetable"
+#define GRY_GAS_COLOR_TAB "planet.greygas.colortable"
+#define GRY_GAS_XLAT_TAB "planet.greygas.translatetable"
+#define HALIDE_COLOR_TAB "planet.halide.colortable"
+#define HALIDE_XLAT_TAB "planet.halide.translatetable"
+#define HANGAR_COLOR_TAB "colortable.hangar"
+#define HYDROCARBON_COLOR_TAB "planet.hydrocarbon.colortable"
+#define HYDROCARBON_XLAT_TAB "planet.hydrocarbon.translatetable"
+#define HYPER_COLOR_TAB "colortable.hyperspace"
+#define INFRARED_COLOR_TAB "planet.infrared.colortable"
+#define INFRARED_XLAT_TAB "planet.infrared.translatetable"
+#define INTROPRES_STRTAB "slides.intro"
+#define IODINE_COLOR_TAB "planet.iodine.colortable"
+#define IODINE_XLAT_TAB "planet.iodine.translatetable"
+#define IPSUN_COLOR_MAP "colortable.truespace"
+#define JOYSTICK_ALPHA_STRTAB "text.joyalpha"
+#define LANTHANIDE_COLOR_TAB "planet.lanthanide.colortable"
+#define LANTHANIDE_XLAT_TAB "planet.lanthanide.translatetable"
+#define MAGMA_COLOR_TAB "planet.magma.colortable"
+#define MAGMA_XLAT_TAB "planet.magma.translatetable"
+#define MAGNETIC_COLOR_TAB "planet.magnetic.colortable"
+#define MAGNETIC_XLAT_TAB "planet.magnetic.translatetable"
+#define MAIDENS_STRTAB "text.maidens"
+#define MAROON_COLOR_TAB "planet.maroon.colortable"
+#define MAROON_XLAT_TAB "planet.maroon.translatetable"
+#define METAL_COLOR_TAB "planet.metal.colortable"
+#define METAL_XLAT_TAB "planet.metal.translatetable"
+#define MOONBASE_STRTAB "text.moonbase"
+#define NOBLE_COLOR_TAB "planet.noble.colortable"
+#define NOBLE_XLAT_TAB "planet.noble.translatetable"
+#define OOLITE_COLOR_TAB "planet.oolite.colortable"
+#define OOLITE_XLAT_TAB "planet.oolite.translatetable"
+#define OPALESCENT_COLOR_TAB "planet.opalescent.colortable"
+#define OPALESCENT_XLAT_TAB "planet.opalescent.translatetable"
+#define ORA_GAS_COLOR_TAB "planet.orangegas.colortable"
+#define ORA_GAS_XLAT_TAB "planet.orangegas.translatetable"
+#define ORBPLAN_COLOR_MAP "colortable.orbplan"
+#define ORGANIC_COLOR_TAB "planet.organic.colortable"
+#define ORGANIC_XLAT_TAB "planet.organic.translatetable"
+#define PELLUCID_COLOR_TAB "planet.pellucid.colortable"
+#define PELLUCID_XLAT_TAB "planet.pellucid.translatetable"
+#define PKUNK_RUINS_STRTAB "text.spindle"
+#define PLUTONIC_COLOR_TAB "planet.plutonic.colortable"
+#define PLUTONIC_XLAT_TAB "planet.plutonic.translatetable"
+#define PRIMORDIAL_COLOR_TAB "planet.primordial.colortable"
+#define PRIMORDIAL_XLAT_TAB "planet.primordial.translatetable"
+#define PURPLE_COLOR_TAB "planet.purple.colortable"
+#define PURPLE_XLAT_TAB "planet.purple.translatetable"
+#define PUR_GAS_COLOR_TAB "planet.purplegas.colortable"
+#define PUR_GAS_XLAT_TAB "planet.purplegas.translatetable"
+#define QUASI_DEGENERATE_COLOR_TAB "planet.quasidegenerate.colortable"
+#define QUASI_DEGENERATE_XLAT_TAB "planet.quasidegenerate.translatetable"
+#define RADIOACTIVE_COLOR_TAB "planet.radioactive.colortable"
+#define RADIOACTIVE_XLAT_TAB "planet.radioactive.translatetable"
+#define RAINBOW_COLOR_TAB "planet.rainbow.colortable"
+#define RAINBOW_XLAT_TAB "planet.rainbow.translatetable"
+#define REDUX_COLOR_TAB "planet.redux.colortable"
+#define REDUX_XLAT_TAB "planet.redux.translatetable"
+#define RED_GAS_COLOR_TAB "planet.redgas.colortable"
+#define RED_GAS_XLAT_TAB "planet.redgas.translatetable"
+#define RUBY_COLOR_TAB "planet.ruby.colortable"
+#define RUBY_XLAT_TAB "planet.ruby.translatetable"
+#define RUINS_STRTAB "text.ruins"
+#define SAPPHIRE_COLOR_TAB "planet.sapphire.colortable"
+#define SAPPHIRE_XLAT_TAB "planet.sapphire.translatetable"
+#define SELENIC_COLOR_TAB "planet.selenic.colortable"
+#define SELENIC_XLAT_TAB "planet.selenic.translatetable"
+#define SETUP_MENU_STRTAB "text.setupmenu"
+#define SHATTERED_COLOR_TAB "planet.shattered.colortable"
+#define SHATTERED_XLAT_TAB "planet.shattered.translatetable"
+#define SPAPLUTO_STRTAB "text.fwiffo"
+#define STARCON_COLOR_MAP "colortable.main"
+#define STARCON_GAME_STRINGS "text.starcon"
+#define SUN_DEVICE_STRTAB "text.sundevice"
+#define SUPER_DENSE_COLOR_TAB "planet.superdense.colortable"
+#define SUPER_DENSE_XLAT_TAB "planet.superdense.translatetable"
+#define SUPOX_RUINS_STRTAB "text.ultron"
+#define TAALO_DEVICE_STRTAB "text.taalodevice"
+#define TELLURIC_COLOR_TAB "planet.telluric.colortable"
+#define TELLURIC_XLAT_TAB "planet.telluric.translatetable"
+#define TREASURE_COLOR_TAB "planet.treasure.colortable"
+#define TREASURE_XLAT_TAB "planet.treasure.translatetable"
+#define ULTRAMARINE_COLOR_TAB "planet.ultramarine.colortable"
+#define ULTRAMARINE_XLAT_TAB "planet.ultramarine.translatetable"
+#define ULTRAVIOLET_COLOR_TAB "planet.ultraviolet.colortable"
+#define ULTRAVIOLET_XLAT_TAB "planet.ultraviolet.translatetable"
+#define UMGAH_BCS_STRTAB "text.umgahcaster"
+#define UREA_COLOR_TAB "planet.urea.colortable"
+#define UREA_XLAT_TAB "planet.urea.translatetable"
+#define VAULT_STRTAB "text.syreenvault"
+#define VINYLOGOUS_COLOR_TAB "planet.vinylogous.colortable"
+#define VINYLOGOUS_XLAT_TAB "planet.vinylogous.translatetable"
+#define VIO_GAS_COLOR_TAB "planet.violetgas.colortable"
+#define VIO_GAS_XLAT_TAB "planet.violetgas.translatetable"
+#define WATER_COLOR_TAB "planet.water.colortable"
+#define WATER_XLAT_TAB "planet.water.translatetable"
+#define WRECK_STRTAB "text.urquanwreck"
+#define XENOLITHIC_COLOR_TAB "planet.xenolithic.colortable"
+#define XENOLITHIC_XLAT_TAB "planet.xenolithic.translatetable"
+#define YEL_GAS_COLOR_TAB "planet.yellowgas.colortable"
+#define YEL_GAS_XLAT_TAB "planet.yellowgas.translatetable"
+#define YTTRIC_COLOR_TAB "planet.yttric.colortable"
+#define YTTRIC_XLAT_TAB "planet.yttric.translatetable"
diff --git a/src/uqm/load.c b/src/uqm/load.c
new file mode 100644
index 0000000..2e7dcbd
--- /dev/null
+++ b/src/uqm/load.c
@@ -0,0 +1,774 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+
+#include "build.h"
+#include "encount.h"
+#include "starmap.h"
+#include "libs/file.h"
+#include "globdata.h"
+#include "options.h"
+#include "save.h"
+#include "setup.h"
+#include "state.h"
+#include "grpintrn.h"
+
+#include "libs/tasklib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+//#define DEBUG_LOAD
+
+ACTIVITY NextActivity;
+
+static inline size_t
+read_8 (void *fp, BYTE *v)
+{
+ BYTE t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadResFile (v, 1, 1, fp);
+}
+
+static inline size_t
+read_16 (void *fp, UWORD *v)
+{
+ UWORD t = 0;
+ int shift, i;
+ for (i = 0, shift = 0; i < 2; ++i, shift += 8)
+ {
+ BYTE b;
+ if (read_8 (fp, &b) != 1)
+ return 0;
+ t |= ((UWORD)b) << shift;
+ }
+
+ if (v)
+ *v = t;
+
+ return 1;
+}
+
+static inline size_t
+read_16s (void *fp, SWORD *v)
+{
+ return read_16 (fp, (UWORD *) v);
+}
+
+static inline size_t
+read_32 (void *fp, DWORD *v)
+{
+ DWORD t = 0;
+ int shift, i;
+ for (i = 0, shift = 0; i < 4; ++i, shift += 8)
+ {
+ BYTE b;
+ if (read_8 (fp, &b) != 1)
+ return 0;
+ t |= ((DWORD)b) << shift;
+ }
+
+ if (v)
+ *v = t;
+
+ return 1;
+}
+
+static inline size_t
+read_32s (void *fp, SDWORD *v)
+{
+ return read_32 (fp, (DWORD *) v);
+}
+
+static inline size_t
+read_a8 (void *fp, BYTE *ar, COUNT count)
+{
+ assert (ar != NULL);
+ return ReadResFile (ar, 1, count, fp) == count;
+}
+
+static inline size_t
+read_a8s (void *fp, char *ar, COUNT count)
+{
+ return read_a8(fp, (BYTE *) ar, count);
+}
+
+static inline size_t
+skip_8 (void *fp, COUNT count)
+{
+ int i;
+ for (i = 0; i < count; ++i)
+ {
+ if (read_8(fp, NULL) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+static inline size_t
+read_str (void *fp, char *str, COUNT count)
+{
+ // no type conversion needed for strings
+ return read_a8 (fp, (BYTE *)str, count);
+}
+
+static inline size_t
+read_a16 (void *fp, UWORD *ar, COUNT count)
+{
+ assert (ar != NULL);
+
+ for ( ; count > 0; --count, ++ar)
+ {
+ if (read_16 (fp, ar) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+LoadShipQueue (void *fh, QUEUE *pQueue, DWORD size)
+{
+ COUNT num_links = size / 11;
+
+ while (num_links--)
+ {
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+ COUNT Index;
+
+ read_16 (fh, &Index);
+
+ hStarShip = CloneShipFragment (Index, pQueue, 0);
+ FragPtr = LockShipFrag (pQueue, hStarShip);
+
+ // Read SHIP_FRAGMENT elements
+ read_8 (fh, &FragPtr->captains_name_index);
+ read_8 (fh, &FragPtr->race_id);
+ read_8 (fh, &FragPtr->index);
+ read_16 (fh, &FragPtr->crew_level);
+ read_16 (fh, &FragPtr->max_crew);
+ read_8 (fh, &FragPtr->energy_level);
+ read_8 (fh, &FragPtr->max_energy);
+
+ UnlockShipFrag (pQueue, hStarShip);
+ }
+}
+
+static void
+LoadRaceQueue (void *fh, QUEUE *pQueue, DWORD size)
+{
+ COUNT num_links = size / 30;
+
+ while (num_links--)
+ {
+ HFLEETINFO hStarShip;
+ FLEET_INFO *FleetPtr;
+ COUNT Index;
+
+ read_16 (fh, &Index);
+
+ hStarShip = GetStarShipFromIndex (pQueue, Index);
+ FleetPtr = LockFleetInfo (pQueue, hStarShip);
+
+ // Read FLEET_INFO elements
+ read_16 (fh, &FleetPtr->allied_state);
+ read_8 (fh, &FleetPtr->days_left);
+ read_8 (fh, &FleetPtr->growth_fract);
+ read_16 (fh, &FleetPtr->crew_level);
+ read_16 (fh, &FleetPtr->max_crew);
+ read_8 (fh, &FleetPtr->growth);
+ read_8 (fh, &FleetPtr->max_energy);
+ read_16s(fh, &FleetPtr->loc.x);
+ read_16s(fh, &FleetPtr->loc.y);
+
+ read_16 (fh, &FleetPtr->actual_strength);
+ read_16 (fh, &FleetPtr->known_strength);
+ read_16s(fh, &FleetPtr->known_loc.x);
+ read_16s(fh, &FleetPtr->known_loc.y);
+ read_8 (fh, &FleetPtr->growth_err_term);
+ read_8 (fh, &FleetPtr->func_index);
+ read_16s(fh, &FleetPtr->dest_loc.x);
+ read_16s(fh, &FleetPtr->dest_loc.y);
+
+ UnlockFleetInfo (pQueue, hStarShip);
+ }
+}
+
+static void
+LoadGroupQueue (void *fh, QUEUE *pQueue, DWORD size)
+{
+ COUNT num_links = size / 13;
+
+ while (num_links--)
+ {
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ hGroup = BuildGroup (pQueue, 0);
+ GroupPtr = LockIpGroup (pQueue, hGroup);
+
+ read_16 (fh, &GroupPtr->group_counter);
+ read_8 (fh, &GroupPtr->race_id);
+ read_8 (fh, &GroupPtr->sys_loc);
+ read_8 (fh, &GroupPtr->task);
+ read_8 (fh, &GroupPtr->in_system); /* was crew_level */
+ read_8 (fh, &GroupPtr->dest_loc);
+ read_8 (fh, &GroupPtr->orbit_pos);
+ read_8 (fh, &GroupPtr->group_id); /* was max_energy */
+ read_16s(fh, &GroupPtr->loc.x);
+ read_16s(fh, &GroupPtr->loc.y);
+
+ UnlockIpGroup (pQueue, hGroup);
+ }
+}
+
+static void
+LoadEncounter (ENCOUNTER *EncounterPtr, void *fh)
+{
+ COUNT i;
+
+ EncounterPtr->pred = 0;
+ EncounterPtr->succ = 0;
+ EncounterPtr->hElement = 0;
+ read_16s (fh, &EncounterPtr->transition_state);
+ read_16s (fh, &EncounterPtr->origin.x);
+ read_16s (fh, &EncounterPtr->origin.y);
+ read_16 (fh, &EncounterPtr->radius);
+ // former STAR_DESC fields
+ read_16s (fh, &EncounterPtr->loc_pt.x);
+ read_16s (fh, &EncounterPtr->loc_pt.y);
+ read_8 (fh, &EncounterPtr->race_id);
+ read_8 (fh, &EncounterPtr->num_ships);
+ read_8 (fh, &EncounterPtr->flags);
+
+ // Load each entry in the BRIEF_SHIP_INFO array
+ for (i = 0; i < MAX_HYPER_SHIPS; i++)
+ {
+ BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
+
+ read_8 (fh, &ShipInfo->race_id);
+ read_16 (fh, &ShipInfo->crew_level);
+ read_16 (fh, &ShipInfo->max_crew);
+ read_8 (fh, &ShipInfo->max_energy);
+ }
+
+ // Load the stuff after the BRIEF_SHIP_INFO array
+ read_32s (fh, &EncounterPtr->log_x);
+ read_32s (fh, &EncounterPtr->log_y);
+}
+
+static void
+LoadEvent (EVENT *EventPtr, void *fh)
+{
+ EventPtr->pred = 0;
+ EventPtr->succ = 0;
+ read_8 (fh, &EventPtr->day_index);
+ read_8 (fh, &EventPtr->month_index);
+ read_16 (fh, &EventPtr->year_index);
+ read_8 (fh, &EventPtr->func_index);
+}
+
+static void
+LoadClockState (CLOCK_STATE *ClockPtr, void *fh)
+{
+ read_8 (fh, &ClockPtr->day_index);
+ read_8 (fh, &ClockPtr->month_index);
+ read_16 (fh, &ClockPtr->year_index);
+ read_16s (fh, &ClockPtr->tick_count);
+ read_16s (fh, &ClockPtr->day_in_ticks);
+}
+
+static BOOLEAN
+LoadGameState (GAME_STATE *GSPtr, void *fh)
+{
+ DWORD magic;
+ read_32 (fh, &magic);
+ if (magic != GLOBAL_STATE_TAG)
+ {
+ return FALSE;
+ }
+ read_32 (fh, &magic);
+ if (magic != 75)
+ {
+ /* Chunk is the wrong size. */
+ return FALSE;
+ }
+ read_8 (fh, &GSPtr->glob_flags);
+ read_8 (fh, &GSPtr->CrewCost);
+ read_8 (fh, &GSPtr->FuelCost);
+ read_a8 (fh, GSPtr->ModuleCost, NUM_MODULES);
+ read_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
+ read_16 (fh, &GSPtr->CurrentActivity);
+
+ LoadClockState (&GSPtr->GameClock, fh);
+
+ read_16s (fh, &GSPtr->autopilot.x);
+ read_16s (fh, &GSPtr->autopilot.y);
+ read_16s (fh, &GSPtr->ip_location.x);
+ read_16s (fh, &GSPtr->ip_location.y);
+ /* STAMP ShipStamp */
+ read_16s (fh, &GSPtr->ShipStamp.origin.x);
+ read_16s (fh, &GSPtr->ShipStamp.origin.y);
+ read_16 (fh, &GSPtr->ShipFacing);
+ read_8 (fh, &GSPtr->ip_planet);
+ read_8 (fh, &GSPtr->in_orbit);
+
+ /* VELOCITY_DESC velocity */
+ read_16 (fh, &GSPtr->velocity.TravelAngle);
+ read_16s (fh, &GSPtr->velocity.vector.width);
+ read_16s (fh, &GSPtr->velocity.vector.height);
+ read_16s (fh, &GSPtr->velocity.fract.width);
+ read_16s (fh, &GSPtr->velocity.fract.height);
+ read_16s (fh, &GSPtr->velocity.error.width);
+ read_16s (fh, &GSPtr->velocity.error.height);
+ read_16s (fh, &GSPtr->velocity.incr.width);
+ read_16s (fh, &GSPtr->velocity.incr.height);
+
+ read_32 (fh, &magic);
+ if (magic != GAME_STATE_TAG)
+ {
+ return FALSE;
+ }
+ memset (GSPtr->GameState, 0, sizeof (GSPtr->GameState));
+ read_32 (fh, &magic);
+ if (magic > sizeof (GSPtr->GameState))
+ {
+ read_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState));
+ skip_8 (fh, magic - sizeof (GSPtr->GameState));
+ }
+ else
+ {
+ read_a8 (fh, GSPtr->GameState, magic);
+ }
+ return TRUE;
+}
+
+static BOOLEAN
+LoadSisState (SIS_STATE *SSPtr, void *fp)
+{
+ if (
+ read_32s (fp, &SSPtr->log_x) != 1 ||
+ read_32s (fp, &SSPtr->log_y) != 1 ||
+ read_32 (fp, &SSPtr->ResUnits) != 1 ||
+ read_32 (fp, &SSPtr->FuelOnBoard) != 1 ||
+ read_16 (fp, &SSPtr->CrewEnlisted) != 1 ||
+ read_16 (fp, &SSPtr->TotalElementMass) != 1 ||
+ read_16 (fp, &SSPtr->TotalBioMass) != 1 ||
+ read_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 ||
+ read_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 ||
+ read_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 ||
+ read_8 (fp, &SSPtr->NumLanders) != 1 ||
+ read_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 ||
+
+ read_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
+ read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
+ read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1
+ )
+ return FALSE;
+ return TRUE;
+}
+
+static BOOLEAN
+LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
+{
+ DWORD magic;
+ DWORD nameSize = 0;
+ if (!read_32 (fp, &magic))
+ return FALSE;
+ if (magic == SAVEFILE_TAG)
+ {
+ if (read_32 (fp, &magic) != 1 || magic != SUMMARY_TAG)
+ return FALSE;
+ if (read_32 (fp, &magic) != 1 || magic < 160)
+ return FALSE;
+ nameSize = magic - 160;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ if (!LoadSisState (&SummPtr->SS, fp))
+ return FALSE;
+
+ if ( read_8 (fp, &SummPtr->Activity) != 1 ||
+ read_8 (fp, &SummPtr->Flags) != 1 ||
+ read_8 (fp, &SummPtr->day_index) != 1 ||
+ read_8 (fp, &SummPtr->month_index) != 1 ||
+ read_16 (fp, &SummPtr->year_index) != 1 ||
+ read_8 (fp, &SummPtr->MCreditLo) != 1 ||
+ read_8 (fp, &SummPtr->MCreditHi) != 1 ||
+ read_8 (fp, &SummPtr->NumShips) != 1 ||
+ read_8 (fp, &SummPtr->NumDevices) != 1 ||
+ read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
+ read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1
+ )
+ return FALSE;
+
+ if (nameSize < SAVE_NAME_SIZE)
+ {
+ if (read_a8s (fp, SummPtr->SaveName, nameSize) != 1)
+ return FALSE;
+ SummPtr->SaveName[nameSize] = 0;
+ }
+ else
+ {
+ DWORD remaining = nameSize - SAVE_NAME_SIZE + 1;
+ if (read_a8s (fp, SummPtr->SaveName, SAVE_NAME_SIZE-1) != 1)
+ return FALSE;
+ SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0;
+ if (skip_8 (fp, remaining) != 1)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+LoadStarDesc (STAR_DESC *SDPtr, void *fh)
+{
+ read_16s(fh, &SDPtr->star_pt.x);
+ read_16s(fh, &SDPtr->star_pt.y);
+ read_8 (fh, &SDPtr->Type);
+ read_8 (fh, &SDPtr->Index);
+ read_8 (fh, &SDPtr->Prefix);
+ read_8 (fh, &SDPtr->Postfix);
+}
+
+static void
+LoadScanInfo (uio_Stream *fh, DWORD flen)
+{
+ GAME_STATE_FILE *fp = OpenStateFile (STARINFO_FILE, "wb");
+ if (fp)
+ {
+ while (flen)
+ {
+ DWORD val;
+ read_32 (fh, &val);
+ swrite_32 (fp, val);
+ flen -= 4;
+ }
+ CloseStateFile (fp);
+ }
+}
+
+static void
+LoadGroupList (uio_Stream *fh, DWORD chunksize)
+{
+ GAME_STATE_FILE *fp = OpenStateFile (RANDGRPINFO_FILE, "rb");
+ if (fp)
+ {
+ GROUP_HEADER h;
+ BYTE LastEnc, NumGroups;
+ int i;
+ ReadGroupHeader (fp, &h);
+ /* There's only supposed to be one of these, so group 0 should be
+ * zero here whenever we're here. We add the group list to the
+ * end here. */
+ h.GroupOffset[0] = LengthStateFile (fp);
+ SeekStateFile (fp, 0, SEEK_SET);
+ WriteGroupHeader (fp, &h);
+ SeekStateFile (fp, h.GroupOffset[0], SEEK_SET);
+ read_8 (fh, &LastEnc);
+ NumGroups = (chunksize - 1) / 14;
+ swrite_8 (fp, LastEnc);
+ swrite_8 (fp, NumGroups);
+ for (i = 0; i < NumGroups; ++i)
+ {
+ BYTE race_outer;
+ IP_GROUP ip;
+ read_8 (fh, &race_outer);
+ read_16 (fh, &ip.group_counter);
+ read_8 (fh, &ip.race_id);
+ read_8 (fh, &ip.sys_loc);
+ read_8 (fh, &ip.task);
+ read_8 (fh, &ip.in_system);
+ read_8 (fh, &ip.dest_loc);
+ read_8 (fh, &ip.orbit_pos);
+ read_8 (fh, &ip.group_id);
+ read_16s (fh, &ip.loc.x);
+ read_16s (fh, &ip.loc.y);
+
+ swrite_8 (fp, race_outer);
+ WriteIpGroup (fp, &ip);
+ }
+ CloseStateFile (fp);
+ }
+}
+
+static void
+LoadBattleGroup (uio_Stream *fh, DWORD chunksize)
+{
+ GAME_STATE_FILE *fp;
+ GROUP_HEADER h;
+ DWORD encounter, offset;
+ BYTE current;
+ int i;
+
+ read_32 (fh, &encounter);
+ read_8 (fh, &current);
+ chunksize -= 5;
+ if (encounter)
+ {
+ /* This is a defined group, so it's new */
+ fp = OpenStateFile (DEFGRPINFO_FILE, "rb");
+ offset = LengthStateFile (fp);
+ memset (&h, 0, sizeof (GROUP_HEADER));
+ }
+ else
+ {
+ /* This is the random group. Load in what was there,
+ * as we might have already seen the Group List. */
+ fp = OpenStateFile (RANDGRPINFO_FILE, "rb");
+ current = FALSE;
+ offset = 0;
+ ReadGroupHeader (fp, &h);
+ }
+ if (!fp)
+ {
+ skip_8 (fh, chunksize);
+ return;
+ }
+ read_16 (fh, &h.star_index);
+ read_8 (fh, &h.day_index);
+ read_8 (fh, &h.month_index);
+ read_16 (fh, &h.year_index);
+ read_8 (fh, &h.NumGroups);
+ chunksize -= 7;
+ /* Write out the half-finished state file so that we can use
+ * the file size to compute group offsets */
+ SeekStateFile (fp, offset, SEEK_SET);
+ WriteGroupHeader (fp, &h);
+ for (i = 1; i <= h.NumGroups; ++i)
+ {
+ int j;
+ BYTE icon, NumShips;
+ read_8 (fh, &icon);
+ read_8 (fh, &NumShips);
+ chunksize -= 2;
+ h.GroupOffset[i] = LengthStateFile (fp);
+ SeekStateFile (fp, h.GroupOffset[i], SEEK_SET);
+ swrite_8 (fp, icon);
+ swrite_8 (fp, NumShips);
+ for (j = 0; j < NumShips; ++j)
+ {
+ BYTE race_outer;
+ SHIP_FRAGMENT sf;
+ read_8 (fh, &race_outer);
+ read_8 (fh, &sf.captains_name_index);
+ read_8 (fh, &sf.race_id);
+ read_8 (fh, &sf.index);
+ read_16 (fh, &sf.crew_level);
+ read_16 (fh, &sf.max_crew);
+ read_8 (fh, &sf.energy_level);
+ read_8 (fh, &sf.max_energy);
+ chunksize -= 10;
+
+ swrite_8 (fp, race_outer);
+ WriteShipFragment (fp, &sf);
+ }
+ }
+ /* Now that the GroupOffset array is properly initialized,
+ * write the header back out. */
+ SeekStateFile (fp, offset, SEEK_SET);
+ WriteGroupHeader (fp, &h);
+ CloseStateFile (fp);
+ /* And update the gamestate accordingly, if we're a defined group. */
+ if (encounter)
+ {
+ SET_GAME_STATE_32 (SHOFIXTI_GRPOFFS0 + (encounter - 1) * 32, offset);
+ if (current)
+ {
+ GLOBAL (BattleGroupRef) = offset;
+ }
+ }
+ /* Consistency check. */
+ if (chunksize)
+ {
+ log_add (log_Warning, "BattleGroup chunk mis-sized!");
+ }
+}
+
+BOOLEAN
+LoadGame (COUNT which_game, SUMMARY_DESC *SummPtr)
+{
+ uio_Stream *in_fp;
+ char file[PATH_MAX];
+ SUMMARY_DESC loc_sd;
+ COUNT num_links;
+ STAR_DESC SD;
+ ACTIVITY Activity;
+ DWORD chunk, chunkSize;
+ BOOLEAN first_group_spec = TRUE;
+
+ sprintf (file, "uqmsave.%02u", which_game);
+ in_fp = res_OpenResFile (saveDir, file, "rb");
+ if (!in_fp)
+ return LoadLegacyGame (which_game, SummPtr);
+
+ if (!LoadSummary (&loc_sd, in_fp))
+ {
+ res_CloseResFile (in_fp);
+ return LoadLegacyGame (which_game, SummPtr);
+ }
+
+ if (!SummPtr)
+ {
+ SummPtr = &loc_sd;
+ }
+ else
+ { // only need summary for displaying to user
+ memcpy (SummPtr, &loc_sd, sizeof (*SummPtr));
+ res_CloseResFile (in_fp);
+ return TRUE;
+ }
+
+ GlobData.SIS_state = SummPtr->SS;
+
+ ReinitQueue (&GLOBAL (GameClock.event_q));
+ ReinitQueue (&GLOBAL (encounter_q));
+ ReinitQueue (&GLOBAL (ip_group_q));
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ ReinitQueue (&GLOBAL (built_ship_q));
+
+ memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState)));
+ Activity = GLOBAL (CurrentActivity);
+ if (!LoadGameState (&GlobData.Game_state, in_fp))
+ {
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+ NextActivity = GLOBAL (CurrentActivity);
+ GLOBAL (CurrentActivity) = Activity;
+
+ chunk = 0;
+ while (TRUE)
+ {
+ if (read_32(in_fp, &chunk) != 1)
+ {
+ break;
+ }
+ if (read_32(in_fp, &chunkSize) != 1)
+ {
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+ switch (chunk)
+ {
+ case RACE_Q_TAG:
+ LoadRaceQueue (in_fp, &GLOBAL (avail_race_q), chunkSize);
+ break;
+ case IP_GRP_Q_TAG:
+ LoadGroupQueue (in_fp, &GLOBAL (ip_group_q), chunkSize);
+ break;
+ case ENCOUNTERS_TAG:
+ num_links = chunkSize / 65;
+ while (num_links--)
+ {
+ HENCOUNTER hEncounter;
+ ENCOUNTER *EncounterPtr;
+
+ hEncounter = AllocEncounter ();
+ LockEncounter (hEncounter, &EncounterPtr);
+
+ LoadEncounter (EncounterPtr, in_fp);
+
+ UnlockEncounter (hEncounter);
+ PutEncounter (hEncounter);
+ }
+ break;
+ case EVENTS_TAG:
+ num_links = chunkSize / 5;
+#ifdef DEBUG_LOAD
+ log_add (log_Debug, "EVENTS:");
+#endif /* DEBUG_LOAD */
+ while (num_links--)
+ {
+ HEVENT hEvent;
+ EVENT *EventPtr;
+
+ hEvent = AllocEvent ();
+ LockEvent (hEvent, &EventPtr);
+
+ LoadEvent (EventPtr, in_fp);
+
+#ifdef DEBUG_LOAD
+ log_add (log_Debug, "\t%u/%u/%u -- %u",
+ EventPtr->month_index,
+ EventPtr->day_index,
+ EventPtr->year_index,
+ EventPtr->func_index);
+#endif /* DEBUG_LOAD */
+ UnlockEvent (hEvent);
+ PutEvent (hEvent);
+ }
+ break;
+ case STAR_TAG:
+ LoadStarDesc (&SD, in_fp);
+ break;
+ case NPC_SHIP_Q_TAG:
+ LoadShipQueue (in_fp, &GLOBAL (npc_built_ship_q), chunkSize);
+ break;
+ case SHIP_Q_TAG:
+ LoadShipQueue (in_fp, &GLOBAL (built_ship_q), chunkSize);
+ break;
+ case SCAN_TAG:
+ LoadScanInfo (in_fp, chunkSize);
+ break;
+ case GROUP_LIST_TAG:
+ if (first_group_spec)
+ {
+ InitGroupInfo (TRUE);
+ GLOBAL (BattleGroupRef) = 0;
+ first_group_spec = FALSE;
+ }
+ LoadGroupList (in_fp, chunkSize);
+ break;
+ case BATTLE_GROUP_TAG:
+ if (first_group_spec)
+ {
+ InitGroupInfo (TRUE);
+ GLOBAL (BattleGroupRef) = 0;
+ first_group_spec = FALSE;
+ }
+ LoadBattleGroup (in_fp, chunkSize);
+ break;
+ default:
+ log_add (log_Debug, "Skipping chunk of tag %08X (size %u)", chunk, chunkSize);
+ if (skip_8(in_fp, chunkSize) != 1)
+ {
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+ break;
+ }
+ }
+ res_CloseResFile (in_fp);
+
+ EncounterGroup = 0;
+ EncounterRace = -1;
+
+ ReinitQueue (&race_q[0]);
+ ReinitQueue (&race_q[1]);
+ CurStarDescPtr = FindStar (NULL, &SD.star_pt, 0, 0);
+ if (!(NextActivity & START_ENCOUNTER)
+ && LOBYTE (NextActivity) == IN_INTERPLANETARY)
+ NextActivity |= START_INTERPLANETARY;
+
+ return TRUE;
+}
diff --git a/src/uqm/load_legacy.c b/src/uqm/load_legacy.c
new file mode 100644
index 0000000..6470a52
--- /dev/null
+++ b/src/uqm/load_legacy.c
@@ -0,0 +1,821 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+
+#include "build.h"
+#include "libs/declib.h"
+#include "encount.h"
+#include "starmap.h"
+#include "libs/file.h"
+#include "globdata.h"
+#include "options.h"
+#include "save.h"
+#include "setup.h"
+#include "state.h"
+#include "grpinfo.h"
+
+#include "libs/tasklib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+//#define DEBUG_LOAD
+
+// XXX: these should handle endian conversions later
+static inline COUNT
+cread_8 (DECODE_REF fh, BYTE *v)
+{
+ BYTE t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return cread (v, 1, 1, fh);
+}
+
+static inline COUNT
+cread_16 (DECODE_REF fh, UWORD *v)
+{
+ UWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return cread (v, 2, 1, fh);
+}
+
+static inline COUNT
+cread_16s (DECODE_REF fh, SWORD *v)
+{
+ UWORD t;
+ COUNT ret;
+ // value was converted to unsigned when saved
+ ret = cread_16 (fh, &t);
+ // unsigned to signed conversion
+ if (v)
+ *v = t;
+ return ret;
+}
+
+static inline COUNT
+cread_32 (DECODE_REF fh, DWORD *v)
+{
+ DWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return cread (v, 4, 1, fh);
+}
+
+static inline COUNT
+cread_32s (DECODE_REF fh, SDWORD *v)
+{
+ DWORD t;
+ COUNT ret;
+ // value was converted to unsigned when saved
+ ret = cread_32 (fh, &t);
+ // unsigned to signed conversion
+ if (v)
+ *v = t;
+ return ret;
+}
+
+static inline COUNT
+cread_ptr (DECODE_REF fh)
+{
+ DWORD t;
+ return cread_32 (fh, &t); /* ptrs are useless in saves */
+}
+
+static inline COUNT
+cread_a8 (DECODE_REF fh, BYTE *ar, COUNT count)
+{
+ assert (ar != NULL);
+ return cread (ar, 1, count, fh) == count;
+}
+
+static inline size_t
+read_8 (void *fp, BYTE *v)
+{
+ BYTE t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadResFile (v, 1, 1, fp);
+}
+
+static inline size_t
+read_16 (void *fp, UWORD *v)
+{
+ UWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadResFile (v, 2, 1, fp);
+}
+
+static inline size_t
+read_32 (void *fp, DWORD *v)
+{
+ DWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadResFile (v, 4, 1, fp);
+}
+
+static inline size_t
+read_32s (void *fp, SDWORD *v)
+{
+ DWORD t;
+ COUNT ret;
+ // value was converted to unsigned when saved
+ ret = read_32 (fp, &t);
+ // unsigned to signed conversion
+ if (v)
+ *v = t;
+ return ret;
+}
+
+static inline size_t
+read_ptr (void *fp)
+{
+ DWORD t;
+ return read_32 (fp, &t); /* ptrs are useless in saves */
+}
+
+static inline size_t
+read_a8 (void *fp, BYTE *ar, COUNT count)
+{
+ assert (ar != NULL);
+ return ReadResFile (ar, 1, count, fp) == count;
+}
+
+static inline size_t
+read_str (void *fp, char *str, COUNT count)
+{
+ // no type conversion needed for strings
+ return read_a8 (fp, (BYTE *)str, count);
+}
+
+static inline size_t
+read_a16 (void *fp, UWORD *ar, COUNT count)
+{
+ assert (ar != NULL);
+
+ for ( ; count > 0; --count, ++ar)
+ {
+ if (read_16 (fp, ar) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+typedef struct struct_GAMESTATE_TRANSPOSE {
+ int start, end, target;
+} GAMESTATE_TRANSPOSE;
+
+#define LEGACY_GAMESTATE_SIZE 155
+
+/* The *_GRPOFFS* states are no longer intermingled with the rest of
+ * the state. We need to shuffle all the rest of the state data
+ * down. */
+static GAMESTATE_TRANSPOSE transpose[] = {
+ { 0, 51, 0 },
+ { 404, 450, 52 },
+ { 483, 878, 99 },
+ { 911, 930, 495 },
+ { 963, 1237, 515 },
+ { -1, -1, -1 } };
+
+static DWORD old_defgrp_offsets[] = { 0, 52, 84, 116, 148, 180, 212, 244,
+ 276, 308, 340, 372, 451, 879, 931 };
+
+static DWORD new_defgrp_offsets[] = {
+ 0,
+ SHOFIXTI_GRPOFFS0,
+ ZOQFOT_GRPOFFS0,
+ MELNORME0_GRPOFFS0,
+ MELNORME1_GRPOFFS0,
+ MELNORME2_GRPOFFS0,
+ MELNORME3_GRPOFFS0,
+ MELNORME4_GRPOFFS0,
+ MELNORME5_GRPOFFS0,
+ MELNORME6_GRPOFFS0,
+ MELNORME7_GRPOFFS0,
+ MELNORME8_GRPOFFS0,
+ URQUAN_PROBE_GRPOFFS0,
+ COLONY_GRPOFFS0,
+ SAMATRA_GRPOFFS0
+};
+
+static void
+InterpretLegacyGameState (BYTE *result, BYTE *legacy)
+{
+ int i;
+ DWORD grpoffs[NUM_DEFGRPS];
+ GAMESTATE_TRANSPOSE *t = &transpose[0];
+ grpoffs[0] = 0;
+ for (i = 1; i < NUM_DEFGRPS; ++i)
+ {
+ grpoffs[i] = getGameState32 (legacy, old_defgrp_offsets[i]);
+ }
+ while (t->start >= 0)
+ {
+ copyGameState (result, t->target, legacy, t->start, t->end);
+ ++t;
+ }
+ for (i = 1; i < NUM_DEFGRPS; ++i)
+ {
+ setGameState32 (result, new_defgrp_offsets[i], grpoffs[i]);
+ }
+}
+
+static void
+LoadEmptyQueue (DECODE_REF fh)
+{
+ COUNT num_links;
+
+ cread_16 (fh, &num_links);
+ if (num_links)
+ {
+ log_add (log_Error, "LoadEmptyQueue(): BUG: the queue is not empty!");
+#ifdef DEBUG
+ explode ();
+#endif
+ }
+}
+
+static void
+LoadShipQueue (DECODE_REF fh, QUEUE *pQueue)
+{
+ COUNT num_links;
+
+ cread_16 (fh, &num_links);
+
+ while (num_links--)
+ {
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+ COUNT Index;
+ BYTE tmpb;
+
+ cread_16 (fh, &Index);
+
+ hStarShip = CloneShipFragment (Index, pQueue, 0);
+ FragPtr = LockShipFrag (pQueue, hStarShip);
+
+ // Read SHIP_FRAGMENT elements
+ cread_16 (fh, NULL); /* unused: was which_side */
+ cread_8 (fh, &FragPtr->captains_name_index);
+ cread_8 (fh, NULL); /* padding */
+ cread_16 (fh, NULL); /* unused: was ship_flags */
+ cread_8 (fh, &FragPtr->race_id);
+ cread_8 (fh, &FragPtr->index);
+ // XXX: reading crew as BYTE to maintain savegame compatibility
+ cread_8 (fh, &tmpb);
+ FragPtr->crew_level = tmpb;
+ cread_8 (fh, &tmpb);
+ FragPtr->max_crew = tmpb;
+ cread_8 (fh, &FragPtr->energy_level);
+ cread_8 (fh, &FragPtr->max_energy);
+ cread_16 (fh, NULL); /* unused; was loc.x */
+ cread_16 (fh, NULL); /* unused; was loc.y */
+
+ UnlockShipFrag (pQueue, hStarShip);
+ }
+}
+
+static void
+LoadRaceQueue (DECODE_REF fh, QUEUE *pQueue)
+{
+ COUNT num_links;
+
+ cread_16 (fh, &num_links);
+
+ while (num_links--)
+ {
+ HFLEETINFO hStarShip;
+ FLEET_INFO *FleetPtr;
+ COUNT Index;
+ BYTE tmpb;
+
+ cread_16 (fh, &Index);
+
+ hStarShip = GetStarShipFromIndex (pQueue, Index);
+ FleetPtr = LockFleetInfo (pQueue, hStarShip);
+
+ // Read FLEET_INFO elements
+ cread_16 (fh, &FleetPtr->allied_state);
+ cread_8 (fh, &FleetPtr->days_left);
+ cread_8 (fh, &FleetPtr->growth_fract);
+ cread_8 (fh, &tmpb);
+ FleetPtr->crew_level = tmpb;
+ cread_8 (fh, &tmpb);
+ FleetPtr->max_crew = tmpb;
+ cread_8 (fh, &FleetPtr->growth);
+ cread_8 (fh, &FleetPtr->max_energy);
+ cread_16s(fh, &FleetPtr->loc.x);
+ cread_16s(fh, &FleetPtr->loc.y);
+
+ cread_16 (fh, &FleetPtr->actual_strength);
+ cread_16 (fh, &FleetPtr->known_strength);
+ cread_16s(fh, &FleetPtr->known_loc.x);
+ cread_16s(fh, &FleetPtr->known_loc.y);
+ cread_8 (fh, &FleetPtr->growth_err_term);
+ cread_8 (fh, &FleetPtr->func_index);
+ cread_16s(fh, &FleetPtr->dest_loc.x);
+ cread_16s(fh, &FleetPtr->dest_loc.y);
+ cread_16 (fh, NULL); /* alignment padding */
+
+ UnlockFleetInfo (pQueue, hStarShip);
+ }
+}
+
+static void
+LoadGroupQueue (DECODE_REF fh, QUEUE *pQueue)
+{
+ COUNT num_links;
+
+ cread_16 (fh, &num_links);
+
+ while (num_links--)
+ {
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+ BYTE tmpb;
+
+ cread_16 (fh, NULL); /* unused; was race_id */
+
+ hGroup = BuildGroup (pQueue, 0);
+ GroupPtr = LockIpGroup (pQueue, hGroup);
+
+ cread_16 (fh, NULL); /* unused; was which_side */
+ cread_8 (fh, NULL); /* unused; was captains_name_index */
+ cread_8 (fh, NULL); /* padding; for savegame compat */
+ cread_16 (fh, &GroupPtr->group_counter);
+ cread_8 (fh, &GroupPtr->race_id);
+ cread_8 (fh, &tmpb); /* was var2 */
+ GroupPtr->sys_loc = LONIBBLE (tmpb);
+ GroupPtr->task = HINIBBLE (tmpb);
+ cread_8 (fh, &GroupPtr->in_system); /* was crew_level */
+ cread_8 (fh, NULL); /* unused; was max_crew */
+ cread_8 (fh, &tmpb); /* was energy_level */
+ GroupPtr->dest_loc = LONIBBLE (tmpb);
+ GroupPtr->orbit_pos = HINIBBLE (tmpb);
+ cread_8 (fh, &GroupPtr->group_id); /* was max_energy */
+ cread_16s(fh, &GroupPtr->loc.x);
+ cread_16s(fh, &GroupPtr->loc.y);
+
+ UnlockIpGroup (pQueue, hGroup);
+ }
+}
+
+static void
+LoadEncounter (ENCOUNTER *EncounterPtr, DECODE_REF fh)
+{
+ COUNT i;
+ BYTE tmpb;
+
+ cread_ptr (fh); /* useless ptr; HENCOUNTER pred */
+ EncounterPtr->pred = 0;
+ cread_ptr (fh); /* useless ptr; HENCOUNTER succ */
+ EncounterPtr->succ = 0;
+ cread_ptr (fh); /* useless ptr; HELEMENT hElement */
+ EncounterPtr->hElement = 0;
+ cread_16s (fh, &EncounterPtr->transition_state);
+ cread_16s (fh, &EncounterPtr->origin.x);
+ cread_16s (fh, &EncounterPtr->origin.y);
+ cread_16 (fh, &EncounterPtr->radius);
+ // former STAR_DESC fields
+ cread_16s (fh, &EncounterPtr->loc_pt.x);
+ cread_16s (fh, &EncounterPtr->loc_pt.y);
+ cread_8 (fh, &EncounterPtr->race_id);
+ cread_8 (fh, &tmpb);
+ EncounterPtr->num_ships = tmpb & ENCOUNTER_SHIPS_MASK;
+ EncounterPtr->flags = tmpb & ENCOUNTER_FLAGS_MASK;
+ cread_16 (fh, NULL); /* alignment padding */
+
+ // Load each entry in the BRIEF_SHIP_INFO array
+ for (i = 0; i < MAX_HYPER_SHIPS; i++)
+ {
+ BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
+
+ cread_16 (fh, NULL); /* useless; was SHIP_INFO.ship_flags */
+ cread_8 (fh, &ShipInfo->race_id);
+ cread_8 (fh, NULL); /* useless; was SHIP_INFO.var2 */
+ // XXX: reading crew as BYTE to maintain savegame compatibility
+ cread_8 (fh, &tmpb);
+ ShipInfo->crew_level = tmpb;
+ cread_8 (fh, &tmpb);
+ ShipInfo->max_crew = tmpb;
+ cread_8 (fh, NULL); /* useless; was SHIP_INFO.energy_level */
+ cread_8 (fh, &ShipInfo->max_energy);
+ cread_16 (fh, NULL); /* useless; was SHIP_INFO.loc.x */
+ cread_16 (fh, NULL); /* useless; was SHIP_INFO.loc.y */
+ cread_32 (fh, NULL); /* useless val; STRING race_strings */
+ cread_ptr (fh); /* useless ptr; FRAME icons */
+ cread_ptr (fh); /* useless ptr; FRAME melee_icon */
+ }
+
+ // Load the stuff after the BRIEF_SHIP_INFO array
+ cread_32s (fh, &EncounterPtr->log_x);
+ cread_32s (fh, &EncounterPtr->log_y);
+}
+
+static void
+LoadEvent (EVENT *EventPtr, DECODE_REF fh)
+{
+ cread_ptr (fh); /* useless ptr; HEVENT pred */
+ EventPtr->pred = 0;
+ cread_ptr (fh); /* useless ptr; HEVENT succ */
+ EventPtr->succ = 0;
+ cread_8 (fh, &EventPtr->day_index);
+ cread_8 (fh, &EventPtr->month_index);
+ cread_16 (fh, &EventPtr->year_index);
+ cread_8 (fh, &EventPtr->func_index);
+ cread_8 (fh, NULL); /* padding */
+ cread_16 (fh, NULL); /* padding */
+}
+
+static void
+DummyLoadQueue (QUEUE *QueuePtr, DECODE_REF fh)
+{
+ /* QUEUE should never actually be loaded since it contains
+ * purely internal representation and the lists
+ * involved are actually loaded separately */
+ (void)QueuePtr; /* silence compiler */
+
+ /* QUEUE format with QUEUE_TABLE defined -- UQM default */
+ cread_ptr (fh); /* HLINK head */
+ cread_ptr (fh); /* HLINK tail */
+ cread_ptr (fh); /* BYTE* pq_tab */
+ cread_ptr (fh); /* HLINK free_list */
+ cread_16 (fh, NULL); /* MEM_HANDLE hq_tab */
+ cread_16 (fh, NULL); /* COUNT object_size */
+ cread_8 (fh, NULL); /* BYTE num_objects */
+
+ cread_8 (fh, NULL); /* padding */
+ cread_16 (fh, NULL); /* padding */
+}
+
+static void
+LoadClockState (CLOCK_STATE *ClockPtr, DECODE_REF fh)
+{
+ cread_8 (fh, &ClockPtr->day_index);
+ cread_8 (fh, &ClockPtr->month_index);
+ cread_16 (fh, &ClockPtr->year_index);
+ cread_16s (fh, &ClockPtr->tick_count);
+ cread_16s (fh, &ClockPtr->day_in_ticks);
+ cread_ptr (fh); /* not loading ptr; Semaphore clock_sem */
+ cread_ptr (fh); /* not loading ptr; Task clock_task */
+ cread_32 (fh, NULL); /* not loading; DWORD TimeCounter */
+
+ DummyLoadQueue (&ClockPtr->event_q, fh);
+}
+
+static void
+LoadGameState (GAME_STATE *GSPtr, DECODE_REF fh)
+{
+ BYTE dummy8, oldstate[LEGACY_GAMESTATE_SIZE];
+
+ cread_8 (fh, &dummy8); /* obsolete */
+ cread_8 (fh, &GSPtr->glob_flags);
+ cread_8 (fh, &GSPtr->CrewCost);
+ cread_8 (fh, &GSPtr->FuelCost);
+ cread_a8 (fh, GSPtr->ModuleCost, NUM_MODULES);
+ cread_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
+ cread_ptr (fh); /* not loading ptr; PRIMITIVE *DisplayArray */
+ cread_16 (fh, &GSPtr->CurrentActivity);
+
+ cread_16 (fh, NULL); /* CLOCK_STATE alignment padding */
+ LoadClockState (&GSPtr->GameClock, fh);
+
+ cread_16s (fh, &GSPtr->autopilot.x);
+ cread_16s (fh, &GSPtr->autopilot.y);
+ cread_16s (fh, &GSPtr->ip_location.x);
+ cread_16s (fh, &GSPtr->ip_location.y);
+ /* STAMP ShipStamp */
+ cread_16s (fh, &GSPtr->ShipStamp.origin.x);
+ cread_16s (fh, &GSPtr->ShipStamp.origin.y);
+ cread_16 (fh, &GSPtr->ShipFacing);
+ cread_8 (fh, &GSPtr->ip_planet);
+ cread_8 (fh, &GSPtr->in_orbit);
+
+ /* VELOCITY_DESC velocity */
+ cread_16 (fh, &GSPtr->velocity.TravelAngle);
+ cread_16s (fh, &GSPtr->velocity.vector.width);
+ cread_16s (fh, &GSPtr->velocity.vector.height);
+ cread_16s (fh, &GSPtr->velocity.fract.width);
+ cread_16s (fh, &GSPtr->velocity.fract.height);
+ cread_16s (fh, &GSPtr->velocity.error.width);
+ cread_16s (fh, &GSPtr->velocity.error.height);
+ cread_16s (fh, &GSPtr->velocity.incr.width);
+ cread_16s (fh, &GSPtr->velocity.incr.height);
+ cread_16 (fh, NULL); /* VELOCITY_DESC padding */
+
+ cread_32 (fh, &GSPtr->BattleGroupRef);
+
+ DummyLoadQueue (&GSPtr->avail_race_q, fh);
+ DummyLoadQueue (&GSPtr->npc_built_ship_q, fh);
+ // Not loading ip_group_q, was not there originally
+ DummyLoadQueue (&GSPtr->encounter_q, fh);
+ DummyLoadQueue (&GSPtr->built_ship_q, fh);
+
+ cread_a8 (fh, oldstate, LEGACY_GAMESTATE_SIZE);
+ InterpretLegacyGameState (GSPtr->GameState, oldstate);
+
+ cread_8 (fh, NULL); /* GAME_STATE alignment padding */
+}
+
+static BOOLEAN
+LoadSisState (SIS_STATE *SSPtr, void *fp)
+{
+ if (
+ read_32s (fp, &SSPtr->log_x) != 1 ||
+ read_32s (fp, &SSPtr->log_y) != 1 ||
+ read_32 (fp, &SSPtr->ResUnits) != 1 ||
+ read_32 (fp, &SSPtr->FuelOnBoard) != 1 ||
+ read_16 (fp, &SSPtr->CrewEnlisted) != 1 ||
+ read_16 (fp, &SSPtr->TotalElementMass) != 1 ||
+ read_16 (fp, &SSPtr->TotalBioMass) != 1 ||
+ read_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS) != 1 ||
+ read_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS) != 1 ||
+ read_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS) != 1 ||
+ read_8 (fp, &SSPtr->NumLanders) != 1 ||
+ read_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES) != 1 ||
+
+ read_str (fp, SSPtr->ShipName, SIS_NAME_SIZE) != 1 ||
+ read_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE) != 1 ||
+ read_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE) != 1 ||
+
+ read_16 (fp, NULL) != 1 /* padding */
+ )
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static BOOLEAN
+LoadSummary (SUMMARY_DESC *SummPtr, void *fp)
+{
+ if (!LoadSisState (&SummPtr->SS, fp))
+ return FALSE;
+
+ if (
+ read_8 (fp, &SummPtr->Activity) != 1 ||
+ read_8 (fp, &SummPtr->Flags) != 1 ||
+ read_8 (fp, &SummPtr->day_index) != 1 ||
+ read_8 (fp, &SummPtr->month_index) != 1 ||
+ read_16 (fp, &SummPtr->year_index) != 1 ||
+ read_8 (fp, &SummPtr->MCreditLo) != 1 ||
+ read_8 (fp, &SummPtr->MCreditHi) != 1 ||
+ read_8 (fp, &SummPtr->NumShips) != 1 ||
+ read_8 (fp, &SummPtr->NumDevices) != 1 ||
+ read_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS) != 1 ||
+ read_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES) != 1 ||
+
+ read_16 (fp, NULL) != 1 /* padding */
+ )
+ return FALSE;
+ else
+ return TRUE;
+}
+
+static void
+LoadStarDesc (STAR_DESC *SDPtr, DECODE_REF fh)
+{
+ cread_16s(fh, &SDPtr->star_pt.x);
+ cread_16s(fh, &SDPtr->star_pt.y);
+ cread_8 (fh, &SDPtr->Type);
+ cread_8 (fh, &SDPtr->Index);
+ cread_8 (fh, &SDPtr->Prefix);
+ cread_8 (fh, &SDPtr->Postfix);
+}
+
+BOOLEAN
+LoadLegacyGame (COUNT which_game, SUMMARY_DESC *SummPtr)
+{
+ uio_Stream *in_fp;
+ char file[PATH_MAX];
+ char buf[256];
+ SUMMARY_DESC loc_sd;
+ GAME_STATE_FILE *fp;
+ DECODE_REF fh;
+ COUNT num_links;
+ STAR_DESC SD;
+ ACTIVITY Activity;
+
+ sprintf (file, "starcon2.%02u", which_game);
+ in_fp = res_OpenResFile (saveDir, file, "rb");
+ if (!in_fp)
+ return FALSE;
+
+ loc_sd.SaveName[0] = '\0';
+ if (!LoadSummary (&loc_sd, in_fp))
+ {
+ log_add (log_Error, "Warning: Savegame is corrupt");
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+
+ if (!SummPtr)
+ {
+ SummPtr = &loc_sd;
+ }
+ else
+ { // only need summary for displaying to user
+ memcpy (SummPtr, &loc_sd, sizeof (*SummPtr));
+ res_CloseResFile (in_fp);
+ return TRUE;
+ }
+
+ // Crude check for big-endian/little-endian incompatibilities.
+ // year_index is suitable as it's a multi-byte value within
+ // a specific recognisable range.
+ if (SummPtr->year_index < START_YEAR ||
+ SummPtr->year_index >= START_YEAR +
+ YEARS_TO_KOHRAH_VICTORY + 1 /* Utwig intervention */ +
+ 1 /* time to destroy all races, plenty */ +
+ 25 /* for cheaters */)
+ {
+ log_add (log_Error, "Warning: Savegame corrupt or from "
+ "an incompatible platform.");
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+
+ GlobData.SIS_state = SummPtr->SS;
+
+ if ((fh = copen (in_fp, FILE_STREAM, STREAM_READ)) == 0)
+ {
+ res_CloseResFile (in_fp);
+ return FALSE;
+ }
+
+ ReinitQueue (&GLOBAL (GameClock.event_q));
+ ReinitQueue (&GLOBAL (encounter_q));
+ ReinitQueue (&GLOBAL (ip_group_q));
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ ReinitQueue (&GLOBAL (built_ship_q));
+
+ memset (&GLOBAL (GameState[0]), 0, sizeof (GLOBAL (GameState)));
+ Activity = GLOBAL (CurrentActivity);
+ LoadGameState (&GlobData.Game_state, fh);
+ NextActivity = GLOBAL (CurrentActivity);
+ GLOBAL (CurrentActivity) = Activity;
+
+ LoadRaceQueue (fh, &GLOBAL (avail_race_q));
+ // START_INTERPLANETARY is only set when saving from Homeworld
+ // encounter screen. When the game is loaded, the
+ // GenerateOrbitalFunction for the current star system will
+ // create the encounter anew and populate the npc queue.
+ if (!(NextActivity & START_INTERPLANETARY))
+ {
+ if (NextActivity & START_ENCOUNTER)
+ LoadShipQueue (fh, &GLOBAL (npc_built_ship_q));
+ else if (LOBYTE (NextActivity) == IN_INTERPLANETARY)
+ // XXX: Technically, this queue does not need to be
+ // saved/loaded at all. IP groups will be reloaded
+ // from group state files. But the original code did,
+ // and so will we until we can prove we do not need to.
+ LoadGroupQueue (fh, &GLOBAL (ip_group_q));
+ else
+ // XXX: The empty queue read is only needed to maintain
+ // the savegame compatibility
+ LoadEmptyQueue (fh);
+ }
+ LoadShipQueue (fh, &GLOBAL (built_ship_q));
+
+ // Load the game events (compressed)
+ cread_16 (fh, &num_links);
+ {
+#ifdef DEBUG_LOAD
+ log_add (log_Debug, "EVENTS:");
+#endif /* DEBUG_LOAD */
+ while (num_links--)
+ {
+ HEVENT hEvent;
+ EVENT *EventPtr;
+
+ hEvent = AllocEvent ();
+ LockEvent (hEvent, &EventPtr);
+
+ LoadEvent (EventPtr, fh);
+
+#ifdef DEBUG_LOAD
+ log_add (log_Debug, "\t%u/%u/%u -- %u",
+ EventPtr->month_index,
+ EventPtr->day_index,
+ EventPtr->year_index,
+ EventPtr->func_index);
+#endif /* DEBUG_LOAD */
+ UnlockEvent (hEvent);
+ PutEvent (hEvent);
+ }
+ }
+
+ // Load the encounters (black globes in HS/QS (compressed))
+ cread_16 (fh, &num_links);
+ {
+ while (num_links--)
+ {
+ HENCOUNTER hEncounter;
+ ENCOUNTER *EncounterPtr;
+
+ hEncounter = AllocEncounter ();
+ LockEncounter (hEncounter, &EncounterPtr);
+
+ LoadEncounter (EncounterPtr, fh);
+
+ UnlockEncounter (hEncounter);
+ PutEncounter (hEncounter);
+ }
+ }
+
+ // Copy the star info file from the compressed stream
+ fp = OpenStateFile (STARINFO_FILE, "wb");
+ if (fp)
+ {
+ DWORD flen;
+
+ cread_32 (fh, &flen);
+ while (flen)
+ {
+ COUNT num_bytes;
+
+ num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
+ cread (buf, num_bytes, 1, fh);
+ WriteStateFile (buf, num_bytes, 1, fp);
+
+ flen -= num_bytes;
+ }
+ CloseStateFile (fp);
+ }
+
+ // Copy the defined groupinfo file from the compressed stream
+ fp = OpenStateFile (DEFGRPINFO_FILE, "wb");
+ if (fp)
+ {
+ DWORD flen;
+
+ cread_32 (fh, &flen);
+ while (flen)
+ {
+ COUNT num_bytes;
+
+ num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
+ cread (buf, num_bytes, 1, fh);
+ WriteStateFile (buf, num_bytes, 1, fp);
+
+ flen -= num_bytes;
+ }
+ CloseStateFile (fp);
+ }
+
+ // Copy the random groupinfo file from the compressed stream
+ fp = OpenStateFile (RANDGRPINFO_FILE, "wb");
+ if (fp)
+ {
+ DWORD flen;
+
+ cread_32 (fh, &flen);
+ while (flen)
+ {
+ COUNT num_bytes;
+
+ num_bytes = flen >= sizeof (buf) ? sizeof (buf) : (COUNT)flen;
+ cread (buf, num_bytes, 1, fh);
+ WriteStateFile (buf, num_bytes, 1, fp);
+
+ flen -= num_bytes;
+ }
+ CloseStateFile (fp);
+ }
+
+ LoadStarDesc (&SD, fh);
+
+ cclose (fh);
+ res_CloseResFile (in_fp);
+
+ EncounterGroup = 0;
+ EncounterRace = -1;
+
+ ReinitQueue (&race_q[0]);
+ ReinitQueue (&race_q[1]);
+ CurStarDescPtr = FindStar (NULL, &SD.star_pt, 0, 0);
+ if (!(NextActivity & START_ENCOUNTER)
+ && LOBYTE (NextActivity) == IN_INTERPLANETARY)
+ NextActivity |= START_INTERPLANETARY;
+
+ return TRUE;
+}
+
+
diff --git a/src/uqm/loadship.c b/src/uqm/loadship.c
new file mode 100644
index 0000000..3396134
--- /dev/null
+++ b/src/uqm/loadship.c
@@ -0,0 +1,200 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "build.h"
+#include "coderes.h"
+#include "corecode.h"
+#include "globdata.h"
+#include "nameref.h"
+#include "races.h"
+#include "init.h"
+
+static RESOURCE code_resources[] = {
+ NULL_RESOURCE,
+ ARILOU_CODE,
+ CHMMR_CODE,
+ HUMAN_CODE,
+ ORZ_CODE,
+ PKUNK_CODE,
+ SHOFIXTI_CODE,
+ SPATHI_CODE,
+ SUPOX_CODE,
+ THRADDASH_CODE,
+ UTWIG_CODE,
+ VUX_CODE,
+ YEHAT_CODE,
+ MELNORME_CODE,
+ DRUUGE_CODE,
+ ILWRATH_CODE,
+ MYCON_CODE,
+ SLYLANDRO_CODE,
+ UMGAH_CODE,
+ URQUAN_CODE,
+ ZOQFOTPIK_CODE,
+ SYREEN_CODE,
+ KOHR_AH_CODE,
+ ANDROSYNTH_CODE,
+ CHENJESU_CODE,
+ MMRNMHRM_CODE,
+ SIS_CODE,
+ SAMATRA_CODE,
+ URQUAN_DRONE_CODE };
+
+RACE_DESC *
+load_ship (SPECIES_ID SpeciesID, BOOLEAN LoadBattleData)
+{
+ RACE_DESC *RDPtr = 0;
+ void *CodeRef;
+
+ if (SpeciesID >= NUM_SPECIES_ID)
+ return NULL;
+
+ CodeRef = CaptureCodeRes (LoadCodeRes (code_resources[SpeciesID]),
+ &GlobData, (void **)(&RDPtr));
+
+ if (!CodeRef)
+ goto BadLoad;
+ RDPtr->CodeRef = CodeRef;
+
+ if (RDPtr->ship_info.icons_rsc != NULL_RESOURCE)
+ {
+ RDPtr->ship_info.icons = CaptureDrawable (LoadGraphic (
+ RDPtr->ship_info.icons_rsc));
+ if (!RDPtr->ship_info.icons)
+ {
+ /* goto BadLoad */
+ }
+ }
+
+ if (RDPtr->ship_info.melee_icon_rsc != NULL_RESOURCE)
+ {
+ RDPtr->ship_info.melee_icon = CaptureDrawable (LoadGraphic (
+ RDPtr->ship_info.melee_icon_rsc));
+ if (!RDPtr->ship_info.melee_icon)
+ {
+ /* goto BadLoad */
+ }
+ }
+
+ if (RDPtr->ship_info.race_strings_rsc != NULL_RESOURCE)
+ {
+ RDPtr->ship_info.race_strings = CaptureStringTable (LoadStringTable (
+ RDPtr->ship_info.race_strings_rsc));
+ if (!RDPtr->ship_info.race_strings)
+ {
+ /* goto BadLoad */
+ }
+ }
+
+ if (LoadBattleData)
+ {
+ DATA_STUFF *RawPtr = &RDPtr->ship_data;
+ if (!load_animation (RawPtr->ship,
+ RawPtr->ship_rsc[0],
+ RawPtr->ship_rsc[1],
+ RawPtr->ship_rsc[2]))
+ goto BadLoad;
+
+ if (RawPtr->weapon_rsc[0] != NULL_RESOURCE)
+ {
+ if (!load_animation (RawPtr->weapon,
+ RawPtr->weapon_rsc[0],
+ RawPtr->weapon_rsc[1],
+ RawPtr->weapon_rsc[2]))
+ goto BadLoad;
+ }
+
+ if (RawPtr->special_rsc[0] != NULL_RESOURCE)
+ {
+ if (!load_animation (RawPtr->special,
+ RawPtr->special_rsc[0],
+ RawPtr->special_rsc[1],
+ RawPtr->special_rsc[2]))
+ goto BadLoad;
+ }
+
+ if (RawPtr->captain_control.captain_rsc != NULL_RESOURCE)
+ {
+ RawPtr->captain_control.background = CaptureDrawable (LoadGraphic (
+ RawPtr->captain_control.captain_rsc));
+ if (!RawPtr->captain_control.background)
+ goto BadLoad;
+ }
+
+ if (RawPtr->victory_ditty_rsc != NULL_RESOURCE)
+ {
+ RawPtr->victory_ditty =
+ LoadMusic (RawPtr->victory_ditty_rsc);
+ if (!RawPtr->victory_ditty)
+ goto BadLoad;
+ }
+
+ if (RawPtr->ship_sounds_rsc != NULL_RESOURCE)
+ {
+ RawPtr->ship_sounds = CaptureSound (
+ LoadSound (RawPtr->ship_sounds_rsc));
+ if (!RawPtr->ship_sounds)
+ goto BadLoad;
+ }
+ }
+
+ExitFunc:
+ return RDPtr;
+
+ // TODO: We should really free the resources that did load here
+BadLoad:
+ if (CodeRef)
+ DestroyCodeRes (ReleaseCodeRes (CodeRef));
+
+ RDPtr = 0; /* failed */
+
+ goto ExitFunc;
+}
+
+void
+free_ship (RACE_DESC *raceDescPtr, BOOLEAN FreeIconData,
+ BOOLEAN FreeBattleData)
+{
+ if (raceDescPtr->uninit_func != NULL)
+ (*raceDescPtr->uninit_func) (raceDescPtr);
+
+ if (FreeBattleData)
+ {
+ DATA_STUFF *shipData = &raceDescPtr->ship_data;
+
+ free_image (shipData->special);
+ free_image (shipData->weapon);
+ free_image (shipData->ship);
+
+ DestroyDrawable (
+ ReleaseDrawable (shipData->captain_control.background));
+ DestroyMusic (shipData->victory_ditty);
+ DestroySound (ReleaseSound (shipData->ship_sounds));
+ }
+
+ if (FreeIconData)
+ {
+ SHIP_INFO *shipInfo = &raceDescPtr->ship_info;
+
+ DestroyDrawable (ReleaseDrawable (shipInfo->melee_icon));
+ DestroyDrawable (ReleaseDrawable (shipInfo->icons));
+ DestroyStringTable (ReleaseStringTable (shipInfo->race_strings));
+ }
+
+ DestroyCodeRes (ReleaseCodeRes (raceDescPtr->CodeRef));
+}
diff --git a/src/uqm/master.c b/src/uqm/master.c
new file mode 100644
index 0000000..5c35aab
--- /dev/null
+++ b/src/uqm/master.c
@@ -0,0 +1,217 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "master.h"
+
+#include "build.h"
+#include "resinst.h"
+#include "displist.h"
+#include "supermelee/melee.h"
+
+
+QUEUE master_q;
+
+void
+LoadMasterShipList (void (* YieldProcessing)(void))
+{
+ COUNT num_entries;
+ SPECIES_ID s_id = ARILOU_ID;
+ num_entries = LAST_MELEE_ID - ARILOU_ID + 1;
+ InitQueue (&master_q, num_entries, sizeof (MASTER_SHIP_INFO));
+ while (num_entries--)
+ {
+ HMASTERSHIP hBuiltShip;
+ char *builtName;
+ HMASTERSHIP hStarShip, hNextShip;
+ MASTER_SHIP_INFO *BuiltPtr;
+ RACE_DESC *RDPtr;
+
+ hBuiltShip = AllocLink (&master_q);
+ if (!hBuiltShip)
+ continue;
+
+ // Allow other things to run
+ // supposedly, loading ship packages and data takes some time
+ if (YieldProcessing)
+ YieldProcessing ();
+
+ BuiltPtr = LockMasterShip (&master_q, hBuiltShip);
+ BuiltPtr->SpeciesID = s_id++;
+ RDPtr = load_ship (BuiltPtr->SpeciesID, FALSE);
+ if (!RDPtr)
+ {
+ UnlockMasterShip (&master_q, hBuiltShip);
+ continue;
+ }
+
+ // Grab a copy of loaded icons, strings and info
+ // XXX: SHIP_INFO implicitly referenced here
+ BuiltPtr->ShipInfo = RDPtr->ship_info;
+ BuiltPtr->Fleet = RDPtr->fleet;
+ free_ship (RDPtr, FALSE, FALSE);
+
+ builtName = GetStringAddress (SetAbsStringTableIndex (
+ BuiltPtr->ShipInfo.race_strings, 2));
+ UnlockMasterShip (&master_q, hBuiltShip);
+
+ // Insert the ship in the master queue in the right location
+ // to keep the list sorted on the name of the race.
+ for (hStarShip = GetHeadLink (&master_q);
+ hStarShip; hStarShip = hNextShip)
+ {
+ char *curName;
+ MASTER_SHIP_INFO *MasterPtr;
+
+ MasterPtr = LockMasterShip (&master_q, hStarShip);
+ hNextShip = _GetSuccLink (MasterPtr);
+ curName = GetStringAddress (SetAbsStringTableIndex (
+ MasterPtr->ShipInfo.race_strings, 2));
+ UnlockMasterShip (&master_q, hStarShip);
+
+ if (strcmp (builtName, curName) < 0)
+ break;
+ }
+ InsertQueue (&master_q, hBuiltShip, hStarShip);
+ }
+}
+
+void
+FreeMasterShipList (void)
+{
+ HMASTERSHIP hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&master_q);
+ hStarShip != 0; hStarShip = hNextShip)
+ {
+ MASTER_SHIP_INFO *MasterPtr;
+
+ MasterPtr = LockMasterShip (&master_q, hStarShip);
+ hNextShip = _GetSuccLink (MasterPtr);
+
+ DestroyDrawable (ReleaseDrawable (MasterPtr->ShipInfo.melee_icon));
+ DestroyDrawable (ReleaseDrawable (MasterPtr->ShipInfo.icons));
+ DestroyStringTable (ReleaseStringTable (
+ MasterPtr->ShipInfo.race_strings));
+
+ UnlockMasterShip (&master_q, hStarShip);
+ }
+
+ UninitQueue (&master_q);
+}
+
+HMASTERSHIP
+FindMasterShip (SPECIES_ID ship_ref)
+{
+ HMASTERSHIP hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&master_q); hStarShip; hStarShip = hNextShip)
+ {
+ SPECIES_ID ref;
+ MASTER_SHIP_INFO *MasterPtr;
+
+ MasterPtr = LockMasterShip (&master_q, hStarShip);
+ hNextShip = _GetSuccLink (MasterPtr);
+ ref = MasterPtr->SpeciesID;
+ UnlockMasterShip (&master_q, hStarShip);
+
+ if (ref == ship_ref)
+ break;
+ }
+
+ return (hStarShip);
+}
+
+int
+FindMasterShipIndex (SPECIES_ID ship_ref)
+{
+ HMASTERSHIP hStarShip, hNextShip;
+ int index;
+
+ for (index = 0, hStarShip = GetHeadLink (&master_q); hStarShip;
+ ++index, hStarShip = hNextShip)
+ {
+ SPECIES_ID ref;
+ MASTER_SHIP_INFO *MasterPtr;
+
+ MasterPtr = LockMasterShip (&master_q, hStarShip);
+ hNextShip = _GetSuccLink (MasterPtr);
+ ref = MasterPtr->SpeciesID;
+ UnlockMasterShip (&master_q, hStarShip);
+
+ if (ref == ship_ref)
+ break;
+ }
+
+ return hStarShip ? index : -1;
+}
+
+COUNT
+GetShipCostFromIndex (unsigned Index)
+{
+ HMASTERSHIP hMasterShip;
+ MASTER_SHIP_INFO *MasterPtr;
+ COUNT val;
+
+ hMasterShip = GetStarShipFromIndex (&master_q, Index);
+ if (!hMasterShip)
+ return 0;
+
+ MasterPtr = LockMasterShip (&master_q, hMasterShip);
+ val = MasterPtr->ShipInfo.ship_cost;
+ UnlockMasterShip (&master_q, hMasterShip);
+
+ return val;
+}
+
+FRAME
+GetShipIconsFromIndex (unsigned Index)
+{
+ HMASTERSHIP hMasterShip;
+ MASTER_SHIP_INFO *MasterPtr;
+ FRAME val;
+
+ hMasterShip = GetStarShipFromIndex (&master_q, Index);
+ if (!hMasterShip)
+ return 0;
+
+ MasterPtr = LockMasterShip (&master_q, hMasterShip);
+ val = MasterPtr->ShipInfo.icons;
+ UnlockMasterShip (&master_q, hMasterShip);
+
+ return val;
+}
+
+FRAME
+GetShipMeleeIconsFromIndex (unsigned Index)
+{
+ HMASTERSHIP hMasterShip;
+ MASTER_SHIP_INFO *MasterPtr;
+ FRAME val;
+
+ hMasterShip = GetStarShipFromIndex (&master_q, Index);
+ if (!hMasterShip)
+ return 0;
+
+ MasterPtr = LockMasterShip (&master_q, hMasterShip);
+ val = MasterPtr->ShipInfo.melee_icon;
+ UnlockMasterShip (&master_q, hMasterShip);
+
+ return val;
+}
+
+
diff --git a/src/uqm/master.h b/src/uqm/master.h
new file mode 100644
index 0000000..adcf6e5
--- /dev/null
+++ b/src/uqm/master.h
@@ -0,0 +1,69 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_MASTER_H_
+#define UQM_MASTER_H_
+
+#include "races.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef HLINK HMASTERSHIP;
+
+typedef struct
+{
+ // LINK elements; must be first
+ HMASTERSHIP pred;
+ HMASTERSHIP succ;
+
+ SPECIES_ID SpeciesID;
+
+ SHIP_INFO ShipInfo;
+ FLEET_STUFF Fleet;
+ // FLEET_STUFF is only necessary here because avail_race_q
+ // is initialized in part from master_q (kinda hacky)
+} MASTER_SHIP_INFO;
+
+extern QUEUE master_q;
+ /* List of ships available in SuperMelee;
+ * queue element is MASTER_SHIP_INFO */
+
+static inline MASTER_SHIP_INFO *
+LockMasterShip (const QUEUE *pq, HMASTERSHIP h)
+{
+ assert (GetLinkSize (pq) == sizeof (MASTER_SHIP_INFO));
+ return (MASTER_SHIP_INFO *) LockLink (pq, h);
+}
+
+#define UnlockMasterShip(pq, h) UnlockLink (pq, h)
+#define FreeMasterShip(pq, h) FreeLink (pq, h)
+
+extern void LoadMasterShipList (void (* YieldProcessing)(void));
+extern void FreeMasterShipList (void);
+extern HMASTERSHIP FindMasterShip (SPECIES_ID ship_ref);
+extern int FindMasterShipIndex (SPECIES_ID ship_ref);
+COUNT GetShipCostFromIndex (unsigned Index);
+FRAME GetShipIconsFromIndex (unsigned Index);
+FRAME GetShipMeleeIconsFromIndex (unsigned Index);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_MASTER_H_ */
diff --git a/src/uqm/menu.c b/src/uqm/menu.c
new file mode 100644
index 0000000..fc46e3b
--- /dev/null
+++ b/src/uqm/menu.c
@@ -0,0 +1,603 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "menustat.h"
+
+#include "colors.h"
+#include "controls.h"
+#include "units.h"
+#include "options.h"
+#include "setup.h"
+#include "gamestr.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/tasklib.h"
+#include "libs/log.h"
+
+static BYTE GetEndMenuState (BYTE BaseState);
+static BYTE GetBeginMenuState (BYTE BaseState);
+static BYTE FixMenuState (BYTE BadState);
+static BYTE NextMenuState (BYTE BaseState, BYTE CurState);
+static BYTE PreviousMenuState (BYTE BaseState, BYTE CurState);
+static BOOLEAN GetAlternateMenu (BYTE *BaseState, BYTE *CurState);
+static BYTE ConvertAlternateMenu (BYTE BaseState, BYTE NewState);
+
+
+/* Draw the blue background for PC Menu Text, with a border around it.
+ * The specified rectangle includes the border. */
+static void
+DrawPCMenuFrame (RECT *r)
+{
+ // Draw the top and left of the outer border.
+ // This actually draws a rectangle, but the bottom and right parts
+ // are drawn over in the next step.
+ SetContextForeGroundColor (PCMENU_TOP_LEFT_BORDER_COLOR);
+ DrawRectangle (r);
+
+ // Draw the right and bottom of the outer border.
+ // This actually draws a rectangle, with the top and left segments just
+ // within the total area, but these segments are drawn over in the next
+ // step.
+ r->corner.x++;
+ r->corner.y++;
+ r->extent.height--;
+ r->extent.width--;
+ SetContextForeGroundColor (PCMENU_BOTTOM_RIGHT_BORDER_COLOR);
+ DrawRectangle (r);
+
+ // Draw the background.
+ r->extent.height--;
+ r->extent.width--;
+ SetContextForeGroundColor (PCMENU_BACKGROUND_COLOR);
+ DrawFilledRectangle (r);
+}
+
+#define ALT_MANIFEST 0x80
+#define ALT_EXIT_MANIFEST 0x81
+
+static UNICODE pm_crew_str[128];
+static UNICODE pm_fuel_str[128];
+
+/* Actually display the menu text */
+static void
+DrawPCMenu (BYTE beg_index, BYTE end_index, BYTE NewState, BYTE hilite, RECT *r)
+{
+#define PC_MENU_HEIGHT 8
+ BYTE pos;
+ COUNT i;
+ int num_items;
+ FONT OldFont;
+ TEXT t;
+ UNICODE buf[256];
+
+ pos = beg_index + NewState;
+ num_items = 1 + end_index - beg_index;
+ r->corner.x -= 1;
+ r->extent.width += 1;
+ DrawFilledRectangle (r);
+ if (num_items * PC_MENU_HEIGHT > r->extent.height)
+ log_add (log_Error, "Warning, no room for all menu items!");
+ else
+ r->corner.y += (r->extent.height - num_items * PC_MENU_HEIGHT) / 2;
+ r->extent.height = num_items * PC_MENU_HEIGHT + 4;
+ DrawPCMenuFrame (r);
+ OldFont = SetContextFont (StarConFont);
+ t.align = ALIGN_LEFT;
+ t.baseline.x = r->corner.x + 2;
+ t.baseline.y = r->corner.y + PC_MENU_HEIGHT -1;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ r->corner.x++;
+ r->extent.width -= 2;
+ for (i = beg_index; i <= end_index; i++)
+ {
+ utf8StringCopy (buf, sizeof buf,
+ (i == PM_FUEL) ? pm_fuel_str :
+ (i == PM_CREW) ? pm_crew_str :
+ GAME_STRING (MAINMENU_STRING_BASE + i));
+ if (hilite && pos == i)
+ {
+ // Currently selected menu option.
+
+ // Draw the background of the selection.
+ SetContextForeGroundColor (PCMENU_SELECTION_BACKGROUND_COLOR);
+ r->corner.y = t.baseline.y - PC_MENU_HEIGHT + 2;
+ r->extent.height = PC_MENU_HEIGHT - 1;
+ DrawFilledRectangle (r);
+
+ // Draw the text of the selected item.
+ SetContextForeGroundColor (PCMENU_SELECTION_TEXT_COLOR);
+ font_DrawText (&t);
+ }
+ else
+ {
+ // Draw the text of an unselected item.
+ SetContextForeGroundColor (PCMENU_TEXT_COLOR);
+ font_DrawText (&t);
+ }
+ t.baseline.y += PC_MENU_HEIGHT;
+ }
+ SetContextFont (OldFont);
+}
+
+/* Determine the last text item to display */
+static BYTE
+GetEndMenuState (BYTE BaseState)
+{
+ switch (BaseState)
+ {
+ case PM_SCAN:
+ case PM_STARMAP:
+ return PM_NAVIGATE;
+ break;
+ case PM_MIN_SCAN:
+ return PM_LAUNCH_LANDER;
+ break;
+ case PM_SAVE_GAME:
+ return PM_EXIT_GAME_MENU;
+ break;
+ case PM_CONVERSE:
+ return PM_ENCOUNTER_GAME_MENU;
+ break;
+ case PM_FUEL:
+ return PM_EXIT_OUTFIT;
+ break;
+ case PM_CREW:
+ return PM_EXIT_SHIPYARD;
+ break;
+ case PM_SOUND_ON:
+ return PM_EXIT_SETTINGS;
+ break;
+ case PM_ALT_SCAN:
+ case PM_ALT_STARMAP:
+ return PM_ALT_NAVIGATE;
+ break;
+ case PM_ALT_CARGO:
+ return PM_ALT_EXIT_MANIFEST;
+ break;
+ case PM_ALT_MSCAN:
+ return PM_ALT_EXIT_SCAN;
+ break;
+ }
+ return BaseState;
+}
+
+static BYTE
+GetBeginMenuState (BYTE BaseState)
+{
+ return BaseState;
+}
+
+/* Correct Menu State for cases where the Menu shouldn't move */
+static BYTE
+FixMenuState (BYTE BadState)
+{
+ switch (BadState)
+ {
+ case PM_SOUND_ON:
+ if (GLOBAL (glob_flags) & SOUND_DISABLED)
+ return PM_SOUND_OFF;
+ else
+ return PM_SOUND_ON;
+ case PM_MUSIC_ON:
+ if (GLOBAL (glob_flags) & MUSIC_DISABLED)
+ return PM_MUSIC_OFF;
+ else
+ return PM_MUSIC_ON;
+ case PM_CYBORG_OFF:
+ return (PM_CYBORG_OFF +
+ ((BYTE)(GLOBAL (glob_flags) & COMBAT_SPEED_MASK) >>
+ COMBAT_SPEED_SHIFT));
+ }
+ return BadState;
+}
+
+/* Choose the next menu to hilight in the 'forward' direction */
+static BYTE
+NextMenuState (BYTE BaseState, BYTE CurState)
+{
+ BYTE NextState;
+ BYTE AdjBase = BaseState;
+
+ if (BaseState == PM_STARMAP)
+ AdjBase--;
+
+ switch (AdjBase + CurState)
+ {
+ case PM_SOUND_ON:
+ case PM_SOUND_OFF:
+ NextState = PM_MUSIC_ON;
+ break;
+ case PM_MUSIC_ON:
+ case PM_MUSIC_OFF:
+ NextState = PM_CYBORG_OFF;
+ break;
+ case PM_CYBORG_OFF:
+ case PM_CYBORG_NORMAL:
+ case PM_CYBORG_DOUBLE:
+ case PM_CYBORG_SUPER:
+ NextState = PM_CHANGE_CAPTAIN;
+ break;
+ default:
+ NextState = AdjBase + CurState + 1;
+ }
+ if (NextState > GetEndMenuState (BaseState))
+ NextState = GetBeginMenuState (BaseState);
+ return (FixMenuState (NextState) - AdjBase);
+}
+
+/* Choose the next menu to hilight in the 'back' direction */
+BYTE
+PreviousMenuState (BYTE BaseState, BYTE CurState)
+{
+ SWORD NextState;
+ BYTE AdjBase = BaseState;
+
+ if (BaseState == PM_STARMAP)
+ AdjBase--;
+
+ switch (AdjBase + CurState)
+ {
+ case PM_SOUND_OFF:
+ NextState = PM_EXIT_SETTINGS;
+ break;
+ case PM_MUSIC_ON:
+ case PM_MUSIC_OFF:
+ NextState = PM_SOUND_ON;
+ break;
+ case PM_CYBORG_OFF:
+ case PM_CYBORG_NORMAL:
+ case PM_CYBORG_DOUBLE:
+ case PM_CYBORG_SUPER:
+ NextState = PM_MUSIC_ON;
+ break;
+ case PM_CHANGE_CAPTAIN:
+ NextState = PM_CYBORG_OFF;
+ break;
+ default:
+ NextState = AdjBase + CurState - 1;
+ }
+ if (NextState < GetBeginMenuState (BaseState))
+ NextState = GetEndMenuState (BaseState);
+ return (FixMenuState ((BYTE)NextState) - AdjBase);
+}
+
+
+/* When using PC hierarchy, convert 3do->PC */
+static BOOLEAN
+GetAlternateMenu (BYTE *BaseState, BYTE *CurState)
+{
+ BYTE AdjBase = *BaseState;
+ BYTE adj = 0;
+ if (*BaseState == PM_STARMAP)
+ {
+ AdjBase--;
+ adj = 1;
+ }
+ if (*CurState & 0x80)
+ {
+ switch (*CurState)
+ {
+ case ALT_MANIFEST:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_MANIFEST - PM_ALT_SCAN - adj;
+ return TRUE;
+ case ALT_EXIT_MANIFEST:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_EXIT_MANIFEST - PM_ALT_CARGO;
+ return TRUE;
+ }
+ log_add (log_Error, "Unknown state combination: %d, %d",
+ *BaseState, *CurState);
+ return FALSE;
+ }
+ else
+ {
+ switch (AdjBase + *CurState)
+ {
+ case PM_SCAN:
+ *BaseState = PM_ALT_SCAN;
+ *CurState = PM_ALT_SCAN - PM_ALT_SCAN;
+ return TRUE;
+ case PM_STARMAP:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_STARMAP - PM_ALT_SCAN - adj;
+ return TRUE;
+ case PM_DEVICES:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_DEVICES - PM_ALT_CARGO;
+ return TRUE;
+ case PM_CARGO:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_CARGO - PM_ALT_CARGO;
+ return TRUE;
+ case PM_ROSTER:
+ *BaseState = PM_ALT_CARGO;
+ *CurState = PM_ALT_ROSTER - PM_ALT_CARGO;
+ return TRUE;
+ case PM_GAME_MENU:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_GAME_MENU - PM_ALT_SCAN - adj;
+ return TRUE;
+ case PM_NAVIGATE:
+ *BaseState = PM_ALT_SCAN + adj;
+ *CurState = PM_ALT_NAVIGATE - PM_ALT_SCAN - adj;
+ return TRUE;
+ case PM_MIN_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_MSCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_ENE_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_ESCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_BIO_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_BSCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_EXIT_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_EXIT_SCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_AUTO_SCAN:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_ASCAN - PM_ALT_MSCAN;
+ return TRUE;
+ case PM_LAUNCH_LANDER:
+ *BaseState = PM_ALT_MSCAN;
+ *CurState = PM_ALT_DISPATCH - PM_ALT_MSCAN;
+ return TRUE;
+ }
+ return FALSE;
+ }
+}
+
+/* When using PC hierarchy, convert PC->3DO */
+static BYTE
+ConvertAlternateMenu (BYTE BaseState, BYTE NewState)
+{
+ switch (BaseState + NewState)
+ {
+ case PM_ALT_SCAN:
+ return (PM_SCAN - PM_SCAN);
+ case PM_ALT_STARMAP:
+ return (PM_STARMAP - PM_SCAN);
+ case PM_ALT_MANIFEST:
+ return (ALT_MANIFEST);
+ case PM_ALT_GAME_MENU:
+ return (PM_GAME_MENU - PM_SCAN);
+ case PM_ALT_NAVIGATE:
+ return (PM_NAVIGATE - PM_SCAN);
+ case PM_ALT_CARGO:
+ return (PM_CARGO - PM_SCAN);
+ case PM_ALT_DEVICES:
+ return (PM_DEVICES - PM_SCAN);
+ case PM_ALT_ROSTER:
+ return (PM_ROSTER - PM_SCAN);
+ case PM_ALT_EXIT_MANIFEST:
+ return (ALT_EXIT_MANIFEST);
+ case PM_ALT_MSCAN:
+ return (PM_MIN_SCAN - PM_MIN_SCAN);
+ case PM_ALT_ESCAN:
+ return (PM_ENE_SCAN - PM_MIN_SCAN);
+ case PM_ALT_BSCAN:
+ return (PM_BIO_SCAN - PM_MIN_SCAN);
+ case PM_ALT_ASCAN:
+ return (PM_AUTO_SCAN - PM_MIN_SCAN);
+ case PM_ALT_DISPATCH:
+ return (PM_LAUNCH_LANDER - PM_MIN_SCAN);
+ case PM_ALT_EXIT_SCAN:
+ return (PM_EXIT_SCAN - PM_MIN_SCAN);
+ }
+ return (NewState);
+}
+
+BOOLEAN
+DoMenuChooser (MENU_STATE *pMS, BYTE BaseState)
+{
+ BYTE NewState = pMS->CurState;
+ BYTE OrigBase = BaseState;
+ BOOLEAN useAltMenu = FALSE;
+ if (optWhichMenu == OPT_PC)
+ useAltMenu = GetAlternateMenu (&BaseState, &NewState);
+ if (PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_UP])
+ NewState = PreviousMenuState (BaseState, NewState);
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT] ||
+ PulsedInputState.menu[KEY_MENU_DOWN])
+ NewState = NextMenuState (BaseState, NewState);
+ else if (useAltMenu && PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ NewState = ConvertAlternateMenu (BaseState, NewState);
+ if (NewState == ALT_MANIFEST)
+ {
+ DrawMenuStateStrings (PM_ALT_CARGO, 0);
+ pMS->CurState = PM_CARGO - PM_SCAN;
+ return TRUE;
+ }
+ if (NewState == ALT_EXIT_MANIFEST)
+ {
+ if (OrigBase == PM_SCAN)
+ DrawMenuStateStrings (PM_ALT_SCAN,
+ PM_ALT_MANIFEST - PM_ALT_SCAN);
+ else
+ DrawMenuStateStrings (PM_ALT_STARMAP,
+ PM_ALT_MANIFEST - PM_ALT_STARMAP);
+ pMS->CurState = ALT_MANIFEST;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ else if ((optWhichMenu == OPT_PC) &&
+ PulsedInputState.menu[KEY_MENU_CANCEL] &&
+ (BaseState == PM_ALT_CARGO))
+ {
+ if (OrigBase == PM_SCAN)
+ DrawMenuStateStrings (PM_ALT_SCAN,
+ PM_ALT_MANIFEST - PM_ALT_SCAN);
+ else
+ DrawMenuStateStrings (PM_ALT_STARMAP,
+ PM_ALT_MANIFEST - PM_ALT_STARMAP);
+ pMS->CurState = ALT_MANIFEST;
+ return TRUE;
+ }
+ else
+ return FALSE;
+
+ DrawMenuStateStrings (BaseState, NewState);
+ if (useAltMenu)
+ NewState = ConvertAlternateMenu (BaseState, NewState);
+ pMS->CurState = NewState;
+ SleepThread (ONE_SECOND / 20);
+ return TRUE;
+}
+
+void
+DrawMenuStateStrings (BYTE beg_index, SWORD NewState)
+{
+ BYTE end_index;
+ RECT r;
+ STAMP s;
+ CONTEXT OldContext;
+ BYTE hilite = 1;
+ extern FRAME PlayFrame;
+
+ if (NewState < 0)
+ {
+ NewState = -NewState;
+ hilite = 0;
+ }
+
+ if (optWhichMenu == OPT_PC)
+ {
+ BYTE tmpState = (BYTE)NewState;
+ GetAlternateMenu (&beg_index, &tmpState);
+ NewState = tmpState;
+ }
+
+ if (beg_index == PM_STARMAP)
+ NewState--;
+ end_index = GetEndMenuState (beg_index);
+
+ s.frame = 0;
+ if (NewState <= end_index - beg_index)
+ s.frame = SetAbsFrameIndex (PlayFrame, beg_index + NewState);
+
+ PreUpdateFlashRect ();
+ OldContext = SetContext (StatusContext);
+ GetContextClipRect (&r);
+ s.origin.x = RADAR_X - r.corner.x;
+ s.origin.y = RADAR_Y - r.corner.y;
+ r.corner.x = s.origin.x - 1;
+ r.corner.y = s.origin.y - 11;
+ r.extent.width = RADAR_WIDTH + 2;
+ BatchGraphics ();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ if (s.frame && optWhichMenu == OPT_PC)
+ {
+ if (beg_index == PM_CREW)
+ snprintf (pm_crew_str, sizeof pm_crew_str, "%s(%d)",
+ GAME_STRING (MAINMENU_STRING_BASE + PM_CREW),
+ GLOBAL (CrewCost));
+ if (beg_index == PM_FUEL)
+ snprintf (pm_fuel_str, sizeof pm_fuel_str, "%s(%d)",
+ GAME_STRING (MAINMENU_STRING_BASE + PM_FUEL),
+ GLOBAL (FuelCost));
+ if (beg_index == PM_SOUND_ON)
+ {
+ end_index = beg_index + 5;
+ switch (beg_index + NewState)
+ {
+ case PM_SOUND_ON:
+ case PM_SOUND_OFF:
+ NewState = 0;
+ break;
+ case PM_MUSIC_ON:
+ case PM_MUSIC_OFF:
+ NewState = 1;
+ break;
+ case PM_CYBORG_OFF:
+ case PM_CYBORG_NORMAL:
+ case PM_CYBORG_DOUBLE:
+ case PM_CYBORG_SUPER:
+ NewState = 2;
+ break;
+ case PM_CHANGE_CAPTAIN:
+ NewState = 3;
+ break;
+ case PM_CHANGE_SHIP:
+ NewState = 4;
+ break;
+ case PM_EXIT_SETTINGS:
+ NewState = 5;
+ break;
+ }
+ }
+ r.extent.height = RADAR_HEIGHT + 11;
+ DrawPCMenu (beg_index, end_index, (BYTE)NewState, hilite, &r);
+ s.frame = 0;
+ }
+ else
+ {
+ if (optWhichMenu == OPT_PC)
+ {
+ r.corner.x -= 1;
+ r.extent.width += 1;
+ r.extent.height = RADAR_HEIGHT + 11;
+ }
+ else
+ r.extent.height = 11;
+ DrawFilledRectangle (&r);
+ }
+ if (s.frame)
+ {
+ DrawStamp (&s);
+ switch (beg_index + NewState)
+ {
+ TEXT t;
+ UNICODE buf[20];
+
+ case PM_CREW:
+ t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
+ t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ snprintf (buf, sizeof buf, "%u", GLOBAL (CrewCost));
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (THREEDOMENU_TEXT_COLOR);
+ font_DrawText (&t);
+ break;
+ case PM_FUEL:
+ t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
+ t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ snprintf (buf, sizeof buf, "%u", GLOBAL (FuelCost));
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (THREEDOMENU_TEXT_COLOR);
+ font_DrawText (&t);
+ break;
+ }
+ }
+ UnbatchGraphics ();
+ SetContext (OldContext);
+ PostUpdateFlashRect ();
+}
+
diff --git a/src/uqm/menustat.h b/src/uqm/menustat.h
new file mode 100644
index 0000000..2aa864d
--- /dev/null
+++ b/src/uqm/menustat.h
@@ -0,0 +1,131 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_MENUSTAT_H_
+#define UQM_MENUSTAT_H_
+
+#include "libs/gfxlib.h"
+#include "libs/sndlib.h"
+#include "flash.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct menu_state
+{
+ // Standard field required by DoInput()
+ BOOLEAN (*InputFunc) (struct menu_state *pMS);
+
+ SIZE Initialized;
+
+ BYTE CurState;
+ FRAME CurFrame;
+ STRING CurString;
+ POINT first_item;
+ SIZE delta_item;
+
+ FRAME ModuleFrame;
+ RECT flash_rect0, flash_rect1;
+ FRAME flash_frame0, flash_frame1;
+ FlashContext *flashContext;
+
+ MUSIC_REF hMusic;
+
+ // For private use by various menus
+ // Usually, a menu associates its internal data struct using this
+ void *privData;
+
+} MENU_STATE;
+
+// XXX: Should probably go to menu.h (does not yet exist)
+enum
+{
+ PM_SCAN = 0,
+ PM_STARMAP,
+ PM_DEVICES,
+ PM_CARGO,
+ PM_ROSTER,
+ PM_GAME_MENU,
+ PM_NAVIGATE,
+
+ PM_MIN_SCAN,
+ PM_ENE_SCAN,
+ PM_BIO_SCAN,
+ PM_EXIT_SCAN,
+ PM_AUTO_SCAN,
+ PM_LAUNCH_LANDER,
+
+ PM_SAVE_GAME,
+ PM_LOAD_GAME,
+ PM_QUIT_GAME,
+ PM_CHANGE_SETTINGS,
+ PM_EXIT_GAME_MENU,
+
+ PM_CONVERSE,
+ PM_ATTACK,
+ PM_ENCOUNTER_GAME_MENU,
+
+ PM_FUEL,
+ PM_MODULE,
+ PM_OUTFIT_GAME_MENU,
+ PM_EXIT_OUTFIT,
+
+ PM_CREW,
+ PM_SHIPYARD_GAME_MENU,
+ PM_EXIT_SHIPYARD,
+
+ PM_SOUND_ON,
+ PM_SOUND_OFF,
+ PM_MUSIC_ON,
+ PM_MUSIC_OFF,
+ PM_CYBORG_OFF,
+ PM_CYBORG_NORMAL,
+ PM_CYBORG_DOUBLE,
+ PM_CYBORG_SUPER,
+ PM_CHANGE_CAPTAIN,
+ PM_CHANGE_SHIP,
+ PM_EXIT_SETTINGS,
+
+ PM_ALT_SCAN,
+ PM_ALT_STARMAP,
+ PM_ALT_MANIFEST,
+ PM_ALT_GAME_MENU,
+ PM_ALT_NAVIGATE,
+
+ PM_ALT_CARGO,
+ PM_ALT_DEVICES,
+ PM_ALT_ROSTER,
+ PM_ALT_EXIT_MANIFEST,
+
+ PM_ALT_MSCAN,
+ PM_ALT_ESCAN,
+ PM_ALT_BSCAN,
+ PM_ALT_ASCAN,
+ PM_ALT_DISPATCH,
+ PM_ALT_EXIT_SCAN,
+};
+
+extern BOOLEAN DoMenuChooser (MENU_STATE *pMS, BYTE BaseState);
+extern void DrawMenuStateStrings (BYTE beg_index, SWORD NewState);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_MENUSTAT_H_ */
diff --git a/src/uqm/misc.c b/src/uqm/misc.c
new file mode 100644
index 0000000..4bc728f
--- /dev/null
+++ b/src/uqm/misc.c
@@ -0,0 +1,407 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "element.h"
+#include "init.h"
+#include "races.h"
+#include "ship.h"
+#include "status.h"
+#include "setup.h"
+#include "sounds.h"
+#include "weapon.h"
+#include "libs/mathlib.h"
+
+
+void
+spawn_planet (void)
+{
+ HELEMENT hPlanetElement;
+
+ hPlanetElement = AllocElement ();
+ if (hPlanetElement)
+ {
+ ELEMENT *PlanetElementPtr;
+ extern FRAME planet[];
+
+ LockElement (hPlanetElement, &PlanetElementPtr);
+ PlanetElementPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ PlanetElementPtr->hit_points = 200;
+ PlanetElementPtr->state_flags = APPEARING;
+ PlanetElementPtr->life_span = NORMAL_LIFE + 1;
+ SetPrimType (&DisplayArray[PlanetElementPtr->PrimIndex], STAMP_PRIM);
+ PlanetElementPtr->current.image.farray = planet;
+ PlanetElementPtr->current.image.frame =
+ PlanetElementPtr->current.image.farray[0];
+ PlanetElementPtr->collision_func = collision;
+ PlanetElementPtr->postprocess_func =
+ (void (*) (struct element *ElementPtr))CalculateGravity;
+ ZeroVelocityComponents (&PlanetElementPtr->velocity);
+ do
+ {
+ PlanetElementPtr->current.location.x =
+ WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
+ PlanetElementPtr->current.location.y =
+ WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
+ } while (CalculateGravity (PlanetElementPtr)
+ || TimeSpaceMatterConflict (PlanetElementPtr));
+ PlanetElementPtr->mass_points = PlanetElementPtr->hit_points;
+ UnlockElement (hPlanetElement);
+
+ PutElement (hPlanetElement);
+ }
+}
+
+extern FRAME asteroid[];
+
+static void
+spawn_rubble (ELEMENT *AsteroidElementPtr)
+{
+ HELEMENT hRubbleElement;
+
+ hRubbleElement = AllocElement ();
+ if (hRubbleElement)
+ {
+ ELEMENT *RubbleElementPtr;
+
+ PutElement (hRubbleElement);
+ LockElement (hRubbleElement, &RubbleElementPtr);
+ RubbleElementPtr->playerNr = AsteroidElementPtr->playerNr;
+ RubbleElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ RubbleElementPtr->life_span = 5;
+ RubbleElementPtr->turn_wait = RubbleElementPtr->next_turn = 0;
+ SetPrimType (&DisplayArray[RubbleElementPtr->PrimIndex], STAMP_PRIM);
+ RubbleElementPtr->current.image.farray = asteroid;
+ RubbleElementPtr->current.image.frame =
+ SetAbsFrameIndex (asteroid[0], ANGLE_TO_FACING (FULL_CIRCLE));
+ RubbleElementPtr->current.location = AsteroidElementPtr->current.location;
+ RubbleElementPtr->preprocess_func = animation_preprocess;
+ RubbleElementPtr->death_func = spawn_asteroid;
+ UnlockElement (hRubbleElement);
+ }
+}
+
+static void
+asteroid_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT frame_index;
+
+ frame_index = GetFrameIndex (ElementPtr->current.image.frame);
+ if (ElementPtr->thrust_wait & (1 << 7))
+ --frame_index;
+ else
+ ++frame_index;
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ NORMALIZE_FACING (frame_index));
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = (unsigned char)(ElementPtr->thrust_wait & ((1 << 7) - 1));
+ }
+}
+
+void
+spawn_asteroid (ELEMENT *ElementPtr)
+{
+ HELEMENT hAsteroidElement;
+
+ if ((hAsteroidElement = AllocElement ()) == 0)
+ {
+ if (ElementPtr != 0)
+ {
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM);
+ ElementPtr->life_span = 1;
+ }
+ }
+ else
+ {
+ ELEMENT *AsteroidElementPtr;
+ COUNT val;
+
+ LockElement (hAsteroidElement, &AsteroidElementPtr);
+ AsteroidElementPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ AsteroidElementPtr->hit_points = 1;
+ AsteroidElementPtr->mass_points = 3;
+ AsteroidElementPtr->state_flags = APPEARING;
+ AsteroidElementPtr->life_span = NORMAL_LIFE;
+ SetPrimType (&DisplayArray[AsteroidElementPtr->PrimIndex], STAMP_PRIM);
+ if ((val = (COUNT)TFB_Random ()) & (1 << 0))
+ {
+ if (!(val & (1 << 1)))
+ AsteroidElementPtr->current.location.x = 0;
+ else
+ AsteroidElementPtr->current.location.x = LOG_SPACE_WIDTH;
+ AsteroidElementPtr->current.location.y =
+ WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
+ }
+ else
+ {
+ AsteroidElementPtr->current.location.x =
+ WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
+ if (!(val & (1 << 1)))
+ AsteroidElementPtr->current.location.y = 0;
+ else
+ AsteroidElementPtr->current.location.y = LOG_SPACE_HEIGHT;
+ }
+
+ {
+ // Using these temporary variables because the execution order
+ // of function arguments may vary per system, which may break
+ // synchronisation on network games.
+ SIZE magnitude =
+ DISPLAY_TO_WORLD (((SIZE)TFB_Random () & 7) + 4);
+ COUNT facing = (COUNT)TFB_Random ();
+ SetVelocityVector (&AsteroidElementPtr->velocity, magnitude,
+ facing);
+ }
+ AsteroidElementPtr->current.image.farray = asteroid;
+ AsteroidElementPtr->current.image.frame =
+ SetAbsFrameIndex (asteroid[0],
+ NORMALIZE_FACING (TFB_Random ()));
+ AsteroidElementPtr->turn_wait =
+ AsteroidElementPtr->thrust_wait =
+ (BYTE)TFB_Random () & (BYTE)((1 << 2) - 1);
+ AsteroidElementPtr->thrust_wait |=
+ (BYTE)TFB_Random () & (BYTE)(1 << 7);
+ AsteroidElementPtr->preprocess_func = asteroid_preprocess;
+ AsteroidElementPtr->death_func = spawn_rubble;
+ AsteroidElementPtr->collision_func = collision;
+ UnlockElement (hAsteroidElement);
+
+ PutElement (hAsteroidElement);
+ }
+}
+
+void
+do_damage (ELEMENT *ElementPtr, SIZE damage)
+{
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ if (!DeltaCrew (ElementPtr, -damage))
+ {
+ ElementPtr->life_span = 0;
+ ElementPtr->state_flags |= NONSOLID;
+ }
+ }
+ else if (!GRAVITY_MASS (ElementPtr->mass_points))
+ {
+ if ((BYTE)damage < ElementPtr->hit_points)
+ ElementPtr->hit_points -= (BYTE)damage;
+ else
+ {
+ ElementPtr->hit_points = 0;
+ ElementPtr->life_span = 0;
+ ElementPtr->state_flags |= NONSOLID;
+ }
+ }
+}
+
+#define CREW_COLOR_LOW_INTENSITY \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02)
+#define CREW_COLOR_HIGH_INTENSITY \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1E, 0x0A), 0x0A)
+void
+crew_preprocess (ELEMENT *ElementPtr)
+{
+ HELEMENT hTarget;
+
+ // Switch from dark to light or vice versa:
+ Color oldColor = GetPrimColor (&DisplayArray[ElementPtr->PrimIndex]);
+ Color newColor = sameColor (oldColor, CREW_COLOR_LOW_INTENSITY) ?
+ CREW_COLOR_HIGH_INTENSITY : CREW_COLOR_LOW_INTENSITY;
+ SetPrimColor (&DisplayArray[ElementPtr->PrimIndex], newColor);
+
+ ElementPtr->state_flags |= CHANGING;
+
+ hTarget = ElementPtr->hTarget;
+ if (hTarget == 0)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr && StarShipPtr->RaceDescPtr->ship_info.crew_level)
+ ElementPtr->hTarget = StarShipPtr->hShip;
+ else
+ {
+ COUNT facing;
+
+ facing = 0;
+ TrackShip (ElementPtr, &facing);
+ }
+ }
+
+ if (hTarget)
+ {
+#define CREW_DELTA SCALED_ONE
+ SIZE delta;
+ ELEMENT *ShipPtr;
+
+ LockElement (hTarget, &ShipPtr);
+ delta = ShipPtr->current.location.x
+ - ElementPtr->current.location.x;
+ delta = WRAP_DELTA_X (delta);
+ if (delta > 0)
+ ElementPtr->next.location.x += CREW_DELTA;
+ else if (delta < 0)
+ ElementPtr->next.location.x -= CREW_DELTA;
+
+ delta = ShipPtr->current.location.y -
+ ElementPtr->current.location.y;
+ delta = WRAP_DELTA_Y (delta);
+ if (delta > 0)
+ ElementPtr->next.location.y += CREW_DELTA;
+ else if (delta < 0)
+ ElementPtr->next.location.y -= CREW_DELTA;
+ UnlockElement (hTarget);
+ }
+}
+
+void
+crew_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && ElementPtr1->life_span >= NORMAL_LIFE
+ && ElementPtr0->hit_points > 0)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ if (!(StarShipPtr->RaceDescPtr->ship_info.ship_flags & CREW_IMMUNE))
+ {
+ ProcessSound (SetAbsSoundIndex (GameSounds, GRAB_CREW), ElementPtr1);
+ DeltaCrew (ElementPtr1, 1);
+ }
+ }
+
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ ElementPtr0->state_flags |= COLLISION | DISAPPEARING | NONSOLID;
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+void
+AbandonShip (ELEMENT *ShipPtr, ELEMENT *TargetPtr,
+ COUNT crew_loss)
+{
+ SIZE dx, dy;
+ COUNT direction;
+ RECT r;
+ STARSHIP *StarShipPtr;
+ HELEMENT hCrew;
+ INTERSECT_CONTROL ShipIntersect;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->ship_info.ship_flags & CREW_IMMUNE)
+ return;
+
+ ShipIntersect = ShipPtr->IntersectControl;
+ GetFrameRect (ShipIntersect.IntersectStamp.frame, &r);
+
+ if ((direction = GetVelocityTravelAngle (
+ &ShipPtr->velocity)) == FULL_CIRCLE)
+ dx = dy = 0;
+ else
+ {
+#define MORE_THAN_ENOUGH 100
+ direction += HALF_CIRCLE;
+ dx = COSINE (direction, MORE_THAN_ENOUGH);
+ dy = SINE (direction, MORE_THAN_ENOUGH);
+ }
+
+ while (crew_loss-- && (hCrew = AllocElement ()))
+ {
+#define CREW_LIFE 300
+ ELEMENT *CrewPtr;
+
+ DeltaCrew (ShipPtr, -1);
+
+ PutElement (hCrew);
+ LockElement (hCrew, &CrewPtr);
+ CrewPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ CrewPtr->hit_points = 1;
+ CrewPtr->state_flags = APPEARING | FINITE_LIFE | CREW_OBJECT;
+ CrewPtr->life_span = CREW_LIFE;
+ SetPrimType (&DisplayArray[CrewPtr->PrimIndex], POINT_PRIM);
+ SetPrimColor (&DisplayArray[CrewPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02));
+ CrewPtr->current.image.frame = DecFrameIndex (stars_in_space);
+ CrewPtr->current.image.farray = &stars_in_space;
+ CrewPtr->preprocess_func = crew_preprocess;
+ CrewPtr->collision_func = crew_collision;
+
+ SetElementStarShip (CrewPtr, StarShipPtr);
+
+ GetElementStarShip (TargetPtr, &StarShipPtr);
+ CrewPtr->hTarget = StarShipPtr->hShip;
+
+ {
+ SIZE w, h;
+ INTERSECT_CONTROL CrewIntersect;
+
+ ShipIntersect.IntersectStamp.origin =
+ ShipPtr->IntersectControl.EndPoint;
+
+ w = (SIZE)((COUNT)TFB_Random () % r.extent.width);
+ h = (SIZE)((COUNT)TFB_Random () % r.extent.height);
+ CrewIntersect.EndPoint = ShipIntersect.EndPoint;
+ CrewIntersect.IntersectStamp.frame = DecFrameIndex (stars_in_space);
+ if (dx == 0 && dy == 0)
+ {
+ CrewIntersect.EndPoint.x += w - (r.extent.width >> 1);
+ CrewIntersect.EndPoint.y += h - (r.extent.height >> 1);
+ CrewIntersect.IntersectStamp.origin =
+ TargetPtr->IntersectControl.EndPoint;
+ }
+ else
+ {
+ if (dx == 0)
+ CrewIntersect.EndPoint.x += w - (r.extent.width >> 1);
+ else if (dx > 0)
+ CrewIntersect.EndPoint.x += w;
+ else
+ CrewIntersect.EndPoint.x -= w;
+ if (dy == 0)
+ CrewIntersect.EndPoint.y += h - (r.extent.height >> 1);
+ else if (dy > 0)
+ CrewIntersect.EndPoint.y += h;
+ else
+ CrewIntersect.EndPoint.y -= h;
+ CrewIntersect.IntersectStamp.origin.x =
+ CrewIntersect.EndPoint.x + dx;
+ CrewIntersect.IntersectStamp.origin.y =
+ CrewIntersect.EndPoint.y + dy;
+ }
+
+ DrawablesIntersect (&CrewIntersect,
+ &ShipIntersect, MAX_TIME_VALUE);
+
+ CrewPtr->current.location.x =
+ DISPLAY_TO_WORLD (CrewIntersect.EndPoint.x);
+ CrewPtr->current.location.y =
+ DISPLAY_TO_WORLD (CrewIntersect.EndPoint.y);
+ }
+ UnlockElement (hCrew);
+ }
+}
+
diff --git a/src/uqm/nameref.h b/src/uqm/nameref.h
new file mode 100644
index 0000000..5192670
--- /dev/null
+++ b/src/uqm/nameref.h
@@ -0,0 +1,33 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_NAMEREF_H_
+#define UQM_NAMEREF_H_
+
+#include "libs/reslib.h"
+
+#define LoadCodeRes LoadCodeResInstance
+#define LoadGraphic (DRAWABLE)LoadGraphicInstance
+#define LoadFont (FONT)LoadGraphicInstance
+#define LoadColorMap LoadColorMapInstance
+#define LoadStringTable LoadStringTableInstance
+#define LoadSound LoadSoundInstance
+#define LoadMusic LoadMusicInstance
+
+#endif /* UQM_NAMEREF_H_ */
+
diff --git a/src/uqm/oscill.c b/src/uqm/oscill.c
new file mode 100644
index 0000000..7c37b64
--- /dev/null
+++ b/src/uqm/oscill.c
@@ -0,0 +1,191 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "oscill.h"
+
+#include "setup.h"
+ // for OffScreenContext
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/sound/sound.h"
+#include "libs/sound/trackplayer.h"
+
+
+static FRAME scope_frame;
+static int scope_init = 0;
+static FRAME scopeWork;
+static Color scopeColor;
+static EXTENT scopeSize;
+BOOLEAN oscillDisabled = FALSE;
+
+void
+InitOscilloscope (FRAME scopeBg)
+{
+ scope_frame = scopeBg;
+ if (!scope_init)
+ {
+ EXTENT size = GetFrameBounds (scope_frame);
+ POINT midPt = {size.width / 2, size.height / 2};
+
+ // mid-image pixel defines the color of scope lines
+ scopeColor = GetFramePixel (scope_frame, midPt);
+ // insist that scope lines be purely opaque
+ scopeColor.a = 0xff;
+
+ scopeWork = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP | MAPPED_TO_DISPLAY,
+ size.width, size.height, 1));
+
+ // assume and subtract the borders
+ scopeSize.width = size.width - 2;
+ scopeSize.height = size.height - 2;
+
+ scope_init = 1;
+ }
+}
+
+void
+UninitOscilloscope (void)
+{
+ // XXX: Is never called (BUG?)
+ DestroyDrawable (ReleaseDrawable (scopeWork));
+ scopeWork = NULL;
+ scope_init = 0;
+}
+
+// draws the oscilloscope
+void
+DrawOscilloscope (void)
+{
+ STAMP s;
+ BYTE scope_data[128];
+
+ if (oscillDisabled)
+ return;
+
+ assert ((size_t)scopeSize.width <= sizeof scope_data);
+ assert (scopeSize.height < 256);
+
+ if (GraphForegroundStream (scope_data, scopeSize.width, scopeSize.height,
+ usingSpeech))
+ {
+ int i;
+ CONTEXT oldContext;
+
+ oldContext = SetContext (OffScreenContext);
+ SetContextFGFrame (scopeWork);
+ SetContextClipRect (NULL);
+
+ // draw the background image
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = scope_frame;
+ DrawStamp (&s);
+
+ // draw the scope lines
+ SetContextForeGroundColor (scopeColor);
+ for (i = 0; i < scopeSize.width - 1; ++i)
+ {
+ LINE line;
+
+ line.first.x = i + 1;
+ line.first.y = scope_data[i] + 1;
+ line.second.x = i + 2;
+ line.second.y = scope_data[i + 1] + 1;
+ DrawLine (&line);
+ }
+
+ SetContext (oldContext);
+
+ s.frame = scopeWork;
+ }
+ else
+ { // no data -- draw blank scope background
+ s.frame = scope_frame;
+ }
+
+ // draw the final scope image to screen
+ s.origin.x = 0;
+ s.origin.y = 0;
+ DrawStamp (&s);
+}
+
+
+static STAMP sliderStamp;
+static STAMP buttonStamp;
+static BOOLEAN sliderChanged = FALSE;
+int sliderSpace; // slider width - button width
+BOOLEAN sliderDisabled = FALSE;
+
+/*
+ * Initialise the communication progress bar
+ * x - x location of slider
+ * y - y location of slider
+ * width - width of slider
+ * height - height of slider
+ * bwidth - width of button indicating current progress
+ * bheight - height of button indicating progress
+ * f - image for the slider
+ */
+
+void
+InitSlider (int x, int y, int width, FRAME sliderFrame, FRAME buttonFrame)
+{
+ EXTENT sliderSize = GetFrameBounds (sliderFrame);
+ EXTENT buttonSize = GetFrameBounds (buttonFrame);
+
+ sliderStamp.origin.x = x;
+ sliderStamp.origin.y = y;
+ sliderStamp.frame = sliderFrame;
+
+ buttonStamp.origin.x = x;
+ buttonStamp.origin.y = y - ((buttonSize.height - sliderSize.height) / 2);
+ buttonStamp.frame = buttonFrame;
+
+ sliderSpace = width - buttonSize.width;
+}
+
+void
+SetSliderImage (FRAME f)
+{
+ sliderChanged = TRUE;
+ buttonStamp.frame = f;
+}
+
+void
+DrawSlider (void)
+{
+ int offs;
+ static int last_offs = -1;
+
+ if (sliderDisabled)
+ return;
+
+ offs = GetTrackPosition (sliderSpace);
+ if (offs != last_offs || sliderChanged)
+ {
+ sliderChanged = FALSE;
+ last_offs = offs;
+ buttonStamp.origin.x = sliderStamp.origin.x + offs;
+ BatchGraphics ();
+ DrawStamp (&sliderStamp);
+ DrawStamp (&buttonStamp);
+ UnbatchGraphics ();
+ }
+}
+
diff --git a/src/uqm/oscill.h b/src/uqm/oscill.h
new file mode 100644
index 0000000..bd021a4
--- /dev/null
+++ b/src/uqm/oscill.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_OSCILL_H_
+#define UQM_OSCILL_H_
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern BOOLEAN sliderDisabled;
+extern BOOLEAN oscillDisabled;
+
+extern void InitOscilloscope (FRAME scopeBg);
+extern void DrawOscilloscope (void);
+extern void UninitOscilloscope (void);
+
+extern void InitSlider (int x, int y, int width, FRAME sliderFrame,
+ FRAME buttonFrame);
+extern void SetSliderImage (FRAME f);
+void DrawSlider (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_OSCILL_H_ */
diff --git a/src/uqm/outfit.c b/src/uqm/outfit.c
new file mode 100644
index 0000000..458dfa0
--- /dev/null
+++ b/src/uqm/outfit.c
@@ -0,0 +1,795 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "options.h"
+#include "colors.h"
+#include "controls.h"
+#include "menustat.h"
+#include "gameopt.h"
+#include "gamestr.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "settings.h"
+#include "starbase.h"
+#include "setup.h"
+#include "sis.h"
+#include "units.h"
+#include "sounds.h"
+#include "planets/planets.h"
+ // for xxx_DISASTER
+#include "libs/graphics/gfx_common.h"
+
+
+enum
+{
+ OUTFIT_FUEL,
+ OUTFIT_MODULES,
+ OUTFIT_SAVELOAD,
+ OUTFIT_EXIT,
+ OUTFIT_DOFUEL
+};
+
+
+static void
+DrawModuleStrings (MENU_STATE *pMS, BYTE NewModule)
+{
+ RECT r;
+ STAMP s;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ GetContextClipRect (&r);
+ s.origin.x = RADAR_X - r.corner.x;
+ s.origin.y = RADAR_Y - r.corner.y;
+ r.corner.x = s.origin.x - 1;
+ r.corner.y = s.origin.y - 11;
+ r.extent.width = RADAR_WIDTH + 2;
+ r.extent.height = 11;
+ BatchGraphics ();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ DrawFilledRectangle (&r);
+ if (NewModule >= EMPTY_SLOT)
+ {
+ r.corner = s.origin;
+ r.extent.width = RADAR_WIDTH;
+ r.extent.height = RADAR_HEIGHT;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x00), 0x00));
+ DrawFilledRectangle (&r);
+ }
+ else if (pMS->CurFrame)
+ {
+ TEXT t;
+ UNICODE buf[40];
+
+ s.frame = SetAbsFrameIndex (pMS->CurFrame, NewModule);
+ DrawStamp (&s);
+ t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
+ t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ sprintf (buf, "%u",
+ GLOBAL (ModuleCost[NewModule]) * MODULE_COST_SCALE);
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1F, 0x00), 0x02));
+ font_DrawText (&t);
+ }
+ UnbatchGraphics ();
+ SetContext (OldContext);
+}
+
+static void
+RedistributeFuel (void)
+{
+ const DWORD FuelVolume = GLOBAL_SIS (FuelOnBoard);
+ const CONTEXT OldContext = SetContext (SpaceContext);
+ RECT r;
+ r.extent.height = 1;
+
+ // Loop through all the rows to draw
+ BatchGraphics ();
+ for (GLOBAL_SIS (FuelOnBoard) = FUEL_RESERVE;
+ GLOBAL_SIS (FuelOnBoard) < GetFTankCapacity (&r.corner);
+ GLOBAL_SIS (FuelOnBoard) += FUEL_VOLUME_PER_ROW)
+ {
+ // If we're less than the fuel level, draw fuel.
+ if (GLOBAL_SIS (FuelOnBoard) < FuelVolume)
+ {
+ r.extent.width = 3;
+ DrawPoint (&r.corner);
+ r.corner.x += r.extent.width + 1;
+ DrawPoint (&r.corner);
+ r.corner.x -= r.extent.width;
+ SetContextForeGroundColor (
+ SetContextBackGroundColor (BLACK_COLOR));
+ }
+ else // Otherwise, draw an empty bar.
+ {
+ r.extent.width = 5;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0B, 0x00, 0x00), 0x2E));
+ }
+ DrawFilledRectangle (&r);
+ }
+ UnbatchGraphics ();
+ SetContext (OldContext);
+
+ GLOBAL_SIS (FuelOnBoard) = FuelVolume;
+}
+
+#define LANDER_X 24
+#define LANDER_Y 67
+#define LANDER_WIDTH 15
+
+static void
+DisplayLanders (MENU_STATE *pMS)
+{
+ STAMP s;
+
+ s.frame = pMS->ModuleFrame;
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 3)
+ {
+ s.origin.x = s.origin.y = 0;
+ s.frame = DecFrameIndex (s.frame);
+ DrawStamp (&s);
+ }
+ else
+ {
+ COUNT i;
+
+ s.origin.x = LANDER_X;
+ s.origin.y = LANDER_Y;
+ for (i = 0; i < GLOBAL_SIS (NumLanders); ++i)
+ {
+ DrawStamp (&s);
+ s.origin.x += LANDER_WIDTH;
+ }
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ for (; i < MAX_LANDERS; ++i)
+ {
+ DrawFilledStamp (&s);
+ s.origin.x += LANDER_WIDTH;
+ }
+ }
+}
+
+static BOOLEAN
+DoInstallModule (MENU_STATE *pMS)
+{
+ BYTE NewState, new_slot_piece, old_slot_piece;
+ SIZE FirstItem, LastItem;
+ BOOLEAN select, cancel, motion;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pMS->InputFunc = DoOutfit;
+ return (TRUE);
+ }
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+ motion = PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_RIGHT] ||
+ PulsedInputState.menu[KEY_MENU_UP] ||
+ PulsedInputState.menu[KEY_MENU_DOWN];
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ FirstItem = 0;
+ NewState = pMS->CurState;
+ switch (NewState)
+ {
+ case PLANET_LANDER:
+ case EMPTY_SLOT + 3:
+ old_slot_piece = pMS->delta_item < GLOBAL_SIS (NumLanders)
+ ? PLANET_LANDER : (EMPTY_SLOT + 3);
+ LastItem = MAX_LANDERS - 1;
+ break;
+ case FUSION_THRUSTER:
+ case EMPTY_SLOT + 0:
+ old_slot_piece = GLOBAL_SIS (DriveSlots[pMS->delta_item]);
+ LastItem = NUM_DRIVE_SLOTS - 1;
+ break;
+ case TURNING_JETS:
+ case EMPTY_SLOT + 1:
+ old_slot_piece = GLOBAL_SIS (JetSlots[pMS->delta_item]);
+ LastItem = NUM_JET_SLOTS - 1;
+ break;
+ default:
+ old_slot_piece = GLOBAL_SIS (ModuleSlots[pMS->delta_item]);
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 3)
+ FirstItem = NUM_BOMB_MODULES;
+ LastItem = NUM_MODULE_SLOTS - 1;
+ break;
+ }
+
+ if (NewState < CREW_POD)
+ FirstItem = LastItem = NewState;
+ else if (NewState < EMPTY_SLOT)
+ FirstItem = CREW_POD, LastItem = NUM_PURCHASE_MODULES - 1;
+
+ if (!pMS->Initialized)
+ {
+ new_slot_piece = old_slot_piece;
+ pMS->Initialized = TRUE;
+
+ pMS->InputFunc = DoInstallModule;
+
+
+ SetContext (SpaceContext);
+ ClearSISRect (CLEAR_SIS_RADAR);
+ SetFlashRect (NULL);
+ goto InitFlash;
+ }
+ else if (select || cancel)
+ {
+ new_slot_piece = pMS->CurState;
+ if (select)
+ {
+ if (new_slot_piece < EMPTY_SLOT)
+ {
+ if (GLOBAL_SIS (ResUnits) <
+ (DWORD)(GLOBAL (ModuleCost[new_slot_piece])
+ * MODULE_COST_SCALE))
+ { // not enough RUs to build
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return (TRUE);
+ }
+ }
+ else if (new_slot_piece == EMPTY_SLOT + 2)
+ {
+ if (old_slot_piece == CREW_POD)
+ {
+ if (GLOBAL_SIS (CrewEnlisted) > CREW_POD_CAPACITY
+ * (CountSISPieces (CREW_POD) - 1))
+ { // crew pod still needed for crew recruited
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return (TRUE);
+ }
+ }
+ else if (old_slot_piece == FUEL_TANK
+ || old_slot_piece == HIGHEFF_FUELSYS)
+ {
+ DWORD volume;
+
+ volume = (DWORD)CountSISPieces (FUEL_TANK)
+ * FUEL_TANK_CAPACITY
+ + (DWORD)CountSISPieces (HIGHEFF_FUELSYS)
+ * HEFUEL_TANK_CAPACITY;
+ volume -= (old_slot_piece == FUEL_TANK
+ ? FUEL_TANK_CAPACITY : HEFUEL_TANK_CAPACITY);
+ if (GLOBAL_SIS (FuelOnBoard) > volume + FUEL_RESERVE)
+ { // fuel tank still needed for the fuel on board
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return (TRUE);
+ }
+ }
+ else if (old_slot_piece == STORAGE_BAY)
+ {
+ if (GLOBAL_SIS (TotalElementMass) > STORAGE_BAY_CAPACITY
+ * (CountSISPieces (STORAGE_BAY) - 1))
+ { // storage bay still needed for the cargo
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+ SetContext (SpaceContext);
+
+ SetFlashRect (NULL);
+
+ if (select)
+ {
+ if (new_slot_piece >= EMPTY_SLOT && old_slot_piece >= EMPTY_SLOT)
+ {
+ new_slot_piece -= EMPTY_SLOT - 1;
+ if (new_slot_piece > CREW_POD)
+ new_slot_piece = PLANET_LANDER;
+ }
+ else
+ {
+ switch (pMS->CurState)
+ {
+ case PLANET_LANDER:
+ ++GLOBAL_SIS (NumLanders);
+ break;
+ case EMPTY_SLOT + 3:
+ --GLOBAL_SIS (NumLanders);
+ break;
+ case FUSION_THRUSTER:
+ case EMPTY_SLOT + 0:
+ GLOBAL_SIS (DriveSlots[pMS->delta_item]) =
+ new_slot_piece;
+ break;
+ case TURNING_JETS:
+ case EMPTY_SLOT + 1:
+ GLOBAL_SIS (JetSlots[pMS->delta_item]) =
+ new_slot_piece;
+ break;
+ default:
+ GLOBAL_SIS (ModuleSlots[pMS->delta_item]) =
+ new_slot_piece;
+ break;
+ }
+
+ if (new_slot_piece < EMPTY_SLOT)
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA,
+ -(GLOBAL (ModuleCost[new_slot_piece])
+ * MODULE_COST_SCALE));
+ else /* if (old_slot_piece < EMPTY_SLOT) */
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA,
+ GLOBAL (ModuleCost[old_slot_piece])
+ * MODULE_COST_SCALE);
+
+ if (pMS->CurState == PLANET_LANDER ||
+ pMS->CurState == EMPTY_SLOT + 3)
+ DisplayLanders (pMS);
+ else
+ {
+ DrawShipPiece (pMS->ModuleFrame, new_slot_piece,
+ pMS->delta_item, FALSE);
+
+ if (new_slot_piece > TURNING_JETS
+ && old_slot_piece > TURNING_JETS)
+ RedistributeFuel ();
+ if (optWhichFonts == OPT_PC)
+ DrawFlagshipStats ();
+ }
+ }
+
+ cancel = FALSE;
+ }
+
+ if (pMS->CurState < EMPTY_SLOT)
+ {
+ pMS->CurState += EMPTY_SLOT - 1;
+ if (pMS->CurState < EMPTY_SLOT)
+ pMS->CurState = EMPTY_SLOT + 3;
+ else if (pMS->CurState > EMPTY_SLOT + 2)
+ pMS->CurState = EMPTY_SLOT + 2;
+ if (cancel)
+ new_slot_piece = pMS->CurState;
+ goto InitFlash;
+ }
+ else if (!cancel)
+ {
+ pMS->CurState = new_slot_piece;
+ goto InitFlash;
+ }
+ else
+ {
+ SetContext (StatusContext);
+ DrawMenuStateStrings (PM_FUEL, pMS->CurState = OUTFIT_MODULES);
+ SetFlashRect (SFR_MENU_3DO);
+
+ pMS->InputFunc = DoOutfit;
+ ClearSISRect (DRAW_SIS_DISPLAY);
+ }
+ }
+ else if (motion)
+ {
+ SIZE NewItem;
+
+ NewItem = NewState < EMPTY_SLOT ? pMS->CurState : pMS->delta_item;
+ do
+ {
+ if (NewState >= EMPTY_SLOT && (PulsedInputState.menu[KEY_MENU_UP]
+ || PulsedInputState.menu[KEY_MENU_DOWN]))
+ {
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (NewState-- == EMPTY_SLOT)
+ NewState = EMPTY_SLOT + 3;
+ }
+ else
+ {
+ if (NewState++ == EMPTY_SLOT + 3)
+ NewState = EMPTY_SLOT;
+ }
+ NewItem = 0;
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 3)
+ {
+ if (NewState == EMPTY_SLOT + 3)
+ NewState = PulsedInputState.menu[KEY_MENU_UP] ?
+ EMPTY_SLOT + 2 : EMPTY_SLOT;
+ if (NewState == EMPTY_SLOT + 2)
+ NewItem = NUM_BOMB_MODULES;
+ }
+ pMS->delta_item = NewItem;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (NewItem-- == FirstItem)
+ NewItem = LastItem;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT] ||
+ PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ if (NewItem++ == LastItem)
+ NewItem = FirstItem;
+ }
+ } while (NewState < EMPTY_SLOT
+ && (GLOBAL (ModuleCost[NewItem]) == 0
+ || (NewItem >= GUN_WEAPON && NewItem <= CANNON_WEAPON
+ && pMS->delta_item > 0 && pMS->delta_item < 13)));
+
+ if (NewState < EMPTY_SLOT)
+ {
+ if (NewItem != pMS->CurState)
+ {
+ pMS->CurState = NewItem;
+ PreUpdateFlashRect ();
+ DrawModuleStrings (pMS, NewItem);
+ PostUpdateFlashRect ();
+ }
+ }
+ else if (NewItem != pMS->delta_item || NewState != pMS->CurState)
+ {
+ SIZE w;
+
+ switch (NewState)
+ {
+ case PLANET_LANDER:
+ case EMPTY_SLOT + 3:
+ new_slot_piece = NewItem < GLOBAL_SIS (NumLanders)
+ ? PLANET_LANDER : (EMPTY_SLOT + 3);
+ break;
+ case FUSION_THRUSTER:
+ case EMPTY_SLOT + 0:
+ new_slot_piece = GLOBAL_SIS (DriveSlots[NewItem]);
+ break;
+ case TURNING_JETS:
+ case EMPTY_SLOT + 1:
+ new_slot_piece = GLOBAL_SIS (JetSlots[NewItem]);
+ break;
+ default:
+ new_slot_piece = GLOBAL_SIS (ModuleSlots[NewItem]);
+ break;
+ }
+
+ SetContext (SpaceContext);
+
+ if (NewState == pMS->CurState)
+ {
+ if (NewState == PLANET_LANDER || NewState == EMPTY_SLOT + 3)
+ w = LANDER_WIDTH;
+ else
+ w = SHIP_PIECE_OFFSET;
+
+ w *= (NewItem - pMS->delta_item);
+ pMS->flash_rect0.corner.x += w;
+ pMS->delta_item = NewItem;
+ }
+ else
+ {
+ pMS->CurState = NewState;
+InitFlash:
+ w = SHIP_PIECE_OFFSET;
+ switch (pMS->CurState)
+ {
+ case PLANET_LANDER:
+ case EMPTY_SLOT + 3:
+ pMS->flash_rect0.corner.x = LANDER_X - 1;
+ pMS->flash_rect0.corner.y = LANDER_Y - 1;
+ pMS->flash_rect0.extent.width = 11 + 2;
+ pMS->flash_rect0.extent.height = 13 + 2;
+
+ w = LANDER_WIDTH;
+ break;
+ case FUSION_THRUSTER:
+ case EMPTY_SLOT + 0:
+ pMS->flash_rect0.corner.x = DRIVE_TOP_X - 1;
+ pMS->flash_rect0.corner.y = DRIVE_TOP_Y - 1;
+ pMS->flash_rect0.extent.width = 8;
+ pMS->flash_rect0.extent.height = 6;
+
+ break;
+ case TURNING_JETS:
+ case EMPTY_SLOT + 1:
+ pMS->flash_rect0.corner.x = JET_TOP_X - 1;
+ pMS->flash_rect0.corner.y = JET_TOP_Y - 1;
+ pMS->flash_rect0.extent.width = 9;
+ pMS->flash_rect0.extent.height = 10;
+
+ break;
+ default:
+ pMS->flash_rect0.corner.x = MODULE_TOP_X - 1;
+ pMS->flash_rect0.corner.y = MODULE_TOP_Y - 1;
+ pMS->flash_rect0.extent.width = SHIP_PIECE_OFFSET + 2;
+ pMS->flash_rect0.extent.height = 34;
+
+ break;
+ }
+
+ w *= pMS->delta_item;
+ pMS->flash_rect0.corner.x += w;
+ }
+
+ DrawModuleStrings (pMS, new_slot_piece);
+ if (pMS->CurState < EMPTY_SLOT)
+ // flash with PC menus too
+ SetFlashRect (SFR_MENU_ANY);
+ else
+ SetFlashRect (&pMS->flash_rect0);
+ }
+ }
+
+ return (TRUE);
+}
+
+static void
+ChangeFuelQuantity (void)
+{
+ int incr = 0; // Fuel increment in fuel points (not units).
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ incr = FUEL_TANK_SCALE; // +1 Unit
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ incr = -FUEL_TANK_SCALE; // -1 Unit
+ else if (PulsedInputState.menu[KEY_MENU_PAGE_UP])
+ incr = FUEL_VOLUME_PER_ROW; // +1 Bar
+ else if (PulsedInputState.menu[KEY_MENU_PAGE_DOWN])
+ incr = -FUEL_VOLUME_PER_ROW; // -1 Bar
+ else
+ return;
+
+ // Clamp incr to what we can afford/hold/have.
+ {
+ const int maxFit = GetFuelTankCapacity () - GLOBAL_SIS (FuelOnBoard);
+ const int maxAfford = GLOBAL_SIS (ResUnits) / GLOBAL (FuelCost);
+ const int minFit = - (int) GLOBAL_SIS (FuelOnBoard);
+
+ if (incr > maxFit)
+ incr = maxFit; // All we can hold.
+
+ if (incr > maxAfford * FUEL_TANK_SCALE)
+ incr = maxAfford * FUEL_TANK_SCALE; // All we can afford.
+
+ if (incr < minFit)
+ incr = minFit; // All we have.
+ }
+
+ if (!incr)
+ {
+ // No more room, not enough RUs, or no fuel left to drain.
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ else
+ {
+ const int cost = (incr / FUEL_TANK_SCALE) * GLOBAL (FuelCost);
+ PreUpdateFlashRect ();
+ DeltaSISGauges (0, incr, -cost);
+ PostUpdateFlashRect ();
+ RedistributeFuel ();
+ }
+
+ { // Make fuel gauge flash.
+ RECT r;
+ CONTEXT oldContext = SetContext (StatusContext);
+ GetGaugeRect (&r, FALSE);
+ SetFlashRect (&r);
+ SetContext (oldContext);
+ }
+}
+
+static void
+onNamingDone (void)
+{
+ // In case player just named a ship, redraw it
+ DrawFlagshipName (FALSE);
+}
+
+BOOLEAN
+DoOutfit (MENU_STATE *pMS)
+{
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ goto ExitOutfit;
+
+ if (!pMS->Initialized)
+ {
+ pMS->InputFunc = DoOutfit;
+ pMS->Initialized = TRUE;
+
+ SetNamingCallback (onNamingDone);
+
+ {
+ COUNT num_frames;
+ STAMP s;
+
+ pMS->CurFrame = CaptureDrawable (
+ LoadGraphic (MODULES_PMAP_ANIM));
+ pMS->hMusic = LoadMusic (OUTFIT_MUSIC);
+ pMS->CurState = OUTFIT_FUEL;
+ pMS->ModuleFrame = CaptureDrawable (
+ LoadGraphic (SISMODS_MASK_PMAP_ANIM));
+ s.origin.x = s.origin.y = 0;
+ s.frame = CaptureDrawable (
+ LoadGraphic (OUTFIT_PMAP_ANIM));
+
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ DrawSISFrame ();
+ DrawSISMessage (GAME_STRING (STARBASE_STRING_BASE + 2));
+ DrawSISTitle (GAME_STRING (STARBASE_STRING_BASE));
+
+ SetContext (SpaceContext);
+
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+
+ for (num_frames = 0; num_frames < NUM_DRIVE_SLOTS; ++num_frames)
+ {
+ BYTE which_piece;
+
+ which_piece = GLOBAL_SIS (DriveSlots[num_frames]);
+ if (which_piece < EMPTY_SLOT)
+ DrawShipPiece (pMS->ModuleFrame, which_piece,
+ num_frames, FALSE);
+ }
+ for (num_frames = 0; num_frames < NUM_JET_SLOTS; ++num_frames)
+ {
+ BYTE which_piece;
+
+ which_piece = GLOBAL_SIS (JetSlots[num_frames]);
+ if (which_piece < EMPTY_SLOT)
+ DrawShipPiece (pMS->ModuleFrame, which_piece,
+ num_frames, FALSE);
+ }
+ for (num_frames = 0; num_frames < NUM_MODULE_SLOTS; ++num_frames)
+ {
+ BYTE which_piece;
+
+ which_piece = GLOBAL_SIS (ModuleSlots[num_frames]);
+ if (which_piece < EMPTY_SLOT)
+ DrawShipPiece (pMS->ModuleFrame, which_piece,
+ num_frames, FALSE);
+ }
+ RedistributeFuel ();
+ DisplayLanders (pMS);
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) < 3)
+ {
+ BYTE ShieldFlags;
+
+ ShieldFlags = GET_GAME_STATE (LANDER_SHIELDS);
+
+ s.frame = SetAbsFrameIndex (pMS->ModuleFrame,
+ GetFrameCount (pMS->ModuleFrame) - 5);
+ if (ShieldFlags & (1 << EARTHQUAKE_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << BIOLOGICAL_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << LIGHTNING_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << LAVASPOT_DISASTER))
+ DrawStamp (&s);
+ }
+
+ DrawMenuStateStrings (PM_FUEL, pMS->CurState);
+ DrawFlagshipName (FALSE);
+ if (optWhichFonts == OPT_PC)
+ DrawFlagshipStats ();
+
+ ScreenTransition (3, NULL);
+ PlayMusic (pMS->hMusic, TRUE, 1);
+ UnbatchGraphics ();
+
+ SetFlashRect (SFR_MENU_3DO);
+
+ GLOBAL_SIS (FuelOnBoard) =
+ (GLOBAL_SIS (FuelOnBoard)
+ + (FUEL_TANK_SCALE >> 1)) / FUEL_TANK_SCALE;
+ GLOBAL_SIS (FuelOnBoard) *= FUEL_TANK_SCALE;
+ }
+
+ SetContext (StatusContext);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_CANCEL]
+ || (PulsedInputState.menu[KEY_MENU_SELECT]
+ && pMS->CurState == OUTFIT_EXIT))
+ {
+ if (pMS->CurState == OUTFIT_DOFUEL)
+ {
+ pMS->CurState = OUTFIT_FUEL;
+ SetFlashRect (SFR_MENU_3DO);
+ }
+ else
+ {
+ExitOutfit:
+ DestroyDrawable (ReleaseDrawable (pMS->CurFrame));
+ pMS->CurFrame = 0;
+ DestroyDrawable (ReleaseDrawable (pMS->ModuleFrame));
+ pMS->ModuleFrame = 0;
+
+ SetNamingCallback (NULL);
+
+ return (FALSE);
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ switch (pMS->CurState)
+ {
+ case OUTFIT_FUEL:
+ {
+ RECT r;
+
+ pMS->CurState = OUTFIT_DOFUEL;
+ SetContext (StatusContext);
+ GetGaugeRect (&r, FALSE);
+ SetFlashRect (&r);
+ break;
+ }
+ case OUTFIT_DOFUEL:
+ pMS->CurState = OUTFIT_FUEL;
+ SetFlashRect (SFR_MENU_3DO);
+ break;
+ case OUTFIT_MODULES:
+ pMS->CurState = EMPTY_SLOT + 2;
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) != 3)
+ pMS->delta_item = 0;
+ else
+ pMS->delta_item = NUM_BOMB_MODULES;
+ pMS->first_item.y = 0;
+ pMS->Initialized = 0;
+ DoInstallModule (pMS);
+ break;
+ case OUTFIT_SAVELOAD:
+ // Clearing FlashRect is not necessary
+ if (!GameOptions ())
+ goto ExitOutfit;
+ DrawMenuStateStrings (PM_FUEL, pMS->CurState);
+ SetFlashRect (SFR_MENU_3DO);
+ break;
+ }
+ }
+ else
+ {
+ switch (pMS->CurState)
+ {
+ case OUTFIT_DOFUEL:
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN |
+ MENU_SOUND_PAGEUP | MENU_SOUND_PAGEDOWN,
+ MENU_SOUND_SELECT | MENU_SOUND_CANCEL);
+ break;
+ default:
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ break;
+ }
+
+ if (pMS->CurState == OUTFIT_DOFUEL)
+ {
+ ChangeFuelQuantity ();
+ SleepThread (ONE_SECOND / 30);
+ }
+ else
+ DoMenuChooser (pMS, PM_FUEL);
+ }
+
+ return (TRUE);
+}
+
diff --git a/src/uqm/pickship.c b/src/uqm/pickship.c
new file mode 100644
index 0000000..3dcced0
--- /dev/null
+++ b/src/uqm/pickship.c
@@ -0,0 +1,501 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "pickship.h"
+
+#include "build.h"
+#include "colors.h"
+#include "controls.h"
+#include "menustat.h"
+#include "supermelee/pickmele.h"
+#include "encount.h"
+#include "battle.h"
+#include "races.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "libs/mathlib.h"
+
+
+#define NUM_PICK_SHIP_ROWS 2
+#define NUM_PICK_SHIP_COLUMNS 6
+
+#define ICON_WIDTH 16
+#define ICON_HEIGHT 16
+
+#define FLAGSHIP_X_OFFS 65
+#define FLAGSHIP_Y_OFFS 4
+#define FLAGSHIP_WIDTH 22
+#define FLAGSHIP_HEIGHT 48
+
+static BOOLEAN
+DoPickBattleShip (MENU_STATE *pMS)
+{
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pMS->CurFrame = 0;
+ return (FALSE);
+ }
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+
+ if (!pMS->Initialized)
+ {
+ pMS->Initialized = TRUE;
+ pMS->InputFunc = DoPickBattleShip;
+
+
+ goto ChangeSelection;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ if ((HSTARSHIP)pMS->CurFrame)
+ {
+ PlayMenuSound (MENU_SOUND_SUCCESS);
+ return (FALSE);
+ }
+ }
+ else
+ {
+ COORD new_row, new_col;
+ int dx = 0, dy = 0;
+ if (PulsedInputState.menu[KEY_MENU_RIGHT]) dx = 1;
+ if (PulsedInputState.menu[KEY_MENU_LEFT]) dx = -1;
+ if (PulsedInputState.menu[KEY_MENU_UP]) dy = -1;
+ if (PulsedInputState.menu[KEY_MENU_DOWN]) dy = 1;
+
+ new_col = pMS->first_item.x + dx;
+ new_row = pMS->first_item.y + dy;
+ if (new_row != pMS->first_item.y
+ || new_col != pMS->first_item.x)
+ {
+ RECT r;
+ TEXT t;
+ COUNT crew_level, max_crew;
+ COUNT ship_index;
+ HSTARSHIP hBattleShip, hNextShip;
+ STARSHIP *StarShipPtr;
+
+ if (new_col < 0)
+ new_col = NUM_PICK_SHIP_COLUMNS;
+ else if (new_col > NUM_PICK_SHIP_COLUMNS)
+ new_col = 0;
+
+ if (new_row < 0)
+ new_row = NUM_PICK_SHIP_ROWS - 1;
+ else if (new_row == NUM_PICK_SHIP_ROWS)
+ new_row = 0;
+
+ PlayMenuSound (MENU_SOUND_MOVE);
+
+
+#ifdef NEVER
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x1D));
+ DrawRectangle (&pMS->flash_rect0);
+#endif /* NEVER */
+ pMS->first_item.y = new_row;
+ pMS->first_item.x = new_col;
+
+ChangeSelection:
+ if (pMS->first_item.x == (NUM_PICK_SHIP_COLUMNS >> 1))
+ {
+ pMS->flash_rect0.corner.x =
+ pMS->flash_rect1.corner.x - 2 + FLAGSHIP_X_OFFS;
+ pMS->flash_rect0.corner.y =
+ pMS->flash_rect1.corner.y - 2 + FLAGSHIP_Y_OFFS;
+ pMS->flash_rect0.extent.width = FLAGSHIP_WIDTH + 4;
+ pMS->flash_rect0.extent.height = FLAGSHIP_HEIGHT + 4;
+
+ hBattleShip = GetTailLink (&race_q[0]); /* Flagship */
+ }
+ else
+ {
+ new_col = pMS->first_item.x;
+ pMS->flash_rect0.corner.x = 5 + pMS->flash_rect1.corner.x - 2
+ + ((ICON_WIDTH + 4) * new_col);
+ if (new_col > (NUM_PICK_SHIP_COLUMNS >> 1))
+ {
+ --new_col;
+ pMS->flash_rect0.corner.x += FLAGSHIP_WIDTH - ICON_WIDTH;
+ }
+ pMS->flash_rect0.corner.y = 16 + pMS->flash_rect1.corner.y - 2
+ + ((ICON_HEIGHT + 4) * pMS->first_item.y);
+ pMS->flash_rect0.extent.width = ICON_WIDTH + 4;
+ pMS->flash_rect0.extent.height = ICON_HEIGHT + 4;
+
+ ship_index = (pMS->first_item.y * NUM_PICK_SHIP_COLUMNS)
+ + new_col;
+
+ for (hBattleShip = GetHeadLink (&race_q[0]);
+ hBattleShip != GetTailLink (&race_q[0]);
+ hBattleShip = hNextShip)
+ {
+ StarShipPtr = LockStarShip (&race_q[0], hBattleShip);
+ if (StarShipPtr->index == ship_index
+ && (StarShipPtr->SpeciesID != NO_ID))
+ {
+ UnlockStarShip (&race_q[0], hBattleShip);
+ break;
+ }
+
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockStarShip (&race_q[0], hBattleShip);
+ }
+
+ if (hBattleShip == GetTailLink (&race_q[0]))
+ hBattleShip = 0;
+ }
+
+ pMS->CurFrame = (FRAME)hBattleShip;
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.x = pMS->flash_rect1.corner.x + 6;
+ r.corner.y = pMS->flash_rect1.corner.y + 5;
+ r.extent.width = ((ICON_WIDTH + 4) * 3) - 4;
+ r.extent.height = 7;
+ DrawFilledRectangle (&r);
+
+ if (hBattleShip == 0)
+ {
+ crew_level = 0;
+ max_crew = 0;
+ // Satisfy compiler.
+ }
+ else
+ {
+ SetContextFont (TinyFont);
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + (r.extent.height - 1);
+ t.align = ALIGN_CENTER;
+
+ StarShipPtr = LockStarShip (&race_q[0], hBattleShip);
+ if (StarShipPtr->captains_name_index == 0)
+ {
+ t.pStr = GLOBAL_SIS (CommanderName);
+ t.CharCount = (COUNT)~0;
+ crew_level = GLOBAL_SIS (CrewEnlisted);
+ max_crew = GetCrewPodCapacity ();
+ }
+ else
+ {
+ STRING locString;
+
+ locString = SetAbsStringTableIndex (
+ StarShipPtr->race_strings,
+ StarShipPtr->captains_name_index);
+ t.pStr = (UNICODE *)GetStringAddress (locString);
+ t.CharCount = GetStringLength (locString);
+ crew_level = StarShipPtr->crew_level;
+ max_crew = StarShipPtr->max_crew;
+ }
+ UnlockStarShip (&race_q[0], hBattleShip);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x0A, 0x00), 0x0C));
+ font_DrawText (&t);
+ SetContextForeGroundColor (BLACK_COLOR);
+ }
+
+ r.corner.x += (ICON_WIDTH + 4)
+ * ((NUM_PICK_SHIP_COLUMNS >> 1) + 1)
+ + FLAGSHIP_WIDTH - ICON_WIDTH;
+ DrawFilledRectangle (&r);
+
+ if (crew_level)
+ {
+ char buf[80];
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ if (crew_level >= max_crew)
+ sprintf (buf, "%u", crew_level);
+ else
+ sprintf (buf, "%u/%u", crew_level, max_crew);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02));
+ font_DrawText (&t);
+ }
+
+ SetFlashRect (NULL);
+ SetFlashRect (&pMS->flash_rect0);
+ }
+ }
+
+ SleepThread (ONE_SECOND / 30);
+
+ return (TRUE);
+}
+
+static HSTARSHIP
+GetArmadaStarShip (void)
+{
+ RECT pick_r;
+ CONTEXT OldContext;
+ HSTARSHIP hBattleShip;
+
+ if (battle_counter[1] == 0)
+ {
+ // No opponents left.
+ return 0;
+ }
+
+// MenuSounds = CaptureSound (LoadSound (MENU_SOUNDS));
+
+OldContext = SetContext (SpaceContext);
+ DrawArmadaPickShip (FALSE, &pick_r);
+
+ {
+ MENU_STATE MenuState;
+
+ MenuState.InputFunc = DoPickBattleShip;
+ MenuState.Initialized = FALSE;
+ MenuState.first_item.x = NUM_PICK_SHIP_COLUMNS >> 1;
+ MenuState.first_item.y = 0;
+ MenuState.CurFrame = 0;
+ MenuState.flash_rect1.corner = pick_r.corner;
+ MenuState.flash_rect1.extent.width = 0;
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ DoInput (&MenuState, FALSE);
+
+ SetFlashRect (NULL);
+
+ hBattleShip = (HSTARSHIP)MenuState.CurFrame;
+ }
+
+ if (hBattleShip)
+ {
+ if (hBattleShip == GetTailLink (&race_q[0]))
+ { // Player chose SIS. There will be no more choices.
+ battle_counter[RPG_PLAYER_NUM] = 1;
+ }
+
+ WaitForSoundEnd (0);
+ }
+
+// DestroySound (ReleaseSound (MenuSounds));
+
+SetContext (OldContext);
+
+ return (hBattleShip);
+}
+
+// Get the next ship to use.
+HSTARSHIP
+GetEncounterStarShip (STARSHIP *LastStarShipPtr, COUNT which_player)
+{
+ if (inHQSpace ())
+ {
+ assert (which_player == RPG_PLAYER_NUM);
+ // SIS for the Hyperspace flight
+ return GetHeadLink (&race_q[which_player]);
+ }
+ else if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ {
+ // Let the player chose their own ship. (May be a computer player).
+ HSTARSHIP hBattleShip;
+
+ if (battle_counter[0] == 0 || battle_counter[1] == 0)
+ { // One side is out of ships. Game over.
+ return 0;
+ }
+
+ if (!GetNextMeleeStarShip (which_player, &hBattleShip))
+ return 0;
+
+ return hBattleShip;
+ }
+ else
+ {
+ // Full game.
+ if (which_player == RPG_PLAYER_NUM)
+ { // Human player in a full game.
+ if (LastStarShipPtr == 0 && battle_counter[which_player] == 1)
+ { // First time picking a ship and player has no escorts
+ // SIS is the last ship in queue (though there is only one)
+ return GetTailLink (&race_q[which_player]);
+ }
+ else if (battle_counter[which_player])
+ { // Player still has ships left
+ return GetArmadaStarShip ();
+ }
+ else if (LastStarShipPtr != 0)
+ { // last ship was the flagship
+#define RUN_AWAY_FUEL_COST (5 * FUEL_TANK_SCALE)
+ if (LastStarShipPtr->crew_level == 0)
+ { // Died in the line of duty
+ GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
+ }
+ else
+ { // Player ran away
+ if (GLOBAL_SIS (FuelOnBoard) > RUN_AWAY_FUEL_COST)
+ GLOBAL_SIS (FuelOnBoard) -= RUN_AWAY_FUEL_COST;
+ else
+ GLOBAL_SIS (FuelOnBoard) = 0;
+ }
+ }
+ return 0;
+ }
+ else
+ { // NPC player in a full game
+ if (FleetIsInfinite (which_player))
+ {
+ if (LastStarShipPtr != 0)
+ { // The current STARSHIP is reused for the next one;
+ // update with new info
+ // XXX: Note that if Syreen had a homeworld you could
+ // fight, all Syreen ships there would be crewed to
+ // the maximum, instead of the normal level
+ LastStarShipPtr->crew_level = LastStarShipPtr->max_crew;
+ LastStarShipPtr->playerNr = which_player;
+ LastStarShipPtr->captains_name_index = PickCaptainName ();
+ }
+ battle_counter[which_player]++;
+
+ return GetHeadLink (&race_q[which_player]);
+ }
+
+ // Get the next ship for the computer
+ if (LastStarShipPtr != 0)
+ return _GetSuccLink (LastStarShipPtr);
+
+ // Get the very first ship for the computer
+ return GetHeadLink (&race_q[which_player]);
+ }
+ }
+}
+
+void
+DrawArmadaPickShip (BOOLEAN draw_salvage_frame, RECT *pPickRect)
+{
+#define PICK_NAME_HEIGHT 6
+ //COUNT i;
+ HSTARSHIP hBattleShip, hNextShip;
+ STARSHIP *StarShipPtr;
+ RECT r, pick_r;
+ STAMP s;
+ TEXT t;
+ CONTEXT OldContext;
+ FRAME PickFrame;
+
+ OldContext = SetContext (SpaceContext);
+
+ PickFrame = CaptureDrawable (LoadGraphic (SC2_PICK_PMAP_ANIM));
+
+ BatchGraphics ();
+
+ s.frame = PickFrame;
+ SetFrameHot (s.frame, MAKE_HOT_SPOT (0, 0));
+ GetFrameRect (s.frame, &pick_r);
+ GetContextClipRect (&r);
+ pick_r.corner.x = (r.extent.width >> 1) - (pick_r.extent.width >> 1);
+ pick_r.corner.y = (r.extent.height >> 1) - (pick_r.extent.height >> 1);
+
+ if (!draw_salvage_frame)
+ *pPickRect = pick_r;
+ else
+ {
+ s.origin.x = r.extent.width >> 1;
+ s.frame = IncFrameIndex (s.frame);
+ SetFrameHot (s.frame, MAKE_HOT_SPOT (0, 0));
+ GetFrameRect (s.frame, &r);
+ s.origin.x -= r.extent.width >> 1;
+ s.origin.y = pick_r.corner.y - (r.extent.height >> 1);
+ DrawStamp (&s);
+ s.frame = DecFrameIndex (s.frame);
+ pick_r.corner.y = s.origin.y + r.extent.height;
+
+ r.corner.x = pick_r.corner.x;
+ r.corner.y = s.origin.y;
+ *pPickRect = r;
+ }
+ s.origin = pick_r.corner;
+ DrawStamp (&s);
+
+ t.baseline.x = pick_r.corner.x + (pick_r.extent.width >> 1);
+ t.baseline.y = pick_r.corner.y + pick_r.extent.height - 5;
+ t.align = ALIGN_CENTER;
+ t.pStr = GLOBAL_SIS (ShipName);
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x12, 0x12, 0x12), 0x17));
+ SetContextFont (StarConFont);
+ font_DrawText (&t);
+
+ r.extent.width = ICON_WIDTH;
+ r.extent.height = ICON_HEIGHT;
+ for (hBattleShip = GetHeadLink (&race_q[0]);
+ hBattleShip != 0; hBattleShip = hNextShip)
+ {
+ StarShipPtr = LockStarShip (&race_q[0], hBattleShip);
+
+ if (StarShipPtr->captains_name_index)
+ { // Escort ship, not SIS
+ COUNT ship_index;
+
+ ship_index = StarShipPtr->index;
+
+ s.origin.x = pick_r.corner.x
+ + (5 + ((ICON_WIDTH + 4)
+ * (ship_index % NUM_PICK_SHIP_COLUMNS)));
+ if ((ship_index % NUM_PICK_SHIP_COLUMNS) >=
+ (NUM_PICK_SHIP_COLUMNS >> 1))
+ s.origin.x += FLAGSHIP_WIDTH + 4;
+ s.origin.y = pick_r.corner.y
+ + (16 + ((ICON_HEIGHT + 4)
+ * (ship_index / NUM_PICK_SHIP_COLUMNS)));
+ s.frame = StarShipPtr->icons;
+ r.corner = s.origin;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ if ((StarShipPtr->SpeciesID != NO_ID) || (StarShipPtr->crew_level == 0))
+ {
+ DrawStamp (&s);
+ if (StarShipPtr->SpeciesID == NO_ID)
+ {
+ /* Dead ship - mark with an X. */
+ s.origin.x -= 1;
+ s.frame = SetAbsFrameIndex (StatusFrame, 3);
+ DrawStamp (&s);
+ }
+ }
+ else
+ {
+ /* Ship ran away */
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01));
+ DrawFilledStamp (&s);
+ }
+ }
+
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockStarShip (&race_q[0], hBattleShip);
+ }
+
+ UnbatchGraphics ();
+
+ DestroyDrawable (ReleaseDrawable (PickFrame));
+
+ SetContext (OldContext);
+}
+
diff --git a/src/uqm/pickship.h b/src/uqm/pickship.h
new file mode 100644
index 0000000..f5a55b3
--- /dev/null
+++ b/src/uqm/pickship.h
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_PICKSHIP_H_INCL_
+#define UQM_PICKSHIP_H_INCL_
+
+#include "libs/compiler.h"
+#include "races.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern HSTARSHIP GetEncounterStarShip (STARSHIP *LastStarShipPtr,
+ COUNT which_player);
+extern void DrawArmadaPickShip (BOOLEAN draw_salvage_frame, RECT *pPickRect);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PICKSHIP_H_INCL_ */
diff --git a/src/uqm/plandata.c b/src/uqm/plandata.c
new file mode 100644
index 0000000..a79c2d5
--- /dev/null
+++ b/src/uqm/plandata.c
@@ -0,0 +1,1850 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gendef.h"
+#include "resinst.h"
+#include "planets/planets.h"
+#include "planets/elemdata.h"
+
+
+STAR_DESC starmap_array[] =
+{
+ // postfix name index (like 'Normae')
+ // prefix name index (like 'Alpha') |
+ // alien presence | |
+ // owner (unused) | | |
+ // x, y star type colour | | | |
+ {{5007, 35}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 74},
+ {{ 708, 41}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 7, 91},
+ {{4714, 78}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 7, 74},
+ {{2187, 83}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 0, 126},
+ {{2814, 89}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 82},
+ {{4244, 91}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 0, 125},
+ {{5652, 98}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 124},
+ {{2939, 116}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 82},
+ {{2771, 146}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 82},
+ {{5313, 150}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 6, 73},
+ {{ 265, 156}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 92},
+ {{4529, 169}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 8, 74},
+ {{4911, 180}, MAKE_STAR (GIANT_STAR, ORANGE_BODY, -1), 0, 1, 74},
+ {{4747, 221}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 74},
+ {{9708, 250}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 0, 112},
+ {{4861, 262}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 74},
+ {{2908, 269}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), SHOFIXTI_DEFINED, 4, 82},
+ {{1855, 270}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 5, 81},
+ {{7958, 270}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 8},
+ {{5160, 280}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 73},
+ {{ 570, 289}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 92},
+ {{4923, 294}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), YEHAT_DEFINED, 3, 74},
+ {{2820, 301}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 5, 82},
+ {{7934, 318}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 8},
+ {{8062, 318}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 8},
+ {{1116, 334}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 91},
+ {{ 803, 337}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 91},
+ {{1787, 338}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 81},
+ {{ 877, 340}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 91},
+ {{5338, 355}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 5, 73},
+ {{5039, 373}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 73},
+ {{ 843, 380}, MAKE_STAR (GIANT_STAR, ORANGE_BODY, -1), 0, 1, 91},
+ {{4872, 408}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 74},
+ {{1740, 423}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 7, 81},
+ {{4596, 429}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 9, 74},
+ {{ 843, 431}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 91},
+ {{2156, 440}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 81},
+ {{2004, 441}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 81},
+ {{ 530, 442}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 92},
+ {{ 958, 468}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 5, 91},
+ {{2058, 475}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 81},
+ {{ 304, 477}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 1, 92},
+ {{ 522, 525}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), PKUNK_DEFINED, 3, 92},
+ {{2100, 554}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 81},
+ {{ 134, 565}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 92},
+ {{6858, 577}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), MYCON_TRAP_DEFINED, 0, 123},
+ {{5014, 584}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 73},
+ {{5256, 608}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 73},
+ {{2411, 718}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 9},
+ {{2589, 741}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 9},
+ {{ 675, 742}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 8, 91},
+ {{9292, 750}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 5},
+ {{1463, 779}, MAKE_STAR (GIANT_STAR, RED_BODY, -1), 0, 6, 80},
+ {{3089, 782}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 9},
+ {{2854, 787}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 9},
+ {{3333, 801}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 9},
+ {{9237, 821}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 5, 5},
+ {{9339, 843}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 5},
+ {{ 242, 857}, MAKE_STAR (GIANT_STAR, ORANGE_BODY, -1), 0, 3, 90},
+ {{1515, 866}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 5, 80},
+ {{4770, 895}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 5, 75},
+ {{1412, 905}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 80},
+ {{4681, 916}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), RAINBOW_DEFINED, 6, 75},
+ {{9333, 937}, MAKE_STAR (SUPER_GIANT_STAR, YELLOW_BODY, -1), MELNORME0_DEFINED, 2, 5},
+ {{9419, 942}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 5},
+ {{ 230, 952}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 90},
+ {{ 146, 955}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 90},
+ {{4873, 968}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 75},
+ {{1559, 993}, MAKE_STAR (SUPER_GIANT_STAR, RED_BODY, -1), MELNORME1_DEFINED, 1, 80},
+ {{1895, 1041}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 93},
+ {{4337, 1066}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 75},
+ {{3732, 1067}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 0, 122},
+ {{1579, 1115}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 80},
+ {{4875, 1145}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 75},
+ {{4604, 1187}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 75},
+ {{5812, 1208}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 72},
+ {{1312, 1260}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 80},
+ {{1916, 1270}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 93},
+ {{6562, 1270}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 0, 121},
+ {{ 416, 1301}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 0, 120},
+ {{3958, 1354}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 89},
+ {{4000, 1363}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 89},
+ {{1752, 1450}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), SOL_DEFINED, 0, 129},
+ {{2187, 1500}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 0, 127},
+ {{1806, 1507}, MAKE_STAR (GIANT_STAR, WHITE_BODY, -1), 0, 0, 128},
+ {{5708, 1520}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 72},
+ {{9469, 1548}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 6},
+ {{4333, 1562}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 88},
+ {{6041, 1562}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 72},
+ {{9375, 1583}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 6},
+ {{2881, 1614}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 96},
+ {{6083, 1625}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 72},
+ {{4250, 1645}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 88},
+ {{ 650, 1646}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 7, 85},
+ {{9477, 1670}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 6},
+ {{2840, 1676}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 96},
+ {{9541, 1687}, MAKE_STAR (GIANT_STAR, RED_BODY, -1), 0, 4, 6},
+ {{7395, 1687}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 69},
+ {{4333, 1687}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), VUX_DEFINED, 2, 88},
+ {{9559, 1735}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 6},
+ {{ 736, 1737}, MAKE_STAR (GIANT_STAR, BLUE_BODY, -1), 0, 6, 85},
+ {{1601, 1746}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 94},
+ {{7395, 1750}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 69},
+ {{ 951, 1770}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 1, 85},
+ {{1666, 1812}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 94},
+ {{7187, 1833}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 69},
+ {{ 705, 1838}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 5, 85},
+ {{1140, 1847}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 85},
+ {{6467, 1878}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 71},
+ {{2791, 1895}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 96},
+ {{6500, 1916}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 71},
+ {{5458, 1916}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 0, 119},
+ {{1048, 1919}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 85},
+ {{3678, 1926}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 99},
+ {{3345, 1931}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), START_COLONY_DEFINED, 0, 98},
+ {{8187, 1937}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 4, 7},
+ {{3352, 1940}, MAKE_STAR (SUPER_GIANT_STAR, WHITE_BODY, -1), MELNORME2_DEFINED, 0, 97},
+ {{ 977, 1953}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 85},
+ {{4221, 1986}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), MAIDENS_DEFINED, 1, 100},
+ {{4500, 2000}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 100},
+ {{6833, 2000}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 70},
+ {{8163, 2009}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 3, 7},
+ {{8080, 2011}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 7},
+ {{6036, 2035}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 4, 71},
+ {{6479, 2062}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), EGG_CASE1_DEFINED, 3, 71},
+ {{2104, 2083}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), ZOQ_SCOUT_DEFINED, 0, 118},
+ {{8062, 2083}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 7},
+ {{ 270, 2187}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 79},
+ {{6500, 2208}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 6, 71},
+ {{6291, 2208}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), MYCON_DEFINED, 5, 71},
+ {{ 125, 2229}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 79},
+ {{ 312, 2250}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 79},
+ {{3884, 2262}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 99},
+ {{ 742, 2268}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), CHMMR_DEFINED, 0, 117},
+ {{2306, 2285}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 95},
+ {{2402, 2309}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 95},
+ {{6395, 2312}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), SUN_DEVICE_DEFINED, 2, 12},
+ {{8875, 2312}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 9, 61},
+ {{3551, 2320}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 99},
+ {{6208, 2333}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 12},
+ {{3354, 2354}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 99},
+ {{9909, 2359}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 0, 111},
+ {{2298, 2385}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 95},
+ {{7020, 2395}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 70},
+ {{9038, 2407}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 61},
+ {{9375, 2416}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 8, 61},
+ {{6500, 2458}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 6, 12},
+ {{ 217, 2509}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 78},
+ {{3641, 2512}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 86},
+ {{5625, 2520}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 14},
+ {{3713, 2537}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), ORZ_DEFINED, 3, 86},
+ {{3587, 2566}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), ANDROSYNTH_DEFINED, 7, 86},
+ {{9291, 2583}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 61},
+ {{3654, 2587}, MAKE_STAR (SUPER_GIANT_STAR, GREEN_BODY, -1), MELNORME3_DEFINED, 1, 86},
+ {{3721, 2619}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), TAALO_PROTECTOR_DEFINED, 4, 86},
+ {{5791, 2625}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 14},
+ {{6416, 2625}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 12},
+ {{6008, 2631}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), EGG_CASE0_DEFINED, 2, 14},
+ {{3608, 2637}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 86},
+ {{3499, 2648}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 87},
+ {{9479, 2666}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 61},
+ {{3668, 2666}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 5, 86},
+ {{ 229, 2666}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 78},
+ {{8895, 2687}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 7, 61},
+ {{ 138, 2696}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 78},
+ {{5375, 2729}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 0, 116},
+ {{6354, 2729}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), EGG_CASE2_DEFINED, 3, 12},
+ {{6458, 2750}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 12},
+ {{2458, 2750}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 106},
+ {{ 351, 2758}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 78},
+ {{7083, 2770}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 70},
+ {{3759, 2778}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 87},
+ {{9333, 2791}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 61},
+ {{3400, 2804}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 87},
+ {{9469, 2806}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), DRUUGE_DEFINED, 6, 61},
+ {{3619, 2830}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 87},
+ {{2208, 2854}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 7, 106},
+ {{9250, 2854}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 4, 61},
+ {{ 672, 2863}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 7, 78},
+ {{ 167, 2875}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 6, 78},
+ {{4030, 2887}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 105},
+ {{ 384, 2900}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 78},
+ {{2727, 2951}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 5, 106},
+ {{4645, 2958}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 105},
+ {{5625, 2958}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 13},
+ {{8270, 2958}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 66},
+ {{8291, 2979}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 66},
+ {{6020, 2979}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), RAINBOW_DEFINED, 3, 13},
+ {{6562, 3020}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 4, 70},
+ {{2011, 3043}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 8, 106},
+ {{8125, 3083}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 66},
+ {{2354, 3166}, MAKE_STAR (GIANT_STAR, YELLOW_BODY, -1), 0, 4, 106},
+ {{3833, 3187}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 105},
+ {{5812, 3208}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 1, 13},
+ {{9000, 3250}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 0, 113},
+ {{ 291, 3250}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 84},
+ {{ 501, 3259}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 84},
+ {{ 791, 3270}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 84},
+ {{2354, 3291}, MAKE_STAR (SUPER_GIANT_STAR, RED_BODY, -1), MELNORME4_DEFINED, 1, 106},
+ {{1104, 3333}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 4, 84},
+ {{2687, 3333}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 106},
+ {{3187, 3375}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 107},
+ {{1758, 3418}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 108},
+ {{2520, 3437}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 106},
+ {{8437, 3458}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 64},
+ {{8770, 3458}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 64},
+ {{3000, 3500}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 107},
+ {{ 149, 3519}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 76},
+ {{8791, 3541}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 64},
+ {{2148, 3551}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 109},
+ {{7375, 3562}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 0, 115},
+ {{9312, 3562}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 63},
+ {{9599, 3583}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 63},
+ {{9375, 3604}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 63},
+ {{ 90, 3614}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 6, 76},
+ {{2770, 3625}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 107},
+ {{8708, 3625}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 64},
+ {{ 267, 3645}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 76},
+ {{1604, 3645}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 108},
+ {{2274, 3663}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 109},
+ {{ 229, 3666}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), ILWRATH_DEFINED, 1, 76},
+ {{3083, 3674}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 107},
+ {{2416, 3687}, MAKE_STAR (GIANT_STAR, ORANGE_BODY, -1), SPATHI_DEFINED, 5, 109},
+ {{9333, 3708}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 63},
+ {{2250, 3708}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 3, 109},
+ {{ 288, 3735}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 76},
+ {{2354, 3741}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 109},
+ {{2583, 3750}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 6, 109},
+ {{4125, 3770}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), SYREEN_DEFINED, 0, 114},
+ {{ 166, 3770}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 4, 76},
+ {{6270, 3833}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 10},
+ {{2145, 3916}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 110},
+ {{6125, 3937}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 10},
+ {{6291, 3937}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 9, 10},
+ {{5937, 3937}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), SHIP_VAULT_DEFINED, 5, 10},
+ {{2479, 3958}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 7, 109},
+ {{ 926, 3972}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 83},
+ {{2062, 3991}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 110},
+ {{5895, 4020}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 8, 10},
+ {{ 285, 4020}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 77},
+ {{6062, 4041}, MAKE_STAR (GIANT_STAR, YELLOW_BODY, -1), 0, 1, 10},
+ {{2875, 4041}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 20},
+ {{8645, 4062}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 65},
+ {{ 860, 4065}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 83},
+ {{5958, 4083}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 10},
+ {{3038, 4083}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 20},
+ {{ 291, 4104}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 77},
+ {{6166, 4125}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 6, 10},
+ {{9812, 4145}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 62},
+ {{8520, 4166}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 65},
+ {{9573, 4182}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 62},
+ {{ 500, 4187}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 77},
+ {{2145, 4208}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 110},
+ {{6208, 4229}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 7, 10},
+ {{2812, 4250}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 7, 20},
+ {{2937, 4306}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 4, 20},
+ {{9416, 4395}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 62},
+ {{2875, 4479}, MAKE_STAR (GIANT_STAR, WHITE_BODY, -1), 0, 1, 20},
+ {{ 250, 4583}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 26},
+ {{7250, 4583}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 68},
+ {{ 479, 4583}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 26},
+ {{5708, 4604}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 0, 104},
+ {{ 479, 4645}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 26},
+ {{2895, 4687}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 20},
+ {{2708, 4708}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 5, 20},
+ {{ 562, 4708}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 26},
+ {{ 416, 4717}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 26},
+ {{5094, 4931}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 11},
+ {{9000, 5000}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 67},
+ {{8958, 5000}, MAKE_STAR (GIANT_STAR, BLUE_BODY, -1), 0, 1, 67},
+ {{5006, 5011}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 11},
+ {{7312, 5062}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 68},
+ {{3679, 5068}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 17},
+ {{9062, 5083}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 67},
+ {{7416, 5083}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), RAINBOW_DEFINED, 3, 68},
+ {{5155, 5122}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 11},
+ {{3875, 5145}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 17},
+ {{4937, 5145}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 11},
+ {{2979, 5166}, MAKE_STAR (GIANT_STAR, ORANGE_BODY, -1), 0, 1, 15},
+ {{3035, 5178}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 15},
+ {{3994, 5185}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 17},
+ {{3541, 5187}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 17},
+ {{5977, 5246}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 102},
+ {{3770, 5250}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 17},
+ {{1520, 5261}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 55},
+ {{1613, 5279}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 55},
+ {{7020, 5291}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 68},
+ {{1416, 5315}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 55},
+ {{2993, 5318}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 15},
+ {{1425, 5404}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 55},
+ {{1854, 5416}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 5, 55},
+ {{3625, 5437}, MAKE_STAR (GIANT_STAR, GREEN_BODY, -1), 0, 1, 16},
+ {{3416, 5437}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 16},
+ {{4000, 5437}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), ZOQFOT_DEFINED, 1, 18},
+ {{6270, 5479}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 102},
+ {{3583, 5479}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 16},
+ {{4083, 5513}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 18},
+ {{2159, 5614}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 6, 55},
+ {{3937, 5625}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 18},
+ {{6014, 5632}, MAKE_STAR (GIANT_STAR, BLUE_BODY, -1), 0, 1, 21},
+ {{ 250, 5687}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 25},
+ {{3625, 5750}, MAKE_STAR (GIANT_STAR, RED_BODY, -1), 0, 2, 19},
+ {{ 371, 5772}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 25},
+ {{6107, 5785}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 21},
+ {{9645, 5791}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), BURVIXESE_DEFINED, 0, 130},
+ {{1545, 5818}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 54},
+ {{3750, 5833}, MAKE_STAR (GIANT_STAR, GREEN_BODY, -1), 0, 1, 19},
+ {{6301, 5875}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 5, 21},
+ {{1923, 5878}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 54},
+ {{4625, 5895}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 131},
+ {{ 152, 5900}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 25},
+ {{5437, 5916}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 33},
+ {{1714, 5926}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 54},
+ {{6200, 5935}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), SAMATRA_DEFINED, 4, 21},
+ {{6429, 5958}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 7, 21},
+ {{4729, 5958}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 131},
+ {{1978, 5968}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), TALKING_PET_DEFINED, 2, 54},
+ {{ 395, 5979}, MAKE_STAR (GIANT_STAR, GREEN_BODY, -1), 0, 1, 22},
+ {{ 563, 5980}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 22},
+ {{ 456, 5989}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 6, 22},
+ {{4625, 6000}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 131},
+ {{6166, 6000}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 3, 21},
+ {{6496, 6032}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 6, 21},
+ {{2228, 6038}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 12, 54},
+ {{4583, 6041}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 131},
+ {{1558, 6058}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 6, 54},
+ {{1902, 6065}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 54},
+ {{2159, 6073}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 11, 54},
+ {{ 365, 6093}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 22},
+ {{ 541, 6145}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 22},
+ {{2200, 6176}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 10, 54},
+ {{ 729, 6208}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 23},
+ {{5250, 6229}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 33},
+ {{8166, 6250}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 40},
+ {{6215, 6255}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 8, 21},
+ {{ 437, 6270}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 22},
+ {{5583, 6291}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 33},
+ {{1881, 6308}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 8, 54},
+ {{1795, 6329}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 7, 54},
+ {{2118, 6379}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 9, 54},
+ {{ 750, 6458}, MAKE_STAR (GIANT_STAR, WHITE_BODY, -1), 0, 1, 23},
+ {{3716, 6458}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 30},
+ {{1360, 6489}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 56},
+ {{7333, 6500}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 40},
+ {{3770, 6500}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 30},
+ {{4500, 6500}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 0, 37},
+ {{ 187, 6520}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 24},
+ {{ 125, 6541}, MAKE_STAR (GIANT_STAR, RED_BODY, -1), 0, 1, 24},
+ {{7812, 6562}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 40},
+ {{ 770, 6602}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 23},
+ {{5910, 6624}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 29},
+ {{ 208, 6625}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 24},
+ {{2604, 6645}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 101},
+ {{1578, 6668}, MAKE_STAR (SUPER_GIANT_STAR, GREEN_BODY, -1), MELNORME5_DEFINED, 1, 56},
+ {{5479, 6687}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 33},
+ {{ 375, 6716}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 24},
+ {{ 312, 6728}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 24},
+ {{6020, 6729}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 29},
+ {{5062, 6750}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 10, 28},
+ {{4208, 6854}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 31},
+ {{5145, 6875}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 9, 28},
+ {{4291, 6937}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 31},
+ {{5145, 6958}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 8, 28},
+ {{7208, 7000}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 39},
+ {{8625, 7000}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), RAINBOW_DEFINED, 1, 41},
+ {{4955, 7034}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 28},
+ {{4895, 7041}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 28},
+ {{4971, 7104}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 28},
+ {{8666, 7104}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 41},
+ {{4854, 7125}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 28},
+ {{5083, 7145}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 7, 28},
+ {{7360, 7184}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 39},
+ {{1020, 7187}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 58},
+ {{3875, 7187}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 32},
+ {{4879, 7201}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 5, 28},
+ {{4958, 7229}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 6, 28},
+ {{7125, 7250}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 39},
+ {{7532, 7258}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 39},
+ {{2416, 7291}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 101},
+ {{3854, 7291}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 2, 32},
+ {{9687, 7333}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 3, 44},
+ {{ 395, 7458}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), RAINBOW_DEFINED, 2, 60},
+ {{4895, 7458}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 36},
+ {{4645, 7479}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 36},
+ {{6940, 7514}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 11, 39},
+ {{7443, 7538}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 6, 39},
+ {{6479, 7541}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 0, 38},
+ {{7208, 7541}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 1, 39},
+ {{5791, 7583}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 3, 34},
+ {{ 333, 7625}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 60},
+ {{5958, 7645}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 34},
+ {{1041, 7708}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 58},
+ {{5875, 7729}, MAKE_STAR (SUPER_GIANT_STAR, YELLOW_BODY, -1), MELNORME6_DEFINED, 1, 34},
+ {{1125, 7791}, MAKE_STAR (GIANT_STAR, BLUE_BODY, -1), 0, 1, 58},
+ {{4979, 7791}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 2, 36},
+ {{4958, 7791}, MAKE_STAR (GIANT_STAR, WHITE_BODY, -1), 0, 1, 36},
+ {{6889, 7803}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 10, 39},
+ {{7200, 7849}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 8, 39},
+ {{7395, 7854}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 7, 39},
+ {{9437, 7854}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 44},
+ {{2836, 7857}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), RAINBOW_DEFINED, 5, 53},
+ {{5375, 7875}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 35},
+ {{6187, 7875}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 7, 35},
+ {{6041, 7916}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 35},
+ {{5979, 7979}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 35},
+ {{7083, 7993}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 9, 39},
+ {{3270, 8000}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 8, 53},
+ {{6104, 8000}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 6, 35},
+ {{ 687, 8000}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 59},
+ {{ 562, 8000}, MAKE_STAR (GIANT_STAR, GREEN_BODY, -1), URQUAN_WRECK_DEFINED, 1, 59},
+ {{5645, 8020}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 35},
+ {{1395, 8041}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 58},
+ {{8229, 8041}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 43},
+ {{2518, 8056}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 3, 53},
+ {{5875, 8062}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 35},
+ {{8416, 8083}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 43},
+ {{9000, 8229}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 44},
+ {{3562, 8250}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 9, 53},
+ {{5437, 8270}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), RAINBOW_DEFINED, 5, 48},
+ {{1520, 8333}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 58},
+ {{2771, 8351}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 53},
+ {{2535, 8358}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), THRADD_DEFINED, 4, 53},
+ {{3151, 8390}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 7, 53},
+ {{2362, 8395}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 11, 53},
+ {{2822, 8395}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 53},
+ {{5500, 8395}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 48},
+ {{2536, 8504}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 4, 2},
+ {{2582, 8507}, MAKE_STAR (SUPER_GIANT_STAR, YELLOW_BODY, -1), MELNORME7_DEFINED, 1, 2},
+ {{8625, 8562}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 6, 3},
+ {{4375, 8562}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 0, 50},
+ {{2593, 8569}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 2},
+ {{2562, 8572}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 2},
+ {{8492, 8578}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 7, 3},
+ {{1125, 8583}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 6, 58},
+ {{8073, 8588}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 46},
+ {{8560, 8638}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 3},
+ {{8750, 8645}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 5, 3},
+ {{5562, 8645}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 1, 48},
+ {{2588, 8653}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 14, 53},
+ {{2458, 8666}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 10, 53},
+ {{7666, 8666}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), RAINBOW_DEFINED, 2, 46},
+ {{2776, 8673}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), AQUA_HELIX_DEFINED, 6, 53},
+ {{8630, 8693}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), UTWIG_DEFINED, 2, 3},
+ {{2310, 8702}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 12, 53},
+ {{ 437, 8770}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 57},
+ {{8534, 8797}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), RAINBOW_DEFINED, 3, 3},
+ {{8588, 8812}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 4, 3},
+ {{7187, 8812}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 3, 46},
+ {{5475, 8823}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 48},
+ {{3050, 8833}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 4, 1},
+ {{2831, 8854}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 1},
+ {{2300, 8861}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 13, 53},
+ {{ 479, 8875}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 57},
+ {{2706, 8910}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 2, 1},
+ {{ 333, 8916}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 57},
+ {{2535, 8917}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 5, 1},
+ {{8322, 8934}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 1, 45},
+ {{8249, 8958}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 45},
+ {{8375, 8958}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 2, 45},
+ {{5645, 8979}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 4, 48},
+ {{2687, 9000}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 1},
+ {{8375, 9041}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 45},
+ {{9960, 9042}, MAKE_STAR (GIANT_STAR, WHITE_BODY, -1), RAINBOW_DEFINED, 0, 42},
+ {{7354, 9062}, MAKE_STAR (DWARF_STAR, BLUE_BODY, -1), 0, 1, 47},
+ {{7833, 9083}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 6, 47},
+ {{2581, 9105}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 6, 1},
+ {{7545, 9107}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 3, 47},
+ {{7414, 9124}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), SUPOX_DEFINED, 2, 47},
+ {{8500, 9125}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 45},
+ {{ 104, 9125}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 5, 27},
+ {{7889, 9181}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 7, 47},
+ {{7791, 9187}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 47},
+ {{7791, 9229}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 5, 47},
+ {{4812, 9270}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 0, 51},
+ {{8500, 9372}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), BOMB_DEFINED, 6, 45},
+ {{7255, 9374}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 11, 45},
+ {{8458, 9393}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 7, 45},
+ {{1000, 9395}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 4, 27},
+ {{5711, 9475}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 2, 49},
+ {{ 62, 9479}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 3, 27},
+ {{5989, 9496}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 7, 49},
+ {{8000, 9505}, MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 9, 45},
+ {{5329, 9538}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 5, 49},
+ {{2916, 9541}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 103},
+ {{8296, 9548}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 8, 45},
+ {{5600, 9552}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 1, 49},
+ {{7664, 9589}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 10, 45},
+ {{6125, 9604}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 8, 49},
+ {{9144, 9686}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 4, 4},
+ {{5781, 9711}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 3, 49},
+ {{5229, 9729}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 6, 49},
+ {{9120, 9741}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 3, 4},
+ {{9186, 9741}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 2, 4},
+ {{9159, 9745}, MAKE_STAR (SUPER_GIANT_STAR, BLUE_BODY, -1), MELNORME8_DEFINED, 1, 4},
+ {{ 333, 9750}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 0, 0},
+ {{9147, 9790}, MAKE_STAR (DWARF_STAR, ORANGE_BODY, -1), 0, 5, 4},
+ {{5704, 9795}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), VUX_BEAST_DEFINED, 4, 49},
+ {{ 333, 9812}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), SLYLANDRO_DEFINED, 2, 27},
+ {{1020, 9937}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 7, 27},
+ {{ 83, 9979}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 6, 27},
+ {{1937, 9979}, MAKE_STAR (DWARF_STAR, RED_BODY, -1), 0, 1, 103},
+ {{4395, 9979}, MAKE_STAR (DWARF_STAR, GREEN_BODY, -1), 0, 0, 52},
+
+ {{MAX_X_UNIVERSE << 1, MAX_Y_UNIVERSE << 1}, 0, 0, 0, 0},
+
+ // QuasiSpace locations
+#define VORTEX_SCALE 20
+ {{(-12* VORTEX_SCALE) + 5000, (-21 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{( 1 * VORTEX_SCALE) + 5000, (-20 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(-16 * VORTEX_SCALE) + 5000, (-18 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{( 8 * VORTEX_SCALE) + 5000, (-17 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{( 3 * VORTEX_SCALE) + 5000, (-13 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(-21 * VORTEX_SCALE) + 5000, (-4 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(-4 * VORTEX_SCALE) + 5000, (-4 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(-12 * VORTEX_SCALE) + 5000, (-2 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(-26 * VORTEX_SCALE) + 5000, (2 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(-17 * VORTEX_SCALE) + 5000, (7 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(10 * VORTEX_SCALE) + 5000, (7 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(15 * VORTEX_SCALE) + 5000, (14 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(22 * VORTEX_SCALE) + 5000, (16 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(-6 * VORTEX_SCALE) + 5000, (19 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+ {{(10 * VORTEX_SCALE) + 5000, (20 * VORTEX_SCALE) + 5000},
+ MAKE_STAR (DWARF_STAR, WHITE_BODY, -1), 0, 0, 132},
+
+ {{6134, 5900}, MAKE_STAR (DWARF_STAR, YELLOW_BODY, -1), 0, 0, 132},
+
+ {{MAX_X_UNIVERSE << 1, MAX_Y_UNIVERSE << 1}, 0, 0, 0, 0},
+};
+
+const BYTE element_array[NUMBER_OF_ELEMENTS] =
+{
+ COMMON, /* HYDROGEN */
+ COMMON, /* HELIUM */
+ COMMON, /* LITHIUM */
+ BASE_METAL, /* BERYLLIUM */
+ BASE_METAL, /* BORON */
+ COMMON, /* CARBON */
+ COMMON, /* NITROGEN */
+ CORROSIVE, /* OXYGEN */
+ CORROSIVE, /* FLUORINE */
+ NOBLE, /* NEON */
+ BASE_METAL, /* SODIUM */
+ BASE_METAL, /* MAGNESIUM */
+ BASE_METAL, /* ALUMINUM */
+ COMMON, /* SILICON */
+ COMMON, /* PHOSPHORUS */
+ CORROSIVE, /* SULFUR */
+ CORROSIVE, /* CHLORINE */
+ NOBLE, /* ARGON */
+ BASE_METAL, /* POTASSIUM */
+ BASE_METAL, /* CALCIUM */
+ BASE_METAL, /* SCANDIUM */
+ BASE_METAL, /* TITANIUM */
+ BASE_METAL, /* VANADIUM */
+ BASE_METAL, /* CHROMIUM */
+ BASE_METAL, /* MANGANESE */
+ BASE_METAL, /* IRON */
+ BASE_METAL, /* COBALT */
+ BASE_METAL, /* NICKEL */
+ BASE_METAL, /* COPPER */
+ BASE_METAL, /* ZINC */
+ BASE_METAL, /* GALLIUM */
+ BASE_METAL, /* GERMANIUM */
+ COMMON, /* ARSENIC */
+ COMMON, /* SELENIUM */
+ CORROSIVE, /* BROMINE */
+ NOBLE, /* KRYPTON */
+ BASE_METAL, /* RUBIDIUM */
+ BASE_METAL, /* STRONTIUM */
+ BASE_METAL, /* YTTRIUM */
+ BASE_METAL, /* ZIRCONIUM */
+ BASE_METAL, /* NIOBIUM */
+ BASE_METAL, /* MOLYBDENUM */
+ RADIOACTIVE, /* TECHNETIUM */
+ BASE_METAL, /* RUTHENIUM */
+ BASE_METAL, /* RHODIUM */
+ PRECIOUS, /* PALLADIUM */
+ PRECIOUS, /* SILVER */
+ BASE_METAL, /* CADMIUM */
+ BASE_METAL, /* INDIUM */
+ BASE_METAL, /* TIN */
+ BASE_METAL, /* ANTIMONY */
+ BASE_METAL, /* TELLURIUM */
+ CORROSIVE, /* IODINE */
+ NOBLE, /* XENON */
+ BASE_METAL, /* CESIUM */
+ BASE_METAL, /* BARIUM */
+ RARE_EARTH, /* LANTHANUM */
+ RARE_EARTH, /* CERIUM */
+ RARE_EARTH, /* PRASEODYMIUM */
+ RARE_EARTH, /* NEODYMIUM */
+ RARE_EARTH, /* PROMETHIUM */
+ RARE_EARTH, /* SAMARIUM */
+ RARE_EARTH, /* EUROPIUM */
+ RARE_EARTH, /* GADOLINIUM */
+ RARE_EARTH, /* TERBIUM */
+ RARE_EARTH, /* DYPROSIUM */
+ RARE_EARTH, /* HOLMIUM */
+ RARE_EARTH, /* ERBIUM */
+ RARE_EARTH, /* THULIUM */
+ RARE_EARTH, /* YTTERBIUM */
+ RARE_EARTH, /* LUTETIUM */
+ BASE_METAL, /* HAFNIUM */
+ BASE_METAL, /* TANTALUM */
+ BASE_METAL, /* TUNGSTEN */
+ BASE_METAL, /* RHENIUM */
+ BASE_METAL, /* OSMIUM */
+ PRECIOUS, /* IRIDIUM */
+ PRECIOUS, /* PLATINUM */
+ PRECIOUS, /* GOLD */
+ BASE_METAL, /* MERCURY */
+ BASE_METAL, /* THALLIUM */
+ BASE_METAL, /* LEAD */
+ BASE_METAL, /* BISMUTH */
+ RADIOACTIVE, /* POLONIUM */
+ RADIOACTIVE, /* ASTATINE */
+ NOBLE, /* RADON */
+ RADIOACTIVE, /* FRANCIUM */
+ RADIOACTIVE, /* RADIUM */
+ RADIOACTIVE, /* ACTINIUM */
+ RADIOACTIVE, /* THORIUM */
+ RADIOACTIVE, /* PROTACTINIUM */
+ RADIOACTIVE, /* URANIUM */
+ RADIOACTIVE, /* NEPTUNIUM */
+ RADIOACTIVE, /* PLUTONIUM */
+
+ COMMON, /* OZONE */
+ COMMON, /* FREE RADICALS */
+ COMMON, /* CARBON DIOXIDE */
+ COMMON, /* CARBON MONOXIDE */
+ COMMON, /* AMMONIA */
+ COMMON, /* METHANE */
+ COMMON, /* SULFURIC ACID */
+ COMMON, /* HYDROCHLORIC ACID */
+ COMMON, /* HYDROCYANIC ACID */
+ COMMON, /* FORMIC ACID */
+ COMMON, /* PHOSPHORIC ACID */
+ COMMON, /* FORMALDEHYDE */
+ COMMON, /* CYANOACETYLENE */
+ COMMON, /* METHANOL */
+ COMMON, /* ETHANOL */
+ COMMON, /* SILICON MONOXIDE */
+ COMMON, /* TITANIUM OXIDE */
+ COMMON, /* ZIRCONIUM OXIDE */
+ COMMON, /* WATER */
+ COMMON, /* SILICON COMPOUNDS */
+ COMMON, /* METAL OXIDES */
+ EXOTIC, /* QUANTUM BLACK HOLES */
+ EXOTIC, /* NEUTRONIUM */
+ EXOTIC, /* MAGNETIC MONOPOLES */
+ EXOTIC, /* DEGENERATE MATTER */
+ EXOTIC, /* SUPER FLUIDS */
+ EXOTIC, /* AGUUTI NODULES */
+ COMMON, /* IRON COMPOUNDS */
+ COMMON, /* ALUMINUM COMPOUNDS */
+ COMMON, /* NITROUS OXIDE */
+ COMMON, /* RADIOACTIVES */
+ COMMON, /* HYDROCARBONS */
+ COMMON, /* CARBON COMPOUNDS */
+ EXOTIC, /* ANTIMATTER */
+ EXOTIC, /* CHARON DUST */
+ EXOTIC, /* REISBURG HELICES */
+ EXOTIC, /* TZO CRYSTALS */
+ COMMON, /* CALCIUM COMPOUNDS */
+ COMMON, /* NITRIC ACID */
+};
+
+/*------------------------------ Global Data ------------------------------ */
+
+#define NO_DEPOSIT 0
+
+#define TRACE_USEFUL MINERAL_DEPOSIT (FEW, LIGHT)
+#define LIGHT_USEFUL MINERAL_DEPOSIT (MODERATE, LIGHT)
+#define MEDIUM_USEFUL MINERAL_DEPOSIT (MODERATE, MEDIUM)
+#define HEAVY_USEFUL MINERAL_DEPOSIT (MODERATE, HEAVY)
+#define HUGE_USEFUL MINERAL_DEPOSIT (NUMEROUS, HEAVY)
+
+const PlanetFrame planet_array[NUMBER_OF_PLANET_TYPES] =
+{
+ { /* OOLITE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ VIOLET_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {HOLMIUM, MEDIUM_USEFUL},
+ {ERBIUM, MEDIUM_USEFUL},
+ {THULIUM, MEDIUM_USEFUL},
+ {YTTERBIUM, MEDIUM_USEFUL},
+ {LUTETIUM, MEDIUM_USEFUL},
+ {PALLADIUM, MEDIUM_USEFUL},
+ {SILVER, MEDIUM_USEFUL},
+ {IRIDIUM, MEDIUM_USEFUL},
+ },
+ OOLITE_COLOR_TAB,
+ OOLITE_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* YTTRIC_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ VIOLET_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {YTTERBIUM, HUGE_USEFUL},
+ {YTTRIUM, HUGE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ YTTRIC_COLOR_TAB,
+ YTTRIC_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* QUASI_DEGENERATE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + TOPO_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ MED_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {DEGENERATE_MATTER, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ QUASI_DEGENERATE_COLOR_TAB,
+ QUASI_DEGENERATE_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* LANTHANIDE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ YELLOW_BODY), /* Color and type/size of planet */
+ HIGH_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {LANTHANUM, MEDIUM_USEFUL},
+ {CERIUM, MEDIUM_USEFUL},
+ {PRASEODYMIUM, MEDIUM_USEFUL},
+ {NEODYMIUM, MEDIUM_USEFUL},
+ {PROMETHIUM, MEDIUM_USEFUL},
+ {SAMARIUM, MEDIUM_USEFUL},
+ {GADOLINIUM, MEDIUM_USEFUL},
+ {TERBIUM, MEDIUM_USEFUL},
+ },
+ LANTHANIDE_COLOR_TAB,
+ LANTHANIDE_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* TREASURE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + TOPO_ALGO,
+ YELLOW_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (SUPER_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {PALLADIUM, HEAVY_USEFUL},
+ {SILVER, HEAVY_USEFUL},
+ {SILVER, HEAVY_USEFUL},
+ {IRIDIUM, HEAVY_USEFUL},
+ {GOLD, HEAVY_USEFUL},
+ {PLATINUM, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ TREASURE_COLOR_TAB,
+ TREASURE_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* UREA_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ YELLOW_BODY), /* Color and type/size of planet */
+ HIGH_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {AMMONIA, LIGHT_USEFUL},
+ {FORMALDEHYDE, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ UREA_COLOR_TAB,
+ UREA_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* METAL_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ ORANGE_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {IRON, HUGE_USEFUL},
+ {NICKEL, HEAVY_USEFUL},
+ {VANADIUM, MEDIUM_USEFUL},
+ {SILVER, MEDIUM_USEFUL},
+ {URANIUM, HEAVY_USEFUL},
+ {SULFUR, MEDIUM_USEFUL},
+ {COPPER, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ },
+ METAL_COLOR_TAB,
+ METAL_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* RADIOACTIVE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ ORANGE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (SUPER_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {ASTATINE, MEDIUM_USEFUL},
+ {FRANCIUM, MEDIUM_USEFUL},
+ {RADIUM, MEDIUM_USEFUL},
+ {ACTINIUM, MEDIUM_USEFUL},
+ {THORIUM, MEDIUM_USEFUL},
+ {PROTACTINIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ RADIOACTIVE_COLOR_TAB,
+ RADIOACTIVE_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* OPALESCENT_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ CYAN_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {SAMARIUM, LIGHT_USEFUL},
+ {GADOLINIUM, LIGHT_USEFUL},
+ {ARGON, LIGHT_USEFUL},
+ {LITHIUM, LIGHT_USEFUL},
+ {SILICON, LIGHT_USEFUL},
+ {ARSENIC, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ OPALESCENT_COLOR_TAB,
+ OPALESCENT_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* CYANIC_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + TOPO_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LIGHT_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {CYANOACETYLENE, HUGE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ CYANIC_COLOR_TAB,
+ CYANIC_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* ACID_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {SULFURIC_ACID, HEAVY_USEFUL},
+ {HYDROCHLORIC_ACID, HEAVY_USEFUL},
+ {FORMIC_ACID, HEAVY_USEFUL},
+ {HYDROCYANIC_ACID, HEAVY_USEFUL},
+ {PHOSPHORIC_ACID, HEAVY_USEFUL},
+ {NITRIC_ACID, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ ACID_COLOR_TAB,
+ ACID_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* ALKALI_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {CALCIUM, MEDIUM_USEFUL},
+ {BARIUM, MEDIUM_USEFUL},
+ {STRONTIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ ALKALI_COLOR_TAB,
+ ALKALI_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* HALIDE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {FLUORINE, MEDIUM_USEFUL},
+ {BROMINE, MEDIUM_USEFUL},
+ {BROMINE, MEDIUM_USEFUL},
+ {ASTATINE, MEDIUM_USEFUL},
+ {IODINE, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ HALIDE_COLOR_TAB,
+ HALIDE_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* GREEN_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ MED_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {PRASEODYMIUM, HEAVY_USEFUL},
+ {NEODYMIUM, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ GREEN_COLOR_TAB,
+ GREEN_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* COPPER_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + TOPO_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {COPPER, HUGE_USEFUL},
+ {COPPER, HUGE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ COPPER_COLOR_TAB,
+ COPPER_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* CARBIDE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {CARBON, HEAVY_USEFUL},
+ {CARBON, HEAVY_USEFUL},
+ {CARBON, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ CARBIDE_COLOR_TAB,
+ CARBIDE_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* ULTRAMARINE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ BLUE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LIGHT_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {KRYPTON, MEDIUM_USEFUL},
+ {COBALT, MEDIUM_USEFUL},
+ {HOLMIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ ULTRAMARINE_COLOR_TAB,
+ ULTRAMARINE_XLAT_TAB,
+ 200, 2, 100, 100,
+ },
+ { /* NOBLE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ BLUE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LIGHT_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {NEON, LIGHT_USEFUL},
+ {RADON, LIGHT_USEFUL},
+ {ARGON, LIGHT_USEFUL},
+ {KRYPTON, LIGHT_USEFUL},
+ {XENON, LIGHT_USEFUL},
+ {HELIUM, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ NOBLE_COLOR_TAB,
+ NOBLE_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* AZURE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ BLUE_BODY), /* Color and type/size of planet */
+ MED_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {INDIUM, LIGHT_USEFUL},
+ {MOLYBDENUM, LIGHT_USEFUL},
+ {VANADIUM, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ AZURE_COLOR_TAB,
+ AZURE_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* CHONDRITE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ PURPLE_BODY), /* Color and type/size of planet */
+ HIGH_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {ETHANOL, HEAVY_USEFUL},
+ {FREE_RADICALS, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ CHONDRITE_COLOR_TAB,
+ CHONDRITE_XLAT_TAB,
+ 500, 1, 100, 190,
+ },
+ { /* PURPLE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ PURPLE_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {RHENIUM, MEDIUM_USEFUL},
+ {CADMIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ PURPLE_COLOR_TAB,
+ PURPLE_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* SUPER_DENSE_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + TOPO_ALGO,
+ PURPLE_BODY), /* Color and type/size of planet */
+ HIGH_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (SUPER_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {LEAD, MEDIUM_USEFUL},
+ {OSMIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ SUPER_DENSE_COLOR_TAB,
+ SUPER_DENSE_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* PELLUCID_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ PURPLE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {TZO_CRYSTALS, TRACE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ PELLUCID_COLOR_TAB,
+ PELLUCID_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* DUST_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {BISMUTH, LIGHT_USEFUL},
+ {ALUMINUM, LIGHT_USEFUL},
+ {POTASSIUM, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ DUST_COLOR_TAB,
+ DUST_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* CRIMSON_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {BARIUM, LIGHT_USEFUL},
+ {BORON, LIGHT_USEFUL},
+ {BERYLLIUM, LIGHT_USEFUL},
+ {BISMUTH, LIGHT_USEFUL},
+ {BROMINE, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ CRIMSON_COLOR_TAB,
+ CRIMSON_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* CIMMERIAN_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + TOPO_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {METHANE, MEDIUM_USEFUL},
+ {AMMONIA, MEDIUM_USEFUL},
+ {METHANOL, MEDIUM_USEFUL},
+ {LITHIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ CIMMERIAN_COLOR_TAB,
+ CIMMERIAN_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* INFRARED_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {MERCURY, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ INFRARED_COLOR_TAB,
+ INFRARED_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* SELENIC_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + CRATERED_ALGO,
+ WHITE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {IRON, LIGHT_USEFUL},
+ {ALUMINUM, LIGHT_USEFUL},
+ {CALCIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ SELENIC_COLOR_TAB,
+ SELENIC_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* AURIC_WORLD */
+ MAKE_BYTE (SMALL_ROCKY_WORLD + TOPO_ALGO,
+ YELLOW_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {GOLD, HUGE_USEFUL},
+ {GOLD, HUGE_USEFUL},
+ {GOLD, HUGE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ AURIC_COLOR_TAB,
+ AURIC_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+
+
+ { /* FLUORESCENT_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ VIOLET_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {TECHNETIUM, HUGE_USEFUL},
+ {NEON, HUGE_USEFUL},
+ {RADON, LIGHT_USEFUL},
+ {POTASSIUM, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ FLUORESCENT_COLOR_TAB,
+ FLUORESCENT_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* ULTRAVIOLET_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ VIOLET_BODY), /* Color and type/size of planet */
+ MED_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {POLONIUM, HUGE_USEFUL},
+ {GOLD, HUGE_USEFUL},
+ {PHOSPHORUS, HUGE_USEFUL},
+ {SCANDIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ ULTRAVIOLET_COLOR_TAB,
+ ULTRAVIOLET_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* PLUTONIC_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ YELLOW_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {BERYLLIUM, HUGE_USEFUL},
+ {BORON, HUGE_USEFUL},
+ {LANTHANUM, MEDIUM_USEFUL},
+ {ASTATINE, MEDIUM_USEFUL},
+ {FRANCIUM, MEDIUM_USEFUL},
+ {TITANIUM, LIGHT_USEFUL},
+ {CERIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ },
+ PLUTONIC_COLOR_TAB,
+ PLUTONIC_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* RAINBOW_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + TOPO_ALGO,
+ YELLOW_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {ACTINIUM, HEAVY_USEFUL},
+ {THORIUM, HEAVY_USEFUL},
+ {PROTACTINIUM, HEAVY_USEFUL},
+ {NEPTUNIUM, HEAVY_USEFUL},
+ {PLUTONIUM, HEAVY_USEFUL},
+ {OZONE, HUGE_USEFUL},
+ {OZONE, HUGE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ },
+ RAINBOW_COLOR_TAB,
+ RAINBOW_XLAT_TAB,
+ 500, 1, 20, 100,
+ },
+ { /* SHATTERED_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ ORANGE_BODY), /* Color and type/size of planet */
+ SUPER_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {PALLADIUM, HUGE_USEFUL},
+ {IRIDIUM, HUGE_USEFUL},
+ {TECHNETIUM, HUGE_USEFUL},
+ {POLONIUM, HUGE_USEFUL},
+ {SODIUM, HUGE_USEFUL},
+ {MANGANESE, HUGE_USEFUL},
+ {CHROMIUM, HUGE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ },
+ SHATTERED_COLOR_TAB,
+ SHATTERED_XLAT_TAB,
+ 500, 1, 0, 185,
+ },
+ { /* SAPPHIRE_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + TOPO_ALGO,
+ CYAN_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {REISBURG_HELICES, HUGE_USEFUL},
+ {RT_SUPER_FLUID, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ SAPPHIRE_COLOR_TAB,
+ SAPPHIRE_XLAT_TAB,
+ 80, 1, 0, 128,
+ },
+ { /* ORGANIC_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + TOPO_ALGO,
+ CYAN_BODY), /* Color and type/size of planet */
+ MED_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {FREE_RADICALS, HEAVY_USEFUL},
+ {FORMALDEHYDE, HEAVY_USEFUL},
+ {CARBON, HEAVY_USEFUL},
+ {CARBON_DIOXIDE, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ ORGANIC_COLOR_TAB,
+ ORGANIC_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* XENOLITHIC_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ CYAN_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {ALUMINUM, HUGE_USEFUL},
+ {PLATINUM, LIGHT_USEFUL},
+ {GERMANIUM, TRACE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ XENOLITHIC_COLOR_TAB,
+ XENOLITHIC_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* REDUX_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {BROMINE, MEDIUM_USEFUL},
+ {OXYGEN, MEDIUM_USEFUL},
+ {FLUORINE, MEDIUM_USEFUL},
+ {SULFUR, MEDIUM_USEFUL},
+ {CHLORINE, MEDIUM_USEFUL},
+ {IODINE, MEDIUM_USEFUL},
+ {ZIRCONIUM, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ },
+ REDUX_COLOR_TAB,
+ REDUX_XLAT_TAB,
+ 500, 1, 0, 190,
+ },
+ { /* PRIMORDIAL_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ SUPER_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {CESIUM, MEDIUM_USEFUL},
+ {BARIUM, MEDIUM_USEFUL},
+ {RUBIDIUM, MEDIUM_USEFUL},
+ {METHANE, HUGE_USEFUL},
+ {AMMONIA, HUGE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ PRIMORDIAL_COLOR_TAB,
+ PRIMORDIAL_XLAT_TAB,
+ 250, 2, 10, 200,
+ },
+ { /* EMERALD_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + TOPO_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {AGUUTI_NODULES, HUGE_USEFUL},
+ {ANTIMATTER, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ EMERALD_COLOR_TAB,
+ EMERALD_XLAT_TAB,
+ 80, 1, 0, 128,
+ },
+ { /* CHLORINE_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ HIGH_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {CHLORINE, HEAVY_USEFUL},
+ {CHLORINE, HEAVY_USEFUL},
+ {HYDROCHLORIC_ACID, HEAVY_USEFUL},
+ {HYDROCHLORIC_ACID, HEAVY_USEFUL},
+ {ZINC, LIGHT_USEFUL},
+ {GALLIUM, TRACE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ CHLORINE_COLOR_TAB,
+ CHLORINE_XLAT_TAB,
+ 500, 1, 0, 190,
+ },
+ { /* MAGNETIC_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ HIGH_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {ZINC, HEAVY_USEFUL},
+ {NICKEL, MEDIUM_USEFUL},
+ {MAGNETIC_MONOPOLES, TRACE_USEFUL},
+ {NIOBIUM, LIGHT_USEFUL},
+ {IRON, HEAVY_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ MAGNETIC_COLOR_TAB,
+ MAGNETIC_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* WATER_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ BLUE_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {IRON, LIGHT_USEFUL},
+ {ALUMINUM, LIGHT_USEFUL},
+ {TIN, LIGHT_USEFUL},
+ {LEAD, LIGHT_USEFUL},
+ {URANIUM, TRACE_USEFUL},
+ {MOLYBDENUM, TRACE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ WATER_COLOR_TAB,
+ WATER_XLAT_TAB,
+ 500, 1, 0, 190,
+ },
+ { /* TELLURIC_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ BLUE_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {IRIDIUM, MEDIUM_USEFUL},
+ {RUTHENIUM, MEDIUM_USEFUL},
+ {THALLIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ TELLURIC_COLOR_TAB,
+ TELLURIC_XLAT_TAB,
+ 250, 2, 80, 200,
+ },
+ { /* HYDROCARBON_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + TOPO_ALGO,
+ BLUE_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {HYDROCARBONS, HUGE_USEFUL},
+ {BISMUTH, LIGHT_USEFUL},
+ {TANTALUM, TRACE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ HYDROCARBON_COLOR_TAB,
+ HYDROCARBON_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* IODINE_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ MED_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {IODINE, HEAVY_USEFUL},
+ {MAGNESIUM, LIGHT_USEFUL},
+ {TUNGSTEN, TRACE_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ IODINE_COLOR_TAB,
+ IODINE_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+ { /* VINYLOGOUS_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ PURPLE_BODY), /* Color and type/size of planet */
+ LOW_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (NORMAL_DENSITY, NOTHING), /* Atmosphere and density */
+ {
+ {TITANIUM, LIGHT_USEFUL},
+ {ARSENIC, LIGHT_USEFUL},
+ {POTASSIUM, LIGHT_USEFUL},
+ {RHENIUM, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ VINYLOGOUS_COLOR_TAB,
+ VINYLOGOUS_XLAT_TAB,
+ 400, 1, 100, 190,
+ },
+ { /* RUBY_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + TOPO_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {TZO_CRYSTALS, HUGE_USEFUL},
+ {NEUTRONIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ RUBY_COLOR_TAB,
+ RUBY_XLAT_TAB,
+ 80, 1, 0, 128,
+ },
+ { /* MAGMA_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + TOPO_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ SUPER_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (HIGH_DENSITY, LIGHT), /* Atmosphere and density */
+ {
+ {LEAD, LIGHT_USEFUL},
+ {NICKEL, LIGHT_USEFUL},
+ {IRON, LIGHT_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ MAGMA_COLOR_TAB,
+ MAGMA_XLAT_TAB,
+ 500, 1, 0, 160,
+ },
+ { /* MAROON_WORLD */
+ MAKE_BYTE (LARGE_ROCKY_WORLD + CRATERED_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (LOW_DENSITY, MEDIUM), /* Atmosphere and density */
+ {
+ {CESIUM, MEDIUM_USEFUL},
+ {SILICON, MEDIUM_USEFUL},
+ {PHOSPHORUS, MEDIUM_USEFUL},
+ {RHODIUM, MEDIUM_USEFUL},
+ {CADMIUM, MEDIUM_USEFUL},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ MAROON_COLOR_TAB,
+ MAROON_XLAT_TAB,
+ 230, 2, 200, 150,
+ },
+
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ BLUE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ BLU_GAS_COLOR_TAB,
+ BLU_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ CYAN_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ CYA_GAS_COLOR_TAB,
+ CYA_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ GREEN_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ GRN_GAS_COLOR_TAB,
+ GRN_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ GRAY_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ GRY_GAS_COLOR_TAB,
+ GRY_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ ORANGE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ ORA_GAS_COLOR_TAB,
+ ORA_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ PURPLE_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ PUR_GAS_COLOR_TAB,
+ PUR_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ RED_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ RED_GAS_COLOR_TAB,
+ RED_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ {
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ VIOLET_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ VIO_GAS_COLOR_TAB,
+ VIO_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+ { /* A Jupiter-like World */
+ MAKE_BYTE (GAS_GIANT + GAS_GIANT_ALGO,
+ YELLOW_BODY), /* Color and type/size of planet */
+ NO_TECTONICS, /* Tectonics - Scaled with Earth at 82 */
+ MAKE_BYTE (GAS_DENSITY, HEAVY), /* Atmosphere and density */
+ {
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ {NOTHING, NO_DEPOSIT},
+ },
+ YEL_GAS_COLOR_TAB,
+ YEL_GAS_XLAT_TAB,
+ 10, 2, 8, 29,
+ },
+};
+
diff --git a/src/uqm/planets/Makeinfo b/src/uqm/planets/Makeinfo
new file mode 100644
index 0000000..da4026f
--- /dev/null
+++ b/src/uqm/planets/Makeinfo
@@ -0,0 +1,7 @@
+uqm_SUBDIRS="generate"
+uqm_CFILES="calc.c cargo.c devices.c gentopo.c lander.c orbits.c
+ oval.c pl_stuff.c planets.c plangen.c pstarmap.c report.c
+ roster.c scan.c solarsys.c surface.c"
+uqm_HFILES="elemdata.h generate.h lander.h lifeform.h plandata.h planets.h
+ scan.h solarsys.h sundata.h"
+
diff --git a/src/uqm/planets/calc.c b/src/uqm/planets/calc.c
new file mode 100644
index 0000000..09bd461
--- /dev/null
+++ b/src/uqm/planets/calc.c
@@ -0,0 +1,530 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* ----------------------------- INCLUDES ---------------------------- */
+#include "planets.h"
+#include "uqm/starmap.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+/* -------------------------------- DATA -------------------------------- */
+
+/* -------------------------------- CODE -------------------------------- */
+
+//#define DEBUG_PLANET_CALC
+
+#define LOW_TEMP 0
+#define MED_TEMP 500
+#define HIGH_TEMP 1500
+#define LOW_TEMP_BONUS 10
+#define MED_TEMP_BONUS 25
+#define HIGH_TEMP_BONUS 50
+#define MAX_TECTONICS 255
+
+enum
+{
+ RED_SUN_INTENSITY = 0,
+ ORANGE_SUN_INTENSITY,
+ YELLOW_SUN_INTENSITY,
+ GREEN_SUN_INTENSITY,
+ BLUE_SUN_INTENSITY,
+ WHITE_SUN_INTENSITY
+};
+
+static UWORD
+CalcFromBase (UWORD base, UWORD variance)
+{
+ return base + LOWORD (RandomContext_Random (SysGenRNG)) % variance;
+}
+
+static inline UWORD
+CalcHalfBaseVariance (UWORD base)
+{
+ return CalcFromBase (base, (base >> 1) + 1);
+}
+
+static void
+CalcSysInfo (SYSTEM_INFO *SysInfoPtr)
+{
+ SysInfoPtr->StarSize = pSolarSysState->SunDesc[0].data_index;
+ switch (STAR_COLOR (CurStarDescPtr->Type))
+ {
+ case BLUE_BODY:
+ SysInfoPtr->StarIntensity = BLUE_SUN_INTENSITY;
+ break;
+ case GREEN_BODY:
+ SysInfoPtr->StarIntensity = GREEN_SUN_INTENSITY;
+ break;
+ case ORANGE_BODY:
+ SysInfoPtr->StarIntensity = ORANGE_SUN_INTENSITY;
+ break;
+ case RED_BODY:
+ SysInfoPtr->StarIntensity = RED_SUN_INTENSITY;
+ break;
+ case WHITE_BODY:
+ SysInfoPtr->StarIntensity = WHITE_SUN_INTENSITY;
+ break;
+ case YELLOW_BODY:
+ SysInfoPtr->StarIntensity = YELLOW_SUN_INTENSITY;
+ break;
+ }
+
+ switch (STAR_TYPE (CurStarDescPtr->Type))
+ {
+ case DWARF_STAR:
+ SysInfoPtr->StarEnergy =
+ (SysInfoPtr->StarIntensity + 1) * DWARF_ENERGY;
+ break;
+ case GIANT_STAR:
+ SysInfoPtr->StarEnergy =
+ (SysInfoPtr->StarIntensity + 1) * GIANT_ENERGY;
+ break;
+ case SUPER_GIANT_STAR:
+ SysInfoPtr->StarEnergy =
+ (SysInfoPtr->StarIntensity + 1) * SUPERGIANT_ENERGY;
+ break;
+ }
+}
+
+static UWORD
+GeneratePlanetComposition (PLANET_INFO *PlanetInfoPtr, SIZE SurfaceTemp,
+ SIZE radius)
+{
+ if (PLANSIZE (PlanetInfoPtr->PlanDataPtr->Type) == GAS_GIANT)
+ {
+ PlanetInfoPtr->Weather = 7 << 5;
+ return (GAS_GIANT_ATMOSPHERE);
+ }
+ else
+ {
+ BYTE range;
+ UWORD atmo;
+
+ PlanetInfoPtr->Weather = 0;
+ atmo = 0;
+ if ((range = HINIBBLE (PlanetInfoPtr->PlanDataPtr->AtmoAndDensity)) <= HEAVY)
+ {
+ if (SurfaceTemp < COLD_THRESHOLD)
+ --range;
+ else if (SurfaceTemp > HOT_THRESHOLD)
+ ++range;
+
+ if (range <= HEAVY + 1)
+ {
+ switch (range)
+ {
+ case LIGHT:
+ atmo = THIN_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 1 << 5;
+ break;
+ case MEDIUM:
+ atmo = NORMAL_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 2 << 5;
+ break;
+ case HEAVY:
+ atmo = THICK_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 4 << 5;
+ break;
+ default:
+ atmo = SUPER_THICK_ATMOSPHERE;
+ PlanetInfoPtr->Weather = 6 << 5;
+ break;
+ }
+
+ radius /= EARTH_RADIUS;
+ if (radius < 2)
+ PlanetInfoPtr->Weather += 1 << 5;
+ else if (radius > 10)
+ PlanetInfoPtr->Weather -= 1 << 5;
+ atmo = CalcHalfBaseVariance (atmo);
+ }
+ }
+
+ return (atmo);
+ }
+}
+
+// This function is called both when the solar system is generated,
+// and when planetary orbit is entered.
+// In the former case, the if() block will not be executed,
+// which means that the temperature calculated in that case will be
+// slightly lower. The result is that the orbits may not always
+// have the colour you'd expect based on the true temperature.
+// (eg. Beta Corvi I). I don't know what the idea behind this is,
+// but the if statement must be there for a reason. -- SvdB
+// Update 2013-03-28: The contents of the if() block is probably there to
+// model a greenhouse effect. It seems that it is taken into account when
+// calculating the actual temperature (when landing or scanning), but not
+// when determining the colors of the drawn orbits. (Thanks to James Scott
+// for this insight.)
+static SIZE
+CalcTemp (SYSTEM_INFO *SysInfoPtr, SIZE radius)
+{
+#define GENERIC_ALBEDO 33 /* In %, 0=black, 100 is reflective */
+#define ADJUST_FOR_KELVIN 273
+#define PLANET_TEMP_CONSTANT 277L
+ DWORD alb;
+ SIZE centigrade, bonus;
+
+ alb = 100 - GENERIC_ALBEDO;
+ alb = square_root (square_root (alb * 100 * 10000))
+ * PLANET_TEMP_CONSTANT * SysInfoPtr->StarEnergy
+ / ((YELLOW_SUN_INTENSITY + 1) * DWARF_ENERGY);
+
+ centigrade = (SIZE)(alb / square_root (radius * 10000L / EARTH_RADIUS))
+ - ADJUST_FOR_KELVIN;
+
+ bonus = 0;
+ if (SysInfoPtr == &pSolarSysState->SysInfo
+ && HINIBBLE (SysInfoPtr->PlanetInfo.PlanDataPtr->AtmoAndDensity) <= HEAVY)
+ {
+#define COLD_BONUS 20
+#define HOT_BONUS 200
+ if (centigrade >= HOT_THRESHOLD)
+ bonus = HOT_BONUS;
+ else if (centigrade >= COLD_THRESHOLD)
+ bonus = COLD_BONUS;
+
+ bonus <<= HINIBBLE (SysInfoPtr->PlanetInfo.PlanDataPtr->AtmoAndDensity);
+ bonus = CalcHalfBaseVariance (bonus);
+ }
+
+ return (centigrade + bonus);
+}
+
+static COUNT
+CalcRotation (PLANET_INFO *PlanetInfoPtr)
+{
+ if (PLANSIZE (PlanetInfoPtr->PlanDataPtr->Type) == GAS_GIANT)
+ return CalcFromBase (80, 80);
+ else if (LOBYTE (RandomContext_Random (SysGenRNG)) % 10 == 0)
+ return CalcFromBase (50 * 240, 200 * 240);
+ else
+ return CalcFromBase (150, 150);
+}
+
+static SIZE
+CalcTilt (void)
+{ /* Calculate Axial Tilt */
+ SIZE tilt;
+ BYTE i;
+
+#define NUM_TOSSES 10
+#define TILT_RANGE 180
+ tilt = -(TILT_RANGE / 2);
+ i = NUM_TOSSES;
+ do /* Using added Randomom values to give bell curve */
+ {
+ tilt += LOWORD (RandomContext_Random (SysGenRNG))
+ % ((TILT_RANGE / NUM_TOSSES) + 1);
+ } while (--i);
+
+ return (tilt);
+}
+
+UWORD
+CalcGravity (const PLANET_INFO *PlanetInfoPtr)
+{
+ return (DWORD)PlanetInfoPtr->PlanetDensity * PlanetInfoPtr->PlanetRadius
+ / 100;
+}
+
+static UWORD
+CalcTectonics (UWORD base, UWORD temp)
+{
+ UWORD tect = CalcFromBase (base, 3 << 5);
+#ifdef OLD
+ if (temp >= HIGH_TEMP)
+ tect += HIGH_TEMP_BONUS;
+ else if (temp >= MED_TEMP)
+ tect += MED_TEMP_BONUS;
+ else if (temp >= LOW_TEMP)
+ tect += LOW_TEMP_BONUS;
+#else /* !OLD */
+ (void) temp; /* silence compiler whining */
+#endif /* OLD */
+ return tect;
+}
+
+// This code moved from planets/surface.c:CalcLifeForms()
+static int
+CalcLifeChance (const PLANET_INFO *PlanetInfoPtr)
+{
+ SIZE life_var = 0;
+
+ if (PLANSIZE (PlanetInfoPtr->PlanDataPtr->Type) == GAS_GIANT)
+ return -1;
+
+ if (PlanetInfoPtr->SurfaceTemperature < -151)
+ life_var -= 300;
+ else if (PlanetInfoPtr->SurfaceTemperature < -51)
+ life_var -= 100;
+ else if (PlanetInfoPtr->SurfaceTemperature < 0)
+ life_var += 100;
+ else if (PlanetInfoPtr->SurfaceTemperature < 50)
+ life_var += 300;
+ else if (PlanetInfoPtr->SurfaceTemperature < 150)
+ life_var += 50;
+ else if (PlanetInfoPtr->SurfaceTemperature < 250)
+ life_var -= 100;
+ else if (PlanetInfoPtr->SurfaceTemperature < 500)
+ life_var -= 400;
+ else
+ life_var -= 800;
+
+ if (PlanetInfoPtr->AtmoDensity == 0)
+ life_var -= 1000;
+ else if (PlanetInfoPtr->AtmoDensity < 15)
+ life_var += 100;
+ else if (PlanetInfoPtr->AtmoDensity < 30)
+ life_var += 200;
+ else if (PlanetInfoPtr->AtmoDensity < 100)
+ life_var += 300;
+ else if (PlanetInfoPtr->AtmoDensity < 1000)
+ life_var += 150;
+ else if (PlanetInfoPtr->AtmoDensity < 2500)
+ ;
+ else
+ life_var -= 100;
+
+#ifndef NOTYET
+ life_var += 200 + 80 + 80;
+#else /* NOTYET */
+ if (PlanetInfoPtr->SurfaceGravity < 10)
+ ;
+ else if (PlanetInfoPtr->SurfaceGravity < 35)
+ life_var += 50;
+ else if (PlanetInfoPtr->SurfaceGravity < 75)
+ life_var += 100;
+ else if (PlanetInfoPtr->SurfaceGravity < 150)
+ life_var += 200;
+ else if (PlanetInfoPtr->SurfaceGravity < 400)
+ life_var += 50;
+ else if (PlanetInfoPtr->SurfaceGravity < 800)
+ ;
+ else
+ life_var -= 100;
+
+ if (PlanetInfoPtr->Tectonics < 1)
+ life_var += 80;
+ else if (PlanetInfoPtr->Tectonics < 2)
+ life_var += 70;
+ else if (PlanetInfoPtr->Tectonics < 3)
+ life_var += 60;
+ else if (PlanetInfoPtr->Tectonics < 4)
+ life_var += 50;
+ else if (PlanetInfoPtr->Tectonics < 5)
+ life_var += 25;
+ else if (PlanetInfoPtr->Tectonics < 6)
+ ;
+ else
+ life_var -= 100;
+
+ if (PlanetInfoPtr->Weather < 1)
+ life_var += 80;
+ else if (PlanetInfoPtr->Weather < 2)
+ life_var += 70;
+ else if (PlanetInfoPtr->Weather < 3)
+ life_var += 60;
+ else if (PlanetInfoPtr->Weather < 4)
+ life_var += 50;
+ else if (PlanetInfoPtr->Weather < 5)
+ life_var += 25;
+ else if (PlanetInfoPtr->Weather < 6)
+ ;
+ else
+ life_var -= 100;
+#endif /* NOTYET */
+
+ return life_var;
+}
+
+// Sets the SysGenRNG to the required state first.
+void
+DoPlanetaryAnalysis (SYSTEM_INFO *SysInfoPtr, PLANET_DESC *pPlanetDesc)
+{
+ assert ((pPlanetDesc->data_index & ~WORLD_TYPE_SPECIAL)
+ < NUMBER_OF_PLANET_TYPES);
+
+ RandomContext_SeedRandom (SysGenRNG, pPlanetDesc->rand_seed);
+
+ CalcSysInfo (SysInfoPtr);
+
+#ifdef DEBUG_PLANET_CALC
+ {
+ BYTE ColorClass[6][8] = {
+ "Red",
+ "Orange",
+ "Yellow",
+ "Green",
+ "Blue",
+ "White",
+ };
+ BYTE SizeName[3][12] = {
+ "Dwarf",
+ "Giant",
+ "Supergiant",
+ };
+
+ log_add (log_Debug, "%s %s",
+ ColorClass[SysInfoPtr->StarIntensity],
+ SizeName[SysInfoPtr->StarSize]);
+ log_add (log_Debug, "Stellar Energy: %d (sol = 3)",
+ SysInfoPtr->StarEnergy);
+ }
+#endif /* DEBUG_PLANET_CALC */
+
+ {
+ SIZE radius;
+
+ SysInfoPtr->PlanetInfo.PlanDataPtr =
+ &PlanData[pPlanetDesc->data_index & ~PLANET_SHIELDED];
+
+ if (pPlanetDesc->pPrevDesc == pSolarSysState->SunDesc)
+ radius = pPlanetDesc->radius;
+ else
+ radius = pPlanetDesc->pPrevDesc->radius;
+ SysInfoPtr->PlanetInfo.PlanetToSunDist = radius;
+
+ SysInfoPtr->PlanetInfo.SurfaceTemperature =
+ CalcTemp (SysInfoPtr, radius);
+ switch (LONIBBLE (SysInfoPtr->PlanetInfo.PlanDataPtr->AtmoAndDensity))
+ {
+ case GAS_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 20;
+ break;
+ case LIGHT_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 33;
+ break;
+ case LOW_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 60;
+ break;
+ case NORMAL_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 100;
+ break;
+ case HIGH_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 150;
+ break;
+ case SUPER_DENSITY:
+ SysInfoPtr->PlanetInfo.PlanetDensity = 200;
+ break;
+ }
+ SysInfoPtr->PlanetInfo.PlanetDensity +=
+ (SysInfoPtr->PlanetInfo.PlanetDensity / 20)
+ - (LOWORD (RandomContext_Random (SysGenRNG))
+ % (SysInfoPtr->PlanetInfo.PlanetDensity / 10));
+
+ switch (PLANSIZE (SysInfoPtr->PlanetInfo.PlanDataPtr->Type))
+ {
+ case SMALL_ROCKY_WORLD:
+#define SMALL_RADIUS 25
+ SysInfoPtr->PlanetInfo.PlanetRadius = CalcHalfBaseVariance (SMALL_RADIUS);
+ break;
+ case LARGE_ROCKY_WORLD:
+#define LARGE_RADIUS 75
+ SysInfoPtr->PlanetInfo.PlanetRadius = CalcHalfBaseVariance (LARGE_RADIUS);
+ break;
+ case GAS_GIANT:
+#define MIN_GAS_RADIUS 300
+#define MAX_GAS_RADIUS 1500
+ SysInfoPtr->PlanetInfo.PlanetRadius =
+ CalcFromBase (MIN_GAS_RADIUS, MAX_GAS_RADIUS - MIN_GAS_RADIUS);
+ break;
+ }
+
+ SysInfoPtr->PlanetInfo.RotationPeriod = CalcRotation (&SysInfoPtr->PlanetInfo);
+ SysInfoPtr->PlanetInfo.SurfaceGravity = CalcGravity (&SysInfoPtr->PlanetInfo);
+ SysInfoPtr->PlanetInfo.AxialTilt = CalcTilt ();
+ if ((SysInfoPtr->PlanetInfo.Tectonics =
+ CalcTectonics (SysInfoPtr->PlanetInfo.PlanDataPtr->BaseTectonics,
+ SysInfoPtr->PlanetInfo.SurfaceTemperature)) > MAX_TECTONICS)
+ SysInfoPtr->PlanetInfo.Tectonics = MAX_TECTONICS;
+
+ SysInfoPtr->PlanetInfo.AtmoDensity =
+ GeneratePlanetComposition (&SysInfoPtr->PlanetInfo,
+ SysInfoPtr->PlanetInfo.SurfaceTemperature, radius);
+
+ SysInfoPtr->PlanetInfo.Tectonics >>= 5;
+ SysInfoPtr->PlanetInfo.Weather >>= 5;
+
+ SysInfoPtr->PlanetInfo.LifeChance = CalcLifeChance (&SysInfoPtr->PlanetInfo);
+
+#ifdef DEBUG_PLANET_CALC
+ radius = (SIZE)((DWORD)UNSCALE_RADIUS (radius) * 100 / UNSCALE_RADIUS (EARTH_RADIUS));
+ log_add (log_Debug, "\tOrbital Distance : %d.%02d AU", radius / 100, radius % 100);
+ //log_add (log_Debug, "\tPlanetary Mass : %d.%02d Earth masses",
+ // SysInfoPtr->PlanetInfo.PlanetMass / 100,
+ // SysInfoPtr->PlanetInfo.PlanetMass % 100);
+ log_add (log_Debug, "\tPlanetary Radius : %d.%02d Earth radii",
+ SysInfoPtr->PlanetInfo.PlanetRadius / 100,
+ SysInfoPtr->PlanetInfo.PlanetRadius % 100);
+ log_add (log_Debug, "\tSurface Gravity: %d.%02d gravities",
+ SysInfoPtr->PlanetInfo.SurfaceGravity / 100,
+ SysInfoPtr->PlanetInfo.SurfaceGravity % 100);
+ log_add (log_Debug, "\tSurface Temperature: %d degrees C",
+ SysInfoPtr->PlanetInfo.SurfaceTemperature );
+ log_add (log_Debug, "\tAxial Tilt : %d degrees",
+ abs (SysInfoPtr->PlanetInfo.AxialTilt));
+ log_add (log_Debug, "\tTectonics : Class %u",
+ SysInfoPtr->PlanetInfo.Tectonics + 1);
+ log_add (log_Debug, "\tAtmospheric Density: %u.%02u",
+ SysInfoPtr->PlanetInfo.AtmoDensity / EARTH_ATMOSPHERE,
+ (SysInfoPtr->PlanetInfo.AtmoDensity * 100 / EARTH_ATMOSPHERE) % 100);
+ if (SysInfoPtr->PlanetInfo.AtmoDensity == 0)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Vacuum)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < THIN_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Thin)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < NORMAL_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Normal)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < THICK_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Thick)");
+ }
+ else if (SysInfoPtr->PlanetInfo.AtmoDensity < SUPER_THICK_ATMOSPHERE)
+ {
+ log_add (log_Debug, "\tAtmosphere: (Super thick)");
+ }
+ else
+ {
+ log_add (log_Debug, "\tAtmosphere: (Gas Giant)");
+ }
+
+ log_add (log_Debug, "\tWeather : Class %u",
+ SysInfoPtr->PlanetInfo.Weather + 1);
+
+ if (SysInfoPtr->PlanetInfo.RotationPeriod >= 480)
+ {
+ log_add (log_Debug, "\tLength of day : %d.%d Earth days",
+ SysInfoPtr->PlanetInfo.RotationPeriod / 240,
+ SysInfoPtr->PlanetInfo.RotationPeriod % 240);
+ }
+ else
+ {
+ log_add (log_Debug, "\tLength of day : %d.%d Earth hours",
+ SysInfoPtr->PlanetInfo.RotationPeriod / 10,
+ SysInfoPtr->PlanetInfo.RotationPeriod % 10);
+ }
+#endif /* DEBUG_PLANET_CALC */
+ }
+}
+
diff --git a/src/uqm/planets/cargo.c b/src/uqm/planets/cargo.c
new file mode 100644
index 0000000..147d95a
--- /dev/null
+++ b/src/uqm/planets/cargo.c
@@ -0,0 +1,356 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../colors.h"
+#include "../controls.h"
+#include "../gamestr.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../util.h"
+#include "../sis.h"
+ // for ClearSISRect(), DrawStatusMessage()
+#include "planets.h"
+#include "libs/graphics/drawable.h"
+ // for GetFrameBounds()
+
+
+#define ELEMENT_ORG_Y 35
+#define FREE_ORG_Y (ELEMENT_ORG_Y + (NUM_ELEMENT_CATEGORIES \
+ * ELEMENT_SPACING_Y))
+#define BIO_ORG_Y 119
+#define ELEMENT_SPACING_Y 9
+
+#define ELEMENT_COL_0 7
+#define ELEMENT_COL_1 32
+#define ELEMENT_COL_2 58
+
+#define ELEMENT_SEL_ORG_X (ELEMENT_COL_0 + 7 + 5)
+#define ELEMENT_SEL_WIDTH (ELEMENT_COL_2 - ELEMENT_SEL_ORG_X + 1)
+
+#define TEXT_BASELINE 6
+
+
+void
+ShowRemainingCapacity (void)
+{
+ RECT r;
+ TEXT t;
+ CONTEXT OldContext;
+ UNICODE buf[40];
+
+ OldContext = SetContext (StatusContext);
+ SetContextFont (TinyFont);
+
+ r.corner.x = 40;
+ r.corner.y = FREE_ORG_Y;
+
+ snprintf (buf, sizeof buf, "%u",
+ GetStorageBayCapacity () - GLOBAL_SIS (TotalElementMass));
+ t.baseline.x = ELEMENT_COL_2 + 1;
+ t.baseline.y = r.corner.y + TEXT_BASELINE;
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ r.extent.width = t.baseline.x - r.corner.x + 1;
+ r.extent.height = ELEMENT_SPACING_Y - 2;
+
+ BatchGraphics ();
+ // erase previous free amount
+ SetContextForeGroundColor (CARGO_BACK_COLOR);
+ DrawFilledRectangle (&r);
+ // print the new free amount
+ SetContextForeGroundColor (CARGO_WORTH_COLOR);
+ font_DrawText (&t);
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static void
+DrawElementAmount (COUNT element, bool selected)
+{
+ RECT r;
+ TEXT t;
+ UNICODE buf[40];
+
+ r.corner.x = ELEMENT_SEL_ORG_X;
+ r.extent.width = ELEMENT_SEL_WIDTH;
+ r.extent.height = ELEMENT_SPACING_Y - 2;
+
+ if (element == NUM_ELEMENT_CATEGORIES)
+ r.corner.y = BIO_ORG_Y;
+ else
+ r.corner.y = ELEMENT_ORG_Y + (element * ELEMENT_SPACING_Y);
+
+ // draw line background
+ SetContextForeGroundColor (selected ?
+ CARGO_SELECTED_BACK_COLOR : CARGO_BACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+ t.baseline.y = r.corner.y + TEXT_BASELINE;
+
+ if (element == NUM_ELEMENT_CATEGORIES)
+ { // Bio
+ snprintf (buf, sizeof buf, "%u", GLOBAL_SIS (TotalBioMass));
+ }
+ else
+ { // Element
+ // print element's worth
+ SetContextForeGroundColor (selected ?
+ CARGO_SELECTED_WORTH_COLOR : CARGO_WORTH_COLOR);
+ t.baseline.x = ELEMENT_COL_1;
+ snprintf (buf, sizeof buf, "%u", GLOBAL (ElementWorth[element]));
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ snprintf (buf, sizeof buf, "%u", GLOBAL_SIS (ElementAmounts[element]));
+ }
+
+ // print the element/bio amount
+ SetContextForeGroundColor (selected ?
+ CARGO_SELECTED_AMOUNT_COLOR : CARGO_AMOUNT_COLOR);
+ t.baseline.x = ELEMENT_COL_2;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+DrawCargoDisplay (void)
+{
+ STAMP s;
+ TEXT t;
+ RECT r;
+ COORD cy;
+ COUNT i;
+
+ r.corner.x = 2;
+ r.extent.width = FIELD_WIDTH + 1;
+ r.corner.y = 20;
+ // XXX: Shouldn't the height be 1 less? This draws the bottom border
+ // 1 pixel too low. Or if not, why do we need another box anyway?
+ r.extent.height = 129 - r.corner.y;
+ DrawStarConBox (&r, 1,
+ SHADOWBOX_MEDIUM_COLOR, SHADOWBOX_DARK_COLOR,
+ TRUE, CARGO_BACK_COLOR);
+
+ // draw the "CARGO" title
+ SetContextFont (StarConFont);
+ t.baseline.x = (STATUS_WIDTH >> 1) - 1;
+ t.baseline.y = 27;
+ t.align = ALIGN_CENTER;
+ t.pStr = GAME_STRING (CARGO_STRING_BASE);
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (CARGO_SELECTED_AMOUNT_COLOR);
+ font_DrawText (&t);
+
+ SetContextFont (TinyFont);
+
+ s.frame = SetAbsFrameIndex (MiscDataFrame,
+ (NUM_SCANDOT_TRANSITIONS * 2) + 3);
+ r.corner.x = ELEMENT_COL_0;
+ r.extent = GetFrameBounds (s.frame);
+ s.origin.x = r.corner.x + (r.extent.width >> 1);
+
+ cy = ELEMENT_ORG_Y;
+
+ // print element column headings
+ t.align = ALIGN_RIGHT;
+ t.baseline.y = cy - 1;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (CARGO_WORTH_COLOR);
+ t.baseline.x = ELEMENT_COL_1;
+ t.pStr = "$";
+ font_DrawText (&t);
+
+ t.baseline.x = ELEMENT_COL_2;
+ t.pStr = "#";
+ font_DrawText (&t);
+
+ // draw element icons and print amounts
+ for (i = 0; i < NUM_ELEMENT_CATEGORIES; ++i, cy += ELEMENT_SPACING_Y)
+ {
+ // erase background under an element icon
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.y = cy;
+ DrawFilledRectangle (&r);
+
+ // draw an element icon
+ s.origin.y = r.corner.y + (r.extent.height >> 1);
+ DrawStamp (&s);
+ s.frame = SetRelFrameIndex (s.frame, 5);
+
+ DrawElementAmount (i, false);
+ }
+
+ // erase background under the Bio icon
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.y = BIO_ORG_Y;
+ DrawFilledRectangle (&r);
+
+ // draw the Bio icon
+ s.origin.y = r.corner.y + (r.extent.height >> 1);
+ s.frame = SetAbsFrameIndex (s.frame, 68);
+ DrawStamp (&s);
+
+ // print the Bio amount
+ DrawElementAmount (NUM_ELEMENT_CATEGORIES, false);
+
+ // draw the line over the Bio amount
+ r.corner.x = 4;
+ r.corner.y = BIO_ORG_Y - 2;
+ r.extent.width = FIELD_WIDTH - 3;
+ r.extent.height = 1;
+ SetContextForeGroundColor (CARGO_SELECTED_BACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ // print "Free"
+ t.baseline.x = 5;
+ t.baseline.y = FREE_ORG_Y + TEXT_BASELINE;
+ t.align = ALIGN_LEFT;
+ t.pStr = GAME_STRING (CARGO_STRING_BASE + 1);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ ShowRemainingCapacity ();
+}
+
+void
+DrawCargoStrings (BYTE OldElement, BYTE NewElement)
+{
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ SetContextFont (TinyFont);
+
+ BatchGraphics ();
+
+ if (OldElement > NUM_ELEMENT_CATEGORIES)
+ { // Asked for the initial display
+ DrawCargoDisplay ();
+
+ // do not draw unselected again this time
+ OldElement = NewElement;
+ }
+
+ if (OldElement != NewElement)
+ { // unselect the previous element
+ DrawElementAmount (OldElement, false);
+ }
+
+ if (NewElement != (BYTE)~0)
+ { // select the new element
+ DrawElementAmount (NewElement, true);
+ }
+
+ UnbatchGraphics ();
+ SetContext (OldContext);
+}
+
+static void
+DrawElementDescription (COUNT element)
+{
+ DrawStatusMessage (GAME_STRING (element + (CARGO_STRING_BASE + 2)));
+}
+
+static BOOLEAN
+DoDiscardCargo (MENU_STATE *pMS)
+{
+ BYTE NewState;
+ BOOLEAN select, cancel, back, forward;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+ back = PulsedInputState.menu[KEY_MENU_UP] || PulsedInputState.menu[KEY_MENU_LEFT];
+ forward = PulsedInputState.menu[KEY_MENU_DOWN] || PulsedInputState.menu[KEY_MENU_RIGHT];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (cancel)
+ {
+ return FALSE;
+ }
+ else if (select)
+ {
+ if (GLOBAL_SIS (ElementAmounts[pMS->CurState]))
+ {
+ --GLOBAL_SIS (ElementAmounts[pMS->CurState]);
+ DrawCargoStrings (pMS->CurState, pMS->CurState);
+
+ --GLOBAL_SIS (TotalElementMass);
+ ShowRemainingCapacity ();
+ }
+ else
+ { // no element left in cargo hold
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ else
+ {
+ NewState = pMS->CurState;
+ if (back)
+ {
+ if (NewState == 0)
+ NewState += NUM_ELEMENT_CATEGORIES;
+ --NewState;
+ }
+ else if (forward)
+ {
+ ++NewState;
+ if (NewState == NUM_ELEMENT_CATEGORIES)
+ NewState = 0;
+ }
+
+ if (NewState != pMS->CurState)
+ {
+ DrawCargoStrings (pMS->CurState, NewState);
+ DrawElementDescription (NewState);
+ pMS->CurState = NewState;
+ }
+ }
+
+ SleepThread (ONE_SECOND / 30);
+
+ return (TRUE);
+}
+
+void
+CargoMenu (void)
+{
+ MENU_STATE MenuState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ // draw the initial cargo display
+ DrawCargoStrings ((BYTE)~0, MenuState.CurState);
+ DrawElementDescription (MenuState.CurState);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoDiscardCargo;
+ DoInput (&MenuState, TRUE);
+
+ // erase the cargo display
+ ClearSISRect (DRAW_SIS_DISPLAY);
+}
+
diff --git a/src/uqm/planets/devices.c b/src/uqm/planets/devices.c
new file mode 100644
index 0000000..e781d5b
--- /dev/null
+++ b/src/uqm/planets/devices.c
@@ -0,0 +1,690 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../build.h"
+#include "../colors.h"
+#include "../gendef.h"
+#include "../starmap.h"
+#include "../encount.h"
+#include "../gamestr.h"
+#include "../controls.h"
+#include "../save.h"
+#include "../settings.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../state.h"
+#include "../sis.h"
+ // for ClearSISRect()
+#include "../grpinfo.h"
+#include "../sounds.h"
+#include "../util.h"
+#include "../hyper.h"
+ // for SaveSisHyperState()
+#include "planets.h"
+ // for SaveSolarSysLocation() and tests
+#include "libs/strlib.h"
+
+
+// If DEBUG_DEVICES is defined, the device list shown in the game will
+// include the pictures of all devices defined, regardless of which
+// devices the player actually possesses.
+//#define DEBUG_DEVICES
+
+#define DEVICE_ICON_WIDTH 16
+#define DEVICE_ICON_HEIGHT 16
+
+#define DEVICE_ORG_Y 33
+#define DEVICE_SPACING_Y (DEVICE_ICON_HEIGHT + 2)
+
+#define DEVICE_COL_0 4
+#define DEVICE_COL_1 40
+
+#define DEVICE_SEL_ORG_X (DEVICE_COL_0 + DEVICE_ICON_WIDTH)
+#define DEVICE_SEL_WIDTH (FIELD_WIDTH + 1 - DEVICE_SEL_ORG_X + 1)
+
+#define ICON_OFS_Y 1
+#define NAME_OFS_Y 2
+#define TEXT_BASELINE 6
+#define TEXT_SPACING_Y 7
+
+#define MAX_VIS_DEVICES ((129 - DEVICE_ORG_Y) / DEVICE_SPACING_Y)
+
+
+typedef enum
+{
+ DEVICE_FAILURE = 0,
+ DEVICE_SUCCESS,
+ DEVICE_SUCCESS_NO_SOUND,
+} DeviceStatus;
+
+typedef struct
+{
+ BYTE list[NUM_DEVICES];
+ // List of all devices player has
+ COUNT count;
+ // Number of devices in the list
+ COUNT topIndex;
+ // Index of the top device displayed
+} DEVICES_STATE;
+
+
+#if 0
+static void
+EraseDevicesBackground (void)
+{
+ RECT r;
+
+ r.corner.x = 2 + 1;
+ r.extent.width = FIELD_WIDTH + 1 - 2;
+ r.corner.y = DEVICE_ORG_Y;
+ r.extent.height = MAX_VIS_DEVICES * DEVICE_SPACING_Y;
+ SetContextForeGroundColor (DEVICES_BACK_COLOR);
+ DrawFilledRectangle (&r);
+}
+#endif
+
+static void
+DrawDevice (COUNT device, COUNT pos, bool selected)
+{
+ RECT r;
+ TEXT t;
+
+ t.align = ALIGN_CENTER;
+ t.baseline.x = DEVICE_COL_1;
+
+ r.extent.width = DEVICE_SEL_WIDTH;
+ r.extent.height = TEXT_SPACING_Y * 2;
+ r.corner.x = DEVICE_SEL_ORG_X;
+
+ // draw line background
+ r.corner.y = DEVICE_ORG_Y + pos * DEVICE_SPACING_Y + NAME_OFS_Y;
+ SetContextForeGroundColor (selected ?
+ DEVICES_SELECTED_BACK_COLOR : DEVICES_BACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ SetContextFont (TinyFont);
+
+ // print device name
+ SetContextForeGroundColor (selected ?
+ DEVICES_SELECTED_NAME_COLOR : DEVICES_NAME_COLOR);
+ t.baseline.y = r.corner.y + TEXT_BASELINE;
+ t.pStr = GAME_STRING (device + DEVICE_STRING_BASE + 1);
+ t.CharCount = utf8StringPos (t.pStr, ' ');
+ font_DrawText (&t);
+ t.baseline.y += TEXT_SPACING_Y;
+ t.pStr = skipUTF8Chars (t.pStr, t.CharCount + 1);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+DrawDevicesDisplay (DEVICES_STATE *devState)
+{
+ TEXT t;
+ RECT r;
+ STAMP s;
+ COORD cy;
+ COUNT i;
+
+ r.corner.x = 2;
+ r.corner.y = 20;
+ r.extent.width = FIELD_WIDTH + 1;
+ // XXX: Shouldn't the height be 1 less? This draws the bottom border
+ // 1 pixel too low. Or if not, why do we need another box anyway?
+ r.extent.height = 129 - r.corner.y;
+ DrawStarConBox (&r, 1,
+ SHADOWBOX_MEDIUM_COLOR, SHADOWBOX_DARK_COLOR,
+ TRUE, DEVICES_BACK_COLOR);
+
+ // print the "DEVICES" title
+ SetContextFont (StarConFont);
+ t.baseline.x = (STATUS_WIDTH >> 1) - 1;
+ t.baseline.y = r.corner.y + 7;
+ t.align = ALIGN_CENTER;
+ t.pStr = GAME_STRING (DEVICE_STRING_BASE);
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (DEVICES_SELECTED_NAME_COLOR);
+ font_DrawText (&t);
+
+ s.origin.x = DEVICE_COL_0;
+ cy = DEVICE_ORG_Y;
+
+ // draw device icons and print names
+ for (i = 0; i < MAX_VIS_DEVICES; ++i, cy += DEVICE_SPACING_Y)
+ {
+ COUNT devIndex = devState->topIndex + i;
+
+ if (devIndex >= devState->count)
+ break;
+
+ // draw device icon
+ s.origin.y = cy + ICON_OFS_Y;
+ s.frame = SetAbsFrameIndex (MiscDataFrame,
+ 77 + devState->list[devIndex]);
+ DrawStamp (&s);
+
+ DrawDevice (devState->list[devIndex], i, false);
+ }
+}
+
+static void
+DrawDevices (DEVICES_STATE *devState, COUNT OldDevice, COUNT NewDevice)
+{
+ BatchGraphics ();
+
+ SetContext (StatusContext);
+
+ if (OldDevice > NUM_DEVICES)
+ { // Asked for the initial display or refresh
+ DrawDevicesDisplay (devState);
+
+ // do not draw unselected again this time
+ OldDevice = NewDevice;
+ }
+
+ if (OldDevice != NewDevice)
+ { // unselect the previous element
+ DrawDevice (devState->list[OldDevice], OldDevice - devState->topIndex,
+ false);
+ }
+
+ if (NewDevice < NUM_DEVICES)
+ { // select the new element
+ DrawDevice (devState->list[NewDevice], NewDevice - devState->topIndex,
+ true);
+ }
+
+ UnbatchGraphics ();
+}
+
+// Returns TRUE if the broadcaster has been successfully activated,
+// and FALSE otherwise.
+static BOOLEAN
+UseCaster (void)
+{
+ if (inHQSpace ())
+ {
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ SET_GAME_STATE (USED_BROADCASTER, 1);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) != IN_INTERPLANETARY
+ || !playerInSolarSystem ())
+ return FALSE;
+
+ if (playerInPlanetOrbit ()
+ && matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc,
+ 1, MATCH_PLANET)
+ && CurStarDescPtr->Index == CHMMR_DEFINED
+ && !GET_GAME_STATE (CHMMR_UNLEASHED))
+ {
+ // In orbit around the Chenjesu/Mmrnmhrm home planet.
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+
+ EncounterGroup = 0;
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ SaveSolarSysLocation ();
+ return TRUE;
+ }
+
+ {
+ BOOLEAN FoundIlwrath;
+ HIPGROUP hGroup;
+
+ FoundIlwrath = (CurStarDescPtr->Index == ILWRATH_DEFINED)
+ && StartSphereTracking (ILWRATH_SHIP);
+ // In the Ilwrath home system and they are alive?
+
+ if (!FoundIlwrath &&
+ (hGroup = GetHeadLink (&GLOBAL (ip_group_q))))
+ {
+ // Is an Ilwrath ship in the system?
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ FoundIlwrath = (GroupPtr->race_id == ILWRATH_SHIP);
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+
+ if (FoundIlwrath)
+ {
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+
+ EncounterGroup = 0;
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (CurStarDescPtr->Index == ILWRATH_DEFINED)
+ {
+ // Ilwrath home system.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 4);
+ }
+ else
+ {
+ // Ilwrath ship.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 5);
+ }
+
+ if (playerInPlanetOrbit ())
+ SaveSolarSysLocation ();
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static DeviceStatus
+InvokeDevice (BYTE which_device)
+{
+ BYTE val;
+
+ switch (which_device)
+ {
+ case ROSY_SPHERE_DEVICE:
+ val = GET_GAME_STATE (ULTRON_CONDITION);
+ if (val)
+ {
+ SET_GAME_STATE (ULTRON_CONDITION, val + 1);
+ SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 0);
+ SET_GAME_STATE (DISCUSSED_ULTRON, 0);
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case ARTIFACT_2_DEVICE:
+ break;
+ case ARTIFACT_3_DEVICE:
+ break;
+ case SUN_EFFICIENCY_DEVICE:
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && playerInPlanetOrbit ())
+ {
+ PlayMenuSound (MENU_SOUND_INVOKED);
+ SleepThreadUntil (FadeScreen (FadeAllToWhite, ONE_SECOND * 1)
+ + (ONE_SECOND * 2));
+ if (CurStarDescPtr->Index != CHMMR_DEFINED
+ || !matchWorld (pSolarSysState,
+ pSolarSysState->pOrbitalDesc,
+ 1, MATCH_PLANET))
+ {
+ FadeScreen (FadeAllToColor, ONE_SECOND * 2);
+ }
+ else
+ {
+ SET_GAME_STATE (CHMMR_EMERGING, 1);
+
+ EncounterGroup = 0;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (CHMMR_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ return DEVICE_SUCCESS_NO_SOUND;
+ }
+ break;
+ case UTWIG_BOMB_DEVICE:
+ SET_GAME_STATE (UTWIG_BOMB, 0);
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
+ return DEVICE_SUCCESS;
+ case ULTRON_0_DEVICE:
+ break;
+ case ULTRON_1_DEVICE:
+ break;
+ case ULTRON_2_DEVICE:
+ break;
+ case ULTRON_3_DEVICE:
+ break;
+ case MAIDENS_DEVICE:
+ break;
+ case TALKING_PET_DEVICE:
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 0);
+ if (inHQSpace ())
+ {
+ if (GetHeadEncounter ())
+ {
+ SET_GAME_STATE (SHIP_TO_COMPEL, 1);
+ }
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+
+ SaveSisHyperState ();
+ }
+ else
+ {
+ EncounterGroup = 0;
+ if (GetHeadLink (&GLOBAL (ip_group_q)))
+ {
+ SET_GAME_STATE (SHIP_TO_COMPEL, 1);
+
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+ }
+
+ if (CurStarDescPtr->Index == SAMATRA_DEFINED)
+ {
+ SET_GAME_STATE (READY_TO_CONFUSE_URQUAN, 1);
+ }
+ if (playerInPlanetOrbit ())
+ SaveSolarSysLocation ();
+ }
+ return DEVICE_SUCCESS;
+ case AQUA_HELIX_DEVICE:
+ val = GET_GAME_STATE (ULTRON_CONDITION);
+ if (val)
+ {
+ SET_GAME_STATE (ULTRON_CONDITION, val + 1);
+ SET_GAME_STATE (AQUA_HELIX_ON_SHIP, 0);
+ SET_GAME_STATE (DISCUSSED_ULTRON, 0);
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case CLEAR_SPINDLE_DEVICE:
+ val = GET_GAME_STATE (ULTRON_CONDITION);
+ if (val)
+ {
+ SET_GAME_STATE (ULTRON_CONDITION, val + 1);
+ SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 0);
+ SET_GAME_STATE (DISCUSSED_ULTRON, 0);
+ SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case UMGAH_HYPERWAVE_DEVICE:
+ case BURVIX_HYPERWAVE_DEVICE:
+ if (UseCaster ())
+ return DEVICE_SUCCESS;
+ break;
+ case TAALO_PROTECTOR_DEVICE:
+ break;
+ case EGG_CASING0_DEVICE:
+ case EGG_CASING1_DEVICE:
+ case EGG_CASING2_DEVICE:
+ break;
+ case SYREEN_SHUTTLE_DEVICE:
+ break;
+ case VUX_BEAST_DEVICE:
+ break;
+ case DESTRUCT_CODE_DEVICE:
+ break;
+ case PORTAL_SPAWNER_DEVICE:
+#define PORTAL_FUEL_COST (10 * FUEL_TANK_SCALE)
+ if (inHyperSpace ()
+ && GLOBAL_SIS (FuelOnBoard) >= PORTAL_FUEL_COST)
+ {
+ /* No DeltaSISGauges because the flagship picture
+ * is currently obscured.
+ */
+ GLOBAL_SIS (FuelOnBoard) -= PORTAL_FUEL_COST;
+ SET_GAME_STATE (PORTAL_COUNTER, 1);
+ return DEVICE_SUCCESS;
+ }
+ break;
+ case URQUAN_WARP_DEVICE:
+ break;
+ case LUNAR_BASE_DEVICE:
+ break;
+ }
+
+ return DEVICE_FAILURE;
+}
+
+static BOOLEAN
+DoManipulateDevices (MENU_STATE *pMS)
+{
+ DEVICES_STATE *devState = pMS->privData;
+ BOOLEAN select, cancel, back, forward;
+ BOOLEAN pagefwd, pageback;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+ back = PulsedInputState.menu[KEY_MENU_UP] ||
+ PulsedInputState.menu[KEY_MENU_LEFT];
+ forward = PulsedInputState.menu[KEY_MENU_DOWN]
+ || PulsedInputState.menu[KEY_MENU_RIGHT];
+ pagefwd = PulsedInputState.menu[KEY_MENU_PAGE_DOWN];
+ pageback = PulsedInputState.menu[KEY_MENU_PAGE_UP];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (cancel)
+ {
+ return FALSE;
+ }
+ else if (select)
+ {
+ DeviceStatus status;
+
+ status = InvokeDevice (devState->list[pMS->CurState]);
+ if (status == DEVICE_FAILURE)
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ else if (status == DEVICE_SUCCESS)
+ PlayMenuSound (MENU_SOUND_INVOKED);
+
+ return (status == DEVICE_FAILURE);
+ }
+ else
+ {
+ SIZE NewTop;
+ SIZE NewState;
+
+ NewTop = devState->topIndex;
+ NewState = pMS->CurState;
+
+ if (back)
+ --NewState;
+ else if (forward)
+ ++NewState;
+ else if (pagefwd)
+ NewState += MAX_VIS_DEVICES;
+ else if (pageback)
+ NewState -= MAX_VIS_DEVICES;
+
+ if (NewState < 0)
+ NewState = 0;
+ else if (NewState >= devState->count)
+ NewState = devState->count - 1;
+
+ if (NewState < NewTop || NewState >= NewTop + MAX_VIS_DEVICES)
+ NewTop = NewState - NewState % MAX_VIS_DEVICES;
+
+ if (NewState != pMS->CurState)
+ {
+ if (NewTop != devState->topIndex)
+ { // redraw the display
+ devState->topIndex = NewTop;
+ DrawDevices (devState, (COUNT)~0, NewState);
+ }
+ else
+ { // move selection to new device
+ DrawDevices (devState, pMS->CurState, NewState);
+ }
+ pMS->CurState = NewState;
+ }
+
+ SleepThread (ONE_SECOND / 30);
+ }
+
+ return TRUE;
+}
+
+SIZE
+InventoryDevices (BYTE *pDeviceMap, COUNT Size)
+{
+ BYTE i;
+ SIZE DevicesOnBoard;
+
+ DevicesOnBoard = 0;
+ for (i = 0; i < NUM_DEVICES && Size > 0; ++i)
+ {
+ BYTE DeviceState;
+
+ DeviceState = 0;
+ switch (i)
+ {
+ case ROSY_SPHERE_DEVICE:
+ DeviceState = GET_GAME_STATE (ROSY_SPHERE_ON_SHIP);
+ break;
+ case ARTIFACT_2_DEVICE:
+ DeviceState = GET_GAME_STATE (ARTIFACT_2_ON_SHIP);
+ break;
+ case ARTIFACT_3_DEVICE:
+ DeviceState = GET_GAME_STATE (ARTIFACT_3_ON_SHIP);
+ break;
+ case SUN_EFFICIENCY_DEVICE:
+ DeviceState = GET_GAME_STATE (SUN_DEVICE_ON_SHIP);
+ break;
+ case UTWIG_BOMB_DEVICE:
+ DeviceState = GET_GAME_STATE (UTWIG_BOMB_ON_SHIP);
+ break;
+ case ULTRON_0_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 1);
+ break;
+ case ULTRON_1_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 2);
+ break;
+ case ULTRON_2_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 3);
+ break;
+ case ULTRON_3_DEVICE:
+ DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 4);
+ break;
+ case MAIDENS_DEVICE:
+ DeviceState = GET_GAME_STATE (MAIDENS_ON_SHIP);
+ break;
+ case TALKING_PET_DEVICE:
+ DeviceState = GET_GAME_STATE (TALKING_PET_ON_SHIP);
+ break;
+ case AQUA_HELIX_DEVICE:
+ DeviceState = GET_GAME_STATE (AQUA_HELIX_ON_SHIP);
+ break;
+ case CLEAR_SPINDLE_DEVICE:
+ DeviceState = GET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP);
+ break;
+ case UMGAH_HYPERWAVE_DEVICE:
+ DeviceState = GET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP);
+ break;
+ case TAALO_PROTECTOR_DEVICE:
+ DeviceState = GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP);
+ break;
+ case EGG_CASING0_DEVICE:
+ DeviceState = GET_GAME_STATE (EGG_CASE0_ON_SHIP);
+ break;
+ case EGG_CASING1_DEVICE:
+ DeviceState = GET_GAME_STATE (EGG_CASE1_ON_SHIP);
+ break;
+ case EGG_CASING2_DEVICE:
+ DeviceState = GET_GAME_STATE (EGG_CASE2_ON_SHIP);
+ break;
+ case SYREEN_SHUTTLE_DEVICE:
+ DeviceState = GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP);
+ break;
+ case VUX_BEAST_DEVICE:
+ DeviceState = GET_GAME_STATE (VUX_BEAST_ON_SHIP);
+ break;
+ case DESTRUCT_CODE_DEVICE:
+#ifdef NEVER
+ DeviceState = GET_GAME_STATE (DESTRUCT_CODE_ON_SHIP);
+#endif /* NEVER */
+ break;
+ case PORTAL_SPAWNER_DEVICE:
+ DeviceState = GET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP);
+ break;
+ case URQUAN_WARP_DEVICE:
+ DeviceState = GET_GAME_STATE (PORTAL_KEY_ON_SHIP);
+ break;
+ case BURVIX_HYPERWAVE_DEVICE:
+ DeviceState = GET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP);
+ break;
+ case LUNAR_BASE_DEVICE:
+ DeviceState = GET_GAME_STATE (MOONBASE_ON_SHIP);
+ break;
+ }
+
+#ifndef DEBUG_DEVICES
+ if (DeviceState)
+#endif /* DEBUG_DEVICES */
+ {
+ *pDeviceMap++ = i;
+ ++DevicesOnBoard;
+ --Size;
+ }
+ }
+
+ return DevicesOnBoard;
+}
+
+BOOLEAN
+DevicesMenu (void)
+{
+ MENU_STATE MenuState;
+ DEVICES_STATE DevicesState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.privData = &DevicesState;
+
+ memset (&DevicesState, 0, sizeof DevicesState);
+
+ DevicesState.count = InventoryDevices (DevicesState.list, NUM_DEVICES);
+ if (!DevicesState.count)
+ return FALSE;
+
+ DrawDevices (&DevicesState, (COUNT)~0, MenuState.CurState);
+
+ SetMenuSounds (MENU_SOUND_ARROWS | MENU_SOUND_PAGEUP | MENU_SOUND_PAGEDOWN,
+ MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoManipulateDevices;
+ DoInput (&MenuState, TRUE);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ if (GLOBAL_SIS (CrewEnlisted) != (COUNT)~0
+ && !(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ ClearSISRect (DRAW_SIS_DISPLAY);
+
+ if (!GET_GAME_STATE (PORTAL_COUNTER)
+ && !(GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
diff --git a/src/uqm/planets/elemdata.h b/src/uqm/planets/elemdata.h
new file mode 100644
index 0000000..53d228c
--- /dev/null
+++ b/src/uqm/planets/elemdata.h
@@ -0,0 +1,215 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_PLANETS_ELEMDATA_H_
+#define UQM_PLANETS_ELEMDATA_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*------------------------------ Type Defines ----------------------------- */
+enum
+{
+ COMMON = 0,
+ CORROSIVE,
+ BASE_METAL,
+ NOBLE,
+ RARE_EARTH,
+ PRECIOUS,
+ RADIOACTIVE,
+ EXOTIC,
+
+ NUM_ELEMENT_CATEGORIES
+};
+
+#define ElementCategory(et) (Elements[et] & 0x7)
+
+/*------------------------------ Global Data ------------------------------ */
+
+enum
+{
+ HYDROGEN,
+ HELIUM,
+ LITHIUM,
+ BERYLLIUM,
+ BORON,
+ CARBON,
+ NITROGEN,
+ OXYGEN,
+ FLUORINE,
+ NEON,
+ SODIUM,
+ MAGNESIUM,
+ ALUMINUM,
+ SILICON,
+ PHOSPHORUS,
+ SULFUR,
+ CHLORINE,
+ ARGON,
+ POTASSIUM,
+ CALCIUM,
+ SCANDIUM,
+ TITANIUM,
+ VANADIUM,
+ CHROMIUM,
+ MANGANESE,
+ IRON,
+ COBALT,
+ NICKEL,
+ COPPER,
+ ZINC,
+ GALLIUM,
+ GERMANIUM,
+ ARSENIC,
+ SELENIUM,
+ BROMINE,
+ KRYPTON,
+ RUBIDIUM,
+ STRONTIUM,
+ YTTRIUM,
+ ZIRCONIUM,
+ NIOBIUM,
+ MOLYBDENUM,
+ TECHNETIUM,
+ RUTHENIUM,
+ RHODIUM,
+ PALLADIUM,
+ SILVER,
+ CADMIUM,
+ INDIUM,
+ TIN,
+ ANTIMONY,
+ TELLURIUM,
+ IODINE,
+ XENON,
+ CESIUM,
+ BARIUM,
+ LANTHANUM,
+ CERIUM,
+ PRASEODYMIUM,
+ NEODYMIUM,
+ PROMETHIUM,
+ SAMARIUM,
+ EUROPIUM,
+ GADOLINIUM,
+ TERBIUM,
+ DYPROSIUM,
+ HOLMIUM,
+ ERBIUM,
+ THULIUM,
+ YTTERBIUM,
+ LUTETIUM,
+ HAFNIUM,
+ TANTALUM,
+ TUNGSTEN,
+ RHENIUM,
+ OSMIUM,
+ IRIDIUM,
+ PLATINUM,
+ GOLD,
+ MERCURY,
+ THALLIUM,
+ LEAD,
+ BISMUTH,
+ POLONIUM,
+ ASTATINE,
+ RADON,
+ FRANCIUM,
+ RADIUM,
+ ACTINIUM,
+ THORIUM,
+ PROTACTINIUM,
+ URANIUM,
+ NEPTUNIUM,
+ PLUTONIUM,
+ NUMBER_OF_NORMAL,
+
+ OZONE = NUMBER_OF_NORMAL,
+ FREE_RADICALS,
+ CARBON_DIOXIDE,
+ CARBON_MONOXIDE,
+ AMMONIA,
+ METHANE,
+ SULFURIC_ACID,
+ HYDROCHLORIC_ACID,
+ HYDROCYANIC_ACID,
+ FORMIC_ACID,
+ PHOSPHORIC_ACID,
+ FORMALDEHYDE,
+ CYANOACETYLENE,
+ METHANOL,
+ ETHANOL,
+ SILICON_MONOXIDE,
+ TITANIUM_OXIDE,
+ ZIRCONIUM_OXIDE,
+ WATER,
+ SILICON_COMPOUNDS,
+ METAL_OXIDES,
+ QUANTUM_BH,
+ NEUTRONIUM,
+ MAGNETIC_MONOPOLES,
+ DEGENERATE_MATTER,
+ RT_SUPER_FLUID,
+ AGUUTI_NODULES,
+ IRON_COMPOUNDS,
+ ALUMINUM_COMPOUNDS,
+ NITROUS_OXIDE,
+ RADIOACTIVE_COMPOUNDS,
+ HYDROCARBONS,
+ CARBON_COMPOUNDS,
+ ANTIMATTER,
+ CHARON_DUST,
+ REISBURG_HELICES,
+ TZO_CRYSTALS,
+ CALCIUM_COMPOUNDS,
+ NITRIC_ACID,
+
+ NUMBER_OF_ELEMENTS
+};
+#define NUMBER_OF_SPECIAL (NUMBER_OF_ELEMENTS - NUMBER_OF_NORMAL)
+
+#define CHANCE_MASK ((1 << 2) - 1)
+
+#define FEW 1
+#define MODERATE 4
+#define NUMEROUS 8
+
+enum
+{
+ LIGHT = 0,
+ MEDIUM,
+ HEAVY
+};
+#define NOTHING (BYTE)(~0)
+
+#define MINERAL_DEPOSIT(qn,ql) MAKE_BYTE (qn, ql)
+#define DEPOSIT_QUANTITY(md) LONIBBLE (md)
+#define DEPOSIT_QUALITY(md) HINIBBLE (md)
+
+#define MAX_ELEMENT_UNITS 0xF
+
+extern const BYTE *Elements;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_ELEMDATA_H_ */
diff --git a/src/uqm/planets/generate.h b/src/uqm/planets/generate.h
new file mode 100644
index 0000000..9942e82
--- /dev/null
+++ b/src/uqm/planets/generate.h
@@ -0,0 +1,110 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GENERATE_H
+#define GENERATE_H
+
+typedef struct GenerateFunctions GenerateFunctions;
+
+#include "types.h"
+#include "planets.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*
+ * To do (for further cleanups):
+ * - split off generateOrbital in a calculation and an activation
+ * (graphics and music) part.
+ * - make generateOrbital return a meaningful value, specifically, whether
+ * or not the player is going into orbit
+ * - for GenerateNameFunction, set the name in an argument, instead
+ * of in GLOBAL_SYS(PlanetName)
+ * - make generateName work for moons
+ * - add parameters to initNcs, reinitNpcs, and uninitNpcs, so that
+ * globals don't have to be used.
+ * - Add a reference from each world to the solar system, so that most
+ * of these functions can do with one less argument.
+ * - (maybe) don't directly call the generate functions via
+ * solarSys->genFuncs->..., but use a function for this, which first
+ * checks for solar system dependent handlers, and if this does not exist,
+ * or returns false, calls the default function.
+ */
+
+// Any of these functions returning true means that the action has been
+// handled, and that the default function should not be called.
+typedef bool (*InitNpcsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*ReinitNpcsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*UninitNpcsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*GeneratePlanetsFunction)(SOLARSYS_STATE *solarSys);
+typedef bool (*GenerateMoonsFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+typedef bool (*GenerateOrbitalFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+typedef bool (*GenerateNameFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world);
+// The following functions return the number of objects being generated
+// (or the index of the current object in some cases)
+typedef COUNT (*GenerateMineralsFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+typedef COUNT (*GenerateEnergyFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+typedef COUNT (*GenerateLifeFunction)(const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+// The following functions return true if the node should be removed
+// from the surface, i.e. picked up.
+typedef bool (*PickupMineralsFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+typedef bool (*PickupEnergyFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+typedef bool (*PickupLifeFunction)(SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+struct GenerateFunctions {
+ InitNpcsFunction initNpcs;
+ // Ships in the solar system, the first time it is accessed.
+ ReinitNpcsFunction reinitNpcs;
+ // Ships in the solar system, every next time it is accessed.
+ UninitNpcsFunction uninitNpcs;
+ // When leaving the solar system.
+ GeneratePlanetsFunction generatePlanets;
+ // Layout of planets within a solar system.
+ GenerateMoonsFunction generateMoons;
+ // Layout of moons around a planet.
+ GenerateNameFunction generateName;
+ // Name of a planet.
+ GenerateOrbitalFunction generateOrbital;
+ // Characteristics of words (planets and moons).
+ GenerateMineralsFunction generateMinerals;
+ // Minerals on the planet surface.
+ GenerateEnergyFunction generateEnergy;
+ // Energy sources on the planet surface.
+ GenerateLifeFunction generateLife;
+ // Bio on the planet surface.
+ PickupMineralsFunction pickupMinerals;
+ PickupEnergyFunction pickupEnergy;
+ PickupLifeFunction pickupLife;
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* GENERATE_H */
+
diff --git a/src/uqm/planets/generate/Makeinfo b/src/uqm/planets/generate/Makeinfo
new file mode 100644
index 0000000..520af9d
--- /dev/null
+++ b/src/uqm/planets/generate/Makeinfo
@@ -0,0 +1,6 @@
+uqm_CFILES="gendefault.c genand.c genburv.c genchmmr.c gencol.c gendru.c
+ genilw.c genmel.c genmyc.c genorz.c genpet.c genpku.c genrain.c
+ gensam.c genshof.c gensly.c gensol.c genspa.c gensup.c gensyr.c
+ genthrad.c gentrap.c genutw.c genvault.c genvux.c genwreck.c
+ genyeh.c genzfpscout.c genzoq.c"
+uqm_HFILES="genall.h gendefault.h"
diff --git a/src/uqm/planets/generate/genall.h b/src/uqm/planets/generate/genall.h
new file mode 100644
index 0000000..3776cff
--- /dev/null
+++ b/src/uqm/planets/generate/genall.h
@@ -0,0 +1,27 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_PLANETS_GENERATE_GENALL_H_
+#define UQM_PLANETS_GENERATE_GENALL_H_
+
+#include "gendefault.h"
+#include "types.h"
+#include "../generate.h"
+#include "libs/compiler.h"
+
+
+#endif /* UQM_PLANETS_GENERATE_GENALL_H_ */
+
diff --git a/src/uqm/planets/generate/genand.c b/src/uqm/planets/generate/genand.c
new file mode 100644
index 0000000..457b5ff
--- /dev/null
+++ b/src/uqm/planets/generate/genand.c
@@ -0,0 +1,164 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../scan.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "../../sounds.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateAndrosynth_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateAndrosynth_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateAndrosynth_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateAndrosynth_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateAndrosynthFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateAndrosynth_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateAndrosynth_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateAndrosynth_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateAndrosynth_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateAndrosynth_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[1].data_index = TELLURIC_WORLD;
+ solarSys->PlanetDesc[1].radius = EARTH_RADIUS * 204L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[1].location.x,
+ solarSys->PlanetDesc[1].location.y);
+ solarSys->PlanetDesc[1].location.x =
+ COSINE (angle, solarSys->PlanetDesc[1].radius);
+ solarSys->PlanetDesc[1].location.y =
+ SINE (angle, solarSys->PlanetDesc[1].radius);
+
+ return true;
+}
+
+static bool
+GenerateAndrosynth_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ COUNT i;
+ COUNT visits = 0;
+
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (ANDROSYNTH_RUINS_STRTAB));
+ // Androsynth ruins are a special case. The DiscoveryString contains
+ // several lander reports which form a story. Each report is given
+ // when the player collides with a new city ruin. Ruins previously
+ // visited are marked in the upper 16 bits of ScanRetrieveMask, and
+ // the lower bits are cleared to keep the ruin nodes on the map.
+ for (i = 16; i < 32; ++i)
+ {
+ if (isNodeRetrieved (&solarSys->SysInfo.PlanetInfo, ENERGY_SCAN, i))
+ ++visits;
+ }
+ if (visits >= GetStringTableCount (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString))
+ { // All the reports were already given
+ DestroyStringTable (ReleaseStringTable (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString = 0;
+ }
+ else
+ { // Advance the report sequence to the first unread
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetRelStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, visits);
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ EARTH_ATMOSPHERE * 144 / 100;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 28;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ }
+
+ return true;
+}
+
+static bool
+GenerateAndrosynth_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
+
+ // Ruins previously visited are marked in the upper 16 bits
+ if (isNodeRetrieved (planetInfo, ENERGY_SCAN, whichNode + 16))
+ return false; // already visited this ruin, do not remove
+
+ setNodeRetrieved (planetInfo, ENERGY_SCAN, whichNode + 16);
+ // We set the retrieved bit manually here and need to indicate
+ // the change to the solar system state functions
+ SET_GAME_STATE (PLANETARY_CHANGE, 1);
+
+ // Androsynth ruins have several lander reports which form a story
+ GenerateDefault_landerReportCycle (solarSys);
+
+ return false; // do not remove the node from the surface
+ }
+
+ return false;
+}
+
+static COUNT
+GenerateAndrosynth_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
diff --git a/src/uqm/planets/generate/genburv.c b/src/uqm/planets/generate/genburv.c
new file mode 100644
index 0000000..aa5b6bd
--- /dev/null
+++ b/src/uqm/planets/generate/genburv.c
@@ -0,0 +1,192 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateBurvixese_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateBurvixese_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateBurvixese_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateBurvixese_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateBurvixese_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateBurvixeseFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateBurvixese_generatePlanets,
+ /* .generateMoons = */ GenerateBurvixese_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateBurvixese_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateBurvixese_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateBurvixese_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateBurvixese_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 39L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ return true;
+}
+
+static bool
+GenerateBurvixese_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 0, MATCH_PLANET))
+ {
+ COUNT angle;
+ DWORD rand_val;
+
+ solarSys->MoonDesc[0].data_index = SELENIC_WORLD;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS
+ + (MAX_MOONS - 1) * MOON_DELTA;
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+ return true;
+}
+
+static bool
+GenerateBurvixese_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+ GenerateLifeForms (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[ENERGY_SCAN] = rand_val;
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (BURV_RUINS_STRTAB));
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ }
+ else if (matchWorld (solarSys, world, 0, 0)
+ && !GET_GAME_STATE (BURVIXESE_BROADCASTERS))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] = CaptureDrawable (
+ LoadGraphic (BURV_BCS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (BURV_BCS_STRTAB));
+ }
+
+ LoadPlanet (NULL);
+
+ return true;
+}
+
+static COUNT
+GenerateBurvixese_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (BURVIXESE_BROADCASTERS))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateBurvixese_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ assert (!GET_GAME_STATE (BURVIXESE_BROADCASTERS) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (BURVIXESE_BROADCASTERS, 1);
+ SET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genchmmr.c b/src/uqm/planets/generate/genchmmr.c
new file mode 100644
index 0000000..672d977
--- /dev/null
+++ b/src/uqm/planets/generate/genchmmr.c
@@ -0,0 +1,154 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../sounds.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+static bool GenerateChmmr_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateChmmr_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateChmmr_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateChmmrFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateChmmr_generatePlanets,
+ /* .generateMoons = */ GenerateChmmr_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateChmmr_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateChmmr_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[1].data_index = SAPPHIRE_WORLD;
+ if (!GET_GAME_STATE (CHMMR_UNLEASHED))
+ solarSys->PlanetDesc[1].data_index |= PLANET_SHIELDED;
+ solarSys->PlanetDesc[1].NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateChmmr_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 1, MATCH_PLANET))
+ {
+ COUNT angle;
+ DWORD rand_val;
+
+ solarSys->MoonDesc[0].data_index = HIERARCHY_STARBASE;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS;
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateChmmr_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 1, MATCH_PLANET))
+ {
+ if (GET_GAME_STATE (CHMMR_UNLEASHED))
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (CHMMR_CONVERSATION);
+
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ {
+ GLOBAL (CurrentActivity) |= END_INTERPLANETARY;
+ }
+
+ return true;
+ }
+ else if (GET_GAME_STATE (SUN_DEVICE_ON_SHIP)
+ && !GET_GAME_STATE (ILWRATH_DECEIVED)
+ && StartSphereTracking (ILWRATH_SHIP))
+ {
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (ILWRATH_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (ILWRATH_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+ }
+ else if (matchWorld (solarSys, world, 1, 0))
+ {
+ /* Starbase */
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (CHMMR_BASE_STRTAB));
+
+ DoDiscoveryReport (MenuSounds);
+
+ DestroyStringTable (ReleaseStringTable (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString = 0;
+ FreeLanderFont (&solarSys->SysInfo.PlanetInfo);
+
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gencol.c b/src/uqm/planets/generate/gencol.c
new file mode 100644
index 0000000..27e4d5b
--- /dev/null
+++ b/src/uqm/planets/generate/gencol.c
@@ -0,0 +1,126 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateColony_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateColony_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateColony_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateColonyFunctions = {
+ /* .initNpcs = */ GenerateColony_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateColony_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateColony_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateColony_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ HIPGROUP hGroup;
+
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (COLONY_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (COLONY_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+
+ GenerateDefault_initNpcs (solarSys);
+
+ if (GLOBAL (BattleGroupRef)
+ && (hGroup = GetHeadLink (&GLOBAL (ip_group_q))))
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ GroupPtr->task = IN_ORBIT;
+ GroupPtr->sys_loc = 0 + 1; /* orbitting colony */
+ GroupPtr->dest_loc = 0 + 1; /* orbitting colony */
+ GroupPtr->loc.x = 0;
+ GroupPtr->loc.y = 0;
+ GroupPtr->group_counter = 0;
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+
+ return true;
+}
+
+static bool
+GenerateColony_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+ PLANET_DESC *pMinPlanet;
+
+ pMinPlanet = &solarSys->PlanetDesc[0];
+ FillOrbits (solarSys, (BYTE)~0, pMinPlanet, FALSE);
+
+ pMinPlanet->radius = EARTH_RADIUS * 115L / 100;
+ angle = ARCTAN (pMinPlanet->location.x, pMinPlanet->location.y);
+ pMinPlanet->location.x = COSINE (angle, pMinPlanet->radius);
+ pMinPlanet->location.y = SINE (angle, pMinPlanet->radius);
+ pMinPlanet->data_index = WATER_WORLD | PLANET_SHIELDED;
+
+ return true;
+}
+
+static bool
+GenerateColony_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ EARTH_ATMOSPHERE * 98 / 100;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 28;
+
+ LoadPlanet (NULL);
+
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gendefault.c b/src/uqm/planets/generate/gendefault.c
new file mode 100644
index 0000000..a88b89c
--- /dev/null
+++ b/src/uqm/planets/generate/gendefault.c
@@ -0,0 +1,373 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../lander.h"
+#include "../../encount.h"
+#include "../../gamestr.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../races.h"
+#include "../../state.h"
+#include "../../sounds.h"
+#include "libs/mathlib.h"
+
+
+static void GeneratePlanets (SOLARSYS_STATE *system);
+static void check_yehat_rebellion (void);
+
+
+const GenerateFunctions generateDefaultFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+bool
+GenerateDefault_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP))
+ {
+ GLOBAL (BattleGroupRef) = 0;
+ BuildGroups ();
+ }
+
+ (void) solarSys;
+ return true;
+}
+
+bool
+GenerateDefault_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ // This is not a great place to do the Yehat rebellion check, but
+ // since you can start the rebellion in any star system (not just
+ // the Homeworld), I could not find a better place for it.
+ // At least it is better than where it was originally.
+ check_yehat_rebellion ();
+
+ (void) solarSys;
+ return true;
+}
+
+bool
+GenerateDefault_uninitNpcs (SOLARSYS_STATE *solarSys)
+{
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ ReinitQueue (&GLOBAL (ip_group_q));
+
+ (void) solarSys;
+ return true;
+}
+
+bool
+GenerateDefault_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ FillOrbits (solarSys, (BYTE)~0, solarSys->PlanetDesc, FALSE);
+ GeneratePlanets (solarSys);
+ return true;
+}
+
+bool
+GenerateDefault_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ FillOrbits (solarSys, planet->NumPlanets, solarSys->MoonDesc, FALSE);
+ return true;
+}
+
+bool
+GenerateDefault_generateName (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world)
+{
+ COUNT i = planetIndex (solarSys, world);
+ utf8StringCopy (GLOBAL_SIS (PlanetName), sizeof (GLOBAL_SIS (PlanetName)),
+ GAME_STRING (PLANET_NUMBER_BASE + (9 + 7) + i));
+ SET_GAME_STATE (BATTLE_PLANET, world->data_index);
+
+ return true;
+}
+
+bool
+GenerateDefault_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+ SYSTEM_INFO *sysInfo;
+
+#ifdef DEBUG_SOLARSYS
+ if (worldIsPlanet (solarSys, world))
+ {
+ log_add (log_Debug, "Planet index = %d",
+ planetIndex (solarSys, world));
+ }
+ else
+ {
+ log_add (log_Debug, "Planet index = %d, Moon index = %d",
+ planetIndex (solarSys, world),
+ moonIndex (solarSys, world));
+ }
+#endif /* DEBUG_SOLARSYS */
+
+ sysInfo = &solarSys->SysInfo;
+
+ DoPlanetaryAnalysis (sysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ sysInfo->PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+ GenerateLifeForms (sysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ sysInfo->PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (sysInfo, GENERATE_ALL, NULL);
+
+ sysInfo->PlanetInfo.ScanSeed[ENERGY_SCAN] = rand_val;
+ LoadPlanet (NULL);
+
+ return true;
+}
+
+COUNT
+GenerateDefault_generateMinerals (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ return GenerateMineralDeposits (&solarSys->SysInfo, whichNode, info);
+ (void) world;
+}
+
+bool
+GenerateDefault_pickupMinerals (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ // Minerals do not need any extra handling as of now
+ (void) solarSys;
+ (void) world;
+ (void) whichNode;
+ return true;
+}
+
+COUNT
+GenerateDefault_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ (void) whichNode;
+ (void) solarSys;
+ (void) world;
+ (void) info;
+ return 0;
+}
+
+bool
+GenerateDefault_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ // This should never be called since every energy node needs
+ // special handling and the function should be overridden
+ assert (false);
+ (void) solarSys;
+ (void) world;
+ (void) whichNode;
+ return false;
+}
+
+COUNT
+GenerateDefault_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ return GenerateLifeForms (&solarSys->SysInfo, whichNode, info);
+ (void) world;
+}
+
+bool
+GenerateDefault_pickupLife (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ // Bio does not need any extra handling as of now
+ (void) solarSys;
+ (void) world;
+ (void) whichNode;
+ return true;
+}
+
+COUNT
+GenerateDefault_generateArtifact (const SOLARSYS_STATE *solarSys,
+ COUNT whichNode, NODE_INFO *info)
+{
+ // Generate an energy node at a random location
+ return GenerateRandomNodes (&solarSys->SysInfo, ENERGY_SCAN, 1, 0,
+ whichNode, info);
+}
+
+COUNT
+GenerateDefault_generateRuins (const SOLARSYS_STATE *solarSys,
+ COUNT whichNode, NODE_INFO *info)
+{
+ // Generate a standard spread of city ruins of a destroyed civilization
+ return GenerateRandomNodes (&solarSys->SysInfo, ENERGY_SCAN, NUM_RACE_RUINS,
+ 0, whichNode, info);
+}
+
+static inline void
+runLanderReport (void)
+{
+ UnbatchGraphics ();
+ DoDiscoveryReport (MenuSounds);
+ BatchGraphics ();
+}
+
+bool
+GenerateDefault_landerReport (SOLARSYS_STATE *solarSys)
+{
+ PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
+
+ if (!planetInfo->DiscoveryString)
+ return false;
+
+ runLanderReport ();
+
+ // XXX: A non-cycling report is given only once and has to be deleted
+ // in some circumstances (like the Syreen Vault). It does not
+ // hurt to simply delete it in all cases. Nothing should rely on
+ // the presence of DiscoveryString, but the Syreen Vault and the
+ // Mycon Egg Cases rely on its absence.
+ DestroyStringTable (ReleaseStringTable (planetInfo->DiscoveryString));
+ planetInfo->DiscoveryString = 0;
+
+ return true;
+}
+
+bool
+GenerateDefault_landerReportCycle (SOLARSYS_STATE *solarSys)
+{
+ PLANET_INFO *planetInfo = &solarSys->SysInfo.PlanetInfo;
+
+ if (!planetInfo->DiscoveryString)
+ return false;
+
+ runLanderReport ();
+ // Advance to the next report
+ planetInfo->DiscoveryString = SetRelStringTableIndex (
+ planetInfo->DiscoveryString, 1);
+
+ // If our discovery strings have cycled, we're done
+ if (GetStringTableIndex (planetInfo->DiscoveryString) == 0)
+ {
+ DestroyStringTable (ReleaseStringTable (planetInfo->DiscoveryString));
+ planetInfo->DiscoveryString = 0;
+ }
+
+ return true;
+}
+
+// NB. This function modifies the RNG state.
+static void
+GeneratePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT i;
+ PLANET_DESC *planet;
+
+ for (i = solarSys->SunDesc[0].NumPlanets,
+ planet = &solarSys->PlanetDesc[0]; i; --i, ++planet)
+ {
+ DWORD rand_val;
+ BYTE byte_val;
+ BYTE num_moons;
+ BYTE type;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ byte_val = LOBYTE (rand_val);
+
+ num_moons = 0;
+ type = PlanData[planet->data_index & ~PLANET_SHIELDED].Type;
+ switch (PLANSIZE (type))
+ {
+ case LARGE_ROCKY_WORLD:
+ if (byte_val < 0x00FF * 25 / 100)
+ {
+ if (byte_val < 0x00FF * 5 / 100)
+ ++num_moons;
+ ++num_moons;
+ }
+ break;
+ case GAS_GIANT:
+ if (byte_val < 0x00FF * 90 / 100)
+ {
+ if (byte_val < 0x00FF * 75 / 100)
+ {
+ if (byte_val < 0x00FF * 50 / 100)
+ {
+ if (byte_val < 0x00FF * 25 / 100)
+ ++num_moons;
+ ++num_moons;
+ }
+ ++num_moons;
+ }
+ ++num_moons;
+ }
+ break;
+ }
+ planet->NumPlanets = num_moons;
+ }
+}
+
+static void
+check_yehat_rebellion (void)
+{
+ HIPGROUP hGroup, hNextGroup;
+
+ // XXX: Is there a better way to do this? I could not find one.
+ // When you talk to a Yehat ship (YEHAT_SHIP) and start the rebellion,
+ // there is no battle following the comm. There is *never* a battle in
+ // an encounter with Rebels, but the group race_id (YEHAT_REBEL_SHIP)
+ // is different from Royalists (YEHAT_SHIP). There is *always* a battle
+ // in an encounter with Royalists.
+ // TRANSLATION: "If the civil war has not started yet, or the player
+ // battled a ship -- bail."
+ if (!GET_GAME_STATE (YEHAT_CIVIL_WAR) || EncounterRace >= 0)
+ return; // not this time
+
+ // Send Yehat groups to flee the system, but only if the player
+ // has actually talked to a ship.
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q)); hGroup;
+ hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+ // IGNORE_FLAGSHIP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == YEHAT_SHIP
+ && (GroupPtr->task & IGNORE_FLAGSHIP))
+ {
+ GroupPtr->task &= REFORM_GROUP;
+ GroupPtr->task |= FLEE | IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+}
+
+
diff --git a/src/uqm/planets/generate/gendefault.h b/src/uqm/planets/generate/gendefault.h
new file mode 100644
index 0000000..a6d0e71
--- /dev/null
+++ b/src/uqm/planets/generate/gendefault.h
@@ -0,0 +1,66 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GENDEFAULT_H
+#define GENDEFAULT_H
+
+#include "types.h"
+#include "../planets.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool GenerateDefault_initNpcs (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_reinitNpcs (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_uninitNpcs (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_generatePlanets (SOLARSYS_STATE *solarSys);
+bool GenerateDefault_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+bool GenerateDefault_generateName (const SOLARSYS_STATE *,
+ const PLANET_DESC *world);
+bool GenerateDefault_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+COUNT GenerateDefault_generateMinerals (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+COUNT GenerateDefault_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+COUNT GenerateDefault_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+bool GenerateDefault_pickupMinerals (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+bool GenerateDefault_pickupEnergy (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+bool GenerateDefault_pickupLife (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+
+COUNT GenerateDefault_generateArtifact (const SOLARSYS_STATE *,
+ COUNT whichNode, NODE_INFO *info);
+COUNT GenerateDefault_generateRuins (const SOLARSYS_STATE *,
+ COUNT whichNode, NODE_INFO *info);
+bool GenerateDefault_landerReport (SOLARSYS_STATE *);
+bool GenerateDefault_landerReportCycle (SOLARSYS_STATE *);
+
+
+extern const GenerateFunctions generateDefaultFunctions;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* GENDEFAULT_H */
+
diff --git a/src/uqm/planets/generate/gendru.c b/src/uqm/planets/generate/gendru.c
new file mode 100644
index 0000000..7202010
--- /dev/null
+++ b/src/uqm/planets/generate/gendru.c
@@ -0,0 +1,169 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+#include <string.h>
+
+
+static bool GenerateDruuge_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateDruuge_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateDruuge_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateDruuge_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateDruugeFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDruuge_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDruuge_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDruuge_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDruuge_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateDruuge_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ memmove (&solarSys->PlanetDesc[1], &solarSys->PlanetDesc[0],
+ sizeof (solarSys->PlanetDesc[0])
+ * solarSys->SunDesc[0].NumPlanets);
+ ++solarSys->SunDesc[0].NumPlanets;
+
+ solarSys->PlanetDesc[0].data_index = DUST_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 50L / 100;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ angle = HALF_CIRCLE - OCTANT;
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].rand_seed = MAKE_DWORD (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+
+ return true;
+}
+
+static bool
+GenerateDruuge_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (DRUUGE_SHIP))
+ {
+ NotifyOthers (DRUUGE_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (DRUUGE_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (DRUUGE_CONVERSATION);
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (DRUUGE_RUINS_STRTAB));
+ if (GET_GAME_STATE (ROSY_SPHERE))
+ { // Already picked up Rosy Sphere, skip the report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static bool
+GenerateDruuge_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+
+ // The artifact can be picked up from any ruin
+ if (!GET_GAME_STATE (ROSY_SPHERE))
+ { // Just picked up the Rosy Sphere from a ruin
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (ROSY_SPHERE, 1);
+ SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 1);
+ }
+
+ return false; // do not remove the node
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateDruuge_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/genilw.c b/src/uqm/planets/generate/genilw.c
new file mode 100644
index 0000000..31a8fc4
--- /dev/null
+++ b/src/uqm/planets/generate/genilw.c
@@ -0,0 +1,150 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateIlwrath_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateIlwrath_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateIlwrath_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateIlwrath_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateIlwrathFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateIlwrath_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateIlwrath_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateIlwrath_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateIlwrath_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateIlwrath_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = PRIMORDIAL_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 204L / 100;
+ angle = ARCTAN (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateIlwrath_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (ILWRATH_SHIP))
+ {
+ NotifyOthers (ILWRATH_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (ILWRATH_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (ILWRATH_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.Weather = 2;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 3;
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateIlwrath_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateIlwrath_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genmel.c b/src/uqm/planets/generate/genmel.c
new file mode 100644
index 0000000..27f31b6
--- /dev/null
+++ b/src/uqm/planets/generate/genmel.c
@@ -0,0 +1,114 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../../build.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../state.h"
+#include "libs/log.h"
+
+
+static bool GenerateMelnorme_initNpcs (SOLARSYS_STATE *solarSys);
+
+static int SelectMelnormeRefVar (void);
+static DWORD GetMelnormeRef (void);
+static void SetMelnormeRef (DWORD Ref);
+
+
+const GenerateFunctions generateMelnormeFunctions = {
+ /* .initNpcs = */ GenerateMelnorme_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateMelnorme_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ GLOBAL (BattleGroupRef) = GetMelnormeRef ();
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (MELNORME_SHIP, &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SetMelnormeRef (GLOBAL (BattleGroupRef));
+ }
+
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+
+static int
+SelectMelnormeRefVar (void)
+{
+ switch (CurStarDescPtr->Index)
+ {
+ case MELNORME0_DEFINED: return MELNORME0_GRPOFFS0;
+ case MELNORME1_DEFINED: return MELNORME1_GRPOFFS0;
+ case MELNORME2_DEFINED: return MELNORME2_GRPOFFS0;
+ case MELNORME3_DEFINED: return MELNORME3_GRPOFFS0;
+ case MELNORME4_DEFINED: return MELNORME4_GRPOFFS0;
+ case MELNORME5_DEFINED: return MELNORME5_GRPOFFS0;
+ case MELNORME6_DEFINED: return MELNORME6_GRPOFFS0;
+ case MELNORME7_DEFINED: return MELNORME7_GRPOFFS0;
+ case MELNORME8_DEFINED: return MELNORME8_GRPOFFS0;
+ default:
+ return -1;
+ }
+}
+
+static DWORD
+GetMelnormeRef (void)
+{
+ int RefVar = SelectMelnormeRefVar ();
+ if (RefVar < 0)
+ {
+ log_add (log_Warning, "GetMelnormeRef(): reference unknown");
+ return 0;
+ }
+
+ return GET_GAME_STATE_32 (RefVar);
+}
+
+static void
+SetMelnormeRef (DWORD Ref)
+{
+ int RefVar = SelectMelnormeRefVar ();
+ if (RefVar < 0)
+ {
+ log_add (log_Warning, "SetMelnormeRef(): reference unknown");
+ return;
+ }
+
+ SET_GAME_STATE_32 (RefVar, Ref);
+}
+
diff --git a/src/uqm/planets/generate/genmyc.c b/src/uqm/planets/generate/genmyc.c
new file mode 100644
index 0000000..ead32c7
--- /dev/null
+++ b/src/uqm/planets/generate/genmyc.c
@@ -0,0 +1,286 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../scan.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateMycon_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateMycon_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateMycon_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateMycon_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateMycon_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateMyconFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateMycon_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateMycon_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateMycon_generateEnergy,
+ /* .generateLife = */ GenerateMycon_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateMycon_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateMycon_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = SHATTERED_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 80L / 100;
+ if (solarSys->PlanetDesc[0].NumPlanets > 2)
+ solarSys->PlanetDesc[0].NumPlanets = 2;
+ angle = ARCTAN (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateMycon_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if ((CurStarDescPtr->Index == MYCON_DEFINED
+ || CurStarDescPtr->Index == SUN_DEVICE_DEFINED)
+ && StartSphereTracking (MYCON_SHIP))
+ {
+ if (CurStarDescPtr->Index == MYCON_DEFINED
+ || !GET_GAME_STATE (SUN_DEVICE_UNGUARDED))
+ {
+ NotifyOthers (MYCON_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (CurStarDescPtr->Index == MYCON_DEFINED
+ || !GET_GAME_STATE (MYCON_FELL_FOR_AMBUSH))
+ {
+ CloneShipFragment (MYCON_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ }
+ else
+ {
+ COUNT i;
+
+ for (i = 0; i < 5; ++i)
+ CloneShipFragment (MYCON_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ if (CurStarDescPtr->Index == MYCON_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ InitCommunication (MYCON_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ BOOLEAN MyconSurvivors;
+
+ MyconSurvivors =
+ GetHeadLink (&GLOBAL (npc_built_ship_q)) != 0;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (MyconSurvivors)
+ return true;
+
+ SET_GAME_STATE (SUN_DEVICE_UNGUARDED, 1);
+ RepairSISBorder ();
+ }
+ }
+ }
+
+ switch (CurStarDescPtr->Index)
+ {
+ case SUN_DEVICE_DEFINED:
+ if (!GET_GAME_STATE (SUN_DEVICE))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (SUN_DEVICE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (SUN_DEVICE_STRTAB));
+ }
+ break;
+ case EGG_CASE0_DEFINED:
+ case EGG_CASE1_DEFINED:
+ case EGG_CASE2_DEFINED:
+ if (GET_GAME_STATE (KNOW_ABOUT_SHATTERED) == 0)
+ SET_GAME_STATE (KNOW_ABOUT_SHATTERED, 1);
+
+ if (!isNodeRetrieved (&solarSys->SysInfo.PlanetInfo,
+ ENERGY_SCAN, 0))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (EGG_CASE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (EGG_CASE_STRTAB));
+ }
+ break;
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateMycon_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == SUN_DEVICE_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (SUN_DEVICE))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ if ((CurStarDescPtr->Index == EGG_CASE0_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE1_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE2_DEFINED)
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ // XXX: DiscoveryString is set by generateOrbital() only when the
+ // node has not been picked up yet
+ if (!solarSys->SysInfo.PlanetInfo.DiscoveryString)
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateMycon_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == SUN_DEVICE_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (SUN_DEVICE) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (SUN_DEVICE, 1);
+ SET_GAME_STATE (SUN_DEVICE_ON_SHIP, 1);
+ SET_GAME_STATE (MYCON_VISITS, 0);
+
+ return true; // picked up
+ }
+
+ if ((CurStarDescPtr->Index == EGG_CASE0_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE1_DEFINED
+ || CurStarDescPtr->Index == EGG_CASE2_DEFINED)
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ switch (CurStarDescPtr->Index)
+ {
+ case EGG_CASE0_DEFINED:
+ SET_GAME_STATE (EGG_CASE0_ON_SHIP, 1);
+ break;
+ case EGG_CASE1_DEFINED:
+ SET_GAME_STATE (EGG_CASE1_ON_SHIP, 1);
+ break;
+ case EGG_CASE2_DEFINED:
+ SET_GAME_STATE (EGG_CASE2_ON_SHIP, 1);
+ break;
+ }
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateMycon_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ // Gee, I wonder why there isn't any life in Mycon systems...
+ (void) whichNode;
+ (void) solarSys;
+ (void) world;
+ (void) info;
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/genorz.c b/src/uqm/planets/generate/genorz.c
new file mode 100644
index 0000000..a50f318
--- /dev/null
+++ b/src/uqm/planets/generate/genorz.c
@@ -0,0 +1,222 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateOrz_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateOrz_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateOrz_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateOrz_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateOrzFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateOrz_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateOrz_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateOrz_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateOrz_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateOrz_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 156L / 100;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateOrz_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if ((CurStarDescPtr->Index == ORZ_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ || (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED
+ && matchWorld (solarSys, world, 1, 2)
+ && !GET_GAME_STATE (TAALO_PROTECTOR)))
+ {
+ COUNT i;
+
+ if ((CurStarDescPtr->Index == ORZ_DEFINED
+ || !GET_GAME_STATE (TAALO_UNPROTECTED))
+ && StartSphereTracking (ORZ_SHIP))
+ {
+ NotifyOthers (ORZ_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED)
+ {
+ CloneShipFragment (ORZ_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ for (i = 0; i < 14; ++i)
+ {
+ CloneShipFragment (ORZ_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (ORZ_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ BOOLEAN OrzSurvivors;
+
+ OrzSurvivors = GetHeadLink (&GLOBAL (npc_built_ship_q))
+ && (CurStarDescPtr->Index == ORZ_DEFINED
+ || !GET_GAME_STATE (TAALO_UNPROTECTED));
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (OrzSurvivors)
+ return true;
+
+ RepairSISBorder ();
+ }
+ }
+
+ SET_GAME_STATE (TAALO_UNPROTECTED, 1);
+ if (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED)
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (TAALO_DEVICE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (TAALO_DEVICE_STRTAB));
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static COUNT
+GenerateOrz_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED
+ && matchWorld (solarSys, world, 1, 2))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (TAALO_PROTECTOR))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateOrz_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == TAALO_PROTECTOR_DEFINED
+ && matchWorld (solarSys, world, 1, 2))
+ {
+ assert (!GET_GAME_STATE (TAALO_PROTECTOR) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (TAALO_PROTECTOR, 1);
+ SET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ if (CurStarDescPtr->Index == ORZ_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genpet.c b/src/uqm/planets/generate/genpet.c
new file mode 100644
index 0000000..4c5515c
--- /dev/null
+++ b/src/uqm/planets/generate/genpet.c
@@ -0,0 +1,257 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../encount.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateTalkingPet_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateTalkingPet_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateTalkingPet_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateTalkingPet_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+static void ZapToUrquanEncounter (void);
+
+
+const GenerateFunctions generateTalkingPetFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateTalkingPet_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateTalkingPet_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateTalkingPet_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateTalkingPet_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateTalkingPet_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = TELLURIC_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 204L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateTalkingPet_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET)
+ && (GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES)
+ || !GET_GAME_STATE (TALKING_PET)
+ || StartSphereTracking (UMGAH_SHIP)))
+ {
+ NotifyOthers (UMGAH_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (StartSphereTracking (UMGAH_SHIP))
+ {
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ if (!GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES))
+ {
+ CloneShipFragment (UMGAH_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ InitCommunication (UMGAH_CONVERSATION);
+ }
+ else
+ {
+ COUNT i;
+
+ for (i = 0; i < 10; ++i)
+ {
+ CloneShipFragment (UMGAH_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ InitCommunication (TALKING_PET_CONVERSATION);
+ }
+ }
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ BOOLEAN UmgahSurvivors;
+
+ UmgahSurvivors = GetHeadLink (
+ &GLOBAL (npc_built_ship_q)) != 0;
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+
+ if (GET_GAME_STATE (PLAYER_HYPNOTIZED))
+ ZapToUrquanEncounter ();
+ else if (GET_GAME_STATE (UMGAH_ZOMBIE_BLOBBIES)
+ && !UmgahSurvivors)
+ {
+ // Defeated the zombie fleet.
+ InitCommunication (TALKING_PET_CONVERSATION);
+ }
+ else if (!(StartSphereTracking (UMGAH_SHIP)))
+ {
+ // The Kohr-Ah have destroyed the Umgah, but the
+ // talking pet survived.
+ InitCommunication (TALKING_PET_CONVERSATION);
+ }
+
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+
+ return true;
+}
+
+static COUNT
+GenerateTalkingPet_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateTalkingPet_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static void
+ZapToUrquanEncounter (void)
+{
+ HENCOUNTER hEncounter;
+
+ if ((hEncounter = AllocEncounter ()) || (hEncounter = GetHeadEncounter ()))
+ {
+ SIZE dx, dy;
+ ENCOUNTER *EncounterPtr;
+ HFLEETINFO hStarShip;
+ FLEET_INFO *TemplatePtr;
+ BRIEF_SHIP_INFO *BSIPtr;
+
+ LockEncounter (hEncounter, &EncounterPtr);
+
+ if (hEncounter == GetHeadEncounter ())
+ RemoveEncounter (hEncounter);
+ memset (EncounterPtr, 0, sizeof (*EncounterPtr));
+
+ InsertEncounter (hEncounter, GetHeadEncounter ());
+
+ hStarShip = GetStarShipFromIndex (&GLOBAL (avail_race_q), URQUAN_SHIP);
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ EncounterPtr->origin = TemplatePtr->loc;
+ EncounterPtr->radius = TemplatePtr->actual_strength;
+ EncounterPtr->race_id = URQUAN_SHIP;
+ EncounterPtr->num_ships = 1;
+ EncounterPtr->flags = ONE_SHOT_ENCOUNTER;
+ BSIPtr = &EncounterPtr->ShipList[0];
+ BSIPtr->race_id = URQUAN_SHIP;
+ BSIPtr->crew_level = TemplatePtr->crew_level;
+ BSIPtr->max_crew = TemplatePtr->max_crew;
+ BSIPtr->max_energy = TemplatePtr->max_energy;
+ EncounterPtr->loc_pt.x = 5288;
+ EncounterPtr->loc_pt.y = 4892;
+ EncounterPtr->log_x = UNIVERSE_TO_LOGX (EncounterPtr->loc_pt.x);
+ EncounterPtr->log_y = UNIVERSE_TO_LOGY (EncounterPtr->loc_pt.y);
+ GLOBAL_SIS (log_x) = EncounterPtr->log_x;
+ GLOBAL_SIS (log_y) = EncounterPtr->log_y;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+
+ {
+#define LOST_DAYS 15
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND * 2));
+ MoveGameClockDays (LOST_DAYS);
+ }
+
+ GLOBAL (CurrentActivity) = MAKE_WORD (IN_HYPERSPACE, 0) | START_ENCOUNTER;
+
+ dx = CurStarDescPtr->star_pt.x - EncounterPtr->loc_pt.x;
+ dy = CurStarDescPtr->star_pt.y - EncounterPtr->loc_pt.y;
+ dx = (SIZE)square_root ((long)dx * dx + (long)dy * dy)
+ + (FUEL_TANK_SCALE >> 1);
+
+ DeltaSISGauges (0, -dx, 0);
+ if (GLOBAL_SIS (FuelOnBoard) < 5 * FUEL_TANK_SCALE)
+ {
+ dx = ((5 + ((COUNT)TFB_Random () % 5)) * FUEL_TANK_SCALE)
+ - (SIZE)GLOBAL_SIS (FuelOnBoard);
+ DeltaSISGauges (0, dx, 0);
+ }
+ DrawSISMessage (NULL);
+ DrawHyperCoords (EncounterPtr->loc_pt);
+
+ UnlockEncounter (hEncounter);
+ }
+}
+
diff --git a/src/uqm/planets/generate/genpku.c b/src/uqm/planets/generate/genpku.c
new file mode 100644
index 0000000..64b9965
--- /dev/null
+++ b/src/uqm/planets/generate/genpku.c
@@ -0,0 +1,159 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GeneratePkunk_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GeneratePkunk_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GeneratePkunk_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GeneratePkunk_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generatePkunkFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GeneratePkunk_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GeneratePkunk_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GeneratePkunk_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GeneratePkunk_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GeneratePkunk_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 104L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GeneratePkunk_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (PKUNK_SHIP))
+ {
+ NotifyOthers (PKUNK_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (PKUNK_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (PKUNK_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (PKUNK_RUINS_STRTAB));
+ if (GET_GAME_STATE (CLEAR_SPINDLE))
+ { // Already picked up the Clear Spindle, skip the report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static bool
+GeneratePkunk_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+
+ // The artifact can be picked up from any ruin
+ if (!GET_GAME_STATE (CLEAR_SPINDLE))
+ { // Just picked up the Clear Spindle from a ruin
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (CLEAR_SPINDLE, 1);
+ SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 1);
+ }
+
+ return false; // do not remove the node
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GeneratePkunk_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/genrain.c b/src/uqm/planets/generate/genrain.c
new file mode 100644
index 0000000..c149b29
--- /dev/null
+++ b/src/uqm/planets/generate/genrain.c
@@ -0,0 +1,102 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateRainbowWorld_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateRainbowWorld_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateRainbowWorldFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateRainbowWorld_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateRainbowWorld_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateRainbowWorld_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = RAINBOW_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 50L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ if (angle <= QUADRANT)
+ angle += QUADRANT;
+ else if (angle >= FULL_CIRCLE - QUADRANT)
+ angle -= QUADRANT;
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateRainbowWorld_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ BYTE which_rainbow;
+ UWORD rainbow_mask;
+ STAR_DESC *SDPtr;
+
+ rainbow_mask = MAKE_WORD (
+ GET_GAME_STATE (RAINBOW_WORLD0),
+ GET_GAME_STATE (RAINBOW_WORLD1));
+
+ which_rainbow = 0;
+ SDPtr = &star_array[0];
+ while (SDPtr != CurStarDescPtr)
+ {
+ if (SDPtr->Index == RAINBOW_DEFINED)
+ ++which_rainbow;
+ ++SDPtr;
+ }
+ rainbow_mask |= 1 << which_rainbow;
+ SET_GAME_STATE (RAINBOW_WORLD0, LOBYTE (rainbow_mask));
+ SET_GAME_STATE (RAINBOW_WORLD1, HIBYTE (rainbow_mask));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gensam.c b/src/uqm/planets/generate/gensam.c
new file mode 100644
index 0000000..b2398b3
--- /dev/null
+++ b/src/uqm/planets/generate/gensam.c
@@ -0,0 +1,324 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../encount.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSaMatra_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSaMatra_reinitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSaMatra_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSaMatra_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSaMatra_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+static void BuildUrquanGuard (SOLARSYS_STATE *solarSys);
+
+
+const GenerateFunctions generateSaMatraFunctions = {
+ /* .initNpcs = */ GenerateSaMatra_initNpcs,
+ /* .reinitNpcs = */ GenerateSaMatra_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSaMatra_generatePlanets,
+ /* .generateMoons = */ GenerateSaMatra_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSaMatra_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSaMatra_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GET_GAME_STATE (URQUAN_MESSED_UP))
+ {
+ BuildUrquanGuard (solarSys);
+ }
+ else
+ { // Exorcise Ur-Quan ghosts upon system reentry
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ // wipe out the group
+ }
+
+ (void) solarSys;
+ return true;
+}
+
+static bool
+GenerateSaMatra_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ BOOLEAN GuardEngaged;
+ HIPGROUP hGroup;
+ HIPGROUP hNextGroup;
+
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ EncounterGroup = 0;
+ EncounterRace = -1;
+ // Do not want guards to chase the player
+
+ GuardEngaged = FALSE;
+ for (hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ hGroup; hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ if (GET_GAME_STATE (URQUAN_MESSED_UP))
+ {
+ GroupPtr->task &= REFORM_GROUP;
+ GroupPtr->task |= FLEE | IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = 0;
+ }
+ else if (GroupPtr->task & REFORM_GROUP)
+ {
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision
+ // during a collision with the flagship.
+ GroupPtr->task &= ~REFORM_GROUP;
+ GroupPtr->group_counter = 0;
+
+ GuardEngaged = TRUE;
+ }
+
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ }
+
+ if (GuardEngaged)
+ {
+ COUNT angle;
+ POINT org;
+
+ org = planetOuterLocation (4);
+ angle = ARCTAN (GLOBAL (ip_location.x) - org.x,
+ GLOBAL (ip_location.y) - org.y);
+ GLOBAL (ip_location.x) = org.x + COSINE (angle, 3000);
+ GLOBAL (ip_location.y) = org.y + SINE (angle, 3000);
+ XFormIPLoc (&GLOBAL (ip_location),
+ &GLOBAL (ShipStamp.origin), TRUE);
+ }
+
+ (void) solarSys;
+ return true;
+}
+
+static bool
+GenerateSaMatra_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+ solarSys->PlanetDesc[4].NumPlanets = 1;
+ return true;
+}
+
+static bool
+GenerateSaMatra_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 4, MATCH_PLANET))
+ {
+ COUNT angle;
+ DWORD rand_val;
+
+ solarSys->MoonDesc[0].data_index = SA_MATRA;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS + (2 * MOON_DELTA);
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSaMatra_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ /* Samatra */
+ if (matchWorld (solarSys, world, 4, 0))
+ {
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (!GET_GAME_STATE (URQUAN_MESSED_UP))
+ {
+ CloneShipFragment (!GET_GAME_STATE (KOHR_AH_FRENZY) ?
+ URQUAN_SHIP : BLACK_URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ }
+ else
+ {
+#define URQUAN_REMNANTS 3
+ BYTE i;
+
+ for (i = 0; i < URQUAN_REMNANTS; ++i)
+ {
+ CloneShipFragment (URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ CloneShipFragment (BLACK_URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ }
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ SET_GAME_STATE (URQUAN_PROTECTING_SAMATRA, 1);
+ InitCommunication (URQUAN_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ BOOLEAN UrquanSurvivors;
+
+ UrquanSurvivors = GetHeadLink (&GLOBAL (npc_built_ship_q)) != 0;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ if (UrquanSurvivors)
+ {
+ SET_GAME_STATE (URQUAN_PROTECTING_SAMATRA, 0);
+ }
+ else
+ {
+ EncounterGroup = 0;
+ EncounterRace = -1;
+ GLOBAL (CurrentActivity) = IN_LAST_BATTLE | START_ENCOUNTER;
+ if (GET_GAME_STATE (YEHAT_CIVIL_WAR)
+ && StartSphereTracking (YEHAT_SHIP)
+ && EscortFeasibilityStudy (YEHAT_REBEL_SHIP))
+ InitCommunication (YEHAT_REBEL_CONVERSATION);
+ }
+ }
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static void
+BuildUrquanGuard (SOLARSYS_STATE *solarSys)
+{
+ BYTE ship1, ship2;
+ BYTE b0, b1;
+ POINT org;
+ HIPGROUP hGroup, hNextGroup;
+
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (SAMATRA_GRPOFFS0);
+
+ if (!GET_GAME_STATE (KOHR_AH_FRENZY))
+ {
+ ship1 = URQUAN_SHIP;
+ ship2 = BLACK_URQUAN_SHIP;
+ }
+ else
+ {
+ ship1 = BLACK_URQUAN_SHIP;
+ ship2 = URQUAN_SHIP;
+ }
+
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ for (b0 = 0; b0 < MAX_SHIPS_PER_SIDE; ++b0)
+ CloneShipFragment (ship1, &GLOBAL (npc_built_ship_q), 0);
+
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ SET_GAME_STATE_32 (SAMATRA_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+
+#define NUM_URQUAN_GUARDS0 12
+ for (b0 = 1; b0 <= NUM_URQUAN_GUARDS0; ++b0)
+ PutGroupInfo (GLOBAL (BattleGroupRef), b0);
+
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ for (b0 = 0; b0 < MAX_SHIPS_PER_SIDE; ++b0)
+ CloneShipFragment (ship2, &GLOBAL (npc_built_ship_q), 0);
+
+#define NUM_URQUAN_GUARDS1 4
+ for (b0 = 1; b0 <= NUM_URQUAN_GUARDS1; ++b0)
+ PutGroupInfo (GLOBAL (BattleGroupRef),
+ (BYTE)(NUM_URQUAN_GUARDS0 + b0));
+
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+
+ GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP);
+
+ org = planetOuterLocation (4);
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ for (b0 = 0, b1 = 0;
+ b0 < NUM_URQUAN_GUARDS0;
+ ++b0, b1 += FULL_CIRCLE / (NUM_URQUAN_GUARDS0 + NUM_URQUAN_GUARDS1))
+ {
+ IP_GROUP *GroupPtr;
+
+ if (b1 % (FULL_CIRCLE / NUM_URQUAN_GUARDS1) == 0)
+ b1 += FULL_CIRCLE / (NUM_URQUAN_GUARDS0 + NUM_URQUAN_GUARDS1);
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+ GroupPtr->task = ON_STATION | IGNORE_FLAGSHIP;
+ GroupPtr->sys_loc = 0;
+ GroupPtr->dest_loc = 4 + 1;
+ GroupPtr->orbit_pos = NORMALIZE_FACING (ANGLE_TO_FACING (b1));
+ GroupPtr->group_counter = 0;
+ GroupPtr->loc.x = org.x + COSINE (b1, STATION_RADIUS);
+ GroupPtr->loc.y = org.y + SINE (b1, STATION_RADIUS);
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hGroup = hNextGroup;
+ }
+
+ for (b0 = 0, b1 = 0;
+ b0 < NUM_URQUAN_GUARDS1;
+ ++b0, b1 += FULL_CIRCLE / NUM_URQUAN_GUARDS1)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+ GroupPtr->task = ON_STATION | IGNORE_FLAGSHIP;
+ GroupPtr->sys_loc = 0;
+ GroupPtr->dest_loc = 4 + 1;
+ GroupPtr->orbit_pos = NORMALIZE_FACING (ANGLE_TO_FACING (b1));
+ GroupPtr->group_counter = 0;
+ GroupPtr->loc.x = org.x + COSINE (b1, STATION_RADIUS);
+ GroupPtr->loc.y = org.y + SINE (b1, STATION_RADIUS);
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ hGroup = hNextGroup;
+ }
+
+ (void) solarSys;
+}
+
diff --git a/src/uqm/planets/generate/genshof.c b/src/uqm/planets/generate/genshof.c
new file mode 100644
index 0000000..7025f6a
--- /dev/null
+++ b/src/uqm/planets/generate/genshof.c
@@ -0,0 +1,178 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../../build.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+#include "../planets.h"
+
+
+static bool GenerateShofixti_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateShofixti_reinitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateShofixti_uninitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateShofixti_generatePlanets (SOLARSYS_STATE *solarSys);
+
+static void check_old_shofixti (void);
+
+
+const GenerateFunctions generateShofixtiFunctions = {
+ /* .initNpcs = */ GenerateShofixti_initNpcs,
+ /* .reinitNpcs = */ GenerateShofixti_reinitNpcs,
+ /* .uninitNpcs = */ GenerateShofixti_uninitNpcs,
+ /* .generatePlanets = */ GenerateShofixti_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateShofixti_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GET_GAME_STATE (SHOFIXTI_RECRUITED)
+ && (!GET_GAME_STATE (SHOFIXTI_KIA)
+ || (!GET_GAME_STATE (SHOFIXTI_BRO_KIA)
+ && GET_GAME_STATE (MAIDENS_ON_SHIP))))
+ {
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (SHOFIXTI_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0
+ || !GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP))
+ {
+ HSHIPFRAG hStarShip;
+
+ if (GLOBAL (BattleGroupRef) == 0)
+ GLOBAL (BattleGroupRef) = ~0L;
+
+ hStarShip = CloneShipFragment (SHOFIXTI_SHIP,
+ &GLOBAL (npc_built_ship_q), 1);
+ if (hStarShip)
+ { /* Set old Shofixti name; his brother if Tanaka died */
+ SHIP_FRAGMENT *FragPtr = LockShipFrag (
+ &GLOBAL (npc_built_ship_q), hStarShip);
+ /* Name Tanaka or Katana (+1) */
+ FragPtr->captains_name_index =
+ NAME_OFFSET + NUM_CAPTAINS_NAMES +
+ (GET_GAME_STATE (SHOFIXTI_KIA) & 1);
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+
+ GLOBAL (BattleGroupRef) = PutGroupInfo (
+ GLOBAL (BattleGroupRef), 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (SHOFIXTI_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+ }
+
+ // This was originally a fallthrough to REINIT_NPCS.
+ // XXX: is the call to check_old_shofixti() needed?
+ GenerateDefault_initNpcs (solarSys);
+ check_old_shofixti ();
+
+ return true;
+}
+
+static bool
+GenerateShofixti_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_reinitNpcs (solarSys);
+ check_old_shofixti ();
+
+ (void) solarSys;
+ return true;
+}
+
+static bool
+GenerateShofixti_uninitNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (GLOBAL (BattleGroupRef)
+ && !GET_GAME_STATE (SHOFIXTI_RECRUITED)
+ && GetHeadLink (&GLOBAL (ip_group_q)) == 0)
+ {
+ if (!GET_GAME_STATE (SHOFIXTI_KIA))
+ {
+ SET_GAME_STATE (SHOFIXTI_KIA, 1);
+ SET_GAME_STATE (SHOFIXTI_VISITS, 0);
+ }
+ else if (GET_GAME_STATE (MAIDENS_ON_SHIP))
+ {
+ SET_GAME_STATE (SHOFIXTI_BRO_KIA, 1);
+ }
+ }
+
+ GenerateDefault_uninitNpcs (solarSys);
+ return true;
+}
+
+static bool
+GenerateShofixti_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT i;
+
+#define NUM_PLANETS 6
+ solarSys->SunDesc[0].NumPlanets = NUM_PLANETS;
+ for (i = 0; i < NUM_PLANETS; ++i)
+ {
+ PLANET_DESC *pCurDesc = &solarSys->PlanetDesc[i];
+
+ pCurDesc->NumPlanets = 0;
+ if (i < (NUM_PLANETS >> 1))
+ pCurDesc->data_index = SELENIC_WORLD;
+ else
+ pCurDesc->data_index = METAL_WORLD;
+ }
+
+ FillOrbits (solarSys, NUM_PLANETS, solarSys->PlanetDesc, TRUE);
+
+ return true;
+}
+
+
+static void
+check_old_shofixti (void)
+{
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ if (!GLOBAL (BattleGroupRef))
+ return; // nothing to check
+
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ if (!hGroup)
+ return; // still nothing to check
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == SHOFIXTI_SHIP
+ && (GroupPtr->task & REFORM_GROUP)
+ && GET_GAME_STATE (SHOFIXTI_RECRUITED))
+ {
+ GroupPtr->task = FLEE | IGNORE_FLAGSHIP | REFORM_GROUP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+}
+
diff --git a/src/uqm/planets/generate/gensly.c b/src/uqm/planets/generate/gensly.c
new file mode 100644
index 0000000..48ed100
--- /dev/null
+++ b/src/uqm/planets/generate/gensly.c
@@ -0,0 +1,70 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../comm.h"
+
+
+static bool GenerateSlylandro_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSlylandro_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateSlylandroFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSlylandro_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSlylandro_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSlylandro_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[3].data_index = RED_GAS_GIANT;
+ solarSys->PlanetDesc[3].NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateSlylandro_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 3, MATCH_PLANET))
+ {
+ InitCommunication (SLYLANDRO_HOME_CONVERSATION);
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/gensol.c b/src/uqm/planets/generate/gensol.c
new file mode 100644
index 0000000..d6041f3
--- /dev/null
+++ b/src/uqm/planets/generate/gensol.c
@@ -0,0 +1,671 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../lifeform.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../encount.h"
+#include "../../globdata.h"
+#include "../../gamestr.h"
+#include "../../grpinfo.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSol_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSol_reinitNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateSol_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSol_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSol_generateName (const SOLARSYS_STATE *,
+ const PLANET_DESC *world);
+static bool GenerateSol_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateSol_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateSol_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateSol_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+static int init_probe (void);
+static void check_probe (void);
+
+
+const GenerateFunctions generateSolFunctions = {
+ /* .initNpcs = */ GenerateSol_initNpcs,
+ /* .reinitNpcs = */ GenerateSol_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSol_generatePlanets,
+ /* .generateMoons = */ GenerateSol_generateMoons,
+ /* .generateName = */ GenerateSol_generateName,
+ /* .generateOrbital = */ GenerateSol_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateSol_generateEnergy,
+ /* .generateLife = */ GenerateSol_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateSol_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSol_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (URQUAN_PROBE_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (URQUAN_DRONE_SHIP, &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (URQUAN_PROBE_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+
+ if (!init_probe ())
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+static bool
+GenerateSol_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) != 3)
+ {
+ GenerateDefault_reinitNpcs (solarSys);
+ check_probe ();
+ }
+ else
+ {
+ GLOBAL (BattleGroupRef) = 0;
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+ }
+ return true;
+}
+
+static bool
+GenerateSol_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT planetI;
+
+#define SOL_SEED 334241042L
+ RandomContext_SeedRandom (SysGenRNG, SOL_SEED);
+
+ solarSys->SunDesc[0].NumPlanets = 9;
+ for (planetI = 0; planetI < 9; ++planetI)
+ {
+ COUNT angle;
+ DWORD rand_val;
+ UWORD word_val;
+ PLANET_DESC *pCurDesc = &solarSys->PlanetDesc[planetI];
+
+ pCurDesc->rand_seed = RandomContext_Random (SysGenRNG);
+ rand_val = pCurDesc->rand_seed;
+ word_val = LOWORD (rand_val);
+ angle = NORMALIZE_ANGLE ((COUNT)HIBYTE (word_val));
+
+ switch (planetI)
+ {
+ case 0: /* MERCURY */
+ pCurDesc->data_index = METAL_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 39L / 100;
+ pCurDesc->NumPlanets = 0;
+ break;
+ case 1: /* VENUS */
+ pCurDesc->data_index = PRIMORDIAL_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 72L / 100;
+ pCurDesc->NumPlanets = 0;
+ angle = NORMALIZE_ANGLE (FULL_CIRCLE - angle);
+ break;
+ case 2: /* EARTH */
+ pCurDesc->data_index = WATER_WORLD | PLANET_SHIELDED;
+ pCurDesc->radius = EARTH_RADIUS;
+ pCurDesc->NumPlanets = 2;
+ break;
+ case 3: /* MARS */
+ pCurDesc->data_index = DUST_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 152L / 100;
+ pCurDesc->NumPlanets = 0;
+ break;
+ case 4: /* JUPITER */
+ pCurDesc->data_index = RED_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 500L /* 520L */ / 100;
+ pCurDesc->NumPlanets = 4;
+ break;
+ case 5: /* SATURN */
+ pCurDesc->data_index = ORA_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 750L /* 952L */ / 100;
+ pCurDesc->NumPlanets = 1;
+ break;
+ case 6: /* URANUS */
+ pCurDesc->data_index = GRN_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 1000L /* 1916L */ / 100;
+ pCurDesc->NumPlanets = 0;
+ break;
+ case 7: /* NEPTUNE */
+ pCurDesc->data_index = BLU_GAS_GIANT;
+ pCurDesc->radius = EARTH_RADIUS * 1250L /* 2999L */ / 100;
+ pCurDesc->NumPlanets = 1;
+ break;
+ case 8: /* PLUTO */
+ pCurDesc->data_index = PELLUCID_WORLD;
+ pCurDesc->radius = EARTH_RADIUS * 1550L /* 3937L */ / 100;
+ pCurDesc->NumPlanets = 0;
+ angle = FULL_CIRCLE - OCTANT;
+ break;
+ }
+
+ pCurDesc->location.x = COSINE (angle, pCurDesc->radius);
+ pCurDesc->location.y = SINE (angle, pCurDesc->radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSol_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ COUNT planetNr;
+ DWORD rand_val;
+
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ planetNr = planetIndex (solarSys, planet);
+ switch (planetNr)
+ {
+ case 2: /* moons of EARTH */
+ {
+ COUNT angle;
+
+ /* Starbase: */
+ solarSys->MoonDesc[0].data_index = HIERARCHY_STARBASE;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS;
+ angle = HALF_CIRCLE + QUADRANT;
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+
+ /* Luna: */
+ solarSys->MoonDesc[1].data_index = SELENIC_WORLD;
+ solarSys->MoonDesc[1].radius = MIN_MOON_RADIUS
+ + (MAX_MOONS - 1) * MOON_DELTA;
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ solarSys->MoonDesc[1].location.x =
+ COSINE (angle, solarSys->MoonDesc[1].radius);
+ solarSys->MoonDesc[1].location.y =
+ SINE (angle, solarSys->MoonDesc[1].radius);
+ break;
+ }
+ case 4: /* moons of JUPITER */
+ solarSys->MoonDesc[0].data_index = RADIOACTIVE_WORLD;
+ /* Io */
+ solarSys->MoonDesc[1].data_index = HALIDE_WORLD;
+ /* Europa */
+ solarSys->MoonDesc[2].data_index = CYANIC_WORLD;
+ /* Ganymede */
+ solarSys->MoonDesc[3].data_index = PELLUCID_WORLD;
+ /* Callisto */
+ break;
+ case 5: /* moons of SATURN */
+ solarSys->MoonDesc[0].data_index = ALKALI_WORLD;
+ /* Titan */
+ break;
+ case 7: /* moons of NEPTUNE */
+ solarSys->MoonDesc[0].data_index = VINYLOGOUS_WORLD;
+ /* Triton */
+ break;
+ }
+
+ return true;
+}
+
+static bool
+GenerateSol_generateName (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world)
+{
+ COUNT planetNr = planetIndex (solarSys, world);
+ utf8StringCopy (GLOBAL_SIS (PlanetName), sizeof (GLOBAL_SIS (PlanetName)),
+ GAME_STRING (PLANET_NUMBER_BASE + planetNr));
+ SET_GAME_STATE (BATTLE_PLANET, solarSys->PlanetDesc[planetNr].data_index);
+
+ return true;
+}
+
+static bool
+GenerateSol_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+ COUNT planetNr;
+
+ if (matchWorld (solarSys, world, 2, 0))
+ {
+ /* Starbase */
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ EncounterGroup = 0;
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ return true;
+ }
+
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ planetNr = planetIndex (solarSys, world);
+ if (worldIsPlanet (solarSys, world))
+ {
+ switch (planetNr)
+ {
+ case 0: /* MERCURY */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 0;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 98;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 38;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 3;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 2;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 59 * 240;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 165;
+ break;
+ case 1: /* VENUS */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 90 *
+ EARTH_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 95;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 95;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 177;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 243 * 240;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 457;
+ break;
+ case 2: /* EARTH */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ EARTH_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 100;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 100;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 23;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 240;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 22;
+ break;
+ case 3: /* MARS */
+ // XXX: Mars atmo should actually be 1/2 in current units
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 1;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 72;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 53;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 24;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 246;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -53;
+ break;
+ case 4: /* JUPITER */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 24;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 1120;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 3;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 98;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -143;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 520L / 100;
+ break;
+ case 5: /* SATURN */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 13;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 945;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 27;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 102;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -197;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 952L / 100;
+ break;
+ case 6: /* URANUS */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 21;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 411;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 98;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 172;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -217;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 1916L / 100;
+ break;
+ case 7: /* NEPTUNE */
+ solarSys->SysInfo.PlanetInfo.AtmoDensity =
+ GAS_GIANT_ATMOSPHERE;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 28;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 396;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 30;
+ solarSys->SysInfo.PlanetInfo.Weather = 7;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 182;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -229;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 2999L / 100;
+ break;
+ case 8: /* PLUTO */
+ if (!GET_GAME_STATE (FOUND_PLUTO_SPATHI))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (SPAPLUTO_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (SPAPLUTO_STRTAB));
+ }
+
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 0;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 33;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 18;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 119;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 1533;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -235;
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 3937L / 100;
+ break;
+ }
+
+ solarSys->SysInfo.PlanetInfo.SurfaceGravity =
+ CalcGravity (&solarSys->SysInfo.PlanetInfo);
+ LoadPlanet (planetNr == 2 ?
+ CaptureDrawable (LoadGraphic (EARTH_MASK_ANIM)) : NULL);
+ }
+ else
+ {
+ // World is a moon.
+ COUNT moonNr = moonIndex (solarSys, world);
+
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 0;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 0;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ switch (planetNr)
+ {
+ case 2: /* moons of EARTH */
+ // NOTE: Even though we save the seed here, it is irrelevant.
+ // The seed will be used to randomly place the tractors, but
+ // since they are mobile, they will be moved to different
+ // locations not governed by this seed.
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] =
+ rand_val;
+
+ if (!GET_GAME_STATE (MOONBASE_DESTROYED))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (
+ LoadGraphic (MOONBASE_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (MOONBASE_STRTAB));
+ }
+
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 60;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 25;
+ solarSys->SysInfo.PlanetInfo.AxialTilt = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 240 * 29;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -18;
+ break;
+
+ case 4: /* moons of JUPITER */
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 520L / 100;
+ switch (moonNr)
+ {
+ case 0: /* Io */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 69;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 25;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 3;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 390;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -163;
+ break;
+ case 1: /* Europa */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 54;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 25;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 840;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -161;
+ break;
+ case 2: /* Ganymede */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 35;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 41;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 1728;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -164;
+ break;
+ case 3: /* Callisto */
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 35;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 38;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 4008;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -167;
+ break;
+ }
+ break;
+
+ case 5: /* moon of SATURN: Titan */
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 952L / 100;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 160;
+ solarSys->SysInfo.PlanetInfo.Weather = 2;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 34;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 40;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 3816;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -178;
+ break;
+
+ case 7: /* moon of NEPTUNE: Triton */
+ solarSys->SysInfo.PlanetInfo.PlanetToSunDist =
+ EARTH_RADIUS * 2999L / 100;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = 10;
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.PlanetDensity = 95;
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 27;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.RotationPeriod = 4300;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = -216;
+ break;
+ }
+
+ solarSys->SysInfo.PlanetInfo.SurfaceGravity =
+ CalcGravity (&solarSys->SysInfo.PlanetInfo);
+ LoadPlanet (NULL);
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateSol_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 8, MATCH_PLANET))
+ {
+ /* Pluto */
+ // This check is needed because the retrieval bit is not set for
+ // this node to keep it on the surface while the lander is taking off
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI))
+ { // already picked up
+ return 0;
+ }
+
+ if (info)
+ {
+ info->loc_pt.x = 20;
+ info->loc_pt.y = MAP_HEIGHT - 8;
+ }
+
+ return 1; // only matters when count is requested
+ }
+
+ if (matchWorld (solarSys, world, 2, 1))
+ {
+ /* Earth Moon */
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (MOONBASE_DESTROYED))
+ { // already picked up
+ return 0;
+ }
+
+ if (info)
+ {
+ info->loc_pt.x = MAP_WIDTH * 3 / 4;
+ info->loc_pt.y = MAP_HEIGHT * 1 / 4;
+ }
+
+ return 1; // only matters when count is requested
+ }
+
+ (void) whichNode;
+ return 0;
+}
+
+static bool
+GenerateSol_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 8, MATCH_PLANET))
+ { // Pluto
+ assert (!GET_GAME_STATE (FOUND_PLUTO_SPATHI) && whichNode == 0);
+
+ // Ran into Fwiffo on Pluto
+ #define FWIFFO_FRAGS 8
+ if (!KillLanderCrewSeq (FWIFFO_FRAGS, ONE_SECOND / 20))
+ return false; // lander probably died
+
+ SET_GAME_STATE (FOUND_PLUTO_SPATHI, 1);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ // Do not remove the node from the surface while the lander is
+ // taking off. FOUND_PLUTO_SPATHI bit will keep the node from
+ // showing up on subsequent visits.
+ return false;
+ }
+
+ if (matchWorld (solarSys, world, 2, 1))
+ { // Earth Moon
+ assert (!GET_GAME_STATE (MOONBASE_DESTROYED) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (MOONBASE_DESTROYED, 1);
+ SET_GAME_STATE (MOONBASE_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateSol_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 2, 1))
+ {
+ /* Earth Moon */
+ return GenerateRandomNodes (&solarSys->SysInfo, BIOLOGICAL_SCAN, 10,
+ NUM_CREATURE_TYPES + 1, whichNode, info);
+ }
+
+ return 0;
+}
+
+
+static int
+init_probe (void)
+{
+ HIPGROUP hGroup;
+
+ if (!GET_GAME_STATE (PROBE_MESSAGE_DELIVERED)
+ && GetGroupInfo (GLOBAL (BattleGroupRef), GROUP_INIT_IP)
+ && (hGroup = GetHeadLink (&GLOBAL (ip_group_q))))
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ GroupPtr->task = IN_ORBIT;
+ GroupPtr->sys_loc = 2 + 1; /* orbitting earth */
+ GroupPtr->dest_loc = 2 + 1; /* orbitting earth */
+ GroupPtr->loc.x = 0;
+ GroupPtr->loc.y = 0;
+ GroupPtr->group_counter = 0;
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void
+check_probe (void)
+{
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ if (!GLOBAL (BattleGroupRef))
+ return; // nothing to check
+
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ if (!hGroup)
+ return; // still nothing to check
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == URQUAN_DRONE_SHIP
+ && (GroupPtr->task & REFORM_GROUP))
+ {
+ // We just want the probe to take off as fast as possible,
+ // so clear out REFORM_GROUP
+ GroupPtr->task = FLEE | IGNORE_FLAGSHIP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+}
+
diff --git a/src/uqm/planets/generate/genspa.c b/src/uqm/planets/generate/genspa.c
new file mode 100644
index 0000000..26bc412
--- /dev/null
+++ b/src/uqm/planets/generate/genspa.c
@@ -0,0 +1,283 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lifeform.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../scan.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSpathi_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSpathi_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSpathi_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateSpathi_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateSpathi_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateSpathi_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+static bool GenerateSpathi_pickupLife (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateSpathiFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSpathi_generatePlanets,
+ /* .generateMoons = */ GenerateSpathi_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSpathi_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateSpathi_generateEnergy,
+ /* .generateLife = */ GenerateSpathi_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateSpathi_pickupEnergy,
+ /* .pickupLife = */ GenerateSpathi_pickupLife,
+};
+
+
+static bool
+GenerateSpathi_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ PLANET_DESC *pMinPlanet;
+ COUNT angle;
+
+ pMinPlanet = &solarSys->PlanetDesc[0];
+ solarSys->SunDesc[0].NumPlanets = 1;
+ FillOrbits (solarSys,
+ solarSys->SunDesc[0].NumPlanets, pMinPlanet, FALSE);
+
+ pMinPlanet->radius = EARTH_RADIUS * 1150L / 100;
+ angle = ARCTAN (pMinPlanet->location.x, pMinPlanet->location.y);
+ pMinPlanet->location.x = COSINE (angle, pMinPlanet->radius);
+ pMinPlanet->location.y = SINE (angle, pMinPlanet->radius);
+ pMinPlanet->data_index = WATER_WORLD;
+ if (GET_GAME_STATE (SPATHI_SHIELDED_SELVES))
+ pMinPlanet->data_index |= PLANET_SHIELDED;
+ pMinPlanet->NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateSpathi_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ COUNT angle;
+
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 0, MATCH_PLANET))
+ {
+#ifdef NOTYET
+ utf8StringCopy (GLOBAL_SIS (PlanetName),
+ sizeof (GLOBAL_SIS (PlanetName)),
+ "Spathiwa");
+#endif /* NOTYET */
+
+ solarSys->MoonDesc[0].data_index = PELLUCID_WORLD;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS + MOON_DELTA;
+ angle = NORMALIZE_ANGLE (LOWORD (RandomContext_Random (SysGenRNG)));
+ solarSys->MoonDesc[0].location.x =
+ COSINE (angle, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (angle, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSpathi_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ DWORD rand_val;
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ /* Spathiwa's moon */
+ if (!GET_GAME_STATE (SPATHI_SHIELDED_SELVES)
+ && StartSphereTracking (SPATHI_SHIP))
+ {
+ NotifyOthers (SPATHI_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (SPATHI_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (SPATHI_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+ GenerateLifeForms (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[ENERGY_SCAN] = rand_val;
+
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 28;
+ if (!GET_GAME_STATE (UMGAH_BROADCASTERS))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (UMGAH_BCS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (UMGAH_BCS_STRTAB));
+ if (!GET_GAME_STATE (SPATHI_SHIELDED_SELVES))
+ { // The first report talks extensively about Spathi
+ // slave-shielding selves. If they never did so, the report
+ // makes no sense, so use an alternate.
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ LoadPlanet (NULL);
+ return true;
+ }
+ else if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ /* visiting Spathiwa */
+ DoPlanetaryAnalysis (&solarSys->SysInfo, world);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[MINERAL_SCAN] = rand_val;
+ GenerateMineralDeposits (&solarSys->SysInfo, GENERATE_ALL, NULL);
+ rand_val = RandomContext_GetSeed (SysGenRNG);
+
+ solarSys->SysInfo.PlanetInfo.ScanSeed[BIOLOGICAL_SCAN] = rand_val;
+
+ solarSys->SysInfo.PlanetInfo.PlanetRadius = 120;
+ solarSys->SysInfo.PlanetInfo.SurfaceGravity =
+ CalcGravity (&solarSys->SysInfo.PlanetInfo);
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 31;
+
+ LoadPlanet (NULL);
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static COUNT
+GenerateSpathi_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (UMGAH_BROADCASTERS))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateSpathi_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ assert (!GET_GAME_STATE (UMGAH_BROADCASTERS) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (UMGAH_BROADCASTERS, 1);
+ SET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateSpathi_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ #define NUM_EVIL_ONES 32
+ return GenerateRandomNodes (&solarSys->SysInfo, BIOLOGICAL_SCAN, NUM_EVIL_ONES,
+ NUM_CREATURE_TYPES, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateSpathi_pickupLife (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (SPATHI_CREATURES_ELIMINATED) &&
+ !GET_GAME_STATE (SPATHI_SHIELDED_SELVES));
+
+ SET_GAME_STATE (SPATHI_CREATURES_EXAMINED, 1);
+ if (countNodesRetrieved (&solarSys->SysInfo.PlanetInfo, BIOLOGICAL_SCAN)
+ + 1 == NUM_EVIL_ONES)
+ { // last creature picked up
+ SET_GAME_STATE (SPATHI_CREATURES_ELIMINATED, 1);
+ }
+
+ return true; // picked up
+ }
+
+ return GenerateDefault_pickupLife (solarSys, world, whichNode);
+}
diff --git a/src/uqm/planets/generate/gensup.c b/src/uqm/planets/generate/gensup.c
new file mode 100644
index 0000000..a618c89
--- /dev/null
+++ b/src/uqm/planets/generate/gensup.c
@@ -0,0 +1,159 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateSupox_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSupox_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateSupox_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateSupox_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateSupoxFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSupox_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSupox_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateSupox_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateSupox_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSupox_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 2;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 152L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateSupox_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (SUPOX_SHIP))
+ {
+ NotifyOthers (SUPOX_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (SUPOX_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (SUPOX_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (SUPOX_RUINS_STRTAB));
+ if (GET_GAME_STATE (ULTRON_CONDITION))
+ { // Already picked up the Ultron, skip the report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
+static bool
+GenerateSupox_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+
+ // The artifact can be picked up from any ruin
+ if (!GET_GAME_STATE (ULTRON_CONDITION))
+ { // Just picked up the Ultron from a ruin
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (ULTRON_CONDITION, 1);
+ }
+
+ return false; // do not remove the node
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateSupox_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/planets/generate/gensyr.c b/src/uqm/planets/generate/gensyr.c
new file mode 100644
index 0000000..ebf3be4
--- /dev/null
+++ b/src/uqm/planets/generate/gensyr.c
@@ -0,0 +1,102 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../comm.h"
+
+static bool GenerateSyreen_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateSyreen_generateMoons (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *planet);
+static bool GenerateSyreen_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateSyreenFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateSyreen_generatePlanets,
+ /* .generateMoons = */ GenerateSyreen_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateSyreen_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateSyreen_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD | PLANET_SHIELDED;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+
+ return true;
+}
+
+static bool
+GenerateSyreen_generateMoons (SOLARSYS_STATE *solarSys, PLANET_DESC *planet)
+{
+ GenerateDefault_generateMoons (solarSys, planet);
+
+ if (matchWorld (solarSys, planet, 0, MATCH_PLANET))
+ {
+ solarSys->MoonDesc[0].data_index = HIERARCHY_STARBASE;
+ solarSys->MoonDesc[0].radius = MIN_MOON_RADIUS;
+ solarSys->MoonDesc[0].location.x =
+ COSINE (QUADRANT, solarSys->MoonDesc[0].radius);
+ solarSys->MoonDesc[0].location.y =
+ SINE (QUADRANT, solarSys->MoonDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateSyreen_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ /* Syreen home planet */
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 19;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ solarSys->SysInfo.PlanetInfo.Weather = 0;
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = EARTH_ATMOSPHERE * 9 / 10;
+ return true;
+ }
+
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ /* Starbase */
+ InitCommunication (SYREEN_CONVERSATION);
+ return true;
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/genthrad.c b/src/uqm/planets/generate/genthrad.c
new file mode 100644
index 0000000..875e582
--- /dev/null
+++ b/src/uqm/planets/generate/genthrad.c
@@ -0,0 +1,217 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateThraddash_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateThraddash_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateThraddash_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateThraddash_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateThraddashFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateThraddash_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateThraddash_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateThraddash_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateThraddash_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateThraddash_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = PRIMORDIAL_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 65L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+ else /* CurStarDescPtr->Index == THRADD_DEFINED */
+ {
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 98L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+ return true;
+}
+
+static bool
+GenerateThraddash_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (THRADDASH_SHIP)
+ && (CurStarDescPtr->Index == THRADD_DEFINED
+ || (!GET_GAME_STATE (HELIX_UNPROTECTED)
+ && (BYTE)(GET_GAME_STATE (THRADD_MISSION) - 1) >= 3)))
+ {
+ NotifyOthers (THRADDASH_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (THRADDASH_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ if (CurStarDescPtr->Index == THRADD_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ InitCommunication (THRADD_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (CurStarDescPtr->Index == THRADD_DEFINED
+ || (!GET_GAME_STATE (HELIX_UNPROTECTED)
+ && (BYTE)(GET_GAME_STATE (THRADD_MISSION) - 1) >= 3))
+ return true;
+
+ RepairSISBorder ();
+ }
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED
+ && !GET_GAME_STATE (AQUA_HELIX))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (AQUA_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (AQUA_STRTAB));
+ }
+ else if (CurStarDescPtr->Index == THRADD_DEFINED)
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateThraddash_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == THRADD_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (AQUA_HELIX))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateThraddash_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == THRADD_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ if (CurStarDescPtr->Index == AQUA_HELIX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (AQUA_HELIX) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (HELIX_VISITS, 0);
+ SET_GAME_STATE (AQUA_HELIX, 1);
+ SET_GAME_STATE (AQUA_HELIX_ON_SHIP, 1);
+ SET_GAME_STATE (HELIX_UNPROTECTED, 1);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/gentrap.c b/src/uqm/planets/generate/gentrap.c
new file mode 100644
index 0000000..e8451cd
--- /dev/null
+++ b/src/uqm/planets/generate/gentrap.c
@@ -0,0 +1,80 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+
+
+static bool GenerateTrap_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateTrap_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+
+
+const GenerateFunctions generateTrapFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateTrap_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateTrap_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateTrap_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = TELLURIC_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 203L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateTrap_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.AtmoDensity = EARTH_ATMOSPHERE * 2;
+ solarSys->SysInfo.PlanetInfo.SurfaceTemperature = 35;
+ solarSys->SysInfo.PlanetInfo.Weather = 3;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ }
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/genutw.c b/src/uqm/planets/generate/genutw.c
new file mode 100644
index 0000000..71ac2aa
--- /dev/null
+++ b/src/uqm/planets/generate/genutw.c
@@ -0,0 +1,269 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateUtwig_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateUtwig_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateUtwig_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateUtwig_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateUtwig_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateUtwigFunctions = {
+ /* .initNpcs = */ GenerateUtwig_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateUtwig_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateUtwig_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateUtwig_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateUtwig_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateUtwig_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && !GET_GAME_STATE (UTWIG_BOMB))
+ {
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+ }
+ else
+ {
+ GenerateDefault_initNpcs (solarSys);
+ }
+
+ return true;
+}
+
+static bool
+GenerateUtwig_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == UTWIG_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 174L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+
+ return true;
+}
+
+static bool
+GenerateUtwig_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if ((CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ || (CurStarDescPtr->Index == BOMB_DEFINED
+ && matchWorld (solarSys, world, 5, 1)
+ && !GET_GAME_STATE (UTWIG_BOMB)))
+ {
+ if ((CurStarDescPtr->Index == UTWIG_DEFINED
+ || !GET_GAME_STATE (UTWIG_HAVE_ULTRON))
+ && StartSphereTracking (UTWIG_SHIP))
+ {
+ NotifyOthers (UTWIG_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (UTWIG_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ if (CurStarDescPtr->Index == UTWIG_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+ InitCommunication (UTWIG_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+ return true;
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && !GET_GAME_STATE (BOMB_UNPROTECTED)
+ && StartSphereTracking (DRUUGE_SHIP))
+ {
+ COUNT i;
+
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ for (i = 0; i < 5; ++i)
+ {
+ CloneShipFragment (DRUUGE_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ }
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ InitCommunication (DRUUGE_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ BOOLEAN DruugeSurvivors;
+
+ DruugeSurvivors =
+ GetHeadLink (&GLOBAL (npc_built_ship_q)) != 0;
+
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (DruugeSurvivors)
+ return true;
+
+ RepairSISBorder ();
+ SET_GAME_STATE (BOMB_UNPROTECTED, 1);
+ }
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED)
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (BOMB_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (BOMB_STRTAB));
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.Weather = 1;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 1;
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateUtwig_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && matchWorld (solarSys, world, 5, 1))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (UTWIG_BOMB))
+ { // already picked up
+ return 0;
+ }
+
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateUtwig_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == UTWIG_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ if (CurStarDescPtr->Index == BOMB_DEFINED
+ && matchWorld (solarSys, world, 5, 1))
+ {
+ assert (!GET_GAME_STATE (UTWIG_BOMB) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (UTWIG_BOMB, 1);
+ SET_GAME_STATE (UTWIG_BOMB_ON_SHIP, 1);
+ SET_GAME_STATE (DRUUGE_MANNER, 1);
+ SET_GAME_STATE (DRUUGE_VISITS, 0);
+ SET_GAME_STATE (DRUUGE_HOME_VISITS, 0);
+
+ return true; // picked up
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genvault.c b/src/uqm/planets/generate/genvault.c
new file mode 100644
index 0000000..e189897
--- /dev/null
+++ b/src/uqm/planets/generate/genvault.c
@@ -0,0 +1,130 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateVault_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateVault_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateVault_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateVaultFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateVault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateVault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateVault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateVault_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (VAULT_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (VAULT_STRTAB));
+ if (GET_GAME_STATE (SHIP_VAULT_UNLOCKED))
+ {
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 2);
+ }
+ else if (GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP))
+ {
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateVault_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateVault_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, 0))
+ {
+ assert (whichNode == 0);
+
+ if (GET_GAME_STATE (SHIP_VAULT_UNLOCKED))
+ { // Give the final report, "omg empty" and whatnot
+ GenerateDefault_landerReportCycle (solarSys);
+ }
+ else if (GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP))
+ {
+ GenerateDefault_landerReportCycle (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (SHIP_VAULT_UNLOCKED, 1);
+ SET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP, 0);
+ SET_GAME_STATE (SYREEN_HOME_VISITS, 0);
+ }
+ else
+ {
+ GenerateDefault_landerReport (solarSys);
+
+ if (!GET_GAME_STATE (KNOW_SYREEN_VAULT))
+ {
+ SET_GAME_STATE (KNOW_SYREEN_VAULT, 1);
+ }
+ }
+
+ // The Vault cannot be "picked up". It is always on the surface.
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genvux.c b/src/uqm/planets/generate/genvux.c
new file mode 100644
index 0000000..d4a0642
--- /dev/null
+++ b/src/uqm/planets/generate/genvux.c
@@ -0,0 +1,329 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../lifeform.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../gendef.h"
+#include "../../starmap.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../setup.h"
+#include "../../sounds.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateVux_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateVux_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateVux_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static COUNT GenerateVux_generateLife (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateVux_pickupEnergy (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+static bool GenerateVux_pickupLife (SOLARSYS_STATE *, PLANET_DESC *world,
+ COUNT whichNode);
+
+
+const GenerateFunctions generateVuxFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateVux_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateVux_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateVux_generateEnergy,
+ /* .generateLife = */ GenerateVux_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateVux_pickupEnergy,
+ /* .pickupLife = */ GenerateVux_pickupLife,
+};
+
+
+static bool
+GenerateVux_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED)
+ {
+ GenerateDefault_generatePlanets (solarSys);
+ // XXX: this is the second time that this function is
+ // called. Is it safe to remove one, or does this change
+ // the RNG so that the outcome is different?
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 212L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ }
+ else
+ {
+ if (CurStarDescPtr->Index == VUX_DEFINED)
+ {
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 42L / 100;
+ angle = HALF_CIRCLE + OCTANT;
+ }
+ else /* if (CurStarDescPtr->Index == VUX_BEAST_DEFINED) */
+ {
+ memmove (&solarSys->PlanetDesc[1], &solarSys->PlanetDesc[0],
+ sizeof (solarSys->PlanetDesc[0])
+ * solarSys->SunDesc[0].NumPlanets);
+ ++solarSys->SunDesc[0].NumPlanets;
+
+ angle = HALF_CIRCLE - OCTANT;
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 110L / 100;
+ solarSys->PlanetDesc[0].NumPlanets = 0;
+ }
+
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].rand_seed = MAKE_DWORD (
+ solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ }
+ return true;
+}
+
+static bool
+GenerateVux_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if ((matchWorld (solarSys, world, 0, MATCH_PLANET)
+ && (CurStarDescPtr->Index == VUX_DEFINED
+ || (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && !GET_GAME_STATE (ZEX_IS_DEAD))))
+ && StartSphereTracking (VUX_SHIP))
+ {
+ NotifyOthers (VUX_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (VUX_SHIP,
+ &GLOBAL (npc_built_ship_q), INFINITE_FLEET);
+ if (CurStarDescPtr->Index == VUX_DEFINED)
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ }
+ else
+ {
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 6);
+ }
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ InitCommunication (VUX_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+
+ if (CurStarDescPtr->Index == VUX_DEFINED
+ || !GET_GAME_STATE (ZEX_IS_DEAD))
+ return true;
+
+ RepairSISBorder ();
+ }
+ }
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED)
+ {
+ if (!GET_GAME_STATE (SHOFIXTI_MAIDENS))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] = CaptureDrawable (
+ LoadGraphic (MAIDENS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (MAIDENS_STRTAB));
+ }
+ }
+ else if (CurStarDescPtr->Index == VUX_BEAST_DEFINED)
+ {
+ if (!GET_GAME_STATE (VUX_BEAST))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] = 0;
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (
+ LoadStringTable (BEAST_STRTAB));
+ }
+ }
+ else
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ solarSys->SysInfo.PlanetInfo.Weather = 2;
+ solarSys->SysInfo.PlanetInfo.Tectonics = 0;
+ }
+
+ return true;
+}
+
+static COUNT
+GenerateVux_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // This check is redundant since the retrieval bit will keep the
+ // node from showing up again
+ if (GET_GAME_STATE (SHOFIXTI_MAIDENS))
+ { // already picked up
+ return 0;
+ }
+
+ if (info)
+ {
+ info->loc_pt.x = MAP_WIDTH / 3;
+ info->loc_pt.y = MAP_HEIGHT * 5 / 8;
+ }
+
+ return 1; // only matters when count is requested
+ }
+
+ if (CurStarDescPtr->Index == VUX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateVux_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ assert (!GET_GAME_STATE (SHOFIXTI_MAIDENS) && whichNode == 0);
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (SHOFIXTI_MAIDENS, 1);
+ SET_GAME_STATE (MAIDENS_ON_SHIP, 1);
+
+ return true; // picked up
+ }
+
+ if (CurStarDescPtr->Index == VUX_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
+
+static COUNT
+GenerateVux_generateLife (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (CurStarDescPtr->Index == MAIDENS_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ static const SBYTE life[] =
+ {
+ 9, 9, 9, 9, /* Carousel Beast */
+ 14, 14, 14, 14, /* Amorphous Trandicula */
+ 18, 18, 18, 18, /* Penguin Cyclops */
+ -1 /* term */
+ };
+ return GeneratePresetLife (&solarSys->SysInfo, life, whichNode, info);
+ }
+
+ if (CurStarDescPtr->Index == VUX_BEAST_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ static const SBYTE life[] =
+ {
+ NUM_CREATURE_TYPES + 2, /* VUX Beast */
+ // Must be the first node, see pickupLife() below
+ 3, 3, 3, 3, 3, /* Whackin' Bush */
+ 8, 8, 8, 8, 8, /* Glowing Medusa */
+ -1 /* term */
+ };
+ return GeneratePresetLife (&solarSys->SysInfo, life, whichNode, info);
+ }
+
+ return GenerateDefault_generateLife (solarSys, world, whichNode, info);
+}
+
+static bool
+GenerateVux_pickupLife (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (CurStarDescPtr->Index == VUX_BEAST_DEFINED
+ && matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (whichNode == 0)
+ { // Picked up Zex' Beauty
+ assert (!GET_GAME_STATE (VUX_BEAST));
+
+ GenerateDefault_landerReport (solarSys);
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (VUX_BEAST, 1);
+ SET_GAME_STATE (VUX_BEAST_ON_SHIP, 1);
+ }
+
+ return true; // picked up
+ }
+
+ return GenerateDefault_pickupLife (solarSys, world, whichNode);
+}
diff --git a/src/uqm/planets/generate/genwreck.c b/src/uqm/planets/generate/genwreck.c
new file mode 100644
index 0000000..05e956e
--- /dev/null
+++ b/src/uqm/planets/generate/genwreck.c
@@ -0,0 +1,111 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../lander.h"
+#include "../planets.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../resinst.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateWreck_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateWreck_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateWreck_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateWreckFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateWreck_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateWreck_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateWreck_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateWreck_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 6, MATCH_PLANET))
+ {
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (WRECK_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (WRECK_STRTAB));
+ if (GET_GAME_STATE (PORTAL_KEY))
+ { // Already picked it up, skip the first report
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ SetAbsStringTableIndex (
+ solarSys->SysInfo.PlanetInfo.DiscoveryString, 1);
+ }
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateWreck_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 6, MATCH_PLANET))
+ {
+ return GenerateDefault_generateArtifact (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateWreck_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 6, MATCH_PLANET))
+ {
+ assert (whichNode == 0);
+
+ GenerateDefault_landerReportCycle (solarSys);
+
+ if (!GET_GAME_STATE (PORTAL_KEY))
+ {
+ SetLanderTakeoff ();
+
+ SET_GAME_STATE (PORTAL_KEY, 1);
+ SET_GAME_STATE (PORTAL_KEY_ON_SHIP, 1);
+ }
+
+ // The Wreck cannot be "picked up". It is always on the surface.
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genyeh.c b/src/uqm/planets/generate/genyeh.c
new file mode 100644
index 0000000..caae543
--- /dev/null
+++ b/src/uqm/planets/generate/genyeh.c
@@ -0,0 +1,140 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../ipdisp.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateYehat_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateYehat_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateYehat_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateYehat_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateYehatFunctions = {
+ /* .initNpcs = */ GenerateDefault_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateYehat_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateYehat_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateYehat_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateYehat_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateYehat_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+ solarSys->PlanetDesc[0].data_index = WATER_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 106L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateYehat_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (YEHAT_SHIP))
+ {
+ NotifyOthers (YEHAT_SHIP, IPNL_ALL_CLEAR);
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ CloneShipFragment (YEHAT_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (YEHAT_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateYehat_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateYehat_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/generate/genzfpscout.c b/src/uqm/planets/generate/genzfpscout.c
new file mode 100644
index 0000000..93a6d5d
--- /dev/null
+++ b/src/uqm/planets/generate/genzfpscout.c
@@ -0,0 +1,96 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../../build.h"
+#include "../../globdata.h"
+#include "../../grpinfo.h"
+#include "../../state.h"
+
+
+static bool GenerateZoqFotPikScout_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateZoqFotPikScout_reinitNpcs (SOLARSYS_STATE *solarSys);
+
+
+const GenerateFunctions generateZoqFotPikScoutFunctions = {
+ /* .initNpcs = */ GenerateZoqFotPikScout_initNpcs,
+ /* .reinitNpcs = */ GenerateZoqFotPikScout_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateDefault_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateDefault_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateDefault_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateDefault_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateZoqFotPikScout_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (!GET_GAME_STATE (MET_ZOQFOT))
+ {
+ GLOBAL (BattleGroupRef) = GET_GAME_STATE_32 (ZOQFOT_GRPOFFS0);
+ if (GLOBAL (BattleGroupRef) == 0)
+ {
+ CloneShipFragment (ZOQFOTPIK_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+ GLOBAL (BattleGroupRef) = PutGroupInfo (GROUPS_ADD_NEW, 1);
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ SET_GAME_STATE_32 (ZOQFOT_GRPOFFS0, GLOBAL (BattleGroupRef));
+ }
+ }
+
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+static bool
+GenerateZoqFotPikScout_reinitNpcs (SOLARSYS_STATE *solarSys)
+{
+ HIPGROUP hGroup;
+ IP_GROUP *GroupPtr;
+
+ GenerateDefault_reinitNpcs (solarSys);
+
+ if (!GLOBAL (BattleGroupRef))
+ return true; // nothing to check
+
+ hGroup = GetHeadLink (&GLOBAL (ip_group_q));
+ if (!hGroup)
+ return true; // still nothing to check
+
+ GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
+ // REFORM_GROUP was set in ipdisp.c:ip_group_collision()
+ // during a collision with the flagship.
+ if (GroupPtr->race_id == ZOQFOTPIK_SHIP
+ && (GroupPtr->task & REFORM_GROUP))
+ {
+ GroupPtr->task = FLEE | IGNORE_FLAGSHIP | REFORM_GROUP;
+ GroupPtr->dest_loc = 0;
+ }
+ UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
+
+ return true;
+}
+
diff --git a/src/uqm/planets/generate/genzoq.c b/src/uqm/planets/generate/genzoq.c
new file mode 100644
index 0000000..9b30f89
--- /dev/null
+++ b/src/uqm/planets/generate/genzoq.c
@@ -0,0 +1,170 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "genall.h"
+#include "../planets.h"
+#include "../../build.h"
+#include "../../comm.h"
+#include "../../globdata.h"
+#include "../../nameref.h"
+#include "../../state.h"
+#include "libs/mathlib.h"
+
+
+static bool GenerateZoqFotPik_initNpcs (SOLARSYS_STATE *solarSys);
+static bool GenerateZoqFotPik_generatePlanets (SOLARSYS_STATE *solarSys);
+static bool GenerateZoqFotPik_generateOrbital (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world);
+static COUNT GenerateZoqFotPik_generateEnergy (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *);
+static bool GenerateZoqFotPik_pickupEnergy (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT whichNode);
+
+
+const GenerateFunctions generateZoqFotPikFunctions = {
+ /* .initNpcs = */ GenerateZoqFotPik_initNpcs,
+ /* .reinitNpcs = */ GenerateDefault_reinitNpcs,
+ /* .uninitNpcs = */ GenerateDefault_uninitNpcs,
+ /* .generatePlanets = */ GenerateZoqFotPik_generatePlanets,
+ /* .generateMoons = */ GenerateDefault_generateMoons,
+ /* .generateName = */ GenerateDefault_generateName,
+ /* .generateOrbital = */ GenerateZoqFotPik_generateOrbital,
+ /* .generateMinerals = */ GenerateDefault_generateMinerals,
+ /* .generateEnergy = */ GenerateZoqFotPik_generateEnergy,
+ /* .generateLife = */ GenerateDefault_generateLife,
+ /* .pickupMinerals = */ GenerateDefault_pickupMinerals,
+ /* .pickupEnergy = */ GenerateZoqFotPik_pickupEnergy,
+ /* .pickupLife = */ GenerateDefault_pickupLife,
+};
+
+
+static bool
+GenerateZoqFotPik_initNpcs (SOLARSYS_STATE *solarSys)
+{
+ if (GET_GAME_STATE (ZOQFOT_DISTRESS) != 1)
+ GenerateDefault_initNpcs (solarSys);
+
+ return true;
+}
+
+static bool
+GenerateZoqFotPik_generatePlanets (SOLARSYS_STATE *solarSys)
+{
+ COUNT angle;
+
+ GenerateDefault_generatePlanets (solarSys);
+
+ solarSys->PlanetDesc[0].data_index = REDUX_WORLD;
+ solarSys->PlanetDesc[0].NumPlanets = 1;
+ solarSys->PlanetDesc[0].radius = EARTH_RADIUS * 138L / 100;
+ angle = ARCTAN (solarSys->PlanetDesc[0].location.x,
+ solarSys->PlanetDesc[0].location.y);
+ solarSys->PlanetDesc[0].location.x =
+ COSINE (angle, solarSys->PlanetDesc[0].radius);
+ solarSys->PlanetDesc[0].location.y =
+ SINE (angle, solarSys->PlanetDesc[0].radius);
+
+ return true;
+}
+
+static bool
+GenerateZoqFotPik_generateOrbital (SOLARSYS_STATE *solarSys, PLANET_DESC *world)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ if (StartSphereTracking (ZOQFOTPIK_SHIP))
+ {
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ if (GET_GAME_STATE (ZOQFOT_DISTRESS))
+ {
+ CloneShipFragment (BLACK_URQUAN_SHIP,
+ &GLOBAL (npc_built_ship_q), 0);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (BLACKURQ_CONVERSATION);
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ return true;
+
+ if (GetHeadLink (&GLOBAL (npc_built_ship_q)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ return true;
+ }
+ }
+
+ CloneShipFragment (ZOQFOTPIK_SHIP, &GLOBAL (npc_built_ship_q),
+ INFINITE_FLEET);
+
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
+ InitCommunication (ZOQFOTPIK_CONVERSATION);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_INTERPLANETARY;
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ GetGroupInfo (GROUPS_RANDOM, GROUP_LOAD_IP);
+ }
+
+ return true;
+ }
+
+ LoadStdLanderFont (&solarSys->SysInfo.PlanetInfo);
+ solarSys->PlanetSideFrame[1] =
+ CaptureDrawable (LoadGraphic (RUINS_MASK_PMAP_ANIM));
+ solarSys->SysInfo.PlanetInfo.DiscoveryString =
+ CaptureStringTable (LoadStringTable (RUINS_STRTAB));
+ }
+
+ GenerateDefault_generateOrbital (solarSys, world);
+ return true;
+}
+
+static COUNT
+GenerateZoqFotPik_generateEnergy (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT whichNode, NODE_INFO *info)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ return GenerateDefault_generateRuins (solarSys, whichNode, info);
+ }
+
+ return 0;
+}
+
+static bool
+GenerateZoqFotPik_pickupEnergy (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT whichNode)
+{
+ if (matchWorld (solarSys, world, 0, MATCH_PLANET))
+ {
+ // Standard ruins report
+ GenerateDefault_landerReportCycle (solarSys);
+ return false;
+ }
+
+ (void) whichNode;
+ return false;
+}
diff --git a/src/uqm/planets/gentopo.c b/src/uqm/planets/gentopo.c
new file mode 100644
index 0000000..5212143
--- /dev/null
+++ b/src/uqm/planets/gentopo.c
@@ -0,0 +1,206 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// See doc/devel/planettopo for details.
+
+#include "libs/gfxlib.h"
+#include "libs/mathlib.h"
+#include "planets.h"
+
+void
+DeltaTopography (COUNT num_iterations, SBYTE *DepthArray, RECT *pRect,
+ SIZE depth_delta)
+{
+ SIZE width, height, delta_y;
+ struct
+ {
+ COORD x_top, x_bot;
+ SIZE x_incr, delta_x, error_term;
+ } LineDDA0, LineDDA1;
+
+ width = pRect->extent.width;
+ height = pRect->extent.height;
+ delta_y = (height - 1) << 1;
+ do
+ {
+ SIZE d;
+ COUNT h, w1, w2;
+ DWORD rand_val;
+ SBYTE *lpDst;
+
+ if ((RandomContext_Random (SysGenRNG) & 1) == 0)
+ depth_delta = -depth_delta;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ w1 = LOWORD (rand_val);
+ w2 = HIWORD (rand_val);
+
+ LineDDA0.x_top = LOBYTE (w1) % width;
+ LineDDA0.x_bot = HIBYTE (w1) % width;
+ LineDDA0.delta_x = (LineDDA0.x_bot - LineDDA0.x_top) << 1;
+ if (LineDDA0.delta_x >= 0)
+ LineDDA0.x_incr = 1;
+ else
+ {
+ LineDDA0.x_incr = -1;
+ LineDDA0.delta_x = -LineDDA0.delta_x;
+ }
+ if (LineDDA0.delta_x > delta_y)
+ LineDDA0.error_term = -(LineDDA0.delta_x >> 1);
+ else
+ LineDDA0.error_term = -(delta_y >> 1);
+
+ LineDDA1.x_top = (LOBYTE (w2) % (width - 1)) + LineDDA0.x_top + 1;
+ LineDDA1.x_bot = (HIBYTE (w2) % (width - 1)) + LineDDA0.x_bot + 1;
+ LineDDA1.delta_x = (LineDDA1.x_bot - LineDDA1.x_top) << 1;
+ if (LineDDA1.delta_x >= 0)
+ LineDDA1.x_incr = 1;
+ else
+ {
+ LineDDA1.x_incr = -1;
+ LineDDA1.delta_x = -LineDDA1.delta_x;
+ }
+ if (LineDDA1.delta_x > delta_y)
+ LineDDA1.error_term = -(LineDDA1.delta_x >> 1);
+ else
+ LineDDA1.error_term = -(delta_y >> 1);
+
+ lpDst = &DepthArray[LineDDA0.x_top];
+ h = height;
+ do
+ {
+ COUNT w;
+
+ w1 = LineDDA1.x_top - LineDDA0.x_top;
+ w2 = width - w1;
+
+ if ((int)(LineDDA0.x_top + w1) > (int)width)
+ w = width - LineDDA0.x_top;
+ else
+ {
+ w = w1;
+ LineDDA0.x_top += w1;
+ }
+ w1 -= w;
+ while (w--)
+ {
+ d = *lpDst + depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ }
+ if (w1 == 0)
+ {
+ if (LineDDA0.x_top == width)
+ {
+ LineDDA0.x_top = 0;
+ lpDst -= width;
+ }
+ }
+ else
+ {
+ LineDDA0.x_top = w1;
+ lpDst -= width;
+ do
+ {
+ d = *lpDst + depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ } while (--w1);
+ }
+
+ if ((int)(LineDDA0.x_top + w2) > (int)width)
+ w = width - LineDDA0.x_top;
+ else
+ {
+ w = w2;
+ LineDDA0.x_top += w2;
+ }
+ w2 -= w;
+ while (w--)
+ {
+ d = *lpDst - depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ }
+ if (w2 == 0)
+ {
+ if (LineDDA0.x_top == width)
+ {
+ LineDDA0.x_top = 0;
+ lpDst -= width;
+ }
+ }
+ else
+ {
+ LineDDA0.x_top = w2;
+ lpDst -= width;
+ do
+ {
+ d = *lpDst - depth_delta;
+ if (d >= -128 && d <= 127)
+ *lpDst = (SBYTE)d;
+ ++lpDst;
+ } while (--w2);
+ }
+
+ lpDst += pRect->extent.width;
+
+ if (delta_y >= LineDDA0.delta_x)
+ {
+ if ((LineDDA0.error_term += LineDDA0.delta_x) >= 0)
+ {
+ lpDst += LineDDA0.x_incr;
+ LineDDA0.x_top += LineDDA0.x_incr;
+ LineDDA0.error_term -= delta_y;
+ }
+ }
+ else
+ {
+ do
+ {
+ lpDst += LineDDA0.x_incr;
+ LineDDA0.x_top += LineDDA0.x_incr;
+ } while ((LineDDA0.error_term += delta_y) < 0);
+ LineDDA0.error_term -= LineDDA0.delta_x;
+ }
+
+ if (delta_y >= LineDDA1.delta_x)
+ {
+ if ((LineDDA1.error_term += LineDDA1.delta_x) >= 0)
+ {
+ LineDDA1.x_top += LineDDA1.x_incr;
+ LineDDA1.error_term -= delta_y;
+ }
+ }
+ else
+ {
+ do
+ {
+ LineDDA1.x_top += LineDDA1.x_incr;
+ } while ((LineDDA1.error_term += delta_y) < 0);
+ LineDDA1.error_term -= LineDDA1.delta_x;
+ }
+ } while (--h);
+ } while (--num_iterations);
+}
+
+
+
diff --git a/src/uqm/planets/lander.c b/src/uqm/planets/lander.c
new file mode 100644
index 0000000..17aad8f
--- /dev/null
+++ b/src/uqm/planets/lander.c
@@ -0,0 +1,2101 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "lander.h"
+
+#include "lifeform.h"
+#include "scan.h"
+#include "../cons_res.h"
+#include "../controls.h"
+#include "../colors.h"
+#include "../process.h"
+#include "../units.h"
+#include "../gamestr.h"
+#include "../nameref.h"
+#include "../resinst.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../element.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+//define SPIN_ON_LAUNCH to let the planet spin while
+// the lander animation is playing
+#define SPIN_ON_LAUNCH
+
+// PLANET_SIDE_RATE governs how fast the lander,
+// bio and planet effects will be
+// We're using the 3DO speed, which is 35 FPS
+// The PC speed was 30 FPS.
+// Remember that all values need to evenly divide
+// ONE_SECOND.
+#define PLANET_SIDE_RATE (ONE_SECOND / 35)
+
+
+// This is a derived type from INPUT_STATE_DESC.
+typedef struct LanderInputState LanderInputState;
+struct LanderInputState {
+ // Fields required by DoInput()
+ BOOLEAN (*InputFunc) (LanderInputState *pMS);
+
+ BOOLEAN Initialized;
+ TimeCount NextTime;
+ // Frame rate control
+};
+
+FRAME LanderFrame[8];
+static SOUND LanderSounds;
+MUSIC_REF LanderMusic;
+#define NUM_ORBIT_THEMES 5
+static MUSIC_REF OrbitMusic[NUM_ORBIT_THEMES];
+
+const LIFEFORM_DESC CreatureData[] =
+{
+ {SPEED_MOTIONLESS | DANGER_HARMLESS, MAKE_BYTE (1, 1)},
+ // Roto-Dendron
+ {SPEED_MOTIONLESS | DANGER_HARMLESS, MAKE_BYTE (6, 1)},
+ // Macrocillia
+ {SPEED_MOTIONLESS | DANGER_WEAK, MAKE_BYTE (3, 1)},
+ // Splort Wort
+ {SPEED_MOTIONLESS | DANGER_NORMAL, MAKE_BYTE (5, 3)},
+ // Whackin' Bush
+ {SPEED_MOTIONLESS | DANGER_HARMLESS, MAKE_BYTE (2, 10)},
+ // Slot Machine Tree
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_HARMLESS, MAKE_BYTE (1, 2)},
+ // Neon Worm
+ {BEHAVIOR_FLEE | AWARENESS_MEDIUM | SPEED_SLOW | DANGER_HARMLESS, MAKE_BYTE (8, 5)},
+ // Stiletto Urchin
+ {BEHAVIOR_HUNT | AWARENESS_LOW | SPEED_SLOW | DANGER_WEAK, MAKE_BYTE (2, 2)},
+ // Deluxe Blob
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_NORMAL, MAKE_BYTE (3, 8)},
+ // Glowing Medusa
+ {BEHAVIOR_HUNT | AWARENESS_MEDIUM | SPEED_SLOW | DANGER_MONSTROUS, MAKE_BYTE (10, 15)},
+ // Carousel Beast
+ {BEHAVIOR_HUNT | AWARENESS_MEDIUM | SPEED_MEDIUM | DANGER_WEAK, MAKE_BYTE (3, 3)},
+ // Mysterious Bees
+ {BEHAVIOR_FLEE | AWARENESS_MEDIUM | SPEED_MEDIUM | DANGER_HARMLESS, MAKE_BYTE (2, 1)},
+ // Hopping Blobby
+ {BEHAVIOR_UNPREDICTABLE | SPEED_MEDIUM | DANGER_WEAK, MAKE_BYTE (2, 2)},
+ // Blood Monkey
+ {BEHAVIOR_HUNT | AWARENESS_HIGH | SPEED_MEDIUM | DANGER_NORMAL, MAKE_BYTE (4, 6)},
+ // Yompin Yiminy
+ {BEHAVIOR_UNPREDICTABLE | SPEED_MEDIUM | DANGER_MONSTROUS, MAKE_BYTE (9, 12)},
+ // Amorphous Trandicula
+ {BEHAVIOR_HUNT | AWARENESS_HIGH | SPEED_FAST | DANGER_WEAK, MAKE_BYTE (3, 1)},
+ // Crazy Weasel
+ {BEHAVIOR_FLEE | AWARENESS_HIGH | SPEED_FAST | DANGER_HARMLESS, MAKE_BYTE (1, 1)},
+ // Merry Whumpet
+ {BEHAVIOR_HUNT | AWARENESS_LOW | SPEED_FAST | DANGER_NORMAL, MAKE_BYTE (7, 8)},
+ // Fungal Squid
+ {BEHAVIOR_FLEE | AWARENESS_HIGH | SPEED_FAST | DANGER_WEAK, MAKE_BYTE (15, 2)},
+ // Penguin Cyclops
+ {BEHAVIOR_FLEE | AWARENESS_LOW | SPEED_FAST | DANGER_WEAK, MAKE_BYTE (1, 1)},
+ // Chicken
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_WEAK, MAKE_BYTE (6, 2)},
+ // Bubble Vine
+ {BEHAVIOR_FLEE | AWARENESS_HIGH | SPEED_SLOW | DANGER_WEAK, MAKE_BYTE (4, 2)},
+ // Bug-Eyed Bait
+ {SPEED_MOTIONLESS | DANGER_WEAK, MAKE_BYTE (8, 5)},
+ // Goo Burger
+
+ {SPEED_MOTIONLESS | DANGER_MONSTROUS, MAKE_BYTE (1, 1)},
+ // Evil One
+ {BEHAVIOR_UNPREDICTABLE | SPEED_SLOW | DANGER_HARMLESS, MAKE_BYTE (0, 1)},
+ // Brainbox Bulldozers
+ {BEHAVIOR_HUNT | AWARENESS_HIGH | SPEED_FAST | DANGER_MONSTROUS, MAKE_BYTE (15, 15)},
+ // Zex's Beauty
+};
+
+
+extern PRIM_LINKS DisplayLinks;
+
+#define DAMAGE_CYCLE 6
+// XXX: There are actually only 9 explosion images.
+// The last frame is drawn twice.
+#define EXPLOSION_LIFE 10
+// How long to wait after the lander explodes, so that the full
+// gravity of the player's situation sinks in
+#define EXPLOSION_WAIT (ONE_SECOND * 2)
+#define EXPLOSION_WAIT_FRAMES (EXPLOSION_WAIT / PLANET_SIDE_RATE)
+// The actual number of frame that the explosion and wait takes is:
+// EXPLOSION_LIFE * 3 + EXPLOSION_WAIT_FRAMES
+
+#define DEATH_EXPLOSION 0
+
+// TODO: redefine these in terms of CONTEXT width/height
+#define SURFACE_WIDTH SIS_SCREEN_WIDTH
+#define SURFACE_HEIGHT (SIS_SCREEN_HEIGHT - MAP_HEIGHT - MAP_BORDER_HEIGHT)
+
+#define REPAIR_LANDER (1 << 7)
+#define REPAIR_TRANSITION (1 << 6)
+#define KILL_CREW (1 << 5)
+#define ADD_AT_END (1 << 4)
+#define REPAIR_COUNT (0xf)
+
+#define LANDER_SPEED_DENOM 10
+
+static BYTE lander_flags;
+static POINT curLanderLoc;
+static int crew_left;
+static int shieldHit;
+ // which shield was hit, assuming it helped
+static int damage_index;
+ // number of lander damage frames left
+static int explosion_index;
+ // lander explosion progression. Semantics are similar to an
+ // inverse of ELEMENT.life_span
+static int turn_wait;
+ // thus named for similar semantics to ELEMENT.turn_wait
+static int weapon_wait;
+ // semantics similar to STARSHIP.weapon_counter
+
+// TODO: We may want to make the PLANETSIDE_DESC fields into static vars
+static PLANETSIDE_DESC *planetSideDesc;
+
+#define ON_THE_GROUND 0
+
+
+static Color
+DamageColorCycle (Color c, COUNT i)
+{
+ static const Color damage_tab[DAMAGE_CYCLE + 1] =
+ {
+ WHITE_COLOR_INIT,
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1C, 0x00), 0x78),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x0A), 0x0E),
+ };
+
+ if (i)
+ c = damage_tab[i];
+ else if (sameColor(c, WHITE_COLOR))
+ c = damage_tab[6];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E)))
+ c = damage_tab[5];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1C, 0x00), 0x78)))
+ c = damage_tab[4];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)))
+ c = damage_tab[3];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0E, 0x00), 0x7C)))
+ c = damage_tab[2];
+ else if (sameColor(c, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x07, 0x00), 0x7E)))
+ c = damage_tab[1];
+ else
+ c = damage_tab[0];
+
+ return c;
+}
+
+static HELEMENT AddGroundDisaster (COUNT which_disaster);
+
+void
+object_animation (ELEMENT *ElementPtr)
+{
+ COUNT frame_index, angle;
+ PRIMITIVE *pPrim;
+
+ pPrim = &DisplayArray[ElementPtr->PrimIndex];
+ if (GetPrimType (pPrim) == STAMPFILL_PRIM
+ && !((ElementPtr->state_flags & FINITE_LIFE)
+ && ElementPtr->mass_points == EARTHQUAKE_DISASTER))
+ {
+ Color c;
+
+ c = DamageColorCycle (GetPrimColor (pPrim), 0);
+ if (sameColor(c, WHITE_COLOR))
+ {
+ SetPrimType (pPrim, STAMP_PRIM);
+ if (ElementPtr->hit_points == 0)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ pPrim->Object.Stamp.frame =
+ SetAbsFrameIndex (pPrim->Object.Stamp.frame, 0);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LIFEFORM_CANNED),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+ }
+
+ SetPrimColor (pPrim, c);
+ }
+
+ frame_index = GetFrameIndex (pPrim->Object.Stamp.frame) + 1;
+ if (LONIBBLE (ElementPtr->turn_wait))
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->turn_wait += HINIBBLE (ElementPtr->turn_wait);
+
+ pPrim->Object.Stamp.frame = IncFrameIndex (pPrim->Object.Stamp.frame);
+
+ if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ /* A natural disaster */
+ if (ElementPtr->mass_points == DEATH_EXPLOSION)
+ { // Lander explosion
+ ++explosion_index;
+ if (explosion_index >= EXPLOSION_LIFE)
+ { // XXX: The last frame is drawn twice
+ pPrim->Object.Stamp.frame =
+ DecFrameIndex (pPrim->Object.Stamp.frame);
+ }
+ }
+ else if (ElementPtr->mass_points == EARTHQUAKE_DISASTER)
+ {
+ SIZE s;
+
+ if (frame_index >= 13)
+ s = 0;
+ else
+ s = (14 - frame_index) >> 1;
+ // XXX: Was 0x8000 the background flag on 3DO?
+ //SetPrimColor (pPrim, BUILD_COLOR (0x8000 | MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+ SetPrimColor (pPrim, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+ if (frame_index == 13)
+ PlaySound (SetAbsSoundIndex (LanderSounds, EARTHQUAKE_DISASTER),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+
+ if (ElementPtr->mass_points == LAVASPOT_DISASTER
+ && frame_index == 5
+ && TFB_Random () % 100 < 90)
+ {
+ HELEMENT hLavaElement;
+
+ /* Change lava-spot direction of travel */
+ hLavaElement = AddGroundDisaster (LAVASPOT_DISASTER);
+ if (hLavaElement)
+ {
+ ELEMENT *LavaElementPtr;
+
+ angle = FACING_TO_ANGLE (ElementPtr->facing);
+ LockElement (hLavaElement, &LavaElementPtr);
+ LavaElementPtr->next.location = ElementPtr->next.location;
+ LavaElementPtr->next.location.x += COSINE (angle, 4);
+ LavaElementPtr->next.location.y += SINE (angle, 4);
+ if (LavaElementPtr->next.location.y < 0)
+ LavaElementPtr->next.location.y = 0;
+ else if (LavaElementPtr->next.location.y >= (MAP_HEIGHT << MAG_SHIFT))
+ LavaElementPtr->next.location.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+ if (LavaElementPtr->next.location.x < 0)
+ LavaElementPtr->next.location.x += MAP_WIDTH << MAG_SHIFT;
+ else
+ LavaElementPtr->next.location.x %= MAP_WIDTH << MAG_SHIFT;
+ LavaElementPtr->facing = NORMALIZE_FACING (
+ ElementPtr->facing + (TFB_Random () % 3 - 1));
+ UnlockElement (hLavaElement);
+ }
+ }
+ }
+ else if (!(frame_index & 3) && ElementPtr->hit_points)
+ {
+ BYTE index;
+ COUNT speed;
+
+ index = ElementPtr->mass_points & ~CREATURE_AWARE;
+ speed = CreatureData[index].Attributes & SPEED_MASK;
+ if (speed)
+ {
+ SIZE dx, dy;
+ COUNT old_angle;
+
+ dx = curLanderLoc.x - ElementPtr->next.location.x;
+ if (dx < 0 && dx < -(MAP_WIDTH << (MAG_SHIFT - 1)))
+ dx += MAP_WIDTH << MAG_SHIFT;
+ else if (dx > (MAP_WIDTH << (MAG_SHIFT - 1)))
+ dx -= MAP_WIDTH << MAG_SHIFT;
+ dy = curLanderLoc.y - ElementPtr->next.location.y;
+ angle = ARCTAN (dx, dy);
+ if (dx < 0)
+ dx = -dx;
+ if (dy < 0)
+ dy = -dy;
+
+ if (dx >= SURFACE_WIDTH || dy >= SURFACE_WIDTH
+ || dx * dx + dy * dy >= SURFACE_WIDTH * SURFACE_WIDTH)
+ ElementPtr->mass_points &= ~CREATURE_AWARE;
+ else if (!(ElementPtr->mass_points & CREATURE_AWARE))
+ {
+ BYTE DetectPercent;
+
+ DetectPercent = (((BYTE)(CreatureData[index].Attributes
+ & AWARENESS_MASK) >> AWARENESS_SHIFT) + 1)
+ * (30 / 6);
+ // XXX: Shouldn't this be dependent on
+ // PLANET_SIDE_RATE somehow? And why is it
+ // written as '30 / 6' instead of 5? Does the 30
+ // specify the (PC) framerate? That doesn't make
+ // sense; I would expect it to be in the
+ // denominator. And even then, it wouldn't give
+ // the same results with different frame rates,
+ // as repeating 'random(x / 30)' 30 times doesn't
+ // generally have the same result as repeating
+ // 'random(x / 35)' 25 times. - SvdB
+ if (TFB_Random () % 100 < DetectPercent)
+ {
+ ElementPtr->thrust_wait = 0;
+ ElementPtr->mass_points |= CREATURE_AWARE;
+ }
+ }
+
+ if (ElementPtr->next.location.y == 0
+ || ElementPtr->next.location.y ==
+ (MAP_HEIGHT << MAG_SHIFT) - 1)
+ ElementPtr->thrust_wait = 0;
+
+ old_angle = GetVelocityTravelAngle (&ElementPtr->velocity);
+ if (ElementPtr->thrust_wait)
+ {
+ --ElementPtr->thrust_wait;
+ angle = old_angle;
+ }
+ else if (!(ElementPtr->mass_points & CREATURE_AWARE)
+ || (CreatureData[index].Attributes
+ & BEHAVIOR_MASK) == BEHAVIOR_UNPREDICTABLE)
+ {
+ COUNT rand_val;
+
+ rand_val = TFB_Random ();
+ angle = NORMALIZE_ANGLE (LOBYTE (rand_val));
+ ElementPtr->thrust_wait =
+ (HIBYTE (rand_val) >> 2) + 10;
+ }
+ else if ((CreatureData[index].Attributes
+ & BEHAVIOR_MASK) == BEHAVIOR_FLEE)
+ {
+ if (ElementPtr->next.location.y == 0
+ || ElementPtr->next.location.y ==
+ (MAP_HEIGHT << MAG_SHIFT) - 1)
+ {
+ if (angle & (HALF_CIRCLE - 1))
+ angle = HALF_CIRCLE - angle;
+ else if (old_angle == QUADRANT
+ || old_angle == (FULL_CIRCLE - QUADRANT))
+ angle = old_angle;
+ else
+ angle = ((TFB_Random () & 1)
+ * HALF_CIRCLE) - QUADRANT;
+ ElementPtr->thrust_wait = 5;
+ }
+ angle = NORMALIZE_ANGLE (angle + HALF_CIRCLE);
+ }
+
+ switch (speed)
+ {
+ case SPEED_SLOW:
+ speed = WORLD_TO_VELOCITY (2 * 1) >> 2;
+ break;
+ case SPEED_MEDIUM:
+ speed = WORLD_TO_VELOCITY (2 * 1) >> 1;
+ break;
+ case SPEED_FAST:
+ speed = WORLD_TO_VELOCITY (2 * 1) * 9 / 10;
+ break;
+ }
+
+ SetVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, speed), SINE (angle, speed));
+ }
+ }
+ }
+
+ if ((ElementPtr->state_flags & FINITE_LIFE)
+ && ElementPtr->mass_points == DEATH_EXPLOSION
+ && GetSuccLink (DisplayLinks) != ElementPtr->PrimIndex)
+ lander_flags |= ADD_AT_END;
+}
+
+#define NUM_CREW_COLS 6
+#define NUM_CREW_ROWS 2
+
+static void
+DeltaLanderCrew (SIZE crew_delta, COUNT which_disaster)
+{
+ STAMP s;
+ CONTEXT OldContext;
+
+ if (crew_delta > 0)
+ {
+ // Filling up the crew bar when landing.
+ crew_delta = crew_left;
+ crew_left += 1;
+
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 55);
+ }
+ else /* if (crew_delta < 0) */
+ {
+ if (crew_left < 1)
+ return; // irrelevant -- all dead
+
+ shieldHit = GET_GAME_STATE (LANDER_SHIELDS);
+ shieldHit &= 1 << which_disaster;
+ if (!shieldHit || TFB_Random () % 100 >= 95)
+ { // No shield, or it did not help
+ shieldHit = 0;
+ --crew_left;
+ }
+
+ damage_index = DAMAGE_CYCLE;
+ if (shieldHit)
+ return;
+
+ crew_delta = crew_left;
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 56);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_INJURED),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+
+ s.origin.x = 11 + (6 * (crew_delta % NUM_CREW_COLS));
+ s.origin.y = 35 - (6 * (crew_delta / NUM_CREW_COLS));
+
+ OldContext = SetContext (RadarContext);
+ DrawStamp (&s);
+ SetContext (OldContext);
+}
+
+static void
+FillLanderHold (PLANETSIDE_DESC *pPSD, COUNT scan, COUNT NumRetrieved)
+{
+ COUNT start_count;
+ STAMP s;
+ CONTEXT OldContext;
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_PICKUP),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+
+ if (scan == BIOLOGICAL_SCAN)
+ {
+ start_count = pPSD->BiologicalLevel;
+
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 41);
+
+ pPSD->BiologicalLevel += NumRetrieved;
+ }
+ else
+ {
+ start_count = pPSD->ElementLevel;
+ pPSD->ElementLevel += NumRetrieved;
+ if (GET_GAME_STATE (IMPROVED_LANDER_CARGO))
+ {
+ start_count >>= 1;
+ NumRetrieved = (pPSD->ElementLevel >> 1) - start_count;
+ }
+
+ s.frame = SetAbsFrameIndex (LanderFrame[0], 43);
+ }
+
+ s.origin.x = 0;
+ s.origin.y = -(int)start_count;
+ if (!(start_count & 1))
+ s.frame = IncFrameIndex (s.frame);
+
+ OldContext = SetContext (RadarContext);
+ while (NumRetrieved--)
+ {
+ if (start_count++ & 1)
+ s.frame = IncFrameIndex (s.frame);
+ else
+ s.frame = DecFrameIndex (s.frame);
+ DrawStamp (&s);
+ --s.origin.y;
+ }
+ SetContext (OldContext);
+}
+
+// returns true iff the node was picked up.
+static bool
+pickupMineralNode (PLANETSIDE_DESC *pPSD, COUNT NumRetrieved,
+ ELEMENT *ElementPtr, const INTERSECT_CONTROL *LanderControl,
+ const INTERSECT_CONTROL *ElementControl)
+{
+ BYTE EType;
+ UNICODE ch;
+ UNICODE *pStr;
+
+ if (pPSD->ElementLevel >= pPSD->MaxElementLevel)
+ {
+ // Lander full
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_FULL),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ return false;
+ }
+
+ if (pPSD->ElementLevel + NumRetrieved > pPSD->MaxElementLevel)
+ {
+ // Deposit could only be picked up partially.
+ NumRetrieved = (COUNT)(pPSD->MaxElementLevel - pPSD->ElementLevel);
+ }
+
+ FillLanderHold (pPSD, MINERAL_SCAN, NumRetrieved);
+
+ EType = ElementPtr->turn_wait;
+ pPSD->ElementAmounts[ElementCategory (EType)] += NumRetrieved;
+
+ pPSD->NumFrames = NUM_TEXT_FRAMES;
+ sprintf (pPSD->AmountBuf, "%u", NumRetrieved);
+ pStr = GAME_STRING (EType + ELEMENTS_STRING_BASE);
+
+ pPSD->MineralText[0].baseline.x = (SURFACE_WIDTH >> 1)
+ + (ElementControl->EndPoint.x - LanderControl->EndPoint.x);
+ pPSD->MineralText[0].baseline.y = (SURFACE_HEIGHT >> 1)
+ + (ElementControl->EndPoint.y - LanderControl->EndPoint.y);
+ pPSD->MineralText[0].CharCount = (COUNT)~0;
+ pPSD->MineralText[1].pStr = pStr;
+
+ while ((ch = *pStr++) && ch != ' ')
+ ;
+ if (ch == '\0')
+ {
+ pPSD->MineralText[1].CharCount = (COUNT)~0;
+ pPSD->MineralText[2].CharCount = 0;
+ }
+ else /* ch == ' ' */
+ {
+ // Name contains a space. Print over
+ // two lines.
+ pPSD->MineralText[1].CharCount = utf8StringCountN(
+ pPSD->MineralText[1].pStr, pStr - 1);
+ pPSD->MineralText[2].pStr = pStr;
+ pPSD->MineralText[2].CharCount = (COUNT)~0;
+ }
+
+ return true;
+}
+
+static bool
+pickupBioNode (PLANETSIDE_DESC *pPSD, COUNT NumRetrieved)
+{
+ if (pPSD->BiologicalLevel >= MAX_SCROUNGED)
+ {
+ // Lander is full.
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_FULL),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ return false;
+ }
+
+ if (pPSD->BiologicalLevel + NumRetrieved > MAX_SCROUNGED)
+ {
+ // Node could only be picked up partially.
+ NumRetrieved = (COUNT)(MAX_SCROUNGED - pPSD->BiologicalLevel);
+ }
+
+ FillLanderHold (pPSD, BIOLOGICAL_SCAN, NumRetrieved);
+
+ return true;
+}
+
+static void
+shotCreature (ELEMENT *ElementPtr, BYTE value,
+ INTERSECT_CONTROL *LanderControl, PRIMITIVE *pPrim)
+{
+ if (ElementPtr->hit_points == 0)
+ {
+ // Creature is already canned.
+ return;
+ }
+
+ --ElementPtr->hit_points;
+ if (ElementPtr->hit_points == 0)
+ {
+ // Can creature.
+ ElementPtr->mass_points = value;
+ DisplayArray[ElementPtr->PrimIndex].Object.Stamp.frame =
+ pSolarSysState->PlanetSideFrame[0];
+ }
+ else if (CreatureData[ElementPtr->mass_points & ~CREATURE_AWARE]
+ .Attributes & SPEED_MASK)
+ {
+ COUNT angle;
+
+ angle = FACING_TO_ANGLE (GetFrameIndex (
+ LanderControl->IntersectStamp.frame) -
+ ANGLE_TO_FACING (FULL_CIRCLE));
+ DeltaVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (1)),
+ SINE (angle, WORLD_TO_VELOCITY (1)));
+ ElementPtr->thrust_wait = 0;
+ ElementPtr->mass_points |= CREATURE_AWARE;
+ }
+
+ SetPrimType (pPrim, STAMPFILL_PRIM);
+ SetPrimColor (pPrim, WHITE_COLOR);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_HITS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+}
+
+static void
+CheckObjectCollision (COUNT index)
+{
+ INTERSECT_CONTROL LanderControl;
+ DRAWABLE LanderHandle;
+ PRIMITIVE *pPrim;
+ PRIMITIVE *pLanderPrim;
+ PLANETSIDE_DESC *pPSD = planetSideDesc;
+
+ if (index != END_OF_LIST)
+ {
+ pLanderPrim = &DisplayArray[index];
+ LanderControl.IntersectStamp = pLanderPrim->Object.Stamp;
+ index = GetPredLink (GetPrimLinks (pLanderPrim));
+ }
+ else
+ {
+ pLanderPrim = 0;
+ LanderControl.IntersectStamp.origin.x = SURFACE_WIDTH >> 1;
+ LanderControl.IntersectStamp.origin.y = SURFACE_HEIGHT >> 1;
+ LanderControl.IntersectStamp.frame = LanderFrame[0];
+ index = GetSuccLink (DisplayLinks);
+ }
+
+ LanderControl.EndPoint = LanderControl.IntersectStamp.origin;
+ LanderHandle = GetFrameParentDrawable (LanderControl.IntersectStamp.frame);
+
+ for (; index != END_OF_LIST; index = GetPredLink (GetPrimLinks (pPrim)))
+ {
+ INTERSECT_CONTROL ElementControl;
+ HELEMENT hElement, hNextElement;
+
+ pPrim = &DisplayArray[index];
+ ElementControl.IntersectStamp = pPrim->Object.Stamp;
+ ElementControl.EndPoint = ElementControl.IntersectStamp.origin;
+
+ if (GetFrameParentDrawable (ElementControl.IntersectStamp.frame)
+ == LanderHandle)
+ {
+ CheckObjectCollision (index);
+ continue;
+ }
+
+ if (!DrawablesIntersect (&LanderControl,
+ &ElementControl, MAX_TIME_VALUE))
+ continue;
+
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+
+ if (&DisplayArray[ElementPtr->PrimIndex] == pLanderPrim)
+ {
+ ElementPtr->state_flags |= DISAPPEARING;
+ UnlockElement (hElement);
+ continue;
+ }
+
+ if (&DisplayArray[ElementPtr->PrimIndex] != pPrim
+ || ElementPtr->playerNr != PS_NON_PLAYER)
+ {
+ UnlockElement (hElement);
+ continue;
+ }
+
+ {
+ COUNT scan, NumRetrieved;
+ SIZE which_node;
+
+ scan = LOBYTE (ElementPtr->scan_node);
+ if (pLanderPrim == 0)
+ {
+ /* Collision of lander with another object */
+ if (crew_left == 0 || pPSD->InTransit)
+ break;
+
+ if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ /* A natural disaster */
+ scan = ElementPtr->mass_points;
+ switch (scan)
+ {
+ case EARTHQUAKE_DISASTER:
+ case LAVASPOT_DISASTER:
+ if (TFB_Random () % 100 < 25)
+ DeltaLanderCrew (-1, scan);
+ break;
+ }
+
+ UnlockElement (hElement);
+ continue;
+ }
+ else if (scan == ENERGY_SCAN)
+ {
+ // noop; handled by generation funcs, see below
+ }
+ else if (scan == BIOLOGICAL_SCAN && ElementPtr->hit_points)
+ {
+ BYTE danger_vals[] =
+ {
+ 0, 6, 13, 26
+ };
+ int creatureIndex = ElementPtr->mass_points
+ & ~CREATURE_AWARE;
+ int dangerLevel =
+ (CreatureData[creatureIndex].Attributes &
+ DANGER_MASK) >> DANGER_SHIFT;
+
+ if (TFB_Random () % 128 < danger_vals[dangerLevel])
+ {
+ PlaySound (SetAbsSoundIndex (
+ LanderSounds, BIOLOGICAL_DISASTER),
+ NotPositional (), NULL,
+ GAME_SOUND_PRIORITY);
+ DeltaLanderCrew (-1, BIOLOGICAL_DISASTER);
+ }
+ UnlockElement (hElement);
+ continue;
+ }
+
+ NumRetrieved = ElementPtr->mass_points;
+ }
+ else if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ /* Collision of a stun bolt with a natural disaster */
+ UnlockElement (hElement);
+ continue;
+ }
+ else
+ {
+ BYTE value;
+
+ if (scan == ENERGY_SCAN)
+ {
+ /* Collision of a stun bolt with an energy node */
+ UnlockElement (hElement);
+ break;
+ }
+
+ if (scan == BIOLOGICAL_SCAN
+ && (value = LONIBBLE (CreatureData[
+ ElementPtr->mass_points
+ & ~CREATURE_AWARE
+ ].ValueAndHitPoints)))
+ {
+ /* Collision of a stun bolt with a viable creature */
+ shotCreature (ElementPtr, value, &LanderControl,
+ pPrim);
+ UnlockElement (hElement);
+ break;
+ }
+
+ NumRetrieved = 0;
+ }
+
+ if (NumRetrieved)
+ {
+ switch (scan)
+ {
+ case ENERGY_SCAN:
+ break;
+ case MINERAL_SCAN:
+ if (!pickupMineralNode (pPSD, NumRetrieved,
+ ElementPtr, &LanderControl,
+ &ElementControl))
+ continue;
+ break;
+ case BIOLOGICAL_SCAN:
+ if (!pickupBioNode (pPSD, NumRetrieved))
+ continue;
+ break;
+ }
+ }
+
+ which_node = HIBYTE (ElementPtr->scan_node) - 1;
+ if (callPickupForScanType (pSolarSysState,
+ pSolarSysState->pOrbitalDesc, which_node, scan))
+ { // Node retrieved, remove from the surface
+ setNodeRetrieved (&pSolarSysState->SysInfo.PlanetInfo,
+ scan, which_node);
+ SET_GAME_STATE (PLANETARY_CHANGE, 1);
+ ElementPtr->state_flags |= DISAPPEARING;
+ }
+ UnlockElement (hElement);
+ }
+ }
+ }
+}
+
+static void
+lightning_process (ELEMENT *ElementPtr)
+{
+ PRIMITIVE *pPrim;
+
+ pPrim = &DisplayArray[ElementPtr->PrimIndex];
+ if (LONIBBLE (ElementPtr->turn_wait))
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT num_frames;
+
+ num_frames = GetFrameCount (pPrim->Object.Stamp.frame) - 7;
+ if (GetFrameIndex (pPrim->Object.Stamp.frame) >= num_frames)
+ {
+ /* Advance to the next surface strike effect frame */
+ // XXX: This is unused, we never get here
+ pPrim->Object.Stamp.frame =
+ IncFrameIndex (pPrim->Object.Stamp.frame);
+ }
+ else
+ {
+ SIZE s;
+
+ // XXX: Color cycling is largely unused, because the color
+ // never actually changes RGB values (see MAKE_RGB15 below).
+ // This did, however, work in DOS SC2 version (fade effect).
+ s = 7 - ((SIZE)ElementPtr->cycle - (SIZE)ElementPtr->life_span);
+ if (s < 0)
+ s = 0;
+ // XXX: Was 0x8000 the background flag on 3DO?
+ //SetPrimColor (pPrim, BUILD_COLOR (0x8000 | MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+ SetPrimColor (pPrim, BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), s));
+
+ if (ElementPtr->mass_points == LIGHTNING_DISASTER)
+ {
+ /* This one always strikes the lander and can hurt */
+ if (crew_left && TFB_Random () % 100 < 10
+ && !planetSideDesc->InTransit)
+ lander_flags |= KILL_CREW;
+
+ ElementPtr->next.location = curLanderLoc;
+ }
+
+ pPrim->Object.Stamp.frame =
+ SetAbsFrameIndex (pPrim->Object.Stamp.frame,
+ TFB_Random () % num_frames);
+ }
+
+ ElementPtr->turn_wait += HINIBBLE (ElementPtr->turn_wait);
+ }
+
+ if (GetSuccLink (DisplayLinks) != ElementPtr->PrimIndex)
+ lander_flags |= ADD_AT_END;
+}
+
+static void
+AddLightning (void)
+{
+ HELEMENT hLightningElement;
+
+ hLightningElement = AllocElement ();
+ if (hLightningElement)
+ {
+ DWORD rand_val;
+ ELEMENT *LightningElementPtr;
+
+ LockElement (hLightningElement, &LightningElementPtr);
+
+ LightningElementPtr->playerNr = PS_NON_PLAYER;
+ LightningElementPtr->state_flags = FINITE_LIFE;
+ LightningElementPtr->preprocess_func = lightning_process;
+ if (TFB_Random () % 100 >= 25)
+ LightningElementPtr->mass_points = 0; /* harmless */
+ else
+ LightningElementPtr->mass_points = LIGHTNING_DISASTER;
+
+ rand_val = TFB_Random ();
+ LightningElementPtr->life_span = 10 + (HIWORD (rand_val) % 10) + 1;
+ LightningElementPtr->next.location.x = (curLanderLoc.x
+ + ((MAP_WIDTH << MAG_SHIFT) - ((SURFACE_WIDTH >> 1) - 6))
+ + (LOBYTE (rand_val) % (SURFACE_WIDTH - 12))
+ ) % (MAP_WIDTH << MAG_SHIFT);
+ LightningElementPtr->next.location.y = (curLanderLoc.y
+ + ((MAP_HEIGHT << MAG_SHIFT) - ((SURFACE_HEIGHT >> 1) - 6))
+ + (HIBYTE (rand_val) % (SURFACE_HEIGHT - 12))
+ ) % (MAP_HEIGHT << MAG_SHIFT);
+
+ LightningElementPtr->cycle = LightningElementPtr->life_span;
+
+ SetPrimType (&DisplayArray[LightningElementPtr->PrimIndex], STAMPFILL_PRIM);
+ SetPrimColor (&DisplayArray[LightningElementPtr->PrimIndex], WHITE_COLOR);
+ DisplayArray[LightningElementPtr->PrimIndex].Object.Stamp.frame =
+ LanderFrame[2];
+
+ UnlockElement (hLightningElement);
+
+ PutElement (hLightningElement);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LIGHTNING_DISASTER),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+}
+
+static HELEMENT
+AddGroundDisaster (COUNT which_disaster)
+{
+ HELEMENT hGroundDisasterElement;
+
+ hGroundDisasterElement = AllocElement ();
+ if (hGroundDisasterElement)
+ {
+ DWORD rand_val;
+ ELEMENT *GroundDisasterElementPtr;
+ PRIMITIVE *pPrim;
+
+ LockElement (hGroundDisasterElement, &GroundDisasterElementPtr);
+
+ pPrim = &DisplayArray[GroundDisasterElementPtr->PrimIndex];
+ GroundDisasterElementPtr->mass_points = which_disaster;
+ GroundDisasterElementPtr->playerNr = PS_NON_PLAYER;
+ GroundDisasterElementPtr->state_flags = FINITE_LIFE;
+ GroundDisasterElementPtr->preprocess_func = object_animation;
+
+ rand_val = TFB_Random ();
+ GroundDisasterElementPtr->next.location.x = (curLanderLoc.x
+ + ((MAP_WIDTH << MAG_SHIFT) - (SURFACE_WIDTH * 3 / 8))
+ + (LOWORD (rand_val) % (SURFACE_WIDTH * 3 / 4))
+ ) % (MAP_WIDTH << MAG_SHIFT);
+ GroundDisasterElementPtr->next.location.y = (curLanderLoc.y
+ + ((MAP_HEIGHT << MAG_SHIFT) - (SURFACE_HEIGHT * 3 / 8))
+ + (HIWORD (rand_val) % (SURFACE_HEIGHT * 3 / 4))
+ ) % (MAP_HEIGHT << MAG_SHIFT);
+
+
+ if (which_disaster == EARTHQUAKE_DISASTER)
+ {
+ SetPrimType (pPrim, STAMPFILL_PRIM);
+ pPrim->Object.Stamp.frame = LanderFrame[1];
+ GroundDisasterElementPtr->turn_wait = MAKE_BYTE (2, 2);
+ }
+ else /* if (which_disaster == LAVASPOT_DISASTER) */
+ {
+ SetPrimType (pPrim, STAMP_PRIM);
+ GroundDisasterElementPtr->facing =
+ NORMALIZE_FACING (TFB_Random ());
+ pPrim->Object.Stamp.frame = LanderFrame[3];
+ GroundDisasterElementPtr->turn_wait = MAKE_BYTE (0, 0);
+ }
+ GroundDisasterElementPtr->life_span =
+ GetFrameCount (pPrim->Object.Stamp.frame)
+ * (LONIBBLE (GroundDisasterElementPtr->turn_wait) + 1) - 1;
+
+ UnlockElement (hGroundDisasterElement);
+
+ PutElement (hGroundDisasterElement);
+ }
+
+ return (hGroundDisasterElement);
+}
+
+// This function replaces the ELEMENT manipulations typically done by
+// PreProcess() and PostProcess() in process.c. Lander code does not
+// call RedrawQueue() & Co and thus does not reap the benefits (or curses,
+// depending how you look at it) of automatic flags processing.
+static void
+BuildObjectList (void)
+{
+ DWORD rand_val;
+ POINT org;
+ HELEMENT hElement, hNextElement;
+ PLANETSIDE_DESC *pPSD = planetSideDesc;
+
+ DisplayLinks = MakeLinks (END_OF_LIST, END_OF_LIST);
+
+ lander_flags &= ~KILL_CREW;
+
+ rand_val = TFB_Random ();
+ if (LOBYTE (HIWORD (rand_val)) < pPSD->FireChance)
+ {
+ AddGroundDisaster (LAVASPOT_DISASTER);
+ PlaySound (SetAbsSoundIndex (LanderSounds, LAVASPOT_DISASTER),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+ }
+
+ if (HIBYTE (LOWORD (rand_val)) < pPSD->TectonicsChance)
+ AddGroundDisaster (EARTHQUAKE_DISASTER);
+
+ if (LOBYTE (LOWORD (rand_val)) < pPSD->WeatherChance)
+ AddLightning ();
+
+ org = curLanderLoc;
+ for (hElement = GetHeadElement ();
+ hElement; hElement = hNextElement)
+ {
+ SIZE dx, dy;
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+
+ if (ElementPtr->life_span == 0
+ || (ElementPtr->state_flags & DISAPPEARING))
+ {
+ hNextElement = GetSuccElement (ElementPtr);
+ UnlockElement (hElement);
+ RemoveElement (hElement);
+ FreeElement (hElement);
+ continue;
+ }
+ else if (ElementPtr->state_flags & FINITE_LIFE)
+ --ElementPtr->life_span;
+
+ lander_flags &= ~ADD_AT_END;
+
+ if (ElementPtr->preprocess_func)
+ (*ElementPtr->preprocess_func) (ElementPtr);
+
+ GetNextVelocityComponents (&ElementPtr->velocity, &dx, &dy, 1);
+ if (dx || dy)
+ {
+ ElementPtr->next.location.x += dx;
+ ElementPtr->next.location.y += dy;
+ /* if not lander's shot */
+ if (ElementPtr->playerNr != PS_HUMAN_PLAYER)
+ {
+ if (ElementPtr->next.location.y < 0)
+ ElementPtr->next.location.y = 0;
+ else if (ElementPtr->next.location.y >= (MAP_HEIGHT << MAG_SHIFT))
+ ElementPtr->next.location.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+ }
+ if (ElementPtr->next.location.x < 0)
+ ElementPtr->next.location.x += MAP_WIDTH << MAG_SHIFT;
+ else
+ ElementPtr->next.location.x %= MAP_WIDTH << MAG_SHIFT;
+
+ // XXX: APPEARING flag is set by scan.c for scanned blips
+ if (ElementPtr->state_flags & APPEARING)
+ { // Update the location of a moving object on the scan map
+ ElementPtr->current.location.x =
+ ElementPtr->next.location.x >> MAG_SHIFT;
+ ElementPtr->current.location.y =
+ ElementPtr->next.location.y >> MAG_SHIFT;
+ }
+ }
+
+ {
+ PRIMITIVE *pPrim;
+
+ pPrim = &DisplayArray[ElementPtr->PrimIndex];
+ pPrim->Object.Stamp.origin.x =
+ ElementPtr->next.location.x
+ - org.x + (SURFACE_WIDTH >> 1);
+ if (pPrim->Object.Stamp.origin.x >=
+ (MAP_WIDTH << MAG_SHIFT) - (SURFACE_WIDTH * 3 / 2))
+ pPrim->Object.Stamp.origin.x -= MAP_WIDTH << MAG_SHIFT;
+ else if (pPrim->Object.Stamp.origin.x <=
+ -((MAP_WIDTH << MAG_SHIFT) - (SURFACE_WIDTH * 3 / 2)))
+ pPrim->Object.Stamp.origin.x += MAP_WIDTH << MAG_SHIFT;
+
+ pPrim->Object.Stamp.origin.y =
+ ElementPtr->next.location.y
+ - org.y + (SURFACE_HEIGHT >> 1);
+
+ if (lander_flags & ADD_AT_END)
+ InsertPrim (&DisplayLinks, ElementPtr->PrimIndex, END_OF_LIST);
+ else
+ InsertPrim (&DisplayLinks, ElementPtr->PrimIndex, GetPredLink (DisplayLinks));
+ }
+
+ hNextElement = GetSuccElement (ElementPtr);
+ UnlockElement (hElement);
+ }
+}
+
+static void
+ScrollPlanetSide (SIZE dx, SIZE dy, int landingOffset)
+{
+ POINT new_pt;
+ STAMP lander_s, shadow_s, shield_s;
+ CONTEXT OldContext;
+
+ new_pt.y = curLanderLoc.y + dy;
+ if (new_pt.y < 0)
+ {
+ new_pt.y = 0;
+ dy = new_pt.y - curLanderLoc.y;
+ dx = 0;
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ }
+ else if (new_pt.y > (MAP_HEIGHT << MAG_SHIFT) - 1)
+ {
+ new_pt.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+ dy = new_pt.y - curLanderLoc.y;
+ dx = 0;
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ }
+
+ new_pt.x = curLanderLoc.x + dx;
+ if (new_pt.x < 0)
+ new_pt.x += MAP_WIDTH << MAG_SHIFT;
+ else if (new_pt.x >= MAP_WIDTH << MAG_SHIFT)
+ new_pt.x -= MAP_WIDTH << MAG_SHIFT;
+
+ curLanderLoc = new_pt;
+
+ OldContext = SetContext (PlanetContext);
+
+ BatchGraphics ();
+
+ // Display planet area, accounting for horizontal wrapping if
+ // near the edges.
+ {
+ STAMP s;
+
+ ClearDrawable ();
+ s.origin.x = -new_pt.x + (SURFACE_WIDTH >> 1);
+ s.origin.y = -new_pt.y + (SURFACE_HEIGHT >> 1);
+ s.frame = pSolarSysState->Orbit.TopoZoomFrame;
+ DrawStamp (&s);
+ s.origin.x += MAP_WIDTH << MAG_SHIFT;
+ DrawStamp (&s);
+ s.origin.x -= MAP_WIDTH << (MAG_SHIFT + 1);
+ DrawStamp (&s);
+ }
+
+ BuildObjectList ();
+
+ DrawBatch (DisplayArray, DisplayLinks, 0);
+
+ // Draw the lander while is still alive and keep drawing for a few
+ // frames while it is exploding
+ if (crew_left || damage_index || explosion_index < 3)
+ {
+ lander_s.origin.x = SURFACE_WIDTH >> 1;
+ lander_s.origin.y = (SURFACE_HEIGHT >> 1) + landingOffset;
+ lander_s.frame = LanderFrame[0];
+
+ if (landingOffset != ON_THE_GROUND)
+ { // Landing, draw a shadow
+ shadow_s.origin.x = lander_s.origin.y + (SURFACE_WIDTH >> 1) - (SURFACE_HEIGHT >> 1);//2;
+ shadow_s.origin.y = lander_s.origin.y;
+ shadow_s.frame = lander_s.frame;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledStamp (&shadow_s);
+ }
+
+ if (damage_index == 0)
+ { // No damage -- normal lander
+ DrawStamp (&lander_s);
+ }
+ else if (shieldHit)
+ { // Was protected by a shield
+ --damage_index;
+ if (damage_index > 0)
+ {
+ shield_s.origin = lander_s.origin;
+ shield_s.frame = SetEquFrameIndex (
+ LanderFrame[4], lander_s.frame);
+
+ // XXX: Shouldn't this color-cycle with damage_index?
+ // damage_index is used, but only as a VGA index!
+ /*SetContextForeGroundColor (BUILD_COLOR (
+ MAKE_RGB15 (0x1F, 0x1F, 0x1F) | 0x8000,
+ damage_index));*/
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), damage_index));
+ DrawFilledStamp (&shield_s);
+ }
+ DrawStamp (&lander_s);
+ }
+ else
+ { // Direct hit, no shield
+ --damage_index;
+ SetContextForeGroundColor (
+ DamageColorCycle (BLACK_COLOR, damage_index));
+ DrawFilledStamp (&lander_s);
+ }
+ }
+
+ if (landingOffset == ON_THE_GROUND && crew_left
+ && GetPredLink (DisplayLinks) != END_OF_LIST)
+ CheckObjectCollision (END_OF_LIST);
+
+ {
+ PLANETSIDE_DESC *pPSD = planetSideDesc;
+ if (pPSD->NumFrames)
+ {
+ --pPSD->NumFrames;
+ SetContextForeGroundColor (pPSD->ColorCycle[pPSD->NumFrames >> 1]);
+
+ pPSD->MineralText[0].baseline.x -= dx;
+ pPSD->MineralText[0].baseline.y -= dy;
+ font_DrawText (&pPSD->MineralText[0]);
+ pPSD->MineralText[1].baseline.x =
+ pPSD->MineralText[0].baseline.x;
+ pPSD->MineralText[1].baseline.y =
+ pPSD->MineralText[0].baseline.y + 7;
+ font_DrawText (&pPSD->MineralText[1]);
+ pPSD->MineralText[2].baseline.x =
+ pPSD->MineralText[1].baseline.x;
+ pPSD->MineralText[2].baseline.y =
+ pPSD->MineralText[1].baseline.y + 7;
+ font_DrawText (&pPSD->MineralText[2]);
+ }
+ }
+
+ RedrawSurfaceScan (&new_pt);
+
+ if (lander_flags & KILL_CREW)
+ DeltaLanderCrew (-1, LIGHTNING_DISASTER);
+
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static void
+animationInterframe (TimeCount *TimeIn, COUNT periods)
+{
+#define ANIM_FRAME_RATE (ONE_SECOND / 30)
+
+ for ( ; periods; --periods)
+ {
+ RotatePlanetSphere (TRUE);
+
+ SleepThreadUntil (*TimeIn + ANIM_FRAME_RATE);
+ *TimeIn = GetTimeCounter ();
+ }
+}
+
+static void
+AnimateLaunch (FRAME farray)
+{
+ RECT r;
+ STAMP s;
+ COUNT num_frames;
+ TimeCount NextTime;
+
+ SetContext (PlanetContext);
+
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = 0;
+ r.extent.height = 0;
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = farray;
+
+ for (num_frames = GetFrameCount (s.frame); num_frames; --num_frames)
+ {
+ NextTime = GetTimeCounter () + (ONE_SECOND / 22);
+
+ BatchGraphics ();
+ RepairBackRect (&r);
+#ifdef SPIN_ON_LAUNCH
+ RotatePlanetSphere (FALSE);
+#else
+ DrawDefaultPlanetSphere ();
+#endif
+ DrawStamp (&s);
+ UnbatchGraphics ();
+
+ GetFrameRect (s.frame, &r);
+ s.frame = IncFrameIndex (s.frame);
+
+ SleepThreadUntil (NextTime);
+ }
+
+ RepairBackRect (&r);
+}
+
+static void
+AnimateLanderWarmup (void)
+{
+ SIZE num_crew;
+ STAMP s;
+ CONTEXT OldContext;
+ TimeCount TimeIn = GetTimeCounter ();
+
+ OldContext = SetContext (RadarContext);
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (LanderFrame[0],
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 1);
+
+ DrawStamp (&s);
+
+ animationInterframe (&TimeIn, 2);
+
+ for (num_crew = 0; num_crew < (NUM_CREW_COLS * NUM_CREW_ROWS)
+ && GLOBAL_SIS (CrewEnlisted); ++num_crew)
+ {
+ animationInterframe (&TimeIn, 1);
+
+ DeltaSISGauges (-1, 0, 0);
+ DeltaLanderCrew (1, 0);
+ }
+
+ animationInterframe (&TimeIn, 2);
+
+ if (GET_GAME_STATE (IMPROVED_LANDER_SHOT))
+ s.frame = SetAbsFrameIndex (s.frame, 58);
+ else
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 2);
+ DrawStamp (&s);
+
+ animationInterframe (&TimeIn, 2);
+
+ if (GET_GAME_STATE (IMPROVED_LANDER_SPEED))
+ s.frame = SetAbsFrameIndex (s.frame, 57);
+ else
+ {
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 3);
+ DrawStamp (&s);
+
+ animationInterframe (&TimeIn, 2);
+
+ s.frame = IncFrameIndex (s.frame);
+ }
+ DrawStamp (&s);
+
+ if (GET_GAME_STATE (IMPROVED_LANDER_CARGO))
+ {
+ animationInterframe (&TimeIn, 2);
+
+ s.frame = SetAbsFrameIndex (s.frame, 59);
+ DrawStamp (&s);
+ }
+
+ animationInterframe (&TimeIn, 2);
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_DEPARTS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY + 1);
+}
+
+static void
+InitPlanetSide (POINT pt)
+{
+ // Adjust landing location by a random jitter.
+#define RANDOM_MISS 64
+ // Jitter the X landing point.
+ pt.x -= RANDOM_MISS - TFB_Random () % (RANDOM_MISS << 1);
+ if (pt.x < 0)
+ pt.x += (MAP_WIDTH << MAG_SHIFT);
+ else if (pt.x >= (MAP_WIDTH << MAG_SHIFT))
+ pt.x -= (MAP_WIDTH << MAG_SHIFT);
+
+ // Jitter the Y landing point.
+ pt.y -= RANDOM_MISS - TFB_Random () % (RANDOM_MISS << 1);
+ if (pt.y < 0)
+ pt.y = 0;
+ else if (pt.y >= (MAP_HEIGHT << MAG_SHIFT))
+ pt.y = (MAP_HEIGHT << MAG_SHIFT) - 1;
+
+ curLanderLoc = pt;
+
+ SetContext (PlanetContext);
+ SetContextFont (TinyFont);
+
+ {
+ RECT r;
+
+ GetContextClipRect (&r);
+
+ SetTransitionSource (&r);
+ BatchGraphics ();
+
+ {
+ STAMP s;
+
+ // Note - This code is the same as in ScrollPlanetSize,
+ // Display planet area, accounting for horizontal wrapping if
+ // near the edges.
+ ClearDrawable ();
+ s.origin.x = -pt.x + (SURFACE_WIDTH >> 1);
+ s.origin.y = -pt.y + (SURFACE_HEIGHT >> 1);
+ s.frame = pSolarSysState->Orbit.TopoZoomFrame;
+ DrawStamp (&s);
+ s.origin.x += MAP_WIDTH << MAG_SHIFT;
+ DrawStamp (&s);
+ s.origin.x -= MAP_WIDTH << (MAG_SHIFT + 1);
+ DrawStamp (&s);
+ }
+
+ ScreenTransition (3, &r);
+ UnbatchGraphics ();
+ }
+
+
+ SET_GAME_STATE (PLANETARY_LANDING, 1);
+}
+
+static void
+LanderFire (SIZE facing)
+{
+#define SHUTTLE_FIRE_WAIT 15
+ HELEMENT hWeaponElement;
+ SIZE wdx, wdy;
+ ELEMENT *WeaponElementPtr;
+ COUNT angle;
+
+ hWeaponElement = AllocElement ();
+ if (hWeaponElement == NULL)
+ return;
+
+ LockElement (hWeaponElement, &WeaponElementPtr);
+
+ WeaponElementPtr->playerNr = PS_HUMAN_PLAYER;
+ WeaponElementPtr->mass_points = 1;
+ WeaponElementPtr->life_span = 12;
+ WeaponElementPtr->state_flags = FINITE_LIFE;
+ WeaponElementPtr->next.location = curLanderLoc;
+
+ SetPrimType (&DisplayArray[WeaponElementPtr->PrimIndex], STAMP_PRIM);
+ DisplayArray[WeaponElementPtr->PrimIndex].Object.Stamp.frame =
+ SetAbsFrameIndex (LanderFrame[0],
+ /* shot images immediately follow the lander images */
+ facing + ANGLE_TO_FACING (FULL_CIRCLE));
+
+ if (!CurrentInputState.key[PlayerControls[0]][KEY_UP])
+ {
+ wdx = 0;
+ wdy = 0;
+ }
+ else
+ {
+ GetCurrentVelocityComponents (&GLOBAL (velocity), &wdx, &wdy);
+ }
+
+ angle = FACING_TO_ANGLE (facing);
+ SetVelocityComponents (
+ &WeaponElementPtr->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (2 * 3)) + wdx,
+ SINE (angle, WORLD_TO_VELOCITY (2 * 3)) + wdy);
+
+ UnlockElement (hWeaponElement);
+
+ InsertElement (hWeaponElement, GetHeadElement ());
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_SHOOTS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY);
+}
+
+static BOOLEAN
+LanderExplosion (void)
+{
+ HELEMENT hExplosionElement;
+ ELEMENT *ExplosionElementPtr;
+
+ hExplosionElement = AllocElement ();
+ if (!hExplosionElement)
+ return FALSE;
+
+ LockElement (hExplosionElement, &ExplosionElementPtr);
+
+ ExplosionElementPtr->playerNr = PS_HUMAN_PLAYER;
+ ExplosionElementPtr->mass_points = DEATH_EXPLOSION;
+ ExplosionElementPtr->state_flags = FINITE_LIFE;
+ ExplosionElementPtr->next.location = curLanderLoc;
+ ExplosionElementPtr->preprocess_func = object_animation;
+ // Animation advances every 3rd frame
+ ExplosionElementPtr->turn_wait = MAKE_BYTE (2, 2);
+ ExplosionElementPtr->life_span = EXPLOSION_LIFE
+ * (LONIBBLE (ExplosionElementPtr->turn_wait) + 1);
+
+ SetPrimType (&DisplayArray[ExplosionElementPtr->PrimIndex],
+ STAMP_PRIM);
+ DisplayArray[ExplosionElementPtr->PrimIndex].Object.Stamp.frame =
+ SetAbsFrameIndex (LanderFrame[0], 46);
+
+ UnlockElement (hExplosionElement);
+
+ InsertElement (hExplosionElement, GetHeadElement ());
+
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_DESTROYED),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY + 1);
+
+ return TRUE;
+}
+
+static BOOLEAN
+DoPlanetSide (LanderInputState *pMS)
+{
+ SIZE dx = 0;
+ SIZE dy = 0;
+
+#define SHUTTLE_TURN_WAIT 2
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return (FALSE);
+
+ if (!pMS->Initialized)
+ {
+ COUNT landerSpeedNumer;
+ COUNT angle;
+
+ pMS->Initialized = TRUE;
+
+ turn_wait = 0;
+ weapon_wait = 0;
+
+ angle = FACING_TO_ANGLE (GetFrameIndex (LanderFrame[0]));
+ landerSpeedNumer = GET_GAME_STATE (IMPROVED_LANDER_SPEED) ?
+ WORLD_TO_VELOCITY (2 * 14) :
+ WORLD_TO_VELOCITY (2 * 8);
+
+#ifdef FAST_FAST
+landerSpeedNumer = WORLD_TO_VELOCITY (48);
+#endif
+
+ SetVelocityComponents (&GLOBAL (velocity),
+ COSINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM,
+ SINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM);
+
+ return TRUE;
+ }
+ else if (crew_left /* alive and taking off */
+ && ((CurrentInputState.key[PlayerControls[0]][KEY_ESCAPE] ||
+ CurrentInputState.key[PlayerControls[0]][KEY_SPECIAL])
+ || planetSideDesc->InTransit))
+ {
+ return FALSE;
+ }
+ else if (!crew_left && !damage_index)
+ { // Dead, damage dealt, and exploding
+ if (explosion_index > EXPLOSION_LIFE + EXPLOSION_WAIT_FRAMES)
+ return FALSE;
+
+ if (explosion_index > EXPLOSION_LIFE)
+ { // Keep going until the wait expires
+ ++explosion_index;
+ }
+ else if (explosion_index == 0)
+ { // Start the explosion animation
+ if (LanderExplosion ())
+ {
+ // Advance the state only once we've got the element
+ ++explosion_index;
+ }
+ else
+ { // We could not allocate because the queue was full, but
+ // we will get another chance on the next iteration
+ log_add (log_Warning, "DoPlanetSide(): could not"
+ " allocate explosion element!");
+ }
+ }
+ }
+ else
+ {
+ if (crew_left)
+ {
+ SIZE index = GetFrameIndex (LanderFrame[0]);
+ if (turn_wait)
+ --turn_wait;
+ else if (CurrentInputState.key[PlayerControls[0]][KEY_LEFT] ||
+ CurrentInputState.key[PlayerControls[0]][KEY_RIGHT])
+ {
+ COUNT landerSpeedNumer;
+ COUNT angle;
+
+ if (CurrentInputState.key[PlayerControls[0]][KEY_LEFT])
+ --index;
+ else
+ ++index;
+
+ index = NORMALIZE_FACING (index);
+ LanderFrame[0] = SetAbsFrameIndex (LanderFrame[0], index);
+
+ angle = FACING_TO_ANGLE (index);
+ landerSpeedNumer = GET_GAME_STATE (IMPROVED_LANDER_SPEED) ?
+ WORLD_TO_VELOCITY (2 * 14) :
+ WORLD_TO_VELOCITY (2 * 8);
+
+#ifdef FAST_FAST
+landerSpeedNumer = WORLD_TO_VELOCITY (48);
+#endif
+
+ SetVelocityComponents (&GLOBAL (velocity),
+ COSINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM,
+ SINE (angle, landerSpeedNumer) / LANDER_SPEED_DENOM);
+
+ turn_wait = SHUTTLE_TURN_WAIT;
+ }
+
+ if (!CurrentInputState.key[PlayerControls[0]][KEY_UP])
+ {
+ dx = 0;
+ dy = 0;
+ }
+ else
+ GetNextVelocityComponents (&GLOBAL (velocity), &dx, &dy, 1);
+
+ if (weapon_wait)
+ --weapon_wait;
+ else if (CurrentInputState.key[PlayerControls[0]][KEY_WEAPON])
+ {
+ LanderFire (index);
+
+ weapon_wait = SHUTTLE_FIRE_WAIT;
+ if (GET_GAME_STATE (IMPROVED_LANDER_SHOT))
+ weapon_wait >>= 1;
+ }
+ }
+ }
+
+ ScrollPlanetSide (dx, dy, ON_THE_GROUND);
+
+ SleepThreadUntil (pMS->NextTime);
+ // NOTE: The rate is not stabilized
+ pMS->NextTime = GetTimeCounter () + PLANET_SIDE_RATE;
+
+ return TRUE;
+}
+
+void
+FreeLanderData (void)
+{
+ COUNT i;
+ COUNT landerFrameCount;
+
+ if (LanderFrame[0] == NULL)
+ return;
+
+ for (i = 0; i < NUM_ORBIT_THEMES; ++i)
+ {
+ DestroyMusic (OrbitMusic[i]);
+ OrbitMusic[i] = 0;
+ }
+
+ DestroySound (ReleaseSound (LanderSounds));
+ LanderSounds = 0;
+
+ landerFrameCount = sizeof (LanderFrame) / sizeof (LanderFrame[0]);
+ for (i = 0; i < landerFrameCount; ++i)
+ {
+ DestroyDrawable (ReleaseDrawable (LanderFrame[i]));
+ LanderFrame[i] = 0;
+ }
+}
+
+void
+LoadLanderData (void)
+{
+ if (LanderFrame[0] != 0)
+ return;
+
+ LanderFrame[0] =
+ CaptureDrawable (LoadGraphic (LANDER_MASK_PMAP_ANIM));
+ LanderFrame[1] =
+ CaptureDrawable (LoadGraphic (QUAKE_MASK_PMAP_ANIM));
+ LanderFrame[2] =
+ CaptureDrawable (LoadGraphic (LIGHTNING_MASK_ANIM));
+ LanderFrame[3] =
+ CaptureDrawable (LoadGraphic (LAVA_MASK_PMAP_ANIM));
+ LanderFrame[4] =
+ CaptureDrawable (LoadGraphic (LANDER_SHIELD_MASK_ANIM));
+ LanderFrame[5] =
+ CaptureDrawable (LoadGraphic (LANDER_LAUNCH_MASK_PMAP_ANIM));
+ LanderFrame[6] =
+ CaptureDrawable (LoadGraphic (LANDER_RETURN_MASK_PMAP_ANIM));
+ LanderFrame[7] =
+ CaptureDrawable (LoadGraphic (ORBIT_VIEW_ANIM));
+
+ LanderSounds = CaptureSound (LoadSound (LANDER_SOUNDS));
+
+ {
+ COUNT i;
+
+ for (i = 0; i < NUM_ORBIT_THEMES; ++i)
+ OrbitMusic[i] = load_orbit_theme (i);
+ }
+}
+
+void
+SetPlanetMusic (BYTE planet_type)
+{
+ LanderMusic = OrbitMusic[planet_type % NUM_ORBIT_THEMES];
+}
+
+static void
+ReturnToOrbit (void)
+{
+ CONTEXT OldContext;
+ RECT r;
+
+ OldContext = SetContext (PlanetContext);
+ GetContextClipRect (&r);
+
+ SetTransitionSource (&r);
+ BatchGraphics ();
+ DrawStarBackGround ();
+ DrawPlanetSurfaceBorder ();
+ RedrawSurfaceScan (NULL);
+ ScreenTransition (3, &r);
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static void
+IdlePlanetSide (LanderInputState *inputState, TimeCount howLong)
+{
+#define IDLE_OFFSET
+ TimeCount TimeOut = GetTimeCounter () + howLong;
+
+ while (GetTimeCounter () < TimeOut)
+ {
+ // 10 to clear the lander off of the screen
+ ScrollPlanetSide (0, 0, -(SURFACE_HEIGHT / 2 + 10));
+ SleepThreadUntil (inputState->NextTime);
+ inputState->NextTime += PLANET_SIDE_RATE;
+ }
+}
+
+static void
+LandingTakeoffSequence (LanderInputState *inputState, BOOLEAN landing)
+{
+// We cannot solve a quadratic equation in a macro, so use a sensible max
+#define MAX_OFFSETS 20
+// 10 to clear the lander off of the screen
+#define DISTANCE_COVERED (SURFACE_HEIGHT / 2 + 10)
+ int landingOfs[MAX_OFFSETS];
+ int start;
+ int end;
+ int delta;
+ int index;
+
+ // Produce smooth acceleration deltas from a simple 1..x progression
+ delta = 0;
+ for (index = 0; index < MAX_OFFSETS && delta < DISTANCE_COVERED; ++index)
+ {
+ delta += index + 1;
+ landingOfs[index] = -delta;
+ }
+ assert (delta >= DISTANCE_COVERED && "Increase MAX_OFFSETS!");
+
+ if (landing)
+ {
+ start = index - 1;
+ end = -1;
+ delta = -1;
+ }
+ else
+ { // takeoff
+ start = 0;
+ end = index;
+ delta = +1;
+ }
+
+ if (landing)
+ IdlePlanetSide (inputState, ONE_SECOND);
+
+ // Draw the landing/takeoff lander positions
+ for (index = start; index != end; index += delta)
+ {
+ ScrollPlanetSide (0, 0, landingOfs[index]);
+ SleepThreadUntil (inputState->NextTime);
+ inputState->NextTime += PLANET_SIDE_RATE;
+ }
+
+ if (!landing)
+ IdlePlanetSide (inputState, ONE_SECOND / 2);
+}
+
+void
+SetLanderTakeoff (void)
+{
+ assert (planetSideDesc != NULL);
+ if (planetSideDesc)
+ planetSideDesc->InTransit = TRUE;
+}
+
+// Returns whether the lander is still alive at the end of the sequence
+bool
+KillLanderCrewSeq (COUNT numKilled, DWORD period)
+{
+ TimeCount TimeOut;
+ COUNT i;
+
+ TimeOut = GetTimeCounter ();
+ for (i = 0; i < numKilled && crew_left; ++i)
+ {
+ TimeOut += period;
+ DeltaLanderCrew (-1, LANDER_INJURED);
+ SleepThreadUntil (TimeOut);
+ }
+
+ return crew_left > 0;
+}
+
+// Maps a temperature to a (0-7) hazard rating.
+// Thermal hazards aren't exposed to the user as a hazard number,
+// but the code still works with them that way.
+#define ARRAY_SIZE(array) (sizeof(array) / sizeof (*array))
+unsigned
+GetThermalHazardRating (int temp)
+{
+ static const int tempBreakpoints[] = { 50, 100, 150, 250, 350, 550, 800 };
+ const size_t numBreakpoints = ARRAY_SIZE (tempBreakpoints);
+ unsigned i;
+
+ for (i = 0; i < numBreakpoints; ++i)
+ {
+ if (temp < tempBreakpoints[i])
+ return i;
+ }
+
+ return numBreakpoints;
+}
+
+// Given a hazard type and rating, return the chance (out of 256) of the hazard
+// being generated.
+static BYTE
+GetHazardChance (int hazardType, unsigned HazardRating)
+{
+ static const BYTE TectonicsChanceTab[] = {0*3, 0*3, 1*3, 2*3, 4*3, 8*3, 16*3, 32*3};
+ static const BYTE WeatherChanceTab [] = {0*3, 0*3, 1*3, 2*3, 3*3, 6*3, 12*3, 24*3};
+ static const BYTE FireChanceTab [] = {0*3, 0*3, 1*3, 2*3, 4*3, 12*3, 24*3, 48*3};
+
+ switch (hazardType)
+ {
+ case EARTHQUAKE_DISASTER:
+ return TectonicsChanceTab[HazardRating];
+ case LIGHTNING_DISASTER:
+ return WeatherChanceTab[HazardRating];
+ case LAVASPOT_DISASTER:
+ return FireChanceTab[HazardRating];
+ }
+
+ return 0;
+}
+
+void
+PlanetSide (POINT planetLoc)
+{
+ SIZE index;
+ LanderInputState landerInputState;
+ PLANETSIDE_DESC PSD;
+
+ memset (&PSD, 0, sizeof (PSD));
+ PSD.InTransit = TRUE;
+
+ // Set our chances of hazards occurring.
+ PSD.TectonicsChance = GetHazardChance (EARTHQUAKE_DISASTER,
+ pSolarSysState->SysInfo.PlanetInfo.Tectonics);
+ PSD.WeatherChance = GetHazardChance (LIGHTNING_DISASTER,
+ pSolarSysState->SysInfo.PlanetInfo.Weather);
+ PSD.FireChance = GetHazardChance (LAVASPOT_DISASTER, GetThermalHazardRating (
+ pSolarSysState->SysInfo.PlanetInfo.SurfaceTemperature));
+
+ PSD.ElementLevel = GetStorageBayCapacity () - GLOBAL_SIS (TotalElementMass);
+ PSD.MaxElementLevel = MAX_SCROUNGED;
+ if (GET_GAME_STATE (IMPROVED_LANDER_CARGO))
+ PSD.MaxElementLevel <<= 1;
+ if (PSD.ElementLevel < PSD.MaxElementLevel)
+ PSD.MaxElementLevel = PSD.ElementLevel;
+ PSD.ElementLevel = 0;
+
+ PSD.MineralText[0].align = ALIGN_CENTER;
+ PSD.MineralText[0].pStr = PSD.AmountBuf;
+ PSD.MineralText[1] = PSD.MineralText[0];
+ PSD.MineralText[2] = PSD.MineralText[1];
+
+ PSD.ColorCycle[0] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x03, 0x00), 0x7F);
+ PSD.ColorCycle[1] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7D);
+ PSD.ColorCycle[2] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7B);
+ PSD.ColorCycle[3] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x71);
+ for (index = 4; index < (NUM_TEXT_FRAMES >> 1) - 4; ++index)
+ {
+ PSD.ColorCycle[index] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F);
+ }
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 4] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x71);
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 3] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7B);
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 2] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7D);
+ PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 1] =
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x03, 0x00), 0x7F);
+ planetSideDesc = &PSD;
+
+ index = NORMALIZE_FACING (TFB_Random ());
+ LanderFrame[0] = SetAbsFrameIndex (LanderFrame[0], index);
+ crew_left = 0;
+ damage_index = 0;
+ explosion_index = 0;
+
+ AnimateLanderWarmup ();
+ AnimateLaunch (LanderFrame[5]);
+ InitPlanetSide (planetLoc);
+
+ landerInputState.NextTime = GetTimeCounter () + PLANET_SIDE_RATE;
+ LandingTakeoffSequence (&landerInputState, TRUE);
+ PSD.InTransit = FALSE;
+
+ landerInputState.Initialized = FALSE;
+ landerInputState.InputFunc = DoPlanetSide;
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ DoInput (&landerInputState, FALSE);
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (crew_left == 0)
+ {
+ --GLOBAL_SIS (NumLanders);
+ DrawLanders ();
+
+ ReturnToOrbit ();
+ }
+ else
+ {
+ PSD.InTransit = TRUE;
+ PlaySound (SetAbsSoundIndex (LanderSounds, LANDER_RETURNS),
+ NotPositional (), NULL, GAME_SOUND_PRIORITY + 1);
+
+ LandingTakeoffSequence (&landerInputState, FALSE);
+ ReturnToOrbit ();
+ AnimateLaunch (LanderFrame[6]);
+
+ DeltaSISGauges (crew_left, 0, 0);
+
+ if (PSD.ElementLevel)
+ {
+ for (index = 0; index < NUM_ELEMENT_CATEGORIES; ++index)
+ {
+ GLOBAL_SIS (ElementAmounts[index]) +=
+ PSD.ElementAmounts[index];
+ GLOBAL_SIS (TotalElementMass) +=
+ PSD.ElementAmounts[index];
+ }
+ DrawStorageBays (FALSE);
+ }
+
+ GLOBAL_SIS (TotalBioMass) += PSD.BiologicalLevel;
+ }
+ }
+
+ planetSideDesc = NULL;
+
+ {
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = GetHeadElement ();
+ hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = _GetSuccLink (ElementPtr);
+ if (ElementPtr->state_flags & FINITE_LIFE)
+ {
+ UnlockElement (hElement);
+
+ RemoveElement (hElement);
+ FreeElement (hElement);
+
+ continue;
+ }
+ UnlockElement (hElement);
+ }
+ }
+
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+}
+
+void
+InitLander (BYTE LanderFlags)
+{
+ RECT r;
+
+ SetContext (RadarContext);
+ BatchGraphics ();
+
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = RADAR_WIDTH;
+ r.extent.height = RADAR_HEIGHT;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ if (GLOBAL_SIS (NumLanders) || LanderFlags)
+ {
+ BYTE ShieldFlags, capacity_shift;
+ COUNT free_space;
+ STAMP s;
+
+ s.origin.x = 0; /* set up powered-down lander */
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (LanderFrame[0],
+ ANGLE_TO_FACING (FULL_CIRCLE) << 1);
+ DrawStamp (&s);
+ if (LanderFlags == 0)
+ {
+ ShieldFlags = GET_GAME_STATE (LANDER_SHIELDS);
+ capacity_shift = GET_GAME_STATE (IMPROVED_LANDER_CARGO);
+ }
+ else
+ {
+ ShieldFlags = (unsigned char)(LanderFlags &
+ ((1 << EARTHQUAKE_DISASTER)
+ | (1 << BIOLOGICAL_DISASTER)
+ | (1 << LIGHTNING_DISASTER)
+ | (1 << LAVASPOT_DISASTER)));
+ s.frame = IncFrameIndex (s.frame);
+ DrawStamp (&s);
+ if (LanderFlags & (1 << (4 + 0)))
+ s.frame = SetAbsFrameIndex (s.frame, 57);
+ else
+ {
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 3);
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ }
+ DrawStamp (&s);
+ if (!(LanderFlags & (1 << (4 + 1))))
+ capacity_shift = 0;
+ else
+ {
+ capacity_shift = 1;
+ s.frame = SetAbsFrameIndex (s.frame, 59);
+ DrawStamp (&s);
+ }
+ if (LanderFlags & (1 << (4 + 2)))
+ s.frame = SetAbsFrameIndex (s.frame, 58);
+ else
+ s.frame = SetAbsFrameIndex (s.frame,
+ (ANGLE_TO_FACING (FULL_CIRCLE) << 1) + 2);
+ DrawStamp (&s);
+ }
+
+ free_space = GetStorageBayCapacity () - GLOBAL_SIS (TotalElementMass);
+ if ((int)free_space < (int)(MAX_SCROUNGED << capacity_shift))
+ {
+ r.corner.x = 1;
+ r.extent.width = 4;
+ r.extent.height = MAX_SCROUNGED
+ - (free_space >> capacity_shift) + 1;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ }
+
+ s.frame = SetAbsFrameIndex (s.frame, 37);
+ if (ShieldFlags & (1 << EARTHQUAKE_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << BIOLOGICAL_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << LIGHTNING_DISASTER))
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ if (ShieldFlags & (1 << LAVASPOT_DISASTER))
+ DrawStamp (&s);
+ }
+
+ UnbatchGraphics ();
+}
diff --git a/src/uqm/planets/lander.h b/src/uqm/planets/lander.h
new file mode 100644
index 0000000..d41129b
--- /dev/null
+++ b/src/uqm/planets/lander.h
@@ -0,0 +1,88 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_PLANETS_LANDER_H_
+#define UQM_PLANETS_LANDER_H_
+
+#include "elemdata.h"
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "libs/sndlib.h"
+#include "libs/timelib.h"
+#include "../element.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// Surface magnification shift (x4)
+#define MAG_SHIFT 2
+
+#define NUM_TEXT_FRAMES 32
+
+// XXX: This is a private type now. Move it to lander.c?
+// We may also want to merge it with LanderInputState.
+typedef struct
+{
+ BOOLEAN InTransit;
+ // Landing on or taking of from a planet.
+ // Setting it while landed will initiate takeoff.
+
+ COUNT ElementLevel;
+ COUNT MaxElementLevel;
+ COUNT BiologicalLevel;
+ COUNT ElementAmounts[NUM_ELEMENT_CATEGORIES];
+
+ COUNT NumFrames;
+ UNICODE AmountBuf[40];
+ TEXT MineralText[3];
+
+ Color ColorCycle[NUM_TEXT_FRAMES >> 1];
+
+ BYTE TectonicsChance;
+ BYTE WeatherChance;
+ BYTE FireChance;
+} PLANETSIDE_DESC;
+
+extern MUSIC_REF LanderMusic;
+
+extern void PlanetSide (POINT planetLoc);
+extern void DoDiscoveryReport (SOUND ReadOutSounds);
+extern void SetPlanetMusic (BYTE planet_type);
+extern void LoadLanderData (void);
+extern void FreeLanderData (void);
+
+extern void object_animation (ELEMENT *ElementPtr);
+
+extern void SetLanderTakeoff (void);
+extern bool KillLanderCrewSeq (COUNT numKilled, DWORD period);
+
+extern unsigned GetThermalHazardRating (int temp);
+
+// ELEMENT.playerNr constants
+enum
+{
+ PS_HUMAN_PLAYER,
+ PS_NON_PLAYER,
+};
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_LANDER_H_ */
diff --git a/src/uqm/planets/lifeform.h b/src/uqm/planets/lifeform.h
new file mode 100644
index 0000000..fbe2d9d
--- /dev/null
+++ b/src/uqm/planets/lifeform.h
@@ -0,0 +1,75 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_PLANETS_LIFEFORM_H_
+#define UQM_PLANETS_LIFEFORM_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define BEHAVIOR_HUNT (0 << 0)
+#define BEHAVIOR_FLEE (1 << 0)
+#define BEHAVIOR_UNPREDICTABLE (2 << 0)
+
+#define BEHAVIOR_MASK 0x03
+#define BEHAVIOR_SHIFT 0
+
+#define AWARENESS_LOW (0 << 2)
+#define AWARENESS_MEDIUM (1 << 2)
+#define AWARENESS_HIGH (2 << 2)
+
+#define AWARENESS_MASK 0x0C
+#define AWARENESS_SHIFT (BEHAVIOR_SHIFT + 2)
+
+#define SPEED_MOTIONLESS (0 << 4)
+#define SPEED_SLOW (1 << 4)
+#define SPEED_MEDIUM (2 << 4)
+#define SPEED_FAST (3 << 4)
+
+#define SPEED_MASK 0x30
+#define SPEED_SHIFT (AWARENESS_SHIFT + 2)
+
+#define DANGER_HARMLESS (0 << 6)
+#define DANGER_WEAK (1 << 6)
+#define DANGER_NORMAL (2 << 6)
+#define DANGER_MONSTROUS (3 << 6)
+
+#define DANGER_MASK 0xC0
+#define DANGER_SHIFT (SPEED_SHIFT + 2)
+
+#define NUM_CREATURE_TYPES 23
+#define NUM_SPECIAL_CREATURE_TYPES 3
+#define MAX_LIFE_VARIATION 3
+
+#define CREATURE_AWARE (BYTE)(1 << 7)
+
+typedef struct
+{
+ BYTE Attributes, ValueAndHitPoints;
+} LIFEFORM_DESC;
+
+extern const LIFEFORM_DESC CreatureData[];
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_LIFEFORM_H */
diff --git a/src/uqm/planets/orbits.c b/src/uqm/planets/orbits.c
new file mode 100644
index 0000000..f1ad0f4
--- /dev/null
+++ b/src/uqm/planets/orbits.c
@@ -0,0 +1,629 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "planets.h"
+#include "../starmap.h"
+#include "libs/compiler.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+//#define DEBUG_ORBITS
+
+enum
+{
+ PLANET_NEVER = 0,
+ PLANET_RARE = 15,
+ PLANET_FEW = 63,
+ PLANET_COMMON = 127,
+ PLANET_ALWAYS = 255
+};
+
+static BYTE
+BlueDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_ALWAYS, /* OOLITE_WORLD */
+ PLANET_ALWAYS, /* YTTRIC_WORLD */
+ PLANET_ALWAYS, /* QUASI_DEGENERATE_WORLD */
+ PLANET_ALWAYS, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_NEVER, /* CHONDRITE_WORLD */
+ PLANET_NEVER, /* PURPLE_WORLD */
+ PLANET_NEVER, /* SUPER_DENSE_WORLD */
+ PLANET_NEVER, /* PELLUCID_WORLD */
+ PLANET_NEVER, /* DUST_WORLD */
+ PLANET_NEVER, /* CRIMSON_WORLD */
+ PLANET_NEVER, /* CIMMERIAN_WORLD */
+ PLANET_NEVER, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_ALWAYS, /* FLUORESCENT_WORLD */
+ PLANET_ALWAYS, /* ULTRAVIOLET_WORLD */
+ PLANET_ALWAYS, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_ALWAYS, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_NEVER, /* HYDROCARBON_WORLD */
+ PLANET_NEVER, /* IODINE_WORLD */
+ PLANET_NEVER, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_NEVER, /* MAGMA_WORLD */
+ PLANET_NEVER, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+GreenDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_ALWAYS, /* QUASI_DEGENERATE_WORLD */
+ PLANET_ALWAYS, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_NEVER, /* DUST_WORLD */
+ PLANET_NEVER, /* CRIMSON_WORLD */
+ PLANET_NEVER, /* CIMMERIAN_WORLD */
+ PLANET_NEVER, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_ALWAYS, /* FLUORESCENT_WORLD */
+ PLANET_ALWAYS, /* ULTRAVIOLET_WORLD */
+ PLANET_ALWAYS, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_ALWAYS, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_NEVER, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_NEVER, /* MAGMA_WORLD */
+ PLANET_NEVER, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+OrangeDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_NEVER, /* QUASI_DEGENERATE_WORLD */
+ PLANET_NEVER, /* LANTHANIDE_WORLD */
+ PLANET_NEVER, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_NEVER, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_NEVER, /* OPALESCENT_WORLD */
+ PLANET_NEVER, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_ALWAYS, /* DUST_WORLD */
+ PLANET_ALWAYS, /* CRIMSON_WORLD */
+ PLANET_ALWAYS, /* CIMMERIAN_WORLD */
+ PLANET_ALWAYS, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_NEVER, /* AURIC_WORLD */
+
+ PLANET_NEVER, /* FLUORESCENT_WORLD */
+ PLANET_NEVER, /* ULTRAVIOLET_WORLD */
+ PLANET_NEVER, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_NEVER, /* ORGANIC_WORLD */
+ PLANET_NEVER, /* XENOLITHIC_WORLD */
+ PLANET_NEVER, /* REDUX_WORLD */
+ PLANET_NEVER, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_ALWAYS, /* MAGMA_WORLD */
+ PLANET_ALWAYS, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+RedDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_NEVER, /* QUASI_DEGENERATE_WORLD */
+ PLANET_NEVER, /* LANTHANIDE_WORLD */
+ PLANET_NEVER, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_NEVER, /* RADIOACTIVE_WORLD */
+ PLANET_NEVER, /* OPALESCENT_WORLD */
+ PLANET_NEVER, /* CYANIC_WORLD */
+ PLANET_NEVER, /* ACID_WORLD */
+ PLANET_NEVER, /* ALKALI_WORLD */
+ PLANET_NEVER, /* HALIDE_WORLD */
+ PLANET_NEVER, /* GREEN_WORLD */
+ PLANET_NEVER, /* COPPER_WORLD */
+ PLANET_NEVER, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_ALWAYS, /* DUST_WORLD */
+ PLANET_ALWAYS, /* CRIMSON_WORLD */
+ PLANET_ALWAYS, /* CIMMERIAN_WORLD */
+ PLANET_ALWAYS, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_NEVER, /* AURIC_WORLD */
+
+ PLANET_NEVER, /* FLUORESCENT_WORLD */
+ PLANET_NEVER, /* ULTRAVIOLET_WORLD */
+ PLANET_NEVER, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_NEVER, /* ORGANIC_WORLD */
+ PLANET_NEVER, /* XENOLITHIC_WORLD */
+ PLANET_NEVER, /* REDUX_WORLD */
+ PLANET_NEVER, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_NEVER, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_ALWAYS, /* RUBY_WORLD */
+ PLANET_ALWAYS, /* MAGMA_WORLD */
+ PLANET_ALWAYS, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+WhiteDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_ALWAYS, /* OOLITE_WORLD */
+ PLANET_ALWAYS, /* YTTRIC_WORLD */
+ PLANET_ALWAYS, /* QUASI_DEGENERATE_WORLD */
+ PLANET_ALWAYS, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_NEVER, /* GREEN_WORLD */
+ PLANET_NEVER, /* COPPER_WORLD */
+ PLANET_NEVER, /* CARBIDE_WORLD */
+ PLANET_NEVER, /* ULTRAMARINE_WORLD */
+ PLANET_NEVER, /* NOBLE_WORLD */
+ PLANET_NEVER, /* AZURE_WORLD */
+ PLANET_NEVER, /* CHONDRITE_WORLD */
+ PLANET_NEVER, /* PURPLE_WORLD */
+ PLANET_NEVER, /* SUPER_DENSE_WORLD */
+ PLANET_NEVER, /* PELLUCID_WORLD */
+ PLANET_NEVER, /* DUST_WORLD */
+ PLANET_NEVER, /* CRIMSON_WORLD */
+ PLANET_NEVER, /* CIMMERIAN_WORLD */
+ PLANET_NEVER, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_ALWAYS, /* FLUORESCENT_WORLD */
+ PLANET_ALWAYS, /* ULTRAVIOLET_WORLD */
+ PLANET_ALWAYS, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_ALWAYS, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_ALWAYS, /* EMERALD_WORLD */
+ PLANET_NEVER, /* CHLORINE_WORLD */
+ PLANET_NEVER, /* MAGNETIC_WORLD */
+ PLANET_NEVER, /* WATER_WORLD */
+ PLANET_NEVER, /* TELLURIC_WORLD */
+ PLANET_NEVER, /* HYDROCARBON_WORLD */
+ PLANET_NEVER, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_ALWAYS, /* RUBY_WORLD */
+ PLANET_NEVER, /* MAGMA_WORLD */
+ PLANET_NEVER, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+static BYTE
+YellowDistribution (BYTE which_world)
+{
+ const BYTE PlanetDistribution[NUMBER_OF_PLANET_TYPES] =
+ {
+ PLANET_NEVER, /* OOLITE_WORLD */
+ PLANET_NEVER, /* YTTRIC_WORLD */
+ PLANET_NEVER, /* QUASI_DEGENERATE_WORLD */
+ PLANET_NEVER, /* LANTHANIDE_WORLD */
+ PLANET_ALWAYS, /* TREASURE_WORLD */
+ PLANET_ALWAYS, /* UREA_WORLD */
+ PLANET_ALWAYS, /* METAL_WORLD */
+ PLANET_ALWAYS, /* RADIOACTIVE_WORLD */
+ PLANET_ALWAYS, /* OPALESCENT_WORLD */
+ PLANET_ALWAYS, /* CYANIC_WORLD */
+ PLANET_ALWAYS, /* ACID_WORLD */
+ PLANET_ALWAYS, /* ALKALI_WORLD */
+ PLANET_ALWAYS, /* HALIDE_WORLD */
+ PLANET_ALWAYS, /* GREEN_WORLD */
+ PLANET_ALWAYS, /* COPPER_WORLD */
+ PLANET_ALWAYS, /* CARBIDE_WORLD */
+ PLANET_ALWAYS, /* ULTRAMARINE_WORLD */
+ PLANET_ALWAYS, /* NOBLE_WORLD */
+ PLANET_ALWAYS, /* AZURE_WORLD */
+ PLANET_ALWAYS, /* CHONDRITE_WORLD */
+ PLANET_ALWAYS, /* PURPLE_WORLD */
+ PLANET_ALWAYS, /* SUPER_DENSE_WORLD */
+ PLANET_ALWAYS, /* PELLUCID_WORLD */
+ PLANET_ALWAYS, /* DUST_WORLD */
+ PLANET_ALWAYS, /* CRIMSON_WORLD */
+ PLANET_ALWAYS, /* CIMMERIAN_WORLD */
+ PLANET_ALWAYS, /* INFRARED_WORLD */
+ PLANET_ALWAYS, /* SELENIC_WORLD */
+ PLANET_ALWAYS, /* AURIC_WORLD */
+
+ PLANET_NEVER, /* FLUORESCENT_WORLD */
+ PLANET_NEVER, /* ULTRAVIOLET_WORLD */
+ PLANET_NEVER, /* PLUTONIC_WORLD */
+ PLANET_NEVER, /* RAINBOW_WORLD */
+ PLANET_NEVER, /* SHATTERED_WORLD */
+ PLANET_NEVER, /* SAPPHIRE_WORLD */
+ PLANET_ALWAYS, /* ORGANIC_WORLD */
+ PLANET_ALWAYS, /* XENOLITHIC_WORLD */
+ PLANET_ALWAYS, /* REDUX_WORLD */
+ PLANET_ALWAYS, /* PRIMORDIAL_WORLD */
+ PLANET_NEVER, /* EMERALD_WORLD */
+ PLANET_ALWAYS, /* CHLORINE_WORLD */
+ PLANET_ALWAYS, /* MAGNETIC_WORLD */
+ PLANET_ALWAYS, /* WATER_WORLD */
+ PLANET_ALWAYS, /* TELLURIC_WORLD */
+ PLANET_ALWAYS, /* HYDROCARBON_WORLD */
+ PLANET_ALWAYS, /* IODINE_WORLD */
+ PLANET_ALWAYS, /* VINYLOGOUS_WORLD */
+ PLANET_NEVER, /* RUBY_WORLD */
+ PLANET_ALWAYS, /* MAGMA_WORLD */
+ PLANET_ALWAYS, /* MAROON_WORLD */
+
+ PLANET_ALWAYS, /* BLU_GAS_GIANT */
+ PLANET_ALWAYS, /* CYA_GAS_GIANT */
+ PLANET_ALWAYS, /* GRN_GAS_GIANT */
+ PLANET_ALWAYS, /* GRY_GAS_GIANT */
+ PLANET_ALWAYS, /* ORA_GAS_GIANT */
+ PLANET_ALWAYS, /* PUR_GAS_GIANT */
+ PLANET_ALWAYS, /* RED_GAS_GIANT */
+ PLANET_ALWAYS, /* VIO_GAS_GIANT */
+ PLANET_ALWAYS, /* YEL_GAS_GIANT */
+ };
+
+ return (PlanetDistribution[which_world]);
+}
+
+#define DWARF_ROCK_DIST MIN_PLANET_RADIUS
+#define DWARF_GASG_DIST SCALE_RADIUS (12)
+
+#define GIANT_ROCK_DIST SCALE_RADIUS (8)
+#define GIANT_GASG_DIST SCALE_RADIUS (13)
+
+#define SUPERGIANT_ROCK_DIST SCALE_RADIUS (16)
+#define SUPERGIANT_GASG_DIST SCALE_RADIUS (33)
+
+void
+FillOrbits (SOLARSYS_STATE *system, BYTE NumPlanets,
+ PLANET_DESC *pBaseDesc, BOOLEAN TypesDefined)
+{ /* Generate Planets in orbit around star */
+ BYTE StarColor, PlanetCount, MaxPlanet;
+ BOOLEAN GeneratingMoons;
+ COUNT StarSize;
+ PLANET_DESC *pPD;
+ struct
+ {
+ COUNT MinRockyDist, MinGasGDist;
+ } Suns[] =
+ {
+ {DWARF_ROCK_DIST, DWARF_GASG_DIST},
+ {GIANT_ROCK_DIST, GIANT_GASG_DIST},
+ {SUPERGIANT_ROCK_DIST, SUPERGIANT_GASG_DIST},
+ };
+#ifdef DEBUG_ORBITS
+UNICODE buf[256];
+char stype[] = {'D', 'G', 'S'};
+char scolor[] = {'B', 'G', 'O', 'R', 'W', 'Y'};
+#endif /* DEBUG_ORBITS */
+
+ pPD = pBaseDesc;
+ StarSize = system->SunDesc[0].data_index;
+ StarColor = STAR_COLOR (CurStarDescPtr->Type);
+
+ if (NumPlanets == (BYTE)~0)
+ {
+#define MAX_GENERATED_PLANETS 9
+ // XXX: This is pretty funny. Instead of calling RNG once, like so:
+ // 1 + Random % MAX_GENERATED_PLANETS
+ // we spin in a loop until the result > 0.
+ // Note that this behavior must be kept to preserve the universe.
+ do
+ NumPlanets = LOWORD (RandomContext_Random (SysGenRNG))
+ % (MAX_GENERATED_PLANETS + 1);
+ while (NumPlanets == 0);
+ system->SunDesc[0].NumPlanets = NumPlanets;
+ }
+
+#ifdef DEBUG_ORBITS
+ GetClusterName (CurStarDescPtr, buf);
+ log_add (log_Debug, "cluster name = %s color = %c type = %c", buf,
+ scolor[STAR_COLOR (CurStarDescPtr->Type)],
+ stype[STAR_TYPE (CurStarDescPtr->Type)]);
+#endif /* DEBUG_ORBITS */
+ GeneratingMoons = (BOOLEAN) (pBaseDesc == system->MoonDesc);
+ if (GeneratingMoons)
+ MaxPlanet = FIRST_LARGE_ROCKY_WORLD;
+ else
+ MaxPlanet = NUMBER_OF_PLANET_TYPES;
+ PlanetCount = NumPlanets;
+ while (NumPlanets--)
+ {
+ BYTE chance;
+ DWORD rand_val;
+ COUNT min_radius, angle;
+ SIZE delta_r;
+ PLANET_DESC *pLocPD;
+
+ do
+ {
+ rand_val = RandomContext_Random (SysGenRNG);
+ if (TypesDefined)
+ rand_val = 0;
+ else
+ pPD->data_index =
+ (BYTE)(HIBYTE (LOWORD (rand_val)) % MaxPlanet);
+
+ chance = PLANET_NEVER;
+ switch (StarColor)
+ {
+ case BLUE_BODY:
+ chance = BlueDistribution (pPD->data_index);
+ break;
+ case GREEN_BODY:
+ chance = GreenDistribution (pPD->data_index);
+ break;
+ case ORANGE_BODY:
+ chance = OrangeDistribution (pPD->data_index);
+ break;
+ case RED_BODY:
+ chance = RedDistribution (pPD->data_index);
+ break;
+ case WHITE_BODY:
+ chance = WhiteDistribution (pPD->data_index);
+ break;
+ case YELLOW_BODY:
+ chance = YellowDistribution (pPD->data_index);
+ break;
+ }
+ } while (LOBYTE (LOWORD (rand_val)) >= chance);
+
+ if (pPD->data_index < FIRST_GAS_GIANT)
+ min_radius = Suns[StarSize].MinRockyDist;
+ else
+ min_radius = Suns[StarSize].MinGasGDist;
+RelocatePlanet:
+ rand_val = RandomContext_Random (SysGenRNG);
+ if (GeneratingMoons)
+ {
+ pPD->radius = MIN_MOON_RADIUS
+ + ((LOWORD (rand_val) % MAX_MOONS) * MOON_DELTA);
+ for (pLocPD = pPD - 1; pLocPD >= pBaseDesc; --pLocPD)
+ {
+ if (pPD->radius == pLocPD->radius)
+ goto RelocatePlanet;
+ }
+ pPD->NumPlanets = 0;
+ }
+ else
+ {
+ pPD->radius =
+ (LOWORD (rand_val) % (MAX_PLANET_RADIUS - min_radius))
+ + min_radius;
+ for (pLocPD = pPD - 1; pLocPD >= pBaseDesc; --pLocPD)
+ {
+ delta_r = UNSCALE_RADIUS (pLocPD->radius) / 5
+ - UNSCALE_RADIUS (pPD->radius) / 5;
+ if (delta_r < 0)
+ delta_r = -delta_r;
+ if (delta_r <= 1)
+ goto RelocatePlanet;
+ }
+ }
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ angle = NORMALIZE_ANGLE (LOWORD (rand_val));
+ pPD->location.x = COSINE (angle, pPD->radius);
+ pPD->location.y = SINE (angle, pPD->radius);
+ pPD->rand_seed = MAKE_DWORD (pPD->location.x, pPD->location.y);
+
+ ++pPD;
+ }
+
+ {
+ BYTE i;
+
+ for (i = 0; i < PlanetCount; ++i)
+ {
+ BYTE j;
+
+ for (j = (BYTE)(PlanetCount - 1); j > i; --j)
+ {
+ if (pBaseDesc[i].radius > pBaseDesc[j].radius)
+ {
+ PLANET_DESC temp;
+
+ temp = pBaseDesc[i];
+ pBaseDesc[i] = pBaseDesc[j];
+ pBaseDesc[j] = temp;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/uqm/planets/oval.c b/src/uqm/planets/oval.c
new file mode 100644
index 0000000..4112997
--- /dev/null
+++ b/src/uqm/planets/oval.c
@@ -0,0 +1,329 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../units.h"
+#include "libs/gfxlib.h"
+#include "libs/graphics/context.h"
+#include "libs/graphics/drawable.h"
+
+#include "planets.h"
+
+#define NUM_QUADS 4
+
+void
+DrawOval (RECT *pRect, BYTE num_off_pixels)
+{
+#define FIRST_QUAD (1 << 0)
+#define SECOND_QUAD (1 << 1)
+#define THIRD_QUAD (1 << 2)
+#define FOURTH_QUAD (1 << 3)
+ COUNT off;
+ COORD x, y;
+ SIZE A, B;
+ long Asquared, TwoAsquared,
+ Bsquared, TwoBsquared;
+ long d, dx, dy;
+ BYTE quad_visible;
+ LINE corners;
+ POINT mp;
+ PRIMITIVE prim[NUM_QUADS];
+ COUNT StartPrim;
+ RECT ClipRect;
+ BRESENHAM_LINE ClipLine;
+
+ ClipRect.corner.x = ClipRect.corner.y = 0;
+
+ corners.first.x = pRect->corner.x - ClipRect.corner.x;
+ corners.first.y = pRect->corner.y - ClipRect.corner.y;
+ corners.second.x = corners.first.x + pRect->extent.width - 1;
+ corners.second.y = corners.first.y + pRect->extent.height - 1;
+ if (corners.second.x <= corners.first.x
+ || corners.second.y <= corners.first.y)
+ {
+ if (corners.second.x < corners.first.x)
+ corners.second.x = corners.first.x;
+ if (corners.second.y < corners.first.y)
+ corners.second.y = corners.first.y;
+
+ DrawLine (&corners);
+ return;
+ }
+
+ ClipRect.extent.width = SIS_SCREEN_WIDTH;
+ ClipRect.extent.height = SIS_SCREEN_HEIGHT;
+
+ quad_visible = 0;
+ mp.x = (corners.first.x + corners.second.x) >> 1;
+ mp.y = (corners.first.y + corners.second.y) >> 1;
+ ClipRect.corner.x = ClipRect.corner.y = 0;
+
+ if (corners.first.y >= 0 && corners.first.y < ClipRect.extent.height
+ && corners.second.x >= 0 && corners.second.x < ClipRect.extent.width)
+ quad_visible |= FIRST_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.first.y;
+ ClipLine.second.x = corners.second.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= FIRST_QUAD;
+ }
+
+ if (corners.first.y >= 0 && corners.first.y < ClipRect.extent.height
+ && corners.first.x >= 0 && corners.first.x < ClipRect.extent.width)
+ quad_visible |= SECOND_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.first.y;
+ ClipLine.second.x = corners.first.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= SECOND_QUAD;
+ }
+
+ if (corners.second.y >= 0 && corners.second.y < ClipRect.extent.height
+ && corners.first.x >= 0 && corners.first.x < ClipRect.extent.width)
+ quad_visible |= THIRD_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.second.y;
+ ClipLine.second.x = corners.first.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= THIRD_QUAD;
+ }
+
+ if (corners.second.y >= 0 && corners.second.y < ClipRect.extent.height
+ && corners.second.x >= 0 && corners.second.x < ClipRect.extent.width)
+ quad_visible |= FOURTH_QUAD;
+ else
+ {
+ ClipLine.first.x = mp.x;
+ ClipLine.first.y = corners.second.y;
+ ClipLine.second.x = corners.second.x;
+ ClipLine.second.y = mp.y;
+ if (_clip_line (&ClipRect, &ClipLine))
+ quad_visible |= FOURTH_QUAD;
+ }
+
+ if (!quad_visible)
+ return;
+
+ StartPrim = END_OF_LIST;
+ for (x = 0; x < NUM_QUADS; ++x)
+ {
+ if (quad_visible & (1 << x))
+ {
+ SetPrimNextLink (&prim[x], StartPrim);
+ SetPrimType (&prim[x], POINT_PRIM);
+ SetPrimColor (&prim[x], _get_context_fg_color ());
+
+ StartPrim = x;
+ }
+ }
+
+ A = pRect->extent.width >> 1;
+ B = pRect->extent.height >> 1;
+
+ x = 0;
+ y = B;
+
+ Asquared = ((long)A * A) << 1;
+ Bsquared = ((long)B * B) << 1;
+ do
+ {
+ Asquared >>= 1;
+ Bsquared >>= 1;
+ TwoAsquared = Asquared << 1;
+ dy = TwoAsquared * B;
+ } while (dy / B != TwoAsquared);
+ TwoBsquared = Bsquared << 1;
+
+ dx = 0;
+ d = Bsquared - (dy >> 1) + (Asquared >> 2);
+
+ off = 0;
+ A += pRect->corner.x;
+ B += pRect->corner.y;
+ while (dx < dy)
+ {
+ if (off-- == 0)
+ {
+ prim[0].Object.Point.x = prim[3].Object.Point.x = A + x;
+ prim[0].Object.Point.y = prim[1].Object.Point.y = B - y;
+ prim[1].Object.Point.x = prim[2].Object.Point.x = A - x;
+ prim[2].Object.Point.y = prim[3].Object.Point.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+ off = num_off_pixels;
+ }
+
+ if (d > 0)
+ {
+ --y;
+ dy -= TwoAsquared;
+ d -= dy;
+ }
+
+ ++x;
+ dx += TwoBsquared;
+ d += Bsquared + dx;
+ }
+
+ d += ((((Asquared - Bsquared) * 3) >> 1) - (dx + dy)) >> 1;
+
+ while (y >= 0)
+ {
+ if (off-- == 0)
+ {
+ prim[0].Object.Point.x = prim[3].Object.Point.x = A + x;
+ prim[0].Object.Point.y = prim[1].Object.Point.y = B - y;
+ prim[1].Object.Point.x = prim[2].Object.Point.x = A - x;
+ prim[2].Object.Point.y = prim[3].Object.Point.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+ off = num_off_pixels;
+ }
+
+ if (d < 0)
+ {
+ ++x;
+ dx += TwoBsquared;
+ d += dx;
+ }
+
+ --y;
+ dy -= TwoAsquared;
+ d += Asquared - dy;
+ }
+}
+
+void
+DrawFilledOval (RECT *pRect)
+{
+ COORD x, y;
+ SIZE A, B;
+ long Asquared, TwoAsquared,
+ Bsquared, TwoBsquared;
+ long d, dx, dy;
+ LINE corners;
+ PRIMITIVE prim[NUM_QUADS >> 1];
+ COUNT StartPrim;
+
+ corners.first.x = pRect->corner.x;
+ corners.first.y = pRect->corner.y;
+ corners.second.x = corners.first.x + pRect->extent.width - 1;
+ corners.second.y = corners.first.y + pRect->extent.height - 1;
+ if (corners.second.x <= corners.first.x
+ || corners.second.y <= corners.first.y)
+ {
+ if (corners.second.x < corners.first.x)
+ corners.second.x = corners.first.x;
+ if (corners.second.y < corners.first.y)
+ corners.second.y = corners.first.y;
+
+ DrawLine (&corners);
+ return;
+ }
+
+ StartPrim = END_OF_LIST;
+ for (x = 0; x < (NUM_QUADS >> 1); ++x)
+ {
+ SetPrimNextLink (&prim[x], StartPrim);
+ SetPrimType (&prim[x], RECTFILL_PRIM);
+ SetPrimColor (&prim[x], _get_context_fg_color ());
+ prim[x].Object.Rect.extent.height = 1;
+
+ StartPrim = x;
+ }
+
+ A = pRect->extent.width >> 1;
+ B = pRect->extent.height >> 1;
+
+ x = 0;
+ y = B;
+
+ Asquared = ((long)A * A) << 1;
+ Bsquared = ((long)B * B) << 1;
+ do
+ {
+ Asquared >>= 1;
+ Bsquared >>= 1;
+ TwoAsquared = Asquared << 1;
+ dy = TwoAsquared * B;
+ } while (dy / B != TwoAsquared);
+ TwoBsquared = Bsquared << 1;
+
+ dx = 0;
+ d = Bsquared - (dy >> 1) + (Asquared >> 2);
+
+ A += pRect->corner.x;
+ B += pRect->corner.y;
+ while (dx < dy)
+ {
+ if (d > 0)
+ {
+ prim[0].Object.Rect.corner.x =
+ prim[1].Object.Rect.corner.x = A - x;
+ prim[0].Object.Rect.extent.width =
+ prim[1].Object.Rect.extent.width = (x << 1) + 1;
+ prim[0].Object.Rect.corner.y = B - y;
+ prim[1].Object.Rect.corner.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+
+ --y;
+ dy -= TwoAsquared;
+ d -= dy;
+ }
+
+ ++x;
+ dx += TwoBsquared;
+ d += Bsquared + dx;
+ }
+
+ d += ((((Asquared - Bsquared) * 3) >> 1) - (dx + dy)) >> 1;
+
+ while (y >= 0)
+ {
+ prim[0].Object.Rect.corner.x =
+ prim[1].Object.Rect.corner.x = A - x;
+ prim[0].Object.Rect.extent.width =
+ prim[1].Object.Rect.extent.width = (x << 1) + 1;
+ prim[0].Object.Rect.corner.y = B - y;
+ prim[1].Object.Rect.corner.y = B + y;
+
+ DrawBatch (prim, StartPrim, 0);
+
+ if (d < 0)
+ {
+ ++x;
+ dx += TwoBsquared;
+ d += dx;
+ }
+
+ --y;
+ dy -= TwoAsquared;
+ d += Asquared - dy;
+ }
+}
+
+
diff --git a/src/uqm/planets/pl_stuff.c b/src/uqm/planets/pl_stuff.c
new file mode 100644
index 0000000..073e215
--- /dev/null
+++ b/src/uqm/planets/pl_stuff.c
@@ -0,0 +1,318 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "planets.h"
+#include "../colors.h"
+#include "../setup.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/mathlib.h"
+#include "scan.h"
+#include "options.h"
+
+#include <math.h>
+
+
+// define USE_ADDITIVE_SCAN_BLIT to use additive blittting
+// instead of transparency for the planet scans.
+// It still doesn't look right though (it is too bright)
+#define USE_ADDITIVE_SCAN_BLIT
+
+static int rotFrameIndex;
+static int rotDirection;
+static bool throbShield;
+static int rotPointIndex;
+
+// Draw the planet sphere and any extra graphic (like a shield) if present
+void
+DrawPlanetSphere (int x, int y)
+{
+ STAMP s;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ s.origin.x = x;
+ s.origin.y = y;
+
+ BatchGraphics ();
+ s.frame = Orbit->SphereFrame;
+ DrawStamp (&s);
+ if (Orbit->ObjectFrame)
+ {
+ s.frame = Orbit->ObjectFrame;
+ DrawStamp (&s);
+ }
+ UnbatchGraphics ();
+}
+
+void
+DrawDefaultPlanetSphere (void)
+{
+ CONTEXT oldContext;
+
+ oldContext = SetContext (PlanetContext);
+ DrawPlanetSphere (SIS_SCREEN_WIDTH / 2, PLANET_ORG_Y);
+ SetContext (oldContext);
+}
+
+void
+InitSphereRotation (int direction, BOOLEAN shielded)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ rotDirection = direction;
+ rotPointIndex = 0;
+ throbShield = shielded && optWhichShield == OPT_3DO;
+
+ if (throbShield)
+ {
+ // ObjectFrame must contain the shield graphic
+ Orbit->WorkFrame = Orbit->ObjectFrame;
+ // We need a scratch frame so that we can apply throbbing
+ // to the shield, so create one
+ Orbit->ObjectFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP | WANT_ALPHA,
+ GetFrameWidth (Orbit->ObjectFrame),
+ GetFrameHeight (Orbit->ObjectFrame), 2));
+ }
+
+ // Render the first sphere/shield frame
+ // Prepare will set the next one
+ rotFrameIndex = 1;
+ PrepareNextRotationFrame ();
+}
+
+void
+UninitSphereRotation (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ if (Orbit->WorkFrame)
+ {
+ DestroyDrawable (ReleaseDrawable (Orbit->ObjectFrame));
+ Orbit->ObjectFrame = Orbit->WorkFrame;
+ Orbit->WorkFrame = NULL;
+ }
+}
+
+void
+PrepareNextRotationFrame (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ // Generate the next rotation frame
+ // We alternate between the frames because we do not call FlushGraphics()
+ // The frame we just drew may not have made it to the screen yet
+ rotFrameIndex ^= 1;
+
+ // Go to next point, taking care of wraparounds
+ rotPointIndex += rotDirection;
+ if (rotPointIndex < 0)
+ rotPointIndex = MAP_WIDTH - 1;
+ else if (rotPointIndex >= MAP_WIDTH)
+ rotPointIndex = 0;
+
+ // prepare the next sphere frame
+ Orbit->SphereFrame = SetAbsFrameIndex (Orbit->SphereFrame, rotFrameIndex);
+ RenderPlanetSphere (Orbit->SphereFrame, rotPointIndex, throbShield);
+
+ if (throbShield)
+ { // prepare the next shield throb frame
+ Orbit->ObjectFrame = SetAbsFrameIndex (Orbit->ObjectFrame,
+ rotFrameIndex);
+ SetShieldThrobEffect (Orbit->WorkFrame, rotPointIndex,
+ Orbit->ObjectFrame);
+ }
+}
+
+#define ZOOM_RATE 24
+#define ZOOM_TIME (ONE_SECOND * 6 / 5)
+
+// This takes care of zooming the planet sphere into place
+// when entering orbit
+void
+ZoomInPlanetSphere (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ const int base = GSCALE_IDENTITY;
+ int dx, dy;
+ int oldScale;
+ int oldMode;
+ int i;
+ int frameCount;
+ int zoomCorner;
+ RECT frameRect;
+ RECT repairRect;
+ TimeCount NextTime;
+
+ frameCount = ZOOM_TIME / (ONE_SECOND / ZOOM_RATE);
+
+ // Planet zoom in from a randomly chosen corner
+ zoomCorner = TFB_Random ();
+ dx = 1 - (zoomCorner & 1) * 2;
+ dy = 1 - (zoomCorner & 2);
+
+ if (Orbit->ObjectFrame)
+ GetFrameRect (Orbit->ObjectFrame, &frameRect);
+ else
+ GetFrameRect (Orbit->SphereFrame, &frameRect);
+ repairRect = frameRect;
+
+ for (i = 0; i <= frameCount; ++i)
+ {
+ double scale;
+ POINT pt;
+
+ NextTime = GetTimeCounter () + (ONE_SECOND / ZOOM_RATE);
+
+ // Use 1 + e^-2 - e^(-2x / frameCount)) function to get a decelerating
+ // zoom like the one 3DO does (supposedly)
+ if (i < frameCount)
+ scale = 1.134 - exp (-2.0 * i / frameCount);
+ else
+ scale = 1.0; // final frame
+
+ // start from beyond the screen
+ pt.x = SIS_SCREEN_WIDTH / 2 + (int) (dx * (1.0 - scale)
+ * (SIS_SCREEN_WIDTH * 6 / 10) + 0.5);
+ pt.y = PLANET_ORG_Y + (int) (dy * (1.0 - scale)
+ * (SCAN_SCREEN_HEIGHT * 6 / 10) + 0.5);
+
+ SetContext (PlanetContext);
+
+ BatchGraphics ();
+ if (i > 0)
+ RepairBackRect (&repairRect);
+
+ oldMode = SetGraphicScaleMode (TFB_SCALE_BILINEAR);
+ oldScale = SetGraphicScale ((int)(base * scale + 0.5));
+ DrawPlanetSphere (pt.x, pt.y);
+ SetGraphicScale (oldScale);
+ SetGraphicScaleMode (oldMode);
+
+ UnbatchGraphics ();
+
+ repairRect.corner.x = pt.x + frameRect.corner.x;
+ repairRect.corner.y = pt.y + frameRect.corner.y;
+
+ PrepareNextRotationFrame ();
+
+ SleepThreadUntil (NextTime);
+ }
+}
+
+void
+RotatePlanetSphere (BOOLEAN keepRate)
+{
+ static TimeCount NextTime;
+ TimeCount Now = GetTimeCounter ();
+
+ if (keepRate && Now < NextTime)
+ return; // not time yet
+
+ NextTime = Now + PLANET_ROTATION_RATE;
+ DrawDefaultPlanetSphere ();
+
+ PrepareNextRotationFrame ();
+}
+
+static void
+renderTintFrame (Color tintColor)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ CONTEXT oldContext;
+ DrawMode mode, oldMode;
+ STAMP s;
+ RECT r;
+
+ oldContext = SetContext (OffScreenContext);
+ SetContextFGFrame (Orbit->TintFrame);
+ SetContextClipRect (NULL);
+ // get the rect of the whole context (or our frame really)
+ GetContextClipRect (&r);
+
+ // copy the topo frame to the tint frame
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = pSolarSysState->TopoFrame;
+ DrawStamp (&s);
+
+ // apply the tint
+#ifdef USE_ADDITIVE_SCAN_BLIT
+ mode = MAKE_DRAW_MODE (DRAW_ADDITIVE, DRAW_FACTOR_1 / 2);
+#else
+ mode = MAKE_DRAW_MODE (DRAW_ALPHA, DRAW_FACTOR_1 / 2);
+#endif
+ oldMode = SetContextDrawMode (mode);
+ SetContextForeGroundColor (tintColor);
+ DrawFilledRectangle (&r);
+ SetContextDrawMode (oldMode);
+
+ SetContext (oldContext);
+}
+
+// tintColor.a is ignored
+void
+DrawPlanet (int tintY, Color tintColor)
+{
+ STAMP s;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+
+ BatchGraphics ();
+ if (sameColor (tintColor, BLACK_COLOR))
+ { // no tint -- just draw the surface
+ s.frame = pSolarSysState->TopoFrame;
+ DrawStamp (&s);
+ }
+ else
+ { // apply different scan type tints
+ FRAME tintFrame = Orbit->TintFrame;
+ int height = GetFrameHeight (tintFrame);
+
+ if (!sameColor (tintColor, Orbit->TintColor))
+ {
+ renderTintFrame (tintColor);
+ Orbit->TintColor = tintColor;
+ }
+
+ if (tintY < height - 1)
+ { // untinted piece showing, draw regular topo
+ s.frame = pSolarSysState->TopoFrame;
+ DrawStamp (&s);
+ }
+
+ if (tintY >= 0)
+ { // tinted piece showing, draw tinted piece
+ RECT oldClipRect;
+ RECT clipRect;
+
+ // adjust cliprect to confine the tint
+ GetContextClipRect (&oldClipRect);
+ clipRect = oldClipRect;
+ clipRect.extent.height = tintY + 1;
+ SetContextClipRect (&clipRect);
+ s.frame = tintFrame;
+ DrawStamp (&s);
+ SetContextClipRect (&oldClipRect);
+ }
+ }
+ UnbatchGraphics ();
+}
+
diff --git a/src/uqm/planets/plandata.h b/src/uqm/planets/plandata.h
new file mode 100644
index 0000000..cd2a1c7
--- /dev/null
+++ b/src/uqm/planets/plandata.h
@@ -0,0 +1,318 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_PLANETS_PLANDATA_H_
+#define UQM_PLANETS_PLANDATA_H_
+
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*------------------------------ Type Defines ----------------------------- */
+#define NUMBER_OF_ORBITS 16
+#define VACANT 0xFF
+
+enum
+{
+ SMALL_ROCKY_WORLD = 0,
+ LARGE_ROCKY_WORLD,
+ GAS_GIANT,
+
+ NUM_PLANET_TYPES
+};
+
+enum
+{
+ DWARF_STAR = 0,
+ GIANT_STAR,
+ SUPER_GIANT_STAR,
+
+ NUM_STAR_TYPES
+};
+
+enum
+{
+ BLUE_BODY = 0,
+ GREEN_BODY,
+ ORANGE_BODY,
+ RED_BODY,
+ WHITE_BODY,
+ GRAY_BODY = WHITE_BODY,
+ YELLOW_BODY,
+
+ NUM_STAR_COLORS,
+
+ CYAN_BODY = NUM_STAR_COLORS,
+ PURPLE_BODY,
+ VIOLET_BODY
+};
+
+enum
+{
+ OWNER_NOBODY = 0,
+ OWNER_NEUTRAL,
+ OWNER_HIERARCHY,
+
+ OWNER_PLAYER = (1 << 2)
+};
+
+#define STAR_OWNER_SHIFT 0
+#define STAR_TYPE_SHIFT 3 /* STAR_OWNER_SHIFT + 3 */
+#define STAR_COLOR_SHIFT 5 /* STAR_TYPE_SHIFT + 2 */
+#define STAR_COLOR_MASK (BYTE)(0xFF << STAR_COLOR_SHIFT)
+#define STAR_TYPE_MASK (BYTE)((0xFF << STAR_TYPE_SHIFT) \
+ & ~STAR_COLOR_MASK)
+#define STAR_OWNER_MASK (BYTE)((0xFF << STAR_OWNER_SHIFT) \
+ & ~(STAR_COLOR_MASK \
+ | STAR_TYPE_MASK))
+#define STAR_UNKNOWN_MASK (STAR_OWNER_MASK & ~OWNER_PLAYER)
+
+#define MAKE_STAR(t,c,o) \
+ (BYTE)((((BYTE)(t) << STAR_TYPE_SHIFT) & STAR_TYPE_MASK) \
+ | (((BYTE)(c) << STAR_COLOR_SHIFT) & STAR_COLOR_MASK) \
+ | (((BYTE)(o) << STAR_OWNER_SHIFT) & STAR_OWNER_MASK))
+#define STAR_TYPE(f) (BYTE)(((f) & STAR_TYPE_MASK) >> STAR_TYPE_SHIFT)
+#define STAR_COLOR(f) (BYTE)(((f) & STAR_COLOR_MASK) >> STAR_COLOR_SHIFT)
+#define STAR_OWNER(f) (BYTE)(((f) & STAR_OWNER_MASK) >> STAR_OWNER_SHIFT)
+#define STAR_UNKNOWN(f) (BOOLEAN)((STAR_OWNER(f) \
+ & STAR_UNKNOWN_MASK) == STAR_UNKNOWN_MASK)
+
+#define PLAN_SIZE_MASK 0x03
+
+#define TOPO_ALGO (0 << 2)
+#define CRATERED_ALGO (1 << 2)
+#define GAS_GIANT_ALGO (2 << 2)
+#define PLAN_ALGO_MASK 0x0C
+
+#define PLANSIZE(type) ((BYTE)((type) & PLAN_SIZE_MASK))
+#define PLANALGO(type) ((BYTE)((type) & PLAN_ALGO_MASK))
+#define PLANCOLOR(type) HINIBBLE (type)
+
+#define THIN_ATMOSPHERE 10
+#define NORMAL_ATMOSPHERE 75
+#define THICK_ATMOSPHERE 200
+#define SUPER_THICK_ATMOSPHERE 2500
+#define GAS_GIANT_ATMOSPHERE 0xFFFF
+
+enum
+{
+ FIRST_ROCKY_WORLD = 0,
+ FIRST_SMALL_ROCKY_WORLD = FIRST_ROCKY_WORLD,
+
+ OOLITE_WORLD = FIRST_SMALL_ROCKY_WORLD,
+ YTTRIC_WORLD,
+ QUASI_DEGENERATE_WORLD,
+ LANTHANIDE_WORLD,
+ TREASURE_WORLD,
+ UREA_WORLD,
+ METAL_WORLD,
+ RADIOACTIVE_WORLD,
+ OPALESCENT_WORLD,
+ CYANIC_WORLD,
+ ACID_WORLD,
+ ALKALI_WORLD,
+ HALIDE_WORLD,
+ GREEN_WORLD,
+ COPPER_WORLD,
+ CARBIDE_WORLD,
+ ULTRAMARINE_WORLD,
+ NOBLE_WORLD,
+ AZURE_WORLD,
+ CHONDRITE_WORLD,
+ PURPLE_WORLD,
+ SUPER_DENSE_WORLD,
+ PELLUCID_WORLD,
+ DUST_WORLD,
+ CRIMSON_WORLD,
+ CIMMERIAN_WORLD,
+ INFRARED_WORLD,
+ SELENIC_WORLD,
+ AURIC_WORLD,
+ LAST_SMALL_ROCKY_WORLD = AURIC_WORLD,
+
+ FIRST_LARGE_ROCKY_WORLD,
+ FLUORESCENT_WORLD = FIRST_LARGE_ROCKY_WORLD,
+ ULTRAVIOLET_WORLD,
+ PLUTONIC_WORLD,
+ RAINBOW_WORLD,
+ SHATTERED_WORLD,
+ SAPPHIRE_WORLD,
+ ORGANIC_WORLD,
+ XENOLITHIC_WORLD,
+ REDUX_WORLD,
+ PRIMORDIAL_WORLD,
+ EMERALD_WORLD,
+ CHLORINE_WORLD,
+ MAGNETIC_WORLD,
+ WATER_WORLD,
+ TELLURIC_WORLD,
+ HYDROCARBON_WORLD,
+ IODINE_WORLD,
+ VINYLOGOUS_WORLD,
+ RUBY_WORLD,
+ MAGMA_WORLD,
+ MAROON_WORLD,
+ LAST_LARGE_ROCKY_WORLD = MAROON_WORLD,
+
+ FIRST_GAS_GIANT,
+ BLU_GAS_GIANT = FIRST_GAS_GIANT, /* Gas Giants */
+ CYA_GAS_GIANT,
+ GRN_GAS_GIANT,
+ GRY_GAS_GIANT,
+ ORA_GAS_GIANT,
+ PUR_GAS_GIANT,
+ RED_GAS_GIANT,
+ VIO_GAS_GIANT,
+ YEL_GAS_GIANT,
+ LAST_GAS_GIANT = YEL_GAS_GIANT,
+
+ NUMBER_OF_PLANET_TYPES,
+
+ WORLD_TYPE_SPECIAL = 0x80,
+ PLANET_SHIELDED = WORLD_TYPE_SPECIAL,
+
+ HIERARCHY_STARBASE = 127 | WORLD_TYPE_SPECIAL,
+ SA_MATRA = 126 | WORLD_TYPE_SPECIAL,
+};
+
+#define NUMBER_OF_SMALL_ROCKY_WORLDS (LAST_SMALL_ROCKY_WORLD - FIRST_SMALL_ROCKY_WORLD + 1)
+#define NUMBER_OF_LARGE_ROCKY_WORLDS (LAST_LARGE_ROCKY_WORLD - FIRST_LARGE_ROCKY_WORLD + 1)
+#define NUMBER_OF_ROCKY_WORLDS (NUMBER_OF_SMALL_ROCKY_WORLDS + NUMBER_OF_LARGE_ROCKY_WORLDS)
+#define NUMBER_OF_GAS_GIANTS (LAST_GAS_GIANT - FIRST_GAS_GIANT + 1)
+
+// TODO: This struct is highly alignment and padding dependent and
+// should not be used! The data is loaded as binary from files and
+// cast to this struct.
+typedef struct
+{
+ const SIZE level_tab[3];
+ const BYTE xlat_tab[256];
+} XLAT_DESC;
+
+typedef struct
+{
+ BYTE ElementType;
+ /* Index of this element in element_array */
+ BYTE Density;
+ /* bits 0-3: quantity of the deposits (maximum number of
+ * deposits), one of FEW, MODERATE, or NUMEROUS
+ * bits 4-7: quality of the deposit, one of LOW, MEDIUM, or HEAVY
+ */
+} ELEMENT_ENTRY;
+
+// PlanetFrame describes a type of planet. It is not used to describe
+// individual planets.
+typedef struct
+{
+ BYTE Type;
+ /* bits 0-1: size, one of SMALL_ROCKY_WORLD, LARGE_ROCKY_WORLD, or
+ * GAS_GIANT
+ * bits 2-3: map creation algoritm, one of TOPO_ALGO,
+ * CRATERED_ALGO, or GAS_GIANT_ALGO
+ * bits 4-7: interplanetary color, one of BLUE_BODY, GREEN_BODY,
+ * ORANGE_BODY, RED_BODY, WHITE_BODY (same as
+ * GRAY_BODY), YELLOW_BODY, CYAN_BODY, PURPLE_BODY,
+ * VIOLET_BODY)
+ */
+ BYTE BaseTectonics;
+ /* Base constant for calculation of tectonic activity,
+ * relative to Earth at 100.
+ * One of: NO_TECTONICS, LOW_TECTONICS, MED_TECTONICS,
+ * HIGH_TECTONICS, or SUPER_TECTONICS
+ */
+ BYTE AtmoAndDensity;
+ /* bits 0-3: planet density, one of GAS_DENSITY, LIGHT_DENSITY,
+ * LOW_DENSITY, NORMAL_DENSITY, HIGH_DENSITY,
+ * SUPER_DENSITY
+ * bits 4-7: atmosphere, one of LIGHT, MEDIUM, HEAVY, or
+ * (no define for this) super thick.
+ */
+#define NUM_USEFUL_ELEMENTS 8
+ ELEMENT_ENTRY UsefulElements[NUM_USEFUL_ELEMENTS];
+ /* Minerals on the planet */
+
+ RESOURCE CMapInstance;
+ /* Color map */
+ RESOURCE XlatTabInstance;
+ /* Color translation map */
+
+ // Parameters for map-generation algoritms:
+ COUNT num_faults;
+ SIZE fault_depth;
+ COUNT num_blemishes;
+ SIZE base_elevation;
+} PlanetFrame;
+
+typedef struct
+{
+ SIZE AxialTilt;
+ UWORD Tectonics;
+ UWORD Weather;
+ UWORD PlanetDensity;
+ UWORD PlanetRadius;
+ UWORD SurfaceGravity;
+ SIZE SurfaceTemperature;
+ UWORD RotationPeriod;
+ UWORD AtmoDensity;
+ SIZE LifeChance;
+ UWORD PlanetToSunDist;
+
+ const PlanetFrame *PlanDataPtr;
+
+ DWORD ScanSeed[NUM_SCAN_TYPES];
+ DWORD ScanRetrieveMask[NUM_SCAN_TYPES];
+
+ STRING DiscoveryString;
+ FONT LanderFont;
+ FRAME LanderFontEff;
+} PLANET_INFO;
+
+enum
+{
+ GAS_DENSITY,
+ LIGHT_DENSITY,
+ LOW_DENSITY,
+ NORMAL_DENSITY,
+ HIGH_DENSITY,
+ SUPER_DENSITY
+};
+
+extern UWORD CalcGravity (const PLANET_INFO*);
+
+#define EARTH_ATMOSPHERE 50
+
+#define COLD_THRESHOLD -40
+#define HOT_THRESHOLD 100
+
+/*------------------------------ Global Data ------------------------------ */
+
+#define NO_TECTONICS 0
+#define LOW_TECTONICS 40
+#define MED_TECTONICS 80
+#define HIGH_TECTONICS 140
+#define SUPER_TECTONICS 200
+
+extern const PlanetFrame *PlanData;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_PLANDATA_H_ */
diff --git a/src/uqm/planets/planets.c b/src/uqm/planets/planets.c
new file mode 100644
index 0000000..c76c2bb
--- /dev/null
+++ b/src/uqm/planets/planets.c
@@ -0,0 +1,483 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "planets.h"
+
+#include "scan.h"
+#include "lander.h"
+#include "../colors.h"
+#include "../element.h"
+#include "../settings.h"
+#include "../controls.h"
+#include "../sounds.h"
+#include "../gameopt.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../uqmdebug.h"
+#include "../resinst.h"
+#include "../nameref.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+
+
+// PlanetOrbitMenu() items
+enum PlanetMenuItems
+{
+ // XXX: Must match the enum in menustat.h
+ SCAN = 0,
+ STARMAP,
+ EQUIP_DEVICE,
+ CARGO,
+ ROSTER,
+ GAME_MENU,
+ NAVIGATION,
+};
+
+CONTEXT PlanetContext;
+ // Context for rotating planet view and lander surface view
+
+static void
+CreatePlanetContext (void)
+{
+ CONTEXT oldContext;
+ RECT r;
+
+ assert (PlanetContext == NULL);
+
+ // PlanetContext rect is relative to SpaceContext
+ oldContext = SetContext (SpaceContext);
+ GetContextClipRect (&r);
+
+ PlanetContext = CreateContext ("PlanetContext");
+ SetContext (PlanetContext);
+ SetContextFGFrame (Screen);
+ r.extent.height -= MAP_HEIGHT + MAP_BORDER_HEIGHT;
+ SetContextClipRect (&r);
+
+ SetContext (oldContext);
+}
+
+static void
+DestroyPlanetContext (void)
+{
+ if (PlanetContext)
+ {
+ DestroyContext (PlanetContext);
+ PlanetContext = NULL;
+ }
+}
+
+void
+DrawScannedObjects (BOOLEAN Reversed)
+{
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = Reversed ? GetTailElement () : GetHeadElement ();
+ hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = Reversed ?
+ GetPredElement (ElementPtr) :
+ GetSuccElement (ElementPtr);
+
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ STAMP s;
+
+ s.origin = ElementPtr->current.location;
+ s.frame = ElementPtr->next.image.frame;
+ DrawStamp (&s);
+ }
+
+ UnlockElement (hElement);
+ }
+}
+
+void
+DrawPlanetSurfaceBorder (void)
+{
+ CONTEXT oldContext;
+ RECT oldClipRect;
+ RECT clipRect;
+ RECT r;
+
+ oldContext = SetContext (SpaceContext);
+ GetContextClipRect (&oldClipRect);
+
+ // Expand the context clip-rect so that we can tweak the existing border
+ clipRect = oldClipRect;
+ clipRect.corner.x -= 1;
+ clipRect.extent.width += 2;
+ clipRect.extent.height += 1;
+ SetContextClipRect (&clipRect);
+
+ BatchGraphics ();
+
+ // Border bulk
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ r.corner.x = 0;
+ r.corner.y = clipRect.extent.height - MAP_HEIGHT - MAP_BORDER_HEIGHT;
+ r.extent.width = clipRect.extent.width;
+ r.extent.height = MAP_BORDER_HEIGHT - 2;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (SIS_BOTTOM_RIGHT_BORDER_COLOR);
+
+ // Border top shadow line
+ r.extent.width -= 1;
+ r.extent.height = 1;
+ r.corner.x = 1;
+ r.corner.y -= 1;
+ DrawFilledRectangle (&r);
+
+ // XXX: We will need bulk left and right rects here if MAP_WIDTH changes
+
+ // Right shadow line
+ r.extent.width = 1;
+ r.extent.height = MAP_HEIGHT + 2;
+ r.corner.y += MAP_BORDER_HEIGHT - 1;
+ r.corner.x = clipRect.extent.width - 1;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (SIS_LEFT_BORDER_COLOR);
+
+ // Left shadow line
+ r.corner.x -= MAP_WIDTH + 1;
+ DrawFilledRectangle (&r);
+
+ // Border bottom shadow line
+ r.extent.width = MAP_WIDTH + 2;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+
+ UnbatchGraphics ();
+
+ SetContextClipRect (&oldClipRect);
+ SetContext (oldContext);
+}
+
+typedef enum
+{
+ DRAW_ORBITAL_FULL,
+ DRAW_ORBITAL_WAIT,
+ DRAW_ORBITAL_UPDATE,
+
+} DRAW_ORBITAL_MODE;
+
+static void
+DrawOrbitalDisplay (DRAW_ORBITAL_MODE Mode)
+{
+ RECT r;
+
+ SetContext (SpaceContext);
+ GetContextClipRect (&r);
+
+ BatchGraphics ();
+
+ if (Mode != DRAW_ORBITAL_UPDATE)
+ {
+ SetTransitionSource (NULL);
+
+ DrawSISFrame ();
+ DrawSISMessage (NULL);
+ DrawSISTitle (GLOBAL_SIS (PlanetName));
+ DrawStarBackGround ();
+ DrawPlanetSurfaceBorder ();
+ }
+
+ if (Mode == DRAW_ORBITAL_WAIT)
+ {
+ STAMP s;
+
+ SetContext (GetScanContext (NULL));
+ s.frame = CaptureDrawable (LoadGraphic (ORBENTER_PMAP_ANIM));
+ s.origin.x = -SAFE_X;
+ s.origin.y = 0;
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+ }
+ else if (Mode == DRAW_ORBITAL_FULL)
+ {
+ DrawDefaultPlanetSphere ();
+ }
+
+ if (Mode != DRAW_ORBITAL_WAIT)
+ {
+ SetContext (GetScanContext (NULL));
+ DrawPlanet (0, BLACK_COLOR);
+ }
+
+ if (Mode != DRAW_ORBITAL_UPDATE)
+ {
+ ScreenTransition (3, &r);
+ }
+
+ UnbatchGraphics ();
+
+ // for later RepairBackRect()
+ LoadIntoExtraScreen (&r);
+}
+
+// Initialise the surface graphics, and start the planet music.
+// Called from the GenerateFunctions.generateOribital() function
+// (when orbit is entered; either from IP, or from loading a saved game)
+// and when "starmap" is selected from orbit and then cancelled;
+// also after in-orbit comm and after defeating planet guards in combat.
+// SurfDefFrame contains surface definition images when a planet comes
+// with its own bitmap (currently only for Earth)
+void
+LoadPlanet (FRAME SurfDefFrame)
+{
+ bool WaitMode = !(LastActivity & CHECK_LOAD);
+ PLANET_DESC *pPlanetDesc;
+
+#ifdef DEBUG
+ if (disableInteractivity)
+ return;
+#endif
+
+ assert (pSolarSysState->InOrbit && !pSolarSysState->TopoFrame);
+
+ CreatePlanetContext ();
+
+ if (WaitMode)
+ {
+ DrawOrbitalDisplay (DRAW_ORBITAL_WAIT);
+ }
+
+ StopMusic ();
+
+ pPlanetDesc = pSolarSysState->pOrbitalDesc;
+ GeneratePlanetSurface (pPlanetDesc, SurfDefFrame);
+ SetPlanetMusic (pPlanetDesc->data_index & ~PLANET_SHIELDED);
+ GeneratePlanetSide ();
+
+ if (!PLRPlaying ((MUSIC_REF)~0))
+ PlayMusic (LanderMusic, TRUE, 1);
+
+ if (WaitMode)
+ {
+ ZoomInPlanetSphere ();
+ DrawOrbitalDisplay (DRAW_ORBITAL_UPDATE);
+ }
+ else
+ {
+ DrawOrbitalDisplay (DRAW_ORBITAL_FULL);
+ }
+}
+
+void
+FreePlanet (void)
+{
+ COUNT i;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ UninitSphereRotation ();
+
+ StopMusic ();
+
+ for (i = 0; i < sizeof (pSolarSysState->PlanetSideFrame)
+ / sizeof (pSolarSysState->PlanetSideFrame[0]); ++i)
+ {
+ DestroyDrawable (ReleaseDrawable (pSolarSysState->PlanetSideFrame[i]));
+ pSolarSysState->PlanetSideFrame[i] = 0;
+ }
+
+// FreeLanderData ();
+
+ DestroyStringTable (ReleaseStringTable (pSolarSysState->XlatRef));
+ pSolarSysState->XlatRef = 0;
+ DestroyDrawable (ReleaseDrawable (pSolarSysState->TopoFrame));
+ pSolarSysState->TopoFrame = 0;
+ DestroyColorMap (ReleaseColorMap (pSolarSysState->OrbitalCMap));
+ pSolarSysState->OrbitalCMap = 0;
+
+ HFree (Orbit->lpTopoData);
+ Orbit->lpTopoData = 0;
+ DestroyDrawable (ReleaseDrawable (Orbit->TopoZoomFrame));
+ Orbit->TopoZoomFrame = 0;
+ DestroyDrawable (ReleaseDrawable (Orbit->SphereFrame));
+ Orbit->SphereFrame = NULL;
+
+ DestroyDrawable (ReleaseDrawable (Orbit->TintFrame));
+ Orbit->TintFrame = 0;
+ Orbit->TintColor = BLACK_COLOR;
+
+ DestroyDrawable (ReleaseDrawable (Orbit->ObjectFrame));
+ Orbit->ObjectFrame = 0;
+ DestroyDrawable (ReleaseDrawable (Orbit->WorkFrame));
+ Orbit->WorkFrame = 0;
+
+ HFree (Orbit->TopoColors);
+ Orbit->TopoColors = NULL;
+ HFree (Orbit->ScratchArray);
+ Orbit->ScratchArray = NULL;
+
+ DestroyStringTable (ReleaseStringTable (
+ pSolarSysState->SysInfo.PlanetInfo.DiscoveryString
+ ));
+ pSolarSysState->SysInfo.PlanetInfo.DiscoveryString = 0;
+ FreeLanderFont (&pSolarSysState->SysInfo.PlanetInfo);
+
+ // Need to make sure our own CONTEXTs are not active because
+ // we will destroy them now
+ SetContext (SpaceContext);
+ DestroyPlanetContext ();
+ DestroyScanContext ();
+
+}
+
+void
+LoadStdLanderFont (PLANET_INFO *info)
+{
+ info->LanderFont = LoadFont (LANDER_FONT);
+ info->LanderFontEff = CaptureDrawable (
+ LoadGraphic (LANDER_FONTEFF_PMAP_ANIM));
+}
+
+void
+FreeLanderFont (PLANET_INFO *info)
+{
+ DestroyFont (info->LanderFont);
+ info->LanderFont = NULL;
+ DestroyDrawable (ReleaseDrawable (info->LanderFontEff));
+ info->LanderFontEff = NULL;
+}
+
+static BOOLEAN
+DoPlanetOrbit (MENU_STATE *pMS)
+{
+ BOOLEAN select = PulsedInputState.menu[KEY_MENU_SELECT];
+ BOOLEAN handled;
+
+ if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
+ return FALSE;
+
+ // XXX: pMS actually refers to pSolarSysState->MenuState
+ handled = DoMenuChooser (pMS, PM_SCAN);
+ if (handled)
+ return TRUE;
+
+ if (!select)
+ return TRUE;
+
+ SetFlashRect (NULL);
+
+ switch (pMS->CurState)
+ {
+ case SCAN:
+ ScanSystem ();
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ { // Found Fwiffo on Pluto
+ return FALSE;
+ }
+ break;
+ case EQUIP_DEVICE:
+ select = DevicesMenu ();
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ { // Invoked Talking Pet, a Caster or Sun Device over Chmmr,
+ // or a Caster for Ilwrath
+ // Going into conversation
+ return FALSE;
+ }
+ break;
+ case CARGO:
+ CargoMenu ();
+ break;
+ case ROSTER:
+ select = RosterMenu ();
+ break;
+ case GAME_MENU:
+ if (!GameOptions ())
+ return FALSE; // abort or load
+ break;
+ case STARMAP:
+ {
+ BOOLEAN AutoPilotSet;
+ InputFrameCallback *oldCallback;
+
+ // Deactivate planet rotation
+ oldCallback = SetInputCallback (NULL);
+
+ RepairSISBorder ();
+
+ AutoPilotSet = StarMap ();
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ // Reactivate planet rotation
+ SetInputCallback (oldCallback);
+
+ if (!AutoPilotSet)
+ { // Redraw the orbital display
+ DrawOrbitalDisplay (DRAW_ORBITAL_FULL);
+ break;
+ }
+ // Fall through !!!
+ }
+ case NAVIGATION:
+ return FALSE;
+ }
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (select)
+ { // 3DO menu jumps to NAVIGATE after a successful submenu run
+ if (optWhichMenu != OPT_PC)
+ pMS->CurState = NAVIGATION;
+ DrawMenuStateStrings (PM_SCAN, pMS->CurState);
+ }
+ SetFlashRect (SFR_MENU_3DO);
+ }
+
+ return TRUE;
+}
+
+static void
+on_input_frame (void)
+{
+ RotatePlanetSphere (TRUE);
+}
+
+void
+PlanetOrbitMenu (void)
+{
+ MENU_STATE MenuState;
+ InputFrameCallback *oldCallback;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ DrawMenuStateStrings (PM_SCAN, SCAN);
+ SetFlashRect (SFR_MENU_3DO);
+
+ MenuState.CurState = SCAN;
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ oldCallback = SetInputCallback (on_input_frame);
+
+ MenuState.InputFunc = DoPlanetOrbit;
+ DoInput (&MenuState, TRUE);
+
+ SetInputCallback (oldCallback);
+
+ SetFlashRect (NULL);
+ DrawMenuStateStrings (PM_STARMAP, -NAVIGATION);
+}
diff --git a/src/uqm/planets/planets.h b/src/uqm/planets/planets.h
new file mode 100644
index 0000000..c6f440d
--- /dev/null
+++ b/src/uqm/planets/planets.h
@@ -0,0 +1,322 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_PLANETS_PLANETS_H_
+#define UQM_PLANETS_PLANETS_H_
+
+#include "libs/mathlib.h"
+
+#define END_INTERPLANETARY START_INTERPLANETARY
+
+enum PlanetScanTypes
+{
+ MINERAL_SCAN = 0,
+ ENERGY_SCAN,
+ BIOLOGICAL_SCAN,
+
+ NUM_SCAN_TYPES,
+};
+
+#define MAP_WIDTH SIS_SCREEN_WIDTH
+#define MAP_HEIGHT (75 - SAFE_Y)
+
+enum
+{
+ BIOLOGICAL_DISASTER = 0,
+ EARTHQUAKE_DISASTER,
+ LIGHTNING_DISASTER,
+ LAVASPOT_DISASTER,
+
+ /* additional lander sounds */
+ LANDER_INJURED,
+ LANDER_SHOOTS,
+ LANDER_HITS,
+ LIFEFORM_CANNED,
+ LANDER_PICKUP,
+ LANDER_FULL,
+ LANDER_DEPARTS,
+ LANDER_RETURNS,
+ LANDER_DESTROYED
+};
+
+#define MAX_SCROUNGED 50 /* max lander can hold */
+
+#define SCALE_RADIUS(r) ((r) << 6)
+#define UNSCALE_RADIUS(r) ((r) >> 6)
+#define MAX_ZOOM_RADIUS SCALE_RADIUS(128)
+#define MIN_ZOOM_RADIUS (MAX_ZOOM_RADIUS>>3)
+#define EARTH_RADIUS SCALE_RADIUS(8)
+
+#define MIN_PLANET_RADIUS SCALE_RADIUS (4)
+#define MAX_PLANET_RADIUS SCALE_RADIUS (124)
+
+#define DISPLAY_FACTOR ((SIS_SCREEN_WIDTH >> 1) - 8)
+
+#define NUM_SCANDOT_TRANSITIONS 4
+
+#define MIN_MOON_RADIUS 35
+#define MOON_DELTA 20
+
+#define MAX_SUNS 1
+#define MAX_PLANETS 16
+#define MAX_MOONS 4
+
+#define MAP_BORDER_HEIGHT 5
+#define SCAN_SCREEN_HEIGHT (SIS_SCREEN_HEIGHT - MAP_HEIGHT - MAP_BORDER_HEIGHT)
+
+#define PLANET_ROTATION_TIME (ONE_SECOND * 12)
+#define PLANET_ROTATION_RATE (PLANET_ROTATION_TIME / MAP_WIDTH)
+// XXX: -9 to match the original, but why? I have no idea
+#define PLANET_ORG_Y ((SCAN_SCREEN_HEIGHT - 9) / 2)
+
+#define NUM_RACE_RUINS 16
+
+typedef struct planet_desc PLANET_DESC;
+typedef struct star_desc STAR_DESC;
+typedef struct node_info NODE_INFO;
+typedef struct planet_orbit PLANET_ORBIT;
+typedef struct solarsys_state SOLARSYS_STATE;
+
+
+#include "generate.h"
+#include "../menustat.h"
+#include "../units.h"
+
+#include "elemdata.h"
+#include "lifeform.h"
+#include "plandata.h"
+#include "sundata.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct planet_desc
+{
+ DWORD rand_seed;
+
+ BYTE data_index;
+ BYTE NumPlanets;
+ SIZE radius;
+ POINT location;
+
+ Color temp_color;
+ COUNT NextIndex;
+ STAMP image;
+
+ PLANET_DESC *pPrevDesc;
+ // The Sun or planet that this world is orbiting around.
+};
+
+struct star_desc
+{
+ POINT star_pt;
+ BYTE Type;
+ BYTE Index;
+ BYTE Prefix;
+ BYTE Postfix;
+};
+
+struct node_info
+{
+ // This structire is filled in when a generateMinerals, generateEnergy,
+ // or generateLife call is made.
+ POINT loc_pt;
+ // Position of the mineral/bio/energy node on the planet.
+ COUNT density;
+ // For bio and energy: undefined
+ // For minerals the low byte is the gross size of the
+ // deposit (this determines the image), and the high
+ // byte is the fine size (the actual quantity).
+ COUNT type;
+ // For minerals: the type of element
+ // For bio: the type of the creature.
+ // 0 through NUM_CREATURE_TYPES - 1 are normal creatures,
+ // NUM_CREATURE_TYPES is an Evil One
+ // NUM_CREATURE_TYPES + 1 is a Brainbox Bulldozer
+ // NUM_CREATURE_TYPES + 2 is Zex' Beauty
+ // For energy: undefined
+};
+
+struct planet_orbit
+{
+ FRAME TopoZoomFrame;
+ // 4x scaled topo image for planet-side
+ SBYTE *lpTopoData;
+ // normal topo data; expressed in elevation levels
+ // data is signed for planets other than gas giants
+ // transformed to light variance map for 3d planet
+ FRAME SphereFrame;
+ // rotating 3d planet frames (current and next)
+ FRAME ObjectFrame;
+ // any extra planetary object (shield, atmo, rings)
+ // automatically drawn if present
+ FRAME TintFrame;
+ // tinted topo images for current scan type (dynamic)
+ Color TintColor;
+ // the color of the last used tint
+ Color *TopoColors;
+ // RGBA version of topo image; for 3d planet
+ Color *ScratchArray;
+ // temp RGBA data for whatever transforms (nuked often)
+ FRAME WorkFrame;
+ // any extra frame workspace (for dynamic objects)
+};
+
+// See doc/devel/generate for information on how this structure is
+// filled.
+struct solarsys_state
+{
+ // Standard field required by DoInput()
+ BOOLEAN (*InputFunc) (struct solarsys_state *);
+
+ BOOLEAN InIpFlight;
+ // Set to TRUE when player is flying around in interplanetary
+ // Reset to FALSE when going into orbit or encounter
+
+ COUNT WaitIntersect;
+ // Planet/moon number with which the flagship should not collide
+ // For example, if the player just left the planet or inner system
+ // If set to (COUNT)~0, all planet collisions are disabled until
+ // the flagship stops intersecting with all planets.
+ PLANET_DESC SunDesc[MAX_SUNS];
+ PLANET_DESC PlanetDesc[MAX_PLANETS];
+ // Description of the planets in the system.
+ // Only defined after a call to (*genFuncs)->generatePlanets()
+ // and overwritten by subsequent calls.
+ PLANET_DESC MoonDesc[MAX_MOONS];
+ // Description of the moons orbiting the planet pointed to
+ // by pBaseDesc.
+ // Only defined after a call to (*genFuncs)->generateMoons()
+ // as its argument, and overwritten by subsequent calls.
+ PLANET_DESC *pBaseDesc;
+ // In outer system: points to PlanetDesc[]
+ // In inner system: points to MoonDesc[]
+ PLANET_DESC *pOrbitalDesc;
+ // In orbit: points into PlanetDesc or MoonDesc to the planet
+ // currently orbiting.
+ // In inner system: points into PlanetDesc to the planet whose
+ // inner system the ship is inside
+ SIZE FirstPlanetIndex, LastPlanetIndex;
+ // The planets get sorted on their image.origin.y value.
+ // PlanetDesc[FirstPlanetIndex] is the planet with the lowest
+ // image.origin.y, and PlanetDesc[LastPlanetIndex] has the
+ // highest image.origin.y.
+ // PlanetDesc[PlanetDesc[i].NextIndex] is the next planet
+ // after PlanetDesc[i] in the ordering.
+
+ BYTE turn_counter;
+ BYTE turn_wait;
+ BYTE thrust_counter;
+ BYTE max_ship_speed;
+
+ STRING XlatRef;
+ const void *XlatPtr;
+ COLORMAP OrbitalCMap;
+
+ SYSTEM_INFO SysInfo;
+
+ const GenerateFunctions *genFuncs;
+ // Functions to call to fill in various parts of this structure.
+ // See generate.h, doc/devel/generate
+
+ FRAME PlanetSideFrame[3 + MAX_LIFE_VARIATION];
+ /* Frames for planet-side elements.
+ * [0] = bio cannister
+ * [1] = energy node (world-specific)
+ * [2] = unused (formerly static slave shield, presumed)
+ * [3] = bio 1 (world-specific)
+ * [4] = bio 2 (world-specific)
+ * [5] = bio 3 (world-specific)
+ */
+ FRAME TopoFrame;
+ PLANET_ORBIT Orbit;
+ BOOLEAN InOrbit;
+ // Set to TRUE when player hits a world in an inner system
+ // Homeworld encounters count as 'in orbit'
+};
+
+extern SOLARSYS_STATE *pSolarSysState;
+extern MUSIC_REF SpaceMusic;
+extern CONTEXT PlanetContext;
+
+// Random context used for all solar system, planets and surfaces generation
+extern RandomContext *SysGenRNG;
+
+bool playerInSolarSystem (void);
+bool playerInPlanetOrbit (void);
+bool playerInInnerSystem (void);
+bool worldIsPlanet (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world);
+bool worldIsMoon (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world);
+COUNT planetIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world);
+COUNT moonIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *moon);
+#define MATCH_PLANET ((BYTE) -1)
+bool matchWorld (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world,
+ BYTE planetI, BYTE moonI);
+
+DWORD GetRandomSeedForStar (const STAR_DESC *star);
+
+POINT locationToDisplay (POINT pt, SIZE scaleRadius);
+POINT displayToLocation (POINT pt, SIZE scaleRadius);
+POINT planetOuterLocation (COUNT planetI);
+
+extern void LoadPlanet (FRAME SurfDefFrame);
+extern void DrawPlanet (int dy, Color tintColor);
+extern void FreePlanet (void);
+extern void LoadStdLanderFont (PLANET_INFO *info);
+extern void FreeLanderFont (PLANET_INFO *info);
+
+extern void ExploreSolarSys (void);
+extern void DrawStarBackGround (void);
+extern void XFormIPLoc (POINT *pIn, POINT *pOut, BOOLEAN ToDisplay);
+extern void DrawOval (RECT *pRect, BYTE num_off_pixels);
+extern void DrawFilledOval (RECT *pRect);
+extern void FillOrbits (SOLARSYS_STATE *system, BYTE NumPlanets,
+ PLANET_DESC *pBaseDesc, BOOLEAN TypesDefined);
+extern void InitLander (BYTE LanderFlags);
+
+extern void InitSphereRotation (int direction, BOOLEAN shielded);
+extern void UninitSphereRotation (void);
+extern void PrepareNextRotationFrame (void);
+extern void DrawPlanetSphere (int x, int y);
+extern void DrawDefaultPlanetSphere (void);
+extern void RenderPlanetSphere (FRAME Frame, int offset, BOOLEAN doThrob);
+extern void SetShieldThrobEffect (FRAME FromFrame, int offset, FRAME ToFrame);
+
+extern void ZoomInPlanetSphere (void);
+extern void RotatePlanetSphere (BOOLEAN keepRate);
+
+extern void DrawScannedObjects (BOOLEAN Reversed);
+extern void GeneratePlanetSurface (PLANET_DESC *pPlanetDesc,
+ FRAME SurfDefFrame);
+extern void DeltaTopography (COUNT num_iterations, SBYTE *DepthArray,
+ RECT *pRect, SIZE depth_delta);
+
+extern void DrawPlanetSurfaceBorder (void);
+
+extern UNICODE* GetNamedPlanetaryBody (void);
+extern void GetPlanetOrMoonName (UNICODE *buf, COUNT bufsize);
+
+extern void PlanetOrbitMenu (void);
+extern void SaveSolarSysLocation (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_PLANETS_H_ */
diff --git a/src/uqm/planets/plangen.c b/src/uqm/planets/plangen.c
new file mode 100644
index 0000000..005a968
--- /dev/null
+++ b/src/uqm/planets/plangen.c
@@ -0,0 +1,1954 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "planets.h"
+#include "scan.h"
+#include "../nameref.h"
+#include "../resinst.h"
+#include "../setup.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include <math.h>
+#include <time.h>
+
+
+#undef PROFILE_ROTATION
+
+// define USE_ALPHA_SHIELD to use an aloha overlay instead of
+// an additive overlay for the shield effect
+#undef USE_ALPHA_SHIELD
+
+#define SHIELD_GLOW_COMP 120
+#define SHIELD_REFLECT_COMP 100
+
+#define NUM_BATCH_POINTS 64
+#define RADIUS 37
+//2*RADIUS
+#define TWORADIUS (RADIUS << 1)
+//RADIUS^2
+#define RADIUS_2 (RADIUS * RADIUS)
+// distance beyond which all pixels are transparent (for aa)
+#define RADIUS_THRES ((RADIUS + 1) * (RADIUS + 1))
+#define DIAMETER (TWORADIUS + 1)
+#if 0
+# define SPHERE_SPAN_X (MAP_WIDTH >> 1)
+#else
+# define SPHERE_SPAN_X (MAP_HEIGHT)
+#endif
+ // XXX: technically, the sphere's span over X should be MAP_WIDTH/2
+ // but this causes visible surface compression over X, because
+ // the surface dims ratio is H x H*PI, instead of H x 2*H
+ // see bug #885
+
+#define DIFFUSE_BITS 16
+#define AA_WEIGHT_BITS 16
+
+#ifndef M_TWOPI
+ #ifndef M_PI
+ #define M_PI 3.14159265358979323846
+ #endif
+ #define M_TWOPI (M_PI * 2.0)
+#endif
+#ifndef M_DEG2RAD
+#define M_DEG2RAD (M_TWOPI / 360.0)
+#endif
+
+DWORD light_diff[DIAMETER][DIAMETER];
+
+typedef struct
+{
+ POINT p[4];
+ DWORD m[4];
+} MAP3D_POINT;
+
+MAP3D_POINT map_rotate[DIAMETER][DIAMETER];
+
+typedef struct
+{
+ double x, y, z;
+} POINT3;
+
+static void
+RenderTopography (FRAME DstFrame, SBYTE *pTopoData, int w, int h)
+{
+ FRAME OldFrame;
+
+ OldFrame = SetContextFGFrame (DstFrame);
+
+ if (pSolarSysState->XlatRef == 0)
+ {
+ // There is currently nothing we can do w/o an xlat table
+ // This is still called for Earth for 4x scaled topo, but we
+ // do not need it because we cannot land on Earth.
+ }
+ else
+ {
+ COUNT i;
+ BYTE AlgoType;
+ SIZE base, d;
+ const XLAT_DESC *xlatDesc;
+ POINT pt;
+ const PlanetFrame *PlanDataPtr;
+ PRIMITIVE BatchArray[NUM_BATCH_POINTS];
+ PRIMITIVE *pBatch;
+ SBYTE *pSrc;
+ const BYTE *xlat_tab;
+ BYTE *cbase;
+ POINT oldOrigin;
+ RECT ClipRect;
+
+ oldOrigin = SetContextOrigin (MAKE_POINT (0, 0));
+ GetContextClipRect (&ClipRect);
+ SetContextClipRect (NULL);
+
+ pBatch = &BatchArray[0];
+ for (i = 0; i < NUM_BATCH_POINTS; ++i, ++pBatch)
+ {
+ SetPrimNextLink (pBatch, i + 1);
+ SetPrimType (pBatch, POINT_PRIM);
+ }
+ SetPrimNextLink (&pBatch[-1], END_OF_LIST);
+
+ PlanDataPtr = &PlanData[
+ pSolarSysState->pOrbitalDesc->data_index & ~PLANET_SHIELDED
+ ];
+ AlgoType = PLANALGO (PlanDataPtr->Type);
+ base = PlanDataPtr->base_elevation;
+ xlatDesc = (const XLAT_DESC *) pSolarSysState->XlatPtr;
+ xlat_tab = (const BYTE *) xlatDesc->xlat_tab;
+ cbase = GetColorMapAddress (pSolarSysState->OrbitalCMap);
+
+ i = NUM_BATCH_POINTS;
+ pBatch = &BatchArray[i];
+ pSrc = pTopoData;
+ for (pt.y = 0; pt.y < h; ++pt.y)
+ {
+ for (pt.x = 0; pt.x < w; ++pt.x, ++pSrc)
+ {
+ BYTE *ctab;
+
+ d = *pSrc;
+ if (AlgoType == GAS_GIANT_ALGO)
+ { // make elevation value non-negative
+ d &= 255;
+ }
+ else
+ {
+ d += base;
+ if (d < 0)
+ d = 0;
+ else if (d > 255)
+ d = 255;
+ }
+
+ --pBatch;
+ pBatch->Object.Point.x = pt.x;
+ pBatch->Object.Point.y = pt.y;
+
+ d = xlat_tab[d] - cbase[0];
+ ctab = (cbase + 2) + d * 3;
+
+ // fixed planet surfaces being too dark
+ // ctab shifts were previously >> 3 .. -Mika
+ SetPrimColor (pBatch, BUILD_COLOR (MAKE_RGB15 (ctab[0] >> 1,
+ ctab[1] >> 1, ctab[2] >> 1), d));
+
+ if (--i == 0)
+ { // flush the batch and start the next one
+ DrawBatch (BatchArray, 0, 0);
+ i = NUM_BATCH_POINTS;
+ pBatch = &BatchArray[i];
+ }
+ }
+ }
+
+ if (i < NUM_BATCH_POINTS)
+ {
+ DrawBatch (BatchArray, i, 0);
+ }
+
+ SetContextClipRect (&ClipRect);
+ SetContextOrigin (oldOrigin);
+ }
+
+ SetContextFGFrame (OldFrame);
+}
+
+static inline void
+P3mult (POINT3 *res, POINT3 *vec, double cnst)
+{
+ res->x = vec->x * cnst;
+ res->y = vec->y * cnst;
+ res->z = vec->z * cnst;
+}
+
+static inline void
+P3sub (POINT3 *res, POINT3 *v1, POINT3 *v2)
+{
+ res->x = v1->x - v2->x;
+ res->y = v1->y - v2->y;
+ res->z = v1->z - v2->z;
+}
+
+static inline double
+P3dot (POINT3 *v1, POINT3 *v2)
+{
+ return (v1->x * v2->x + v1->y * v2->y + v1->z * v2->z);
+}
+
+static inline void
+P3norm (POINT3 *res, POINT3 *vec)
+{
+ double mag = sqrt (P3dot (vec, vec));
+ P3mult (res, vec, 1/mag);
+}
+
+// GenerateSphereMask builds a shadow map for the rotating planet
+// loc indicates the planet's position relative to the sun
+static void
+GenerateSphereMask (POINT loc)
+{
+ POINT pt;
+ POINT3 light;
+ double lrad;
+ const DWORD step = 1 << DIFFUSE_BITS;
+ int y, x;
+
+#define AMBIENT_LIGHT 0.1
+#define LIGHT_Z 1.2
+ // lrad is the distance from the sun to the planet
+ lrad = sqrt (loc.x * loc.x + loc.y * loc.y);
+ // light is the sun's position. the z-coordinate is whatever
+ // looks good
+ light.x = -((double)loc.x);
+ light.y = -((double)loc.y);
+ light.z = LIGHT_Z * lrad;
+ P3norm (&light, &light);
+
+ for (pt.y = 0, y = -RADIUS; pt.y <= TWORADIUS; ++pt.y, y++)
+ {
+ DWORD y_2 = y * y;
+
+ for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, x++)
+ {
+ DWORD x_2 = x * x;
+ DWORD rad_2 = x_2 + y_2;
+ DWORD diff_int = 0;
+ POINT3 norm;
+ double diff;
+
+ if (rad_2 < RADIUS_THRES)
+ {
+ // norm is the sphere's surface normal.
+ norm.x = (double)x;
+ norm.y = (double)y;
+ norm.z = (sqrt (RADIUS_2 - x_2) * sqrt (RADIUS_2 - y_2)) /
+ RADIUS;
+ P3norm (&norm, &norm);
+ // diffuse component is norm dot light
+ diff = P3dot (&norm, &light);
+ // negative diffuse is bad
+ if (diff < 0)
+ diff = 0.0;
+#if 0
+ // Specular is not used in practice and is left here
+ // if someone decides to use it later for some reason.
+ // Specular highlight is only good for perfectly smooth
+ // surfaces, like balls (of which planets are not)
+ // This is the Phong equation
+#define LIGHT_INTENS 0.3
+#define MSHI 2
+ double fb, spec;
+ POINT3 rvec;
+ POINT3 view;
+
+ // always view along the z-axis
+ // ideally use a view point, and have the view change
+ // per pixel, but that is too much effort for now.
+ // the view MUST be normalized!
+ view.x = 0;
+ view.y = 0;
+ view.z = 1.0;
+
+ // specular highlight is the phong equation:
+ // (rvec dot view)^MSHI
+ // where rvec = (2*diff)*norm - light (reflection of light
+ // around norm)
+ P3mult (&rvec, &norm, 2 * diff);
+ P3sub (&rvec, &rvec, &light);
+ fb = P3dot (&rvec, &view);
+ if (fb > 0.0)
+ spec = LIGHT_INTENS * pow (fb, MSHI);
+ else
+ spec = 0;
+#endif
+ // adjust for the ambient light
+ if (diff < AMBIENT_LIGHT)
+ diff = AMBIENT_LIGHT;
+ // Now we antialias the edge of the spere to look nice
+ if (rad_2 > RADIUS_2)
+ {
+ diff *= 1 - (sqrt(rad_2) - RADIUS);
+ if (diff < 0)
+ diff = 0;
+ }
+ // diff_int allows us multiply by a ratio without using
+ // floating-point.
+ diff_int = (DWORD)(diff * step);
+ }
+
+ light_diff[pt.y][pt.x] = diff_int;
+ }
+ }
+}
+
+//create_aa_points creates weighted averages for
+// 4 points around the 'ideal' point at x,y
+// the concept is to compute the weight based on the
+// distance from the integer location points to the ideal point
+static void
+create_aa_points (MAP3D_POINT *ppt, double x, double y)
+{
+ double deltax, deltay, inv_deltax, inv_deltay;
+ COORD nextx, nexty;
+ COUNT i;
+ double d1, d2, d3, d4, m[4];
+
+ if (x < 0)
+ x = 0;
+ else if (x >= SPHERE_SPAN_X)
+ x = SPHERE_SPAN_X - 1;
+ if (y < 0)
+ y = 0;
+ else if (y >= MAP_HEIGHT)
+ y = MAP_HEIGHT - 1;
+
+ // get the integer value of this point
+ ppt->p[0].x = (COORD)x;
+ ppt->p[0].y = (COORD)y;
+ deltax = x - ppt->p[0].x;
+ deltay = y - ppt->p[0].y;
+
+ // if this point doesn't need modificaton, set m[0]=0
+ if (deltax == 0 && deltay == 0)
+ {
+ ppt->m[0] = 0;
+ return;
+ }
+
+ // get the neighboring points surrounding the 'ideal' point
+ if (deltax != 0)
+ nextx = ppt->p[0].x + 1;
+ else
+ nextx = ppt->p[0].x;
+ if (deltay != 0)
+ nexty = ppt->p[0].y + 1;
+ else
+ nexty = ppt->p[0].y;
+ //(x1,y)
+ ppt->p[1].x = nextx;
+ ppt->p[1].y = ppt->p[0].y;
+ //(x,y1)
+ ppt->p[2].x = ppt->p[0].x;
+ ppt->p[2].y = nexty;
+ //(x1y1)
+ ppt->p[3].x = nextx;
+ ppt->p[3].y = nexty;
+ //the square 1x1, so opposite poinnts are at 1-delta
+ inv_deltax = 1.0 - fabs (deltax);
+ inv_deltax *= inv_deltax;
+ inv_deltay = 1.0 - fabs (deltay);
+ inv_deltay *= inv_deltay;
+ deltax *= deltax;
+ deltay *= deltay;
+ //d1-d4 contain the distances from the poinnts to the ideal point
+ d1 = sqrt (deltax + deltay);
+ d2 = sqrt (inv_deltax + deltay);
+ d3 = sqrt (deltax + inv_deltay);
+ d4 = sqrt (inv_deltax + inv_deltay);
+ //compute the weights. the sum(ppt->m[])=65536
+ m[0] = 1 / (1 + d1 * (1 / d2 + 1 / d3 + 1 / d4));
+ m[1] = m[0] * d1 / d2;
+ m[2] = m[0] * d1 / d3;
+ m[3] = m[0] * d1 / d4;
+
+ for (i = 0; i < 4; i++)
+ ppt->m[i] = (DWORD)(m[i] * (1 << AA_WEIGHT_BITS) + 0.5);
+}
+
+static inline BYTE
+get_color_channel (Color c, int channel)
+{
+ switch (channel)
+ {
+ case 0:
+ return c.r;
+ case 1:
+ return c.g;
+ case 2:
+ return c.b;
+ default:
+ return 0;
+ }
+}
+
+// Creates either a red, green, or blue value by
+// computing the weighted averages of the 4 points in p
+static BYTE
+get_avg_channel (Color p[4], DWORD mult[4], int channel)
+{
+ COUNT j;
+ DWORD ci = 0;
+
+ //sum(mult[])==65536
+ //c is the red/green/blue value of this pixel
+ for (j = 0; j < 4; j++)
+ {
+ BYTE c = get_color_channel (p[j], channel);
+ ci += c * mult[j];
+ }
+ ci >>= AA_WEIGHT_BITS;
+ //check for overflow
+ if (ci > 255)
+ ci = 255;
+
+ return ((UBYTE)ci);
+}
+
+// CreateSphereTiltMap creates 'map_rotate' to map the topo data
+// for a tilted planet. It also does the sphere->plane mapping
+static void
+CreateSphereTiltMap (int angle)
+{
+ int x, y;
+ const double multx = ((double)SPHERE_SPAN_X / M_PI);
+ const double multy = ((double)MAP_HEIGHT / M_PI);
+ const double xadj = ((double)SPHERE_SPAN_X / 2.0);
+
+ for (y = -RADIUS; y <= RADIUS; y++)
+ {
+ int y_2 = y * y;
+
+ for (x = -RADIUS; x <= RADIUS; x++)
+ {
+ double dx, dy, newx, newy;
+ double da, rad, rad_2;
+ double xa, ya;
+ MAP3D_POINT *ppt = &map_rotate[y + RADIUS][x + RADIUS];
+
+ rad_2 = x * x + y_2;
+
+ if (rad_2 >= RADIUS_THRES)
+ { // pixel won't be present
+ ppt->p[0].x = x + RADIUS;
+ ppt->p[0].y = y + RADIUS;
+ ppt->m[0] = 0;
+
+ continue;
+ }
+
+ rad = sqrt (rad_2);
+ // antialiasing goes beyond the actual radius
+ if (rad >= RADIUS)
+ rad = (double)RADIUS - 0.1;
+
+ da = atan2 ((double)y, (double)x);
+ // compute the planet-tilt
+ da += M_DEG2RAD * angle;
+ dx = rad * cos (da);
+ dy = rad * sin (da);
+
+ // Map the sphere onto a plane
+ xa = acos (-dx / RADIUS);
+ ya = acos (-dy / RADIUS);
+ newx = multx * xa;
+ newy = multy * ya;
+ // Adjust for vertical curvature
+ if (ya <= 0.05 || ya >= 3.1 /* almost PI */)
+ newx = xadj; // exact centerline
+ else
+ newx = xadj + ((newx - xadj) / sin (ya));
+
+ create_aa_points (ppt, newx, newy);
+ }
+ }
+}
+
+//CreateShieldMask
+// The shield is created in two parts. This routine creates the Halo.
+// The red tint of the planet is currently applied in RenderPlanetSphere
+// This was done because the shield glows and needs to modify how the planet
+// gets lit. Currently, the planet area is transparent in the mask made by
+// this routine, but a filter can be applied if desired too.
+
+// HALO rim size
+#define SHIELD_HALO 7
+#define SHIELD_RADIUS (RADIUS + SHIELD_HALO)
+#define SHIELD_DIAM ((SHIELD_RADIUS << 1) + 1)
+#define SHIELD_RADIUS_2 (SHIELD_RADIUS * SHIELD_RADIUS)
+#define SHIELD_RADIUS_THRES ((SHIELD_RADIUS + 1) * (SHIELD_RADIUS + 1))
+#define SHIELD_HALO_GLOW (SHIELD_GLOW_COMP + SHIELD_REFLECT_COMP)
+#define SHIELD_HALO_GLOW_MIN (SHIELD_HALO_GLOW >> 2)
+
+static FRAME
+CreateShieldMask (void)
+{
+ Color clear;
+ Color *pix;
+ int x, y;
+ FRAME ShieldFrame;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ ShieldFrame = CaptureDrawable (
+ CreateDrawable (WANT_PIXMAP | WANT_ALPHA,
+ SHIELD_DIAM, SHIELD_DIAM, 1));
+
+ pix = Orbit->ScratchArray;
+ // This is 100% transparent.
+ clear = BUILD_COLOR_RGBA (0, 0, 0, 0);
+
+ for (y = -SHIELD_RADIUS; y <= SHIELD_RADIUS; y++)
+ {
+ for (x = -SHIELD_RADIUS; x <= SHIELD_RADIUS; ++x, ++pix)
+ {
+ int rad_2 = x * x + y * y;
+ // This is a non-transparent red for the halo
+ int red = SHIELD_HALO_GLOW;
+ int alpha = 255;
+ double rad;
+
+ if (rad_2 >= SHIELD_RADIUS_THRES)
+ { // outside all bounds
+ *pix = clear;
+ continue;
+ }
+ // Inside the halo
+ if (rad_2 <= RADIUS_2)
+ { // planet's pixels, ours transparent
+ *pix = clear;
+ continue;
+ }
+
+ // The halo itself
+ rad = sqrt (rad_2);
+
+ if (rad <= RADIUS + 0.8)
+ { // pixels common between the shield and planet
+ // do antialiasing using alpha
+ alpha = (int) (red * (rad - RADIUS));
+ red = 255;
+ }
+ else
+ { // shield pixels
+ red -= (int) ((red - SHIELD_HALO_GLOW_MIN) * (rad - RADIUS)
+ / SHIELD_HALO);
+ if (red < 0)
+ red = 0;
+ }
+
+ *pix = BUILD_COLOR_RGBA (red, 0, 0, alpha);
+ }
+ }
+
+ WriteFramePixelColors (ShieldFrame, Orbit->ScratchArray,
+ SHIELD_DIAM, SHIELD_DIAM);
+ SetFrameHot (ShieldFrame, MAKE_HOT_SPOT (SHIELD_RADIUS + 1,
+ SHIELD_RADIUS + 1));
+
+ return ShieldFrame;
+}
+
+// SetShieldThrobEffect adjusts the red levels in the shield glow graphic
+// the throbbing cycle is tied to the planet rotation cycle
+#define SHIELD_THROBS 12
+ // throb cycles per revolution
+#define THROB_CYCLE ((MAP_WIDTH << 8) / SHIELD_THROBS)
+#define THROB_HALF_CYCLE (THROB_CYCLE >> 1)
+
+#define THROB_MAX_LEVEL 256
+#define THROB_MIN_LEVEL 100
+#define THROB_D_LEVEL (THROB_MAX_LEVEL - THROB_MIN_LEVEL)
+
+static inline int
+shield_level (int offset)
+{
+ int level;
+
+ offset = (offset << 8) % THROB_CYCLE;
+ level = abs (offset - THROB_HALF_CYCLE);
+ level = THROB_MIN_LEVEL + level * THROB_D_LEVEL / THROB_HALF_CYCLE;
+
+ return level;
+}
+
+// See description above
+// offset is effectively the angle of rotation around the planet's axis
+void
+SetShieldThrobEffect (FRAME ShieldFrame, int offset, FRAME ThrobFrame)
+{
+ int i;
+ int width, height;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ Color *pix;
+ int level;
+
+ level = shield_level (offset);
+
+ width = GetFrameWidth (ShieldFrame);
+ height = GetFrameHeight (ShieldFrame);
+ ReadFramePixelColors (ShieldFrame, Orbit->ScratchArray, width, height);
+
+ for (i = 0, pix = Orbit->ScratchArray; i < width * height; ++i, ++pix)
+ {
+ Color p = *pix;
+
+ if (p.a == 255)
+ { // adjust color data for full-alpha pixels
+ p.r = p.r * level / THROB_MAX_LEVEL;
+ p.g = p.g * level / THROB_MAX_LEVEL;
+ p.b = p.b * level / THROB_MAX_LEVEL;
+ }
+ else if (p.a > 0)
+ { // adjust alpha for translucent pixels
+ p.a = p.a * level / THROB_MAX_LEVEL;
+ }
+
+ *pix = p;
+ }
+
+ WriteFramePixelColors (ThrobFrame, Orbit->ScratchArray, width, height);
+ SetFrameHot (ThrobFrame, GetFrameHot (ShieldFrame));
+}
+
+// Apply the shield to the topo image
+static void
+ApplyShieldTint (void)
+{
+ DrawMode mode, oldMode;
+ FRAME oldFrame;
+ Color tint;
+ RECT r;
+
+ // TopoFrame will be permanently changed
+ oldFrame = SetContextFGFrame (pSolarSysState->TopoFrame);
+ SetContextClipRect (NULL);
+ GetContextClipRect (&r);
+
+ tint = BUILD_COLOR_RGBA (0xff, 0x00, 0x00, 0xff);
+#ifdef USE_ALPHA_SHIELD
+ mode = MAKE_DRAW_MODE (DRAW_ALPHA, 150);
+#else
+ mode = MAKE_DRAW_MODE (DRAW_ADDITIVE, DRAW_FACTOR_1);
+#endif
+ oldMode = SetContextDrawMode (mode);
+ SetContextForeGroundColor (tint);
+ DrawFilledRectangle (&r);
+ SetContextDrawMode (oldMode);
+ SetContextFGFrame (oldFrame);
+}
+
+static inline UBYTE
+calc_map_light (UBYTE val, DWORD dif, int lvf)
+{
+ int i;
+
+ // apply diffusion
+ i = (dif * val) >> DIFFUSE_BITS;
+ // apply light variance for 3d lighting effect
+ i += (lvf * val) >> 7;
+
+ if (i < 0)
+ i = 0;
+ else if (i > 255)
+ i = 255;
+
+ return ((UBYTE)i);
+}
+
+static inline Color
+get_map_pixel (Color *pixels, int x, int y)
+{
+ return pixels[y * (MAP_WIDTH + SPHERE_SPAN_X) + x];
+}
+
+static inline int
+get_map_elev (SBYTE *elevs, int x, int y, int offset)
+{
+ return elevs[y * MAP_WIDTH + (offset + x) % MAP_WIDTH];
+}
+
+// RenderPlanetSphere builds a frame for the rotating planet view
+// offset is effectively the angle of rotation around the planet's axis
+// We use the SDL routines to directly write to the SDL_Surface to improve performance
+void
+RenderPlanetSphere (FRAME MaskFrame, int offset, BOOLEAN doThrob)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ POINT pt;
+ Color *pix;
+ Color clear;
+ int x, y;
+ Color *pixels;
+ SBYTE *elevs;
+ int shLevel;
+
+#if PROFILE_ROTATION
+ static clock_t t = 0;
+ static int frames_done = 1;
+ clock_t t1;
+ t1 = clock ();
+#endif
+
+
+ shLevel = shield_level (offset);
+
+ pix = Orbit->ScratchArray;
+ clear = BUILD_COLOR_RGBA (0, 0, 0, 0);
+ pixels = Orbit->TopoColors + offset;
+ elevs = Orbit->lpTopoData;
+
+ for (pt.y = 0, y = -RADIUS; pt.y <= TWORADIUS; ++pt.y, ++y)
+ {
+ for (pt.x = 0, x = -RADIUS; pt.x <= TWORADIUS; ++pt.x, ++x, ++pix)
+ {
+ Color c;
+ DWORD diffus = light_diff[pt.y][pt.x];
+ int i;
+ MAP3D_POINT *ppt = &map_rotate[pt.y][pt.x];
+ int lvf; // light variance factor
+
+ if (diffus == 0)
+ { // full diffusion
+ *pix = clear;
+ continue;
+ }
+
+ // get pixel from topo map and factor from light variance map
+ if (ppt->m[0] == 0)
+ { // exact pixel from the topo map
+ c = get_map_pixel (pixels, ppt->p[0].x, ppt->p[0].y);
+ lvf = get_map_elev (elevs, ppt->p[0].x, ppt->p[0].y, offset);
+ }
+ else
+ { // fractional pixel -- blend from 4
+ Color p[4];
+ int lvsum;
+
+ // compute 'ideal' pixel
+ for (i = 0; i < 4; i++)
+ p[i] = get_map_pixel (pixels, ppt->p[i].x, ppt->p[i].y);
+
+ c.r = get_avg_channel (p, ppt->m, 0);
+ c.g = get_avg_channel (p, ppt->m, 1);
+ c.b = get_avg_channel (p, ppt->m, 2);
+
+ // compute 'ideal' light variance
+ for (i = 0, lvsum = 0; i < 4; i++)
+ lvsum += get_map_elev (elevs, ppt->p[0].x, ppt->p[0].y,
+ offset) * ppt->m[i];
+ lvf = lvsum >> AA_WEIGHT_BITS;
+ }
+
+ // Apply the lighting model. This also bounds the sphere
+ // to make it circular.
+ if (pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ {
+ int r;
+
+ // add lite red filter (3/4) component
+ c.g = (c.g >> 1) + (c.g >> 2);
+ c.b = (c.b >> 1) + (c.b >> 2);
+
+ c.r = calc_map_light (c.r, diffus, lvf);
+ c.g = calc_map_light (c.g, diffus, lvf);
+ c.b = calc_map_light (c.b, diffus, lvf);
+
+ // The shield is glow + reflect (+ filter for others)
+ r = calc_map_light (SHIELD_REFLECT_COMP, diffus, 0);
+ r += SHIELD_GLOW_COMP;
+
+ if (doThrob)
+ { // adjust red level for throbbing shield
+ r = r * shLevel / THROB_MAX_LEVEL;
+ }
+
+ r += c.r;
+ if (r > 255)
+ r = 255;
+ c.r = r;
+ }
+ else
+ {
+ c.r = calc_map_light (c.r, diffus, lvf);
+ c.g = calc_map_light (c.g, diffus, lvf);
+ c.b = calc_map_light (c.b, diffus, lvf);
+ }
+
+ c.a = 0xff;
+ *pix = c;
+ }
+ }
+
+ WriteFramePixelColors (MaskFrame, Orbit->ScratchArray, DIAMETER, DIAMETER);
+ SetFrameHot (MaskFrame, MAKE_HOT_SPOT (RADIUS + 1, RADIUS + 1));
+
+#if PROFILE_ROTATION
+ t += clock() - t1;
+ if (frames_done == MAP_WIDTH)
+ {
+ log_add (log_Debug, "Rotation frames/sec: %d/%ld(msec)=%f",
+ frames_done,
+ (long int) (((double)t / CLOCKS_PER_SEC) * 1000.0 + 0.5),
+ frames_done / ((double)t / CLOCKS_PER_SEC + 0.5));
+ frames_done = 1;
+ t = clock () - t1;
+ }
+ else
+ frames_done++;
+#endif
+}
+
+
+#define RANGE_SHIFT 6
+
+static void
+DitherMap (SBYTE *DepthArray)
+{
+#define DITHER_VARIANCE (1 << (RANGE_SHIFT - 3))
+ COUNT i;
+ SBYTE *elev;
+ DWORD rand_val = 0;
+
+ for (i = 0, elev = DepthArray; i < MAP_WIDTH * MAP_HEIGHT; ++i, ++elev)
+ {
+ // Use up the random value byte by byte
+ if ((i & 3) == 0)
+ rand_val = RandomContext_Random (SysGenRNG);
+ else
+ rand_val >>= 8;
+
+ // Bring the elevation point up or down
+ *elev += DITHER_VARIANCE / 2 - (rand_val & (DITHER_VARIANCE - 1));
+ }
+}
+
+static void
+MakeCrater (RECT *pRect, SBYTE *DepthArray, SIZE rim_delta, SIZE
+ crater_delta, BOOLEAN SetDepth)
+{
+ COORD x, y, lf_x, rt_x;
+ SIZE A, B;
+ long Asquared, TwoAsquared,
+ Bsquared, TwoBsquared;
+ long d, dx, dy;
+ COUNT TopIndex, BotIndex, rim_pixels;
+
+ A = pRect->extent.width >> 1;
+ B = pRect->extent.height >> 1;
+
+ x = 0;
+ y = B;
+
+ Asquared = (DWORD)A * A;
+ TwoAsquared = Asquared << 1;
+ Bsquared = (DWORD)B * B;
+ TwoBsquared = Bsquared << 1;
+
+ dx = 0;
+ dy = TwoAsquared * B;
+ d = Bsquared - (dy >> 1) + (Asquared >> 2);
+
+ A += pRect->corner.x;
+ B += pRect->corner.y;
+ TopIndex = (B - y) * MAP_WIDTH;
+ BotIndex = (B + y) * MAP_WIDTH;
+ rim_pixels = 1;
+ while (dx < dy)
+ {
+ if (d > 0)
+ {
+ lf_x = A - x;
+ rt_x = A + x;
+ if (SetDepth)
+ {
+ memset (&DepthArray[TopIndex + lf_x], 0, rt_x - lf_x + 1);
+ memset (&DepthArray[BotIndex + lf_x], 0, rt_x - lf_x + 1);
+ }
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ rim_pixels = 0;
+ }
+ else
+ {
+ do
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ if (lf_x != rt_x)
+ {
+ DepthArray[TopIndex + rt_x] += rim_delta;
+ DepthArray[BotIndex + rt_x] += rim_delta;
+ }
+ ++lf_x;
+ --rt_x;
+ } while (--rim_pixels);
+
+ while (lf_x < rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ DepthArray[TopIndex + rt_x] += crater_delta;
+ DepthArray[BotIndex + rt_x] += crater_delta;
+ ++lf_x;
+ --rt_x;
+ }
+
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ }
+ }
+
+ --y;
+ TopIndex += MAP_WIDTH;
+ BotIndex -= MAP_WIDTH;
+ dy -= TwoAsquared;
+ d -= dy;
+ }
+
+ ++rim_pixels;
+ ++x;
+ dx += TwoBsquared;
+ d += Bsquared + dx;
+ }
+
+ d += ((((Asquared - Bsquared) * 3) >> 1) - (dx + dy)) >> 1;
+
+ while (y > 0)
+ {
+ lf_x = A - x;
+ rt_x = A + x;
+ if (SetDepth)
+ {
+ memset (&DepthArray[TopIndex + lf_x], 0, rt_x - lf_x + 1);
+ memset (&DepthArray[BotIndex + lf_x], 0, rt_x - lf_x + 1);
+ }
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ }
+ else
+ {
+ do
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ DepthArray[BotIndex + lf_x] += rim_delta;
+ if (lf_x != rt_x)
+ {
+ DepthArray[TopIndex + rt_x] += rim_delta;
+ DepthArray[BotIndex + rt_x] += rim_delta;
+ }
+ ++lf_x;
+ --rt_x;
+ } while (--rim_pixels);
+
+ while (lf_x < rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ DepthArray[TopIndex + rt_x] += crater_delta;
+ DepthArray[BotIndex + rt_x] += crater_delta;
+ ++lf_x;
+ --rt_x;
+ }
+
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[BotIndex + lf_x] += crater_delta;
+ }
+ }
+
+ if (d < 0)
+ {
+ ++x;
+ dx += TwoBsquared;
+ d += dx;
+ }
+
+ rim_pixels = 1;
+ --y;
+ TopIndex += MAP_WIDTH;
+ BotIndex -= MAP_WIDTH;
+ dy -= TwoAsquared;
+ d += Asquared - dy;
+ }
+
+ lf_x = A - x;
+ rt_x = A + x;
+ if (SetDepth)
+ memset (&DepthArray[TopIndex + lf_x], 0, rt_x - lf_x + 1);
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ }
+ else
+ {
+ do
+ {
+ DepthArray[TopIndex + lf_x] += rim_delta;
+ if (lf_x != rt_x)
+ DepthArray[TopIndex + rt_x] += rim_delta;
+ ++lf_x;
+ --rt_x;
+ } while (--rim_pixels);
+
+ while (lf_x < rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ DepthArray[TopIndex + rt_x] += crater_delta;
+ ++lf_x;
+ --rt_x;
+ }
+
+ if (lf_x == rt_x)
+ {
+ DepthArray[TopIndex + lf_x] += crater_delta;
+ }
+ }
+}
+
+#define NUM_BAND_COLORS 4
+
+static void
+MakeStorms (COUNT storm_count, SBYTE *DepthArray)
+{
+#define MAX_STORMS 8
+ COUNT i;
+ RECT storm_r[MAX_STORMS];
+ RECT *pstorm_r;
+
+ pstorm_r = &storm_r[i = storm_count];
+ while (i--)
+ {
+ BOOLEAN intersect;
+ DWORD rand_val;
+ UWORD loword, hiword;
+ SIZE band_delta;
+
+ --pstorm_r;
+ do
+ {
+ COUNT j;
+
+ intersect = FALSE;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loword = LOWORD (rand_val);
+ hiword = HIWORD (rand_val);
+ switch (HIBYTE (hiword) & 31)
+ {
+ case 0:
+ pstorm_r->extent.height =
+ (LOBYTE (hiword) % (MAP_HEIGHT >> 2))
+ + (MAP_HEIGHT >> 2);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ pstorm_r->extent.height =
+ (LOBYTE (hiword) % (MAP_HEIGHT >> 3))
+ + (MAP_HEIGHT >> 3);
+ break;
+ default:
+ pstorm_r->extent.height =
+ (LOBYTE (hiword) % (MAP_HEIGHT >> 4))
+ + 4;
+ break;
+ }
+
+ if (pstorm_r->extent.height <= 4)
+ pstorm_r->extent.height += 4;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loword = LOWORD (rand_val);
+ hiword = HIWORD (rand_val);
+
+ pstorm_r->extent.width = pstorm_r->extent.height
+ + (LOBYTE (loword) % pstorm_r->extent.height);
+
+ pstorm_r->corner.x = HIBYTE (loword)
+ % (MAP_WIDTH - pstorm_r->extent.width);
+ pstorm_r->corner.y = LOBYTE (loword)
+ % (MAP_HEIGHT - pstorm_r->extent.height);
+
+ for (j = i + 1; j < storm_count; ++j)
+ {
+ COORD x, y;
+ SIZE w, h;
+
+ x = storm_r[j].corner.x - pstorm_r->corner.x;
+ y = storm_r[j].corner.y - pstorm_r->corner.y;
+ w = x + storm_r[j].extent.width + 4;
+ h = y + storm_r[j].extent.height + 4;
+ intersect = (BOOLEAN) (w > 0 && h > 0
+ && x < pstorm_r->extent.width + 4
+ && y < pstorm_r->extent.height + 4);
+ if (intersect)
+ break;
+ }
+
+ } while (intersect);
+
+ MakeCrater (pstorm_r, DepthArray, 6, 6, FALSE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+
+ band_delta = HIBYTE (loword) & ((3 << RANGE_SHIFT) + 20);
+
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+
+ band_delta += 2;
+ if (pstorm_r->extent.width > 2 && pstorm_r->extent.height > 2)
+ {
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+ }
+
+ band_delta += 2;
+ if (pstorm_r->extent.width > 2 && pstorm_r->extent.height > 2)
+ {
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ ++pstorm_r->corner.x;
+ ++pstorm_r->corner.y;
+ pstorm_r->extent.width -= 2;
+ pstorm_r->extent.height -= 2;
+ }
+
+ band_delta += 4;
+ MakeCrater (pstorm_r, DepthArray,
+ band_delta, band_delta, TRUE);
+ }
+}
+
+static void
+MakeGasGiant (COUNT num_bands, SBYTE *DepthArray, RECT *pRect, SIZE
+ depth_delta)
+{
+ COORD last_y, next_y;
+ SIZE band_error, band_bump, band_delta;
+ COUNT i, j, band_height;
+ SBYTE *lpDst;
+ UWORD loword, hiword;
+ DWORD rand_val;
+
+ band_height = pRect->extent.height / num_bands;
+ band_bump = pRect->extent.height % num_bands;
+ band_error = num_bands >> 1;
+ lpDst = DepthArray;
+
+ band_delta = ((LOWORD (RandomContext_Random (SysGenRNG))
+ & (NUM_BAND_COLORS - 1)) << RANGE_SHIFT)
+ + (1 << (RANGE_SHIFT - 1));
+ last_y = next_y = 0;
+ for (i = num_bands; i > 0; --i)
+ {
+ COORD cur_y;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loword = LOWORD (rand_val);
+ hiword = HIWORD (rand_val);
+
+ next_y += band_height;
+ if ((band_error -= band_bump) < 0)
+ {
+ ++next_y;
+ band_error += num_bands;
+ }
+ if (i == 1)
+ cur_y = pRect->extent.height;
+ else
+ {
+ RECT r;
+
+ cur_y = next_y
+ + ((band_height - 2) >> 1)
+ - ((LOBYTE (hiword) % (band_height - 2)) + 1);
+ r.corner.x = r.corner.y = 0;
+ r.extent.width = pRect->extent.width;
+ r.extent.height = 5;
+ DeltaTopography (50,
+ &DepthArray[(cur_y - 2) * r.extent.width],
+ &r, depth_delta);
+ }
+
+ for (j = cur_y - last_y; j > 0; --j)
+ {
+ COUNT k;
+
+ for (k = pRect->extent.width; k > 0; --k)
+ *lpDst++ += band_delta;
+ }
+
+ last_y = cur_y;
+ band_delta = (band_delta
+ + ((((LOBYTE (loword) & 1) << 1) - 1) << RANGE_SHIFT))
+ & (((1 << RANGE_SHIFT) * NUM_BAND_COLORS) - 1);
+ }
+
+ MakeStorms (4 + (RandomContext_Random (SysGenRNG) & 3) + 1, DepthArray);
+
+ DitherMap (DepthArray);
+}
+
+static void
+ValidateMap (SBYTE *DepthArray)
+{
+ BYTE state;
+ BYTE pixel_count[2], lb[2];
+ SBYTE last_byte;
+ COUNT i;
+ SBYTE *lpDst;
+
+ i = MAP_WIDTH - 1;
+ lpDst = DepthArray;
+ last_byte = *lpDst++;
+ state = pixel_count[0] = pixel_count[1] = 0;
+ do
+ {
+ if (pixel_count[state]++ == 0)
+ lb[state] = last_byte;
+
+ if (last_byte > *lpDst)
+ {
+ if (last_byte - *lpDst > 128)
+ state ^= 1;
+ }
+ else
+ {
+ if (*lpDst - last_byte > 128)
+ state ^= 1;
+ }
+ last_byte = *lpDst++;
+ } while (--i);
+
+ i = MAP_WIDTH * MAP_HEIGHT;
+ lpDst = DepthArray;
+ if (pixel_count[0] > pixel_count[1])
+ last_byte = lb[0];
+ else
+ last_byte = lb[1];
+ do
+ {
+ if (last_byte > *lpDst)
+ {
+ if (last_byte - *lpDst > 128)
+ *lpDst = last_byte;
+ }
+ else
+ {
+ if (*lpDst - last_byte > 128)
+ *lpDst = last_byte;
+ }
+ last_byte = *lpDst++;
+ } while (--i);
+}
+
+static void
+planet_orbit_init (void)
+{
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+
+ Orbit->SphereFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP | WANT_ALPHA, DIAMETER, DIAMETER, 2));
+ Orbit->TintFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, MAP_WIDTH, MAP_HEIGHT, 1));
+ Orbit->ObjectFrame = 0;
+ Orbit->WorkFrame = 0;
+ Orbit->lpTopoData = HCalloc (MAP_WIDTH * MAP_HEIGHT);
+ Orbit->TopoZoomFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, MAP_WIDTH << 2, MAP_HEIGHT << 2, 1));
+ Orbit->TopoColors = HMalloc (sizeof (Orbit->TopoColors[0])
+ * (MAP_HEIGHT * (MAP_WIDTH + SPHERE_SPAN_X)));
+ // always allocate the scratch array to largest needed size
+ Orbit->ScratchArray = HMalloc (sizeof (Orbit->ScratchArray[0])
+ * (SHIELD_DIAM) * (SHIELD_DIAM));
+}
+
+static unsigned
+frandom (void)
+{
+ static unsigned seed = 0x12345678;
+
+ if (seed == 0)
+ seed = 15807;
+ seed = (seed >> 4) * 227;
+
+ return seed;
+}
+
+static inline int
+TopoVarianceFactor (int step, int allowed, int min)
+{
+#define SCALE_SHIFT 8
+ return ((abs(step) * allowed) >> SCALE_SHIFT) + min;
+}
+
+static inline int
+TopoVarianceCalc (int factor)
+{
+ if (factor == 0)
+ return 0;
+ else
+ return (frandom () % factor) - (factor >> 1);
+}
+
+static void
+TopoScale4x (SBYTE *pDstTopo, SBYTE *pSrcTopo, int num_faults, int fault_var)
+{
+ // Interpolate the topographical data by connecting the elevations
+ // to their nearest neighboors using straight lines (in random
+ // direction) with random variance factors defined by
+ // num_faults and fault_var args
+#define AVG_VARIANCE 250
+ int x, y;
+ const int w = MAP_WIDTH, h = MAP_HEIGHT;
+ const int spitch = MAP_WIDTH, dpitch = MAP_WIDTH * 4;
+ SBYTE *pSrc;
+ SBYTE *pDst;
+ int* prevrow;
+ int* prow;
+ int elev[5][5];
+ int var_allow, var_min;
+ static const struct line_def_t
+ {
+ int x0, y0, x1, y1;
+ int dx, dy;
+ }
+ fill_lines[4][6] =
+ {
+ { // diag set 0
+ { 0, 2, 2, 0, 1, -1},
+ { 0, 3, 3, 0, 1, -1},
+ { 0, 4, 4, 0, 1, -1},
+ { 1, 4, 4, 1, 1, -1},
+ { 2, 4, 4, 2, 1, -1},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ { // diag set 1
+ { 0, 2, 2, 4, 1, 1},
+ { 0, 1, 3, 4, 1, 1},
+ { 0, 0, 4, 4, 1, 1},
+ { 1, 0, 4, 3, 1, 1},
+ { 2, 0, 4, 2, 1, 1},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ { // horizontal
+ { 0, 1, 4, 1, 1, 0},
+ { 0, 2, 4, 2, 1, 0},
+ { 0, 3, 4, 3, 1, 0},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ { // vertical
+ { 1, 0, 1, 4, 0, 1},
+ { 2, 0, 2, 4, 0, 1},
+ { 3, 0, 3, 4, 0, 1},
+ {-1, -1, -1, -1, 0, 0}, // term
+ },
+ };
+
+ prevrow = (int *) HMalloc ((MAP_WIDTH * 4 + 1) * sizeof(prevrow[0]));
+
+ var_allow = (num_faults << SCALE_SHIFT) / AVG_VARIANCE;
+ var_min = fault_var << SCALE_SHIFT;
+
+ //memset (pDstTopo, 0, MAP_WIDTH * MAP_HEIGHT * 16);
+
+ // init the first row in advance
+ pSrc = pSrcTopo;
+ prow = prevrow;
+#define STEP_RANGE (4 - 1)
+ prow[0] = ((int)pSrc[0]) << SCALE_SHIFT;;
+ for (x = 0; x < w; ++x, ++pSrc, prow += 4)
+ {
+ int x2;
+ int val, step, rndfact;
+
+ // next point in row
+ if (x < w - 1)
+ // one right
+ prow[4] = ((int)pSrc[1]) << SCALE_SHIFT;
+ else
+ // wrap around
+ prow[4] = ((int)pSrc[1 - spitch]) << SCALE_SHIFT;
+
+ // compute elevations between 2 points
+ val = prow[0];
+ step = (prow[4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (x2 = 1, val += step; x2 < 4; ++x2, val += step)
+ prow[x2] = val + TopoVarianceCalc (rndfact);
+ }
+
+ pSrc = pSrcTopo;
+ pDst = pDstTopo;
+ for (y = 0; y < h; ++y, pDst += dpitch * 3)
+ {
+ int x2, y2;
+ SBYTE *p;
+ int val, step, rndfact;
+ const struct line_def_t* pld;
+
+ prow = prevrow;
+ // prime the first interpolated column
+ elev[4][0] = prow[0];
+ if (y < h - 1)
+ elev[4][4] = ((int)pSrc[spitch]) << SCALE_SHIFT;
+ else
+ elev[4][4] = elev[4][0];
+ // compute elevations for interpolated column
+ val = elev[4][0];
+ step = (elev[4][4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (y2 = 1, val += step; y2 < 4; ++y2, val += step)
+ elev[4][y2] = val + TopoVarianceCalc (rndfact);
+
+ for (x = 0; x < w; ++x, ++pSrc, pDst += 4, prow += 4)
+ {
+ // recall the first interpolated row from prevrow
+ for (x2 = 0; x2 <= 4; ++x2)
+ elev[x2][0] = prow[x2];
+ // recall the first interpolated column
+ for (y2 = 1; y2 <= 4; ++y2)
+ elev[0][y2] = elev[4][y2];
+
+ if (y < h - 1)
+ {
+ if (x < w - 1)
+ // one right, one down
+ elev[4][4] = ((int)pSrc[1 + spitch]) << SCALE_SHIFT;
+ else
+ // wrap around, one down
+ elev[4][4] = ((int)pSrc[1]) << SCALE_SHIFT;
+ }
+ else
+ {
+ elev[4][4] = elev[4][0];
+ }
+
+ // compute elevations for the rest of square borders first
+ val = elev[0][4];
+ step = (elev[4][4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (x2 = 1, val += step; x2 < 4; ++x2, val += step)
+ elev[x2][4] = val + TopoVarianceCalc (rndfact);
+
+ val = elev[4][0];
+ step = (elev[4][4] - val) / STEP_RANGE;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+ for (y2 = 1, val += step; y2 < 4; ++y2, val += step)
+ elev[4][y2] = val + TopoVarianceCalc (rndfact);
+
+ // fill in the rest by connecting opposing elevations
+ // some randomness to determine which elevations to connect
+ for (pld = fill_lines[frandom () & 3]; pld->x0 >= 0; ++pld)
+ {
+ int num_steps;
+
+ x2 = pld->x0;
+ y2 = pld->y0;
+ val = elev[x2][y2];
+ num_steps = pld->x1 - pld->x0;
+ if (num_steps == 0)
+ num_steps = pld->y1 - pld->y0;
+ step = (elev[pld->x1][pld->y1] - val) / num_steps;
+ rndfact = TopoVarianceFactor (step, var_allow, var_min);
+
+ for (x2 += pld->dx, y2 += pld->dy, val += step;
+ x2 != pld->x1 || y2 != pld->y1;
+ x2 += pld->dx, y2 += pld->dy, val += step)
+ {
+ elev[x2][y2] = val + TopoVarianceCalc (rndfact);
+ }
+ }
+
+ // output the interpolated topography
+ for (y2 = 0; y2 < 4; ++y2)
+ {
+ p = pDst + y2 * dpitch;
+ for (x2 = 0; x2 < 4; ++x2, ++p)
+ {
+ int e = elev[x2][y2] >> SCALE_SHIFT;
+ if (e > 127)
+ e = 127;
+ else if (e < -128)
+ e = -128;
+ *p = (SBYTE)e;
+ }
+ }
+
+ // save last interpolated row to prevrow for later
+ for (x2 = 0; x2 < 4; ++x2)
+ prow[x2] = elev[x2][4];
+ }
+ // save last row point
+ prow[0] = elev[4][4];
+ }
+
+ HFree (prevrow);
+}
+
+
+// GenerateLightMap produces a surface light variance map for the
+// rotating planet by, first, transforming absolute elevation data
+// into normalized relative and then applying a weighted
+// average-median of surrounding points
+// Lots of pure Voodoo here ;)
+// the goal is a 3D illusion, not mathematically correct lighting
+
+#define LMAP_AVG_BLOCK ((MAP_HEIGHT + 4) / 5)
+#define LMAP_MAX_DIST ((LMAP_AVG_BLOCK + 1) >> 1)
+#define LMAP_WEIGHT_THRES (LMAP_MAX_DIST * 2 / 3)
+
+typedef struct
+{
+ int min;
+ int max;
+ int avg;
+
+} elev_block_t;
+
+static inline void
+get_vblock_avg (elev_block_t *pblk, SBYTE *pTopo, int x, int y)
+{
+ SBYTE *elev = pTopo;
+ int y0, y1, i;
+ int min = 127, max = -127;
+ int avg = 0, total_weight = 0;
+
+ // surface wraps around along x
+ x = (x + MAP_WIDTH) % MAP_WIDTH;
+
+ y0 = y - LMAP_MAX_DIST;
+ y1 = y + LMAP_MAX_DIST;
+ if (y0 < 0)
+ y0 = 0;
+ if (y1 > MAP_HEIGHT)
+ y1 = MAP_HEIGHT;
+
+ elev = pTopo + y0 * MAP_WIDTH + x;
+ for (i = y0; i < y1; ++i, elev += MAP_WIDTH)
+ {
+ int delta = abs (i - y);
+ int weight = 255; // full weight
+ int v = *elev;
+
+ if (delta >= LMAP_WEIGHT_THRES)
+ { // too far -- progressively reduced weight
+ weight = weight * (LMAP_MAX_DIST - delta + 1)
+ / (LMAP_MAX_DIST - LMAP_WEIGHT_THRES + 2);
+ }
+
+ if (v > max)
+ max = v;
+ if (v < min)
+ min = v;
+ avg += v * weight;
+ total_weight += weight;
+ }
+ avg /= total_weight;
+
+ pblk->min = min;
+ pblk->max = max;
+ pblk->avg = avg / (y1 - y0);
+}
+
+// See description above
+static void
+GenerateLightMap (SBYTE *pTopo, int w, int h)
+{
+#define LMAP_BLOCKS (2 * LMAP_MAX_DIST + 1)
+ int x, y;
+ elev_block_t vblocks[LMAP_BLOCKS];
+ // we use a running block average to reduce the amount of work
+ // where a block is a vertical line of map points
+ SBYTE *elev;
+ int min, max, med;
+ int sfact, spread;
+
+ // normalize the topo data
+ min = 127;
+ max = -128;
+ for (x = 0, elev = pTopo; x < w * h; ++x, ++elev)
+ {
+ int v = *elev;
+ if (v > max)
+ max = v;
+ if (v < min)
+ min = v;
+ }
+ med = (min + max) / 2;
+ spread = max - med;
+
+ if (spread == 0)
+ { // perfectly smooth surface -- nothing to do but
+ // level it out completely
+ if (max != 0)
+ memset (pTopo, 0, w * h);
+ return;
+ }
+
+ // these are whatever looks right
+ if (spread < 10)
+ sfact = 30; // minimal spread
+ else if (spread < 30)
+ sfact = 60;
+ else
+ sfact = 100; // full spread
+
+ // apply spread
+ for (x = 0, elev = pTopo; x < w * h; ++x, ++elev)
+ {
+ int v = *elev;
+ v = (v - med) * sfact / spread;
+ *elev = v;
+ }
+
+ // compute and apply weighted averages of surrounding points
+ for (y = 0, elev = pTopo; y < h; ++y)
+ {
+ elev_block_t *pblk;
+ int i;
+
+ // prime the running block average
+ // get the minimum, maximum and avg elevation for each block
+ for (i = -LMAP_MAX_DIST; i < LMAP_MAX_DIST; ++i)
+ {
+ // blocks wrap around on both sides
+ pblk = vblocks + ((i + LMAP_BLOCKS) % LMAP_BLOCKS);
+
+ get_vblock_avg (pblk, pTopo, i, y);
+ }
+
+ for (x = 0; x < w; ++x, ++elev)
+ {
+ int avg = 0, total_weight = 0;
+
+ min = 127;
+ max = -127;
+
+ // prepare next block as we move along x
+ pblk = vblocks + ((x + LMAP_MAX_DIST) % LMAP_BLOCKS);
+ get_vblock_avg (pblk, pTopo, x + LMAP_MAX_DIST, y);
+
+ // compute the min, max and weighted avg of blocks
+ for (i = x - LMAP_MAX_DIST; i <= x + LMAP_MAX_DIST; ++i)
+ {
+ int delta = abs (i - x);
+ int weight = 255; // full weight
+
+ pblk = vblocks + ((i + LMAP_BLOCKS) % LMAP_BLOCKS);
+
+ if (delta >= LMAP_WEIGHT_THRES)
+ { // too far -- progressively reduced weight
+ weight = weight * (LMAP_MAX_DIST - delta + 1)
+ / (LMAP_MAX_DIST - LMAP_WEIGHT_THRES + 2);
+ }
+
+ if (pblk->max > max)
+ max = pblk->max;
+ if (pblk->min < min)
+ min = pblk->min;
+
+ avg += pblk->avg * weight;
+ total_weight += weight;
+ }
+ avg /= total_weight;
+
+ // This is mostly Voodoo
+ // figure out what kind of relative lighting factor
+ // to assign to this point
+#if 0
+ // relative to median
+ med = (min + max) / 2; // median
+ *elev = (int)*elev - med;
+#else
+ // relative to median of (average, median)
+ med = (min + max) / 2; // median
+ med = (med + avg) / 2;
+ *elev = (int)*elev - med;
+#endif
+ }
+ }
+}
+
+// Sets the SysGenRNG to the required state first.
+void
+GeneratePlanetSurface (PLANET_DESC *pPlanetDesc, FRAME SurfDefFrame)
+{
+ RECT r;
+ const PlanetFrame *PlanDataPtr;
+ PLANET_INFO *PlanetInfo = &pSolarSysState->SysInfo.PlanetInfo;
+ COUNT i, y;
+ POINT loc;
+ CONTEXT OldContext;
+ CONTEXT TopoContext;
+ PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
+ BOOLEAN shielded = (pPlanetDesc->data_index & PLANET_SHIELDED) != 0;
+
+ RandomContext_SeedRandom (SysGenRNG, pPlanetDesc->rand_seed);
+
+ TopoContext = CreateContext ("Plangen.TopoContext");
+ OldContext = SetContext (TopoContext);
+ planet_orbit_init ();
+
+ PlanDataPtr = &PlanData[pPlanetDesc->data_index & ~PLANET_SHIELDED];
+
+ if (SurfDefFrame)
+ { // This is a defined planet; pixmap for the topography and
+ // elevation data is supplied in Surface Definition frame
+ BOOLEAN DeleteDef = FALSE;
+ FRAME ElevFrame;
+
+ // surface pixmap
+ SurfDefFrame = SetAbsFrameIndex (SurfDefFrame, 0);
+ if (GetFrameWidth (SurfDefFrame) != MAP_WIDTH
+ || GetFrameHeight (SurfDefFrame) != MAP_HEIGHT)
+ {
+ pSolarSysState->TopoFrame = CaptureDrawable (RescaleFrame (
+ SurfDefFrame, MAP_WIDTH, MAP_HEIGHT));
+ // will not need the passed FRAME anymore
+ DeleteDef = TRUE;
+ }
+ else
+ pSolarSysState->TopoFrame = SurfDefFrame;
+
+ if (GetFrameCount (SurfDefFrame) > 1)
+ { // 2nd frame is elevation data
+ int i;
+ SBYTE* elev;
+
+ ElevFrame = SetAbsFrameIndex (SurfDefFrame, 1);
+ if (GetFrameWidth (ElevFrame) != MAP_WIDTH
+ || GetFrameHeight (ElevFrame) != MAP_HEIGHT)
+ {
+ ElevFrame = CaptureDrawable (RescaleFrame (ElevFrame,
+ MAP_WIDTH, MAP_HEIGHT));
+ }
+
+ // grab the elevation data in 1 byte per pixel format
+ ReadFramePixelIndexes (ElevFrame, (BYTE *)Orbit->lpTopoData,
+ MAP_WIDTH, MAP_HEIGHT);
+ // the supplied data is in unsigned format, must convert
+ for (i = 0, elev = Orbit->lpTopoData;
+ i < MAP_WIDTH * MAP_HEIGHT;
+ ++i, ++elev)
+ {
+ *elev = *(BYTE *)elev - 128;
+ }
+ }
+ else
+ { // no elevation data -- planet flat as a pancake
+ memset (Orbit->lpTopoData, 0, MAP_WIDTH * MAP_HEIGHT);
+ }
+
+ if (DeleteDef)
+ DestroyDrawable (ReleaseDrawable (SurfDefFrame));
+ }
+ else
+ { // Generate planet surface elevation data and look
+
+ r.corner.x = r.corner.y = 0;
+ r.extent.width = MAP_WIDTH;
+ r.extent.height = MAP_HEIGHT;
+ {
+ memset (Orbit->lpTopoData, 0, MAP_WIDTH * MAP_HEIGHT);
+ switch (PLANALGO (PlanDataPtr->Type))
+ {
+ case GAS_GIANT_ALGO:
+ MakeGasGiant (PlanDataPtr->num_faults,
+ Orbit->lpTopoData, &r, PlanDataPtr->fault_depth);
+ break;
+ case TOPO_ALGO:
+ case CRATERED_ALGO:
+ if (PlanDataPtr->num_faults)
+ DeltaTopography (PlanDataPtr->num_faults,
+ Orbit->lpTopoData, &r,
+ PlanDataPtr->fault_depth);
+
+ for (i = 0; i < PlanDataPtr->num_blemishes; ++i)
+ {
+ RECT crater_r;
+ UWORD loword;
+
+ loword = LOWORD (RandomContext_Random (SysGenRNG));
+ switch (HIBYTE (loword) & 31)
+ {
+ case 0:
+ crater_r.extent.width =
+ (LOBYTE (loword) % (MAP_HEIGHT >> 2))
+ + (MAP_HEIGHT >> 2);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ crater_r.extent.width =
+ (LOBYTE (loword) % (MAP_HEIGHT >> 3))
+ + (MAP_HEIGHT >> 3);
+ break;
+ default:
+ crater_r.extent.width =
+ (LOBYTE (loword) % (MAP_HEIGHT >> 4))
+ + 4;
+ break;
+ }
+
+ loword = LOWORD (RandomContext_Random (SysGenRNG));
+ crater_r.extent.height = crater_r.extent.width;
+ crater_r.corner.x = HIBYTE (loword)
+ % (MAP_WIDTH - crater_r.extent.width);
+ crater_r.corner.y = LOBYTE (loword)
+ % (MAP_HEIGHT - crater_r.extent.height);
+ MakeCrater (&crater_r, Orbit->lpTopoData,
+ PlanDataPtr->fault_depth << 2,
+ -(PlanDataPtr->fault_depth << 2),
+ FALSE);
+ }
+
+ if (PLANALGO (PlanDataPtr->Type) == CRATERED_ALGO)
+ DitherMap (Orbit->lpTopoData);
+ ValidateMap (Orbit->lpTopoData);
+ break;
+ }
+ }
+ pSolarSysState->TopoFrame = CaptureDrawable (
+ CreateDrawable (WANT_PIXMAP, (SIZE)MAP_WIDTH,
+ (SIZE)MAP_HEIGHT, 1));
+ pSolarSysState->OrbitalCMap = CaptureColorMap (
+ LoadColorMap (PlanDataPtr->CMapInstance));
+ pSolarSysState->XlatRef = CaptureStringTable (
+ LoadStringTable (PlanDataPtr->XlatTabInstance));
+
+ if (PlanetInfo->SurfaceTemperature > HOT_THRESHOLD)
+ {
+ pSolarSysState->OrbitalCMap = SetAbsColorMapIndex (
+ pSolarSysState->OrbitalCMap, 2);
+ pSolarSysState->XlatRef = SetAbsStringTableIndex (
+ pSolarSysState->XlatRef, 2);
+ }
+ else if (PlanetInfo->SurfaceTemperature > COLD_THRESHOLD)
+ {
+ pSolarSysState->OrbitalCMap = SetAbsColorMapIndex (
+ pSolarSysState->OrbitalCMap, 1);
+ pSolarSysState->XlatRef = SetAbsStringTableIndex (
+ pSolarSysState->XlatRef, 1);
+ }
+ pSolarSysState->XlatPtr = GetStringAddress (pSolarSysState->XlatRef);
+ RenderTopography (pSolarSysState->TopoFrame,
+ Orbit->lpTopoData, MAP_WIDTH, MAP_HEIGHT);
+
+ }
+
+ if (!shielded && PlanetInfo->AtmoDensity != GAS_GIANT_ATMOSPHERE)
+ { // produce 4x scaled topo image for Planetside
+ // for the planets that we can land on
+ SBYTE *pScaledTopo = HMalloc (MAP_WIDTH * 4 * MAP_HEIGHT * 4);
+ if (pScaledTopo)
+ {
+ TopoScale4x (pScaledTopo, Orbit->lpTopoData,
+ PlanDataPtr->num_faults, PlanDataPtr->fault_depth
+ * (PLANALGO (PlanDataPtr->Type) == CRATERED_ALGO ? 2 : 1 ));
+ RenderTopography (Orbit->TopoZoomFrame, pScaledTopo,
+ MAP_WIDTH * 4, MAP_HEIGHT * 4);
+
+ HFree (pScaledTopo);
+ }
+ }
+
+ // Generate a pixel array from the Topography map.
+ // We use this instead of lpTopoData because it needs to be
+ // WAP_WIDTH+SPHERE_SPAN_X wide and we need this method for Earth anyway.
+ // It may be more efficient to build it from lpTopoData instead of the
+ // FRAMPTR though.
+ ReadFramePixelColors (pSolarSysState->TopoFrame, Orbit->TopoColors,
+ MAP_WIDTH + SPHERE_SPAN_X, MAP_HEIGHT);
+ // Extend the width from MAP_WIDTH to MAP_WIDTH+SPHERE_SPAN_X
+ for (y = 0; y < MAP_HEIGHT * (MAP_WIDTH + SPHERE_SPAN_X);
+ y += MAP_WIDTH + SPHERE_SPAN_X)
+ memcpy (Orbit->TopoColors + y + MAP_WIDTH, Orbit->TopoColors + y,
+ SPHERE_SPAN_X * sizeof (Orbit->TopoColors[0]));
+
+ if (PLANALGO (PlanDataPtr->Type) != GAS_GIANT_ALGO)
+ { // convert topo data to a light map, based on relative
+ // map point elevations
+ GenerateLightMap (Orbit->lpTopoData, MAP_WIDTH, MAP_HEIGHT);
+ }
+ else
+ { // gas giants are pretty much flat
+ memset (Orbit->lpTopoData, 0, MAP_WIDTH * MAP_HEIGHT);
+ }
+
+ if (pSolarSysState->pOrbitalDesc->pPrevDesc ==
+ &pSolarSysState->SunDesc[0])
+ { // this is a planet -- get its location
+ loc = pSolarSysState->pOrbitalDesc->location;
+ }
+ else
+ { // this is a moon -- get its planet's location
+ loc = pSolarSysState->pOrbitalDesc->pPrevDesc->location;
+ }
+
+ // Rotating planet sphere initialization
+ GenerateSphereMask (loc);
+ CreateSphereTiltMap (PlanetInfo->AxialTilt);
+ if (shielded)
+ Orbit->ObjectFrame = CreateShieldMask ();
+ InitSphereRotation (1 - 2 * (PlanetInfo->AxialTilt & 1), shielded);
+
+ if (shielded)
+ { // This overwrites pSolarSysState->TopoFrame, so everything that
+ // needs it has to come before
+ ApplyShieldTint ();
+ }
+
+ SetContext (OldContext);
+ DestroyContext (TopoContext);
+}
+
diff --git a/src/uqm/planets/pstarmap.c b/src/uqm/planets/pstarmap.c
new file mode 100644
index 0000000..cd33858
--- /dev/null
+++ b/src/uqm/planets/pstarmap.c
@@ -0,0 +1,1631 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "scan.h"
+#include "../colors.h"
+#include "../controls.h"
+#include "../menustat.h"
+#include "../starmap.h"
+#include "../races.h"
+#include "../gameopt.h"
+#include "../gamestr.h"
+#include "../globdata.h"
+#include "../shipcont.h"
+#include "../units.h"
+#include "../hyper.h"
+#include "../sis.h"
+ // for DrawHyperCoords(), DrawStatusMessage()
+#include "../settings.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../state.h"
+#include "../uqmdebug.h"
+#include "options.h"
+#include "libs/inplib.h"
+#include "libs/strlib.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+#include "libs/memlib.h"
+
+#include <stdlib.h>
+
+
+static POINT cursorLoc;
+static POINT mapOrigin;
+static int zoomLevel;
+static FRAME StarMapFrame;
+
+
+static inline long
+signedDivWithError (long val, long divisor)
+{
+ int invert = 0;
+ if (val < 0)
+ {
+ invert = 1;
+ val = -val;
+ }
+ val = (val + ROUNDING_ERROR (divisor)) / divisor;
+ return invert ? -val : val;
+}
+
+#define MAP_FIT_X ((MAX_X_UNIVERSE + 1) / SIS_SCREEN_WIDTH + 1)
+
+static inline COORD
+universeToDispx (long ux)
+{
+ return signedDivWithError (((ux - mapOrigin.x) << zoomLevel)
+ * SIS_SCREEN_WIDTH, MAX_X_UNIVERSE + MAP_FIT_X)
+ + ((SIS_SCREEN_WIDTH - 1) >> 1);
+}
+#define UNIVERSE_TO_DISPX(ux) universeToDispx(ux)
+
+static inline COORD
+universeToDispy (long uy)
+{
+ return signedDivWithError (((mapOrigin.y - uy) << zoomLevel)
+ * SIS_SCREEN_HEIGHT, MAX_Y_UNIVERSE + 2)
+ + ((SIS_SCREEN_HEIGHT - 1) >> 1);
+}
+#define UNIVERSE_TO_DISPY(uy) universeToDispy(uy)
+
+static inline COORD
+dispxToUniverse (COORD dx)
+{
+ return (((long)(dx - ((SIS_SCREEN_WIDTH - 1) >> 1))
+ * (MAX_X_UNIVERSE + MAP_FIT_X)) >> zoomLevel)
+ / SIS_SCREEN_WIDTH + mapOrigin.x;
+}
+#define DISP_TO_UNIVERSEX(dx) dispxToUniverse(dx)
+
+static inline COORD
+dispyToUniverse (COORD dy)
+{
+ return (((long)(((SIS_SCREEN_HEIGHT - 1) >> 1) - dy)
+ * (MAX_Y_UNIVERSE + 2)) >> zoomLevel)
+ / SIS_SCREEN_HEIGHT + mapOrigin.y;
+}
+#define DISP_TO_UNIVERSEY(dy) dispyToUniverse(dy)
+
+static BOOLEAN transition_pending;
+
+static void
+flashCurrentLocation (POINT *where)
+{
+ static BYTE c = 0;
+ static int val = -2;
+ static POINT universe;
+ static TimeCount NextTime = 0;
+
+ if (where)
+ universe = *where;
+
+ if (GetTimeCounter () >= NextTime)
+ {
+ Color OldColor;
+ CONTEXT OldContext;
+ STAMP s;
+
+ NextTime = GetTimeCounter () + (ONE_SECOND / 16);
+
+ OldContext = SetContext (SpaceContext);
+
+ if (c == 0x00 || c == 0x1A)
+ val = -val;
+ c += val;
+ OldColor = SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (c, c, c), c));
+ s.origin.x = UNIVERSE_TO_DISPX (universe.x);
+ s.origin.y = UNIVERSE_TO_DISPY (universe.y);
+ s.frame = IncFrameIndex (StarMapFrame);
+ DrawFilledStamp (&s);
+ SetContextForeGroundColor (OldColor);
+
+ SetContext (OldContext);
+ }
+}
+
+static void
+DrawCursor (COORD curs_x, COORD curs_y)
+{
+ STAMP s;
+
+ s.origin.x = curs_x;
+ s.origin.y = curs_y;
+ s.frame = StarMapFrame;
+
+ DrawStamp (&s);
+}
+
+static void
+DrawAutoPilot (POINT *pDstPt)
+{
+ SIZE dx, dy,
+ xincr, yincr,
+ xerror, yerror,
+ cycle, delta;
+ POINT pt;
+
+ if (!inHQSpace ())
+ pt = CurStarDescPtr->star_pt;
+ else
+ {
+ pt.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ pt.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+ pt.x = UNIVERSE_TO_DISPX (pt.x);
+ pt.y = UNIVERSE_TO_DISPY (pt.y);
+
+ dx = UNIVERSE_TO_DISPX (pDstPt->x) - pt.x;
+ if (dx >= 0)
+ xincr = 1;
+ else
+ {
+ xincr = -1;
+ dx = -dx;
+ }
+ dx <<= 1;
+
+ dy = UNIVERSE_TO_DISPY (pDstPt->y) - pt.y;
+ if (dy >= 0)
+ yincr = 1;
+ else
+ {
+ yincr = -1;
+ dy = -dy;
+ }
+ dy <<= 1;
+
+ if (dx >= dy)
+ cycle = dx;
+ else
+ cycle = dy;
+ delta = xerror = yerror = cycle >> 1;
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x04, 0x04, 0x1F), 0x01));
+
+ delta &= ~1;
+ while (delta--)
+ {
+ if (!(delta & 1))
+ DrawPoint (&pt);
+
+ if ((xerror -= dx) <= 0)
+ {
+ pt.x += xincr;
+ xerror += cycle;
+ }
+ if ((yerror -= dy) <= 0)
+ {
+ pt.y += yincr;
+ yerror += cycle;
+ }
+ }
+}
+
+static void
+GetSphereRect (FLEET_INFO *FleetPtr, RECT *pRect, RECT *pRepairRect)
+{
+ long diameter;
+
+ diameter = (long)(FleetPtr->known_strength * SPHERE_RADIUS_INCREMENT);
+ pRect->extent.width = UNIVERSE_TO_DISPX (diameter)
+ - UNIVERSE_TO_DISPX (0);
+ if (pRect->extent.width < 0)
+ pRect->extent.width = -pRect->extent.width;
+ else if (pRect->extent.width == 0)
+ pRect->extent.width = 1;
+ pRect->extent.height = UNIVERSE_TO_DISPY (diameter)
+ - UNIVERSE_TO_DISPY (0);
+ if (pRect->extent.height < 0)
+ pRect->extent.height = -pRect->extent.height;
+ else if (pRect->extent.height == 0)
+ pRect->extent.height = 1;
+
+ pRect->corner.x = UNIVERSE_TO_DISPX (FleetPtr->known_loc.x);
+ pRect->corner.y = UNIVERSE_TO_DISPY (FleetPtr->known_loc.y);
+ pRect->corner.x -= pRect->extent.width >> 1;
+ pRect->corner.y -= pRect->extent.height >> 1;
+
+ {
+ TEXT t;
+ STRING locString;
+
+ SetContextFont (TinyFont);
+
+ t.baseline.x = pRect->corner.x + (pRect->extent.width >> 1);
+ t.baseline.y = pRect->corner.y + (pRect->extent.height >> 1) - 1;
+ t.align = ALIGN_CENTER;
+ locString = SetAbsStringTableIndex (FleetPtr->race_strings, 1);
+ t.CharCount = GetStringLength (locString);
+ t.pStr = (UNICODE *)GetStringAddress (locString);
+ TextRect (&t, pRepairRect, NULL);
+
+ if (pRepairRect->corner.x <= 0)
+ pRepairRect->corner.x = 1;
+ else if (pRepairRect->corner.x + pRepairRect->extent.width >=
+ SIS_SCREEN_WIDTH)
+ pRepairRect->corner.x =
+ SIS_SCREEN_WIDTH - pRepairRect->extent.width - 1;
+ if (pRepairRect->corner.y <= 0)
+ pRepairRect->corner.y = 1;
+ else if (pRepairRect->corner.y + pRepairRect->extent.height >=
+ SIS_SCREEN_HEIGHT)
+ pRepairRect->corner.y =
+ SIS_SCREEN_HEIGHT - pRepairRect->extent.height - 1;
+
+ BoxUnion (pRepairRect, pRect, pRepairRect);
+ pRepairRect->extent.width++;
+ pRepairRect->extent.height++;
+ }
+}
+
+static void
+DrawStarMap (COUNT race_update, RECT *pClipRect)
+{
+#define GRID_DELTA 500
+ SIZE i;
+ COUNT which_space;
+ long diameter;
+ RECT r, old_r;
+ POINT oldOrigin = {0, 0};
+ STAMP s;
+ FRAME star_frame;
+ STAR_DESC *SDPtr;
+ BOOLEAN draw_cursor;
+
+ if (pClipRect == (RECT*)-1)
+ {
+ pClipRect = 0;
+ draw_cursor = FALSE;
+ }
+ else
+ {
+ draw_cursor = TRUE;
+ }
+
+ SetContext (SpaceContext);
+ if (pClipRect)
+ {
+ GetContextClipRect (&old_r);
+ pClipRect->corner.x += old_r.corner.x;
+ pClipRect->corner.y += old_r.corner.y;
+ SetContextClipRect (pClipRect);
+ pClipRect->corner.x -= old_r.corner.x;
+ pClipRect->corner.y -= old_r.corner.y;
+ // Offset the origin so that we draw the correct gfx in the cliprect
+ oldOrigin = SetContextOrigin (MAKE_POINT (-pClipRect->corner.x,
+ -pClipRect->corner.y));
+ }
+
+ if (transition_pending)
+ {
+ SetTransitionSource (NULL);
+ }
+ BatchGraphics ();
+
+ which_space = GET_GAME_STATE (ARILOU_SPACE_SIDE);
+
+ if (which_space <= 1)
+ {
+ SDPtr = &star_array[0];
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x07), 0x57));
+ SetContextBackGroundColor (BLACK_COLOR);
+ }
+ else
+ {
+ SDPtr = &star_array[NUM_SOLAR_SYSTEMS + 1];
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x0B, 0x00), 0x6D));
+ SetContextBackGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x08, 0x00), 0x6E));
+ }
+ ClearDrawable ();
+
+ // Draw the fuel range circle
+ if (race_update == 0
+ && which_space < 2
+ && (diameter = (long)GLOBAL_SIS (FuelOnBoard) << 1))
+ {
+ Color OldColor;
+
+ if (!inHQSpace ())
+ r.corner = CurStarDescPtr->star_pt;
+ else
+ {
+ r.corner.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ r.corner.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+
+ // Cap the diameter to a sane range
+ if (diameter > MAX_X_UNIVERSE * 4)
+ diameter = MAX_X_UNIVERSE * 4;
+
+ r.extent.width = UNIVERSE_TO_DISPX (diameter)
+ - UNIVERSE_TO_DISPX (0);
+ if (r.extent.width < 0)
+ r.extent.width = -r.extent.width;
+ r.extent.height = UNIVERSE_TO_DISPY (diameter)
+ - UNIVERSE_TO_DISPY (0);
+ if (r.extent.height < 0)
+ r.extent.height = -r.extent.height;
+
+ r.corner.x = UNIVERSE_TO_DISPX (r.corner.x)
+ - (r.extent.width >> 1);
+ r.corner.y = UNIVERSE_TO_DISPY (r.corner.y)
+ - (r.extent.height >> 1);
+
+ OldColor = SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x03, 0x03, 0x03), 0x22));
+ DrawFilledOval (&r);
+ SetContextForeGroundColor (OldColor);
+ }
+
+ for (i = MAX_Y_UNIVERSE + 1; i >= 0; i -= GRID_DELTA)
+ {
+ SIZE j;
+
+ r.corner.x = UNIVERSE_TO_DISPX (0);
+ r.corner.y = UNIVERSE_TO_DISPY (i);
+ r.extent.width = SIS_SCREEN_WIDTH << zoomLevel;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+
+ r.corner.y = UNIVERSE_TO_DISPY (MAX_Y_UNIVERSE + 1);
+ r.extent.width = 1;
+ r.extent.height = SIS_SCREEN_HEIGHT << zoomLevel;
+ for (j = MAX_X_UNIVERSE + 1; j >= 0; j -= GRID_DELTA)
+ {
+ r.corner.x = UNIVERSE_TO_DISPX (j);
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ star_frame = SetRelFrameIndex (StarMapFrame, 2);
+ if (which_space <= 1)
+ {
+ COUNT index;
+ HFLEETINFO hStarShip, hNextShip;
+ static const Color race_colors[] =
+ {
+ RACE_COLORS
+ };
+
+ for (index = 0,
+ hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip != 0; ++index, hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if (FleetPtr->known_strength)
+ {
+ RECT repair_r;
+
+ GetSphereRect (FleetPtr, &r, &repair_r);
+ if (r.corner.x < SIS_SCREEN_WIDTH
+ && r.corner.y < SIS_SCREEN_HEIGHT
+ && r.corner.x + r.extent.width > 0
+ && r.corner.y + r.extent.height > 0
+ && (pClipRect == 0
+ || (repair_r.corner.x < pClipRect->corner.x + pClipRect->extent.width
+ && repair_r.corner.y < pClipRect->corner.y + pClipRect->extent.height
+ && repair_r.corner.x + repair_r.extent.width > pClipRect->corner.x
+ && repair_r.corner.y + repair_r.extent.height > pClipRect->corner.y)))
+ {
+ Color c;
+ TEXT t;
+ STRING locString;
+
+ c = race_colors[index];
+ if (index + 1 == race_update)
+ SetContextForeGroundColor (WHITE_COLOR);
+ else
+ SetContextForeGroundColor (c);
+ DrawOval (&r, 0);
+
+ SetContextFont (TinyFont);
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + (r.extent.height >> 1) - 1;
+ t.align = ALIGN_CENTER;
+ locString = SetAbsStringTableIndex (
+ FleetPtr->race_strings, 1);
+ t.CharCount = GetStringLength (locString);
+ t.pStr = (UNICODE *)GetStringAddress (locString);
+ TextRect (&t, &r, NULL);
+
+ if (r.corner.x <= 0)
+ t.baseline.x -= r.corner.x - 1;
+ else if (r.corner.x + r.extent.width >= SIS_SCREEN_WIDTH)
+ t.baseline.x -= (r.corner.x + r.extent.width)
+ - SIS_SCREEN_WIDTH + 1;
+ if (r.corner.y <= 0)
+ t.baseline.y -= r.corner.y - 1;
+ else if (r.corner.y + r.extent.height >= SIS_SCREEN_HEIGHT)
+ t.baseline.y -= (r.corner.y + r.extent.height)
+ - SIS_SCREEN_HEIGHT + 1;
+
+ // The text color is slightly lighter than the color of
+ // the SoI.
+ c.r = (c.r >= 0xff - CC5TO8 (0x03)) ?
+ 0xff : c.r + CC5TO8 (0x03);
+ c.g = (c.g >= 0xff - CC5TO8 (0x03)) ?
+ 0xff : c.g + CC5TO8 (0x03);
+ c.b = (c.b >= 0xff - CC5TO8 (0x03)) ?
+ 0xff : c.b + CC5TO8 (0x03);
+
+ SetContextForeGroundColor (c);
+ font_DrawText (&t);
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+ }
+
+ do
+ {
+ BYTE star_type;
+
+ star_type = SDPtr->Type;
+
+ s.origin.x = UNIVERSE_TO_DISPX (SDPtr->star_pt.x);
+ s.origin.y = UNIVERSE_TO_DISPY (SDPtr->star_pt.y);
+ if (which_space <= 1)
+ s.frame = SetRelFrameIndex (star_frame,
+ STAR_TYPE (star_type)
+ * NUM_STAR_COLORS
+ + STAR_COLOR (star_type));
+ else if (SDPtr->star_pt.x == ARILOU_HOME_X
+ && SDPtr->star_pt.y == ARILOU_HOME_Y)
+ s.frame = SetRelFrameIndex (star_frame,
+ SUPER_GIANT_STAR * NUM_STAR_COLORS + GREEN_BODY);
+ else
+ s.frame = SetRelFrameIndex (star_frame,
+ GIANT_STAR * NUM_STAR_COLORS + GREEN_BODY);
+ DrawStamp (&s);
+
+ ++SDPtr;
+ } while (SDPtr->star_pt.x <= MAX_X_UNIVERSE
+ && SDPtr->star_pt.y <= MAX_Y_UNIVERSE);
+
+ if (GET_GAME_STATE (ARILOU_SPACE))
+ {
+ if (which_space <= 1)
+ {
+ s.origin.x = UNIVERSE_TO_DISPX (ARILOU_SPACE_X);
+ s.origin.y = UNIVERSE_TO_DISPY (ARILOU_SPACE_Y);
+ }
+ else
+ {
+ s.origin.x = UNIVERSE_TO_DISPX (QUASI_SPACE_X);
+ s.origin.y = UNIVERSE_TO_DISPY (QUASI_SPACE_Y);
+ }
+ s.frame = SetRelFrameIndex (star_frame,
+ GIANT_STAR * NUM_STAR_COLORS + GREEN_BODY);
+ DrawStamp (&s);
+ }
+
+ if (race_update == 0
+ && GLOBAL (autopilot.x) != ~0
+ && GLOBAL (autopilot.y) != ~0)
+ DrawAutoPilot (&GLOBAL (autopilot));
+
+ if (transition_pending)
+ {
+ GetContextClipRect (&r);
+ ScreenTransition (3, &r);
+ transition_pending = FALSE;
+ }
+
+ UnbatchGraphics ();
+
+ if (pClipRect)
+ {
+ SetContextClipRect (&old_r);
+ SetContextOrigin (oldOrigin);
+ }
+
+ if (race_update == 0)
+ {
+ if (draw_cursor)
+ {
+ GetContextClipRect (&r);
+ LoadIntoExtraScreen (&r);
+ DrawCursor (UNIVERSE_TO_DISPX (cursorLoc.x),
+ UNIVERSE_TO_DISPY (cursorLoc.y));
+ }
+ }
+}
+
+static void
+EraseCursor (COORD curs_x, COORD curs_y)
+{
+ RECT r;
+
+ GetFrameRect (StarMapFrame, &r);
+
+ if ((r.corner.x += curs_x) < 0)
+ {
+ r.extent.width += r.corner.x;
+ r.corner.x = 0;
+ }
+ else if (r.corner.x + r.extent.width >= SIS_SCREEN_WIDTH)
+ r.extent.width = SIS_SCREEN_WIDTH - r.corner.x;
+ if ((r.corner.y += curs_y) < 0)
+ {
+ r.extent.height += r.corner.y;
+ r.corner.y = 0;
+ }
+ else if (r.corner.y + r.extent.height >= SIS_SCREEN_HEIGHT)
+ r.extent.height = SIS_SCREEN_HEIGHT - r.corner.y;
+
+#ifndef OLD
+ RepairBackRect (&r);
+#else /* NEW */
+ r.extent.height += r.corner.y & 1;
+ r.corner.y &= ~1;
+ DrawStarMap (0, &r);
+#endif /* OLD */
+}
+
+static void
+ZoomStarMap (SIZE dir)
+{
+#define MAX_ZOOM_SHIFT 4
+ if (dir > 0)
+ {
+ if (zoomLevel < MAX_ZOOM_SHIFT)
+ {
+ ++zoomLevel;
+ mapOrigin = cursorLoc;
+
+ DrawStarMap (0, NULL);
+ SleepThread (ONE_SECOND / 8);
+ }
+ }
+ else if (dir < 0)
+ {
+ if (zoomLevel > 0)
+ {
+ if (zoomLevel > 1)
+ mapOrigin = cursorLoc;
+ else
+ {
+ mapOrigin.x = MAX_X_UNIVERSE >> 1;
+ mapOrigin.y = MAX_Y_UNIVERSE >> 1;
+ }
+ --zoomLevel;
+
+ DrawStarMap (0, NULL);
+ SleepThread (ONE_SECOND / 8);
+ }
+ }
+}
+
+static void
+UpdateCursorLocation (int sx, int sy, const POINT *newpt)
+{
+ STAMP s;
+ POINT pt;
+
+ pt.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ pt.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+
+ if (newpt)
+ { // absolute move
+ sx = sy = 0;
+ s.origin.x = UNIVERSE_TO_DISPX (newpt->x);
+ s.origin.y = UNIVERSE_TO_DISPY (newpt->y);
+ cursorLoc = *newpt;
+ }
+ else
+ { // incremental move
+ s.origin.x = pt.x + sx;
+ s.origin.y = pt.y + sy;
+ }
+
+ if (sx)
+ {
+ cursorLoc.x = DISP_TO_UNIVERSEX (s.origin.x) - sx;
+ while (UNIVERSE_TO_DISPX (cursorLoc.x) == pt.x)
+ cursorLoc.x += sx;
+
+ if (cursorLoc.x < 0)
+ cursorLoc.x = 0;
+ else if (cursorLoc.x > MAX_X_UNIVERSE)
+ cursorLoc.x = MAX_X_UNIVERSE;
+
+ s.origin.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ }
+
+ if (sy)
+ {
+ cursorLoc.y = DISP_TO_UNIVERSEY (s.origin.y) + sy;
+ while (UNIVERSE_TO_DISPY (cursorLoc.y) == pt.y)
+ cursorLoc.y -= sy;
+
+ if (cursorLoc.y < 0)
+ cursorLoc.y = 0;
+ else if (cursorLoc.y > MAX_Y_UNIVERSE)
+ cursorLoc.y = MAX_Y_UNIVERSE;
+
+ s.origin.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+ }
+
+ if (s.origin.x < 0 || s.origin.y < 0
+ || s.origin.x >= SIS_SCREEN_WIDTH
+ || s.origin.y >= SIS_SCREEN_HEIGHT)
+ {
+ mapOrigin = cursorLoc;
+ DrawStarMap (0, NULL);
+
+ s.origin.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ s.origin.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+ }
+ else
+ {
+ EraseCursor (pt.x, pt.y);
+ // ClearDrawable ();
+ DrawCursor (s.origin.x, s.origin.y);
+ }
+}
+
+#define CURSOR_INFO_BUFSIZE 256
+
+static void
+UpdateCursorInfo (UNICODE *prevbuf)
+{
+ UNICODE buf[CURSOR_INFO_BUFSIZE] = "";
+ POINT pt;
+ STAR_DESC *SDPtr;
+ STAR_DESC *BestSDPtr;
+
+ pt.x = UNIVERSE_TO_DISPX (cursorLoc.x);
+ pt.y = UNIVERSE_TO_DISPY (cursorLoc.y);
+
+ SDPtr = BestSDPtr = 0;
+ while ((SDPtr = FindStar (SDPtr, &cursorLoc, 75, 75)))
+ {
+ if (UNIVERSE_TO_DISPX (SDPtr->star_pt.x) == pt.x
+ && UNIVERSE_TO_DISPY (SDPtr->star_pt.y) == pt.y
+ && (BestSDPtr == 0
+ || STAR_TYPE (SDPtr->Type) >= STAR_TYPE (BestSDPtr->Type)))
+ BestSDPtr = SDPtr;
+ }
+
+ if (BestSDPtr)
+ {
+ cursorLoc = BestSDPtr->star_pt;
+ GetClusterName (BestSDPtr, buf);
+ }
+ else
+ { // No star found. Reset the coordinates to the cursor's location
+ cursorLoc.x = DISP_TO_UNIVERSEX (pt.x);
+ if (cursorLoc.x < 0)
+ cursorLoc.x = 0;
+ else if (cursorLoc.x > MAX_X_UNIVERSE)
+ cursorLoc.x = MAX_X_UNIVERSE;
+ cursorLoc.y = DISP_TO_UNIVERSEY (pt.y);
+ if (cursorLoc.y < 0)
+ cursorLoc.y = 0;
+ else if (cursorLoc.y > MAX_Y_UNIVERSE)
+ cursorLoc.y = MAX_Y_UNIVERSE;
+ }
+
+ if (GET_GAME_STATE (ARILOU_SPACE))
+ {
+ POINT ari_pt;
+
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ ari_pt.x = ARILOU_SPACE_X;
+ ari_pt.y = ARILOU_SPACE_Y;
+ }
+ else
+ {
+ ari_pt.x = QUASI_SPACE_X;
+ ari_pt.y = QUASI_SPACE_Y;
+ }
+
+ if (UNIVERSE_TO_DISPX (ari_pt.x) == pt.x
+ && UNIVERSE_TO_DISPY (ari_pt.y) == pt.y)
+ {
+ cursorLoc = ari_pt;
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (STAR_STRING_BASE + 132));
+ }
+ }
+
+ DrawHyperCoords (cursorLoc);
+ if (strcmp (buf, prevbuf) != 0)
+ {
+ strcpy (prevbuf, buf);
+ DrawSISMessage (buf);
+ }
+}
+
+static void
+UpdateFuelRequirement (void)
+{
+ UNICODE buf[80];
+ COUNT fuel_required;
+ DWORD f;
+ POINT pt;
+
+ if (!inHQSpace ())
+ pt = CurStarDescPtr->star_pt;
+ else
+ {
+ pt.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ pt.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+ pt.x -= cursorLoc.x;
+ pt.y -= cursorLoc.y;
+
+ f = (DWORD)((long)pt.x * pt.x + (long)pt.y * pt.y);
+ if (f == 0 || GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1)
+ fuel_required = 0;
+ else
+ fuel_required = square_root (f) + (FUEL_TANK_SCALE / 20);
+
+ sprintf (buf, "%s %u.%u",
+ GAME_STRING (NAVIGATION_STRING_BASE + 4),
+ fuel_required / FUEL_TANK_SCALE,
+ (fuel_required % FUEL_TANK_SCALE) / 10);
+
+ DrawStatusMessage (buf);
+}
+
+#define STAR_SEARCH_BUFSIZE 256
+
+typedef struct starsearch_state
+{
+ // TODO: pMS field is probably not needed anymore
+ MENU_STATE *pMS;
+ UNICODE Text[STAR_SEARCH_BUFSIZE];
+ UNICODE LastText[STAR_SEARCH_BUFSIZE];
+ DWORD LastChangeTime;
+ int FirstIndex;
+ int CurIndex;
+ int LastIndex;
+ BOOLEAN SingleClust;
+ BOOLEAN SingleMatch;
+ UNICODE Buffer[STAR_SEARCH_BUFSIZE];
+ const UNICODE *Prefix;
+ const UNICODE *Cluster;
+ int PrefixLen;
+ int ClusterLen;
+ int ClusterPos;
+ int SortedStars[NUM_SOLAR_SYSTEMS];
+} STAR_SEARCH_STATE;
+
+static int
+compStarName (const void *ptr1, const void *ptr2)
+{
+ int index1;
+ int index2;
+
+ index1 = *(const int *) ptr1;
+ index2 = *(const int *) ptr2;
+ if (star_array[index1].Postfix != star_array[index2].Postfix)
+ {
+ return utf8StringCompare (GAME_STRING (star_array[index1].Postfix),
+ GAME_STRING (star_array[index2].Postfix));
+ }
+
+ if (star_array[index1].Prefix < star_array[index2].Prefix)
+ return -1;
+
+ if (star_array[index1].Prefix > star_array[index2].Prefix)
+ return 1;
+
+ return 0;
+}
+
+static void
+SortStarsOnName (STAR_SEARCH_STATE *pSS)
+{
+ int i;
+ int *sorted = pSS->SortedStars;
+
+ for (i = 0; i < NUM_SOLAR_SYSTEMS; i++)
+ sorted[i] = i;
+
+ qsort (sorted, NUM_SOLAR_SYSTEMS, sizeof (int), compStarName);
+}
+
+static void
+SplitStarName (STAR_SEARCH_STATE *pSS)
+{
+ UNICODE *buf = pSS->Buffer;
+ UNICODE *next;
+ UNICODE *sep = NULL;
+
+ pSS->Prefix = 0;
+ pSS->PrefixLen = 0;
+ pSS->Cluster = 0;
+ pSS->ClusterLen = 0;
+ pSS->ClusterPos = 0;
+
+ // skip leading space
+ for (next = buf; *next != '\0' &&
+ getCharFromString ((const UNICODE **)&next) == ' ';
+ buf = next)
+ ;
+ if (*buf == '\0')
+ { // no text
+ return;
+ }
+
+ pSS->Prefix = buf;
+
+ // See if player gave a prefix
+ for (buf = next; *next != '\0' &&
+ getCharFromString ((const UNICODE **)&next) != ' ';
+ buf = next)
+ ;
+ if (*buf != '\0')
+ { // found possibly separating ' '
+ sep = buf;
+ // skip separating space
+ for (buf = next; *next != '\0' &&
+ getCharFromString ((const UNICODE **)&next) == ' ';
+ buf = next)
+ ;
+ }
+
+ if (*buf == '\0')
+ { // reached the end -- cluster only
+ pSS->Cluster = pSS->Prefix;
+ pSS->ClusterLen = utf8StringCount (pSS->Cluster);
+ pSS->ClusterPos = utf8StringCountN (pSS->Buffer, pSS->Cluster);
+ pSS->Prefix = 0;
+ return;
+ }
+
+ // consider the rest cluster name (whatever there is)
+ pSS->Cluster = buf;
+ pSS->ClusterLen = utf8StringCount (pSS->Cluster);
+ pSS->ClusterPos = utf8StringCountN (pSS->Buffer, pSS->Cluster);
+ *sep = '\0'; // split
+ pSS->PrefixLen = utf8StringCount (pSS->Prefix);
+}
+
+static inline int
+SkipStarCluster (int *sortedStars, int istar)
+{
+ int Postfix = star_array[sortedStars[istar]].Postfix;
+
+ for (++istar; istar < NUM_SOLAR_SYSTEMS &&
+ star_array[sortedStars[istar]].Postfix == Postfix;
+ ++istar)
+ ;
+ return istar;
+}
+
+static int
+FindNextStarIndex (STAR_SEARCH_STATE *pSS, int from, BOOLEAN WithinClust)
+{
+ int i;
+
+ if (!pSS->Cluster)
+ return -1; // nothing to search for
+
+ for (i = from; i < NUM_SOLAR_SYSTEMS; ++i)
+ {
+ STAR_DESC *SDPtr = &star_array[pSS->SortedStars[i]];
+ UNICODE FullName[STAR_SEARCH_BUFSIZE];
+ UNICODE *ClusterName = GAME_STRING (SDPtr->Postfix);
+ const UNICODE *sptr;
+ const UNICODE *dptr;
+ int dlen;
+ int c;
+
+ dlen = utf8StringCount (ClusterName);
+ if (pSS->ClusterLen > dlen)
+ { // no match, skip the rest of cluster
+ i = SkipStarCluster (pSS->SortedStars, i) - 1;
+ continue;
+ }
+
+ for (c = 0, sptr = pSS->Cluster, dptr = ClusterName;
+ c < pSS->ClusterLen; ++c)
+ {
+ UniChar sc = getCharFromString (&sptr);
+ UniChar dc = getCharFromString (&dptr);
+
+ if (UniChar_toUpper (sc) != UniChar_toUpper (dc))
+ break;
+ }
+
+ if (c < pSS->ClusterLen)
+ { // no match here, skip the rest of cluster
+ i = SkipStarCluster (pSS->SortedStars, i) - 1;
+ continue;
+ }
+
+ if (pSS->Prefix && !SDPtr->Prefix)
+ // we were given a prefix but found a singular star;
+ // that is a no match
+ continue;
+
+ if (WithinClust)
+ // searching within clusters; any prefix is a match
+ break;
+
+ if (!pSS->Prefix)
+ { // searching for cluster name only
+ // return only the first stars in a cluster
+ if (i == 0 || SDPtr->Postfix !=
+ star_array[pSS->SortedStars[i - 1]].Postfix)
+ { // found one
+ break;
+ }
+ else
+ { // another star in the same cluster, skip cluster
+ i = SkipStarCluster (pSS->SortedStars, i) - 1;
+ continue;
+ }
+ }
+
+ // check prefix
+ GetClusterName (SDPtr, FullName);
+ dlen = utf8StringCount (FullName);
+ if (pSS->PrefixLen > dlen)
+ continue;
+
+ for (c = 0, sptr = pSS->Prefix, dptr = FullName;
+ c < pSS->PrefixLen; ++c)
+ {
+ UniChar sc = getCharFromString (&sptr);
+ UniChar dc = getCharFromString (&dptr);
+
+ if (UniChar_toUpper (sc) != UniChar_toUpper (dc))
+ break;
+ }
+
+ if (c >= pSS->PrefixLen)
+ break; // found one
+ }
+
+ return (i < NUM_SOLAR_SYSTEMS) ? i : -1;
+}
+
+static void
+DrawMatchedStarName (TEXTENTRY_STATE *pTES)
+{
+ STAR_SEARCH_STATE *pSS = (STAR_SEARCH_STATE *) pTES->CbParam;
+ UNICODE buf[STAR_SEARCH_BUFSIZE] = "";
+ SIZE ExPos = 0;
+ SIZE CurPos = -1;
+ STAR_DESC *SDPtr = &star_array[pSS->SortedStars[pSS->CurIndex]];
+ COUNT flags;
+
+ if (pSS->SingleClust || pSS->SingleMatch)
+ { // draw full star name
+ GetClusterName (SDPtr, buf);
+ ExPos = -1;
+ flags = DSME_SETFR;
+ }
+ else
+ { // draw substring match
+ UNICODE *pstr = buf;
+
+ strcpy (pstr, pSS->Text);
+ ExPos = pSS->ClusterPos;
+ pstr = skipUTF8Chars (pstr, pSS->ClusterPos);
+
+ strcpy (pstr, GAME_STRING (SDPtr->Postfix));
+ ExPos += pSS->ClusterLen;
+ CurPos = pTES->CursorPos;
+
+ flags = DSME_CLEARFR;
+ if (pTES->JoystickMode)
+ flags |= DSME_BLOCKCUR;
+ }
+
+ DrawSISMessageEx (buf, CurPos, ExPos, flags);
+ DrawHyperCoords (cursorLoc);
+}
+
+static void
+MatchNextStar (STAR_SEARCH_STATE *pSS, BOOLEAN Reset)
+{
+ if (Reset)
+ pSS->FirstIndex = -1; // reset cache
+
+ if (pSS->FirstIndex < 0)
+ { // first time after changes
+ pSS->CurIndex = -1;
+ pSS->LastIndex = -1;
+ pSS->SingleClust = FALSE;
+ pSS->SingleMatch = FALSE;
+ strcpy (pSS->Buffer, pSS->Text);
+ SplitStarName (pSS);
+ }
+
+ pSS->CurIndex = FindNextStarIndex (pSS, pSS->CurIndex + 1,
+ pSS->SingleClust);
+ if (pSS->FirstIndex < 0) // first search
+ pSS->FirstIndex = pSS->CurIndex;
+
+ if (pSS->CurIndex >= 0)
+ { // remember as last (searching forward-only)
+ pSS->LastIndex = pSS->CurIndex;
+ }
+ else
+ { // wrap around
+ pSS->CurIndex = pSS->FirstIndex;
+
+ if (pSS->FirstIndex == pSS->LastIndex && pSS->FirstIndex != -1)
+ {
+ if (!pSS->Prefix)
+ { // only one cluster matching
+ pSS->SingleClust = TRUE;
+ }
+ else
+ { // exact match
+ pSS->SingleMatch = TRUE;
+ }
+ }
+ }
+}
+
+static BOOLEAN
+OnStarNameChange (TEXTENTRY_STATE *pTES)
+{
+ STAR_SEARCH_STATE *pSS = (STAR_SEARCH_STATE *) pTES->CbParam;
+ COUNT flags;
+ BOOLEAN ret = TRUE;
+
+ if (strcmp (pSS->Text, pSS->LastText) != 0)
+ { // string changed
+ pSS->LastChangeTime = GetTimeCounter ();
+ strcpy (pSS->LastText, pSS->Text);
+
+ // reset the search
+ MatchNextStar (pSS, TRUE);
+ }
+
+ if (pSS->CurIndex < 0)
+ { // nothing found
+ if (pSS->Text[0] == '\0')
+ flags = DSME_SETFR;
+ else
+ flags = DSME_CLEARFR;
+ if (pTES->JoystickMode)
+ flags |= DSME_BLOCKCUR;
+
+ ret = DrawSISMessageEx (pSS->Text, pTES->CursorPos, -1, flags);
+ }
+ else
+ {
+ STAR_DESC *SDPtr;
+
+ // move the cursor to the found star
+ SDPtr = &star_array[pSS->SortedStars[pSS->CurIndex]];
+ UpdateCursorLocation (0, 0, &SDPtr->star_pt);
+
+ DrawMatchedStarName (pTES);
+ UpdateFuelRequirement ();
+ }
+
+ return ret;
+}
+
+static BOOLEAN
+OnStarNameFrame (TEXTENTRY_STATE *pTES)
+{
+ STAR_SEARCH_STATE *pSS = (STAR_SEARCH_STATE *) pTES->CbParam;
+
+ if (PulsedInputState.menu[KEY_MENU_NEXT])
+ { // search for next match
+ STAR_DESC *SDPtr;
+
+ MatchNextStar (pSS, FALSE);
+
+ if (pSS->CurIndex < 0)
+ { // nothing found
+ if (PulsedInputState.menu[KEY_MENU_NEXT])
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ // move the cursor to the found star
+ SDPtr = &star_array[pSS->SortedStars[pSS->CurIndex]];
+ UpdateCursorLocation (0, 0, &SDPtr->star_pt);
+
+ DrawMatchedStarName (pTES);
+ UpdateFuelRequirement ();
+ }
+
+ flashCurrentLocation (NULL);
+
+ SleepThread (ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static BOOLEAN
+DoStarSearch (MENU_STATE *pMS)
+{
+ TEXTENTRY_STATE tes;
+ STAR_SEARCH_STATE *pss;
+ BOOLEAN success;
+
+ pss = HMalloc (sizeof (*pss));
+ if (!pss)
+ return FALSE;
+
+ DrawSISMessageEx ("", 0, 0, DSME_SETFR);
+
+ pss->pMS = pMS;
+ pss->LastChangeTime = 0;
+ pss->Text[0] = '\0';
+ pss->LastText[0] = '\0';
+ pss->FirstIndex = -1;
+ SortStarsOnName (pss);
+
+ // text entry setup
+ tes.Initialized = FALSE;
+ tes.BaseStr = pss->Text;
+ tes.MaxSize = sizeof (pss->Text);
+ tes.CursorPos = 0;
+ tes.CbParam = pss;
+ tes.ChangeCallback = OnStarNameChange;
+ tes.FrameCallback = OnStarNameFrame;
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetDefaultMenuRepeatDelay ();
+ success = DoTextEntry (&tes);
+
+ DrawSISMessageEx (pss->Text, -1, -1, DSME_CLEARFR);
+
+ HFree (pss);
+
+ return success;
+}
+
+static BOOLEAN
+DoMoveCursor (MENU_STATE *pMS)
+{
+#define MIN_ACCEL_DELAY (ONE_SECOND / 60)
+#define MAX_ACCEL_DELAY (ONE_SECOND / 8)
+#define STEP_ACCEL_DELAY (ONE_SECOND / 120)
+ static UNICODE last_buf[CURSOR_INFO_BUFSIZE];
+ DWORD TimeIn = GetTimeCounter ();
+
+ if (!pMS->Initialized)
+ {
+ POINT universe;
+
+ pMS->Initialized = TRUE;
+ pMS->InputFunc = DoMoveCursor;
+
+ if (!inHQSpace ())
+ universe = CurStarDescPtr->star_pt;
+ else
+ {
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+ flashCurrentLocation (&universe);
+
+ last_buf[0] = '\0';
+ UpdateCursorInfo (last_buf);
+ UpdateFuelRequirement ();
+
+ return TRUE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ GLOBAL (autopilot) = cursorLoc;
+#ifdef DEBUG
+ if (instantMove)
+ {
+ PlayMenuSound (MENU_SOUND_INVOKED);
+
+ if (inHQSpace ())
+ {
+ // Move to the new location immediately.
+ doInstantMove ();
+ }
+ else if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
+ {
+ // We're in a solar system; exit it.
+ GLOBAL (CurrentActivity) |= END_INTERPLANETARY;
+
+ // Set a hook to move to the new location:
+ debugHook = doInstantMove;
+ }
+
+ return FALSE;
+ }
+#endif
+ DrawStarMap (0, NULL);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SEARCH])
+ {
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ { // HyperSpace search
+ POINT oldpt = cursorLoc;
+
+ if (!DoStarSearch (pMS))
+ { // search failed or canceled - return cursor
+ UpdateCursorLocation (0, 0, &oldpt);
+ }
+ // make sure cmp fails
+ strcpy (last_buf, " <random garbage> ");
+ UpdateCursorInfo (last_buf);
+ UpdateFuelRequirement ();
+
+ SetMenuRepeatDelay (MIN_ACCEL_DELAY, MAX_ACCEL_DELAY,
+ STEP_ACCEL_DELAY, TRUE);
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ }
+ else
+ { // no search in QuasiSpace
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ else
+ {
+ SBYTE sx, sy;
+ SIZE ZoomIn, ZoomOut;
+
+ ZoomIn = ZoomOut = 0;
+ if (PulsedInputState.menu[KEY_MENU_ZOOM_IN])
+ ZoomIn = 1;
+ else if (PulsedInputState.menu[KEY_MENU_ZOOM_OUT])
+ ZoomOut = 1;
+
+ ZoomStarMap (ZoomIn - ZoomOut);
+
+ sx = sy = 0;
+ if (PulsedInputState.menu[KEY_MENU_LEFT]) sx = -1;
+ if (PulsedInputState.menu[KEY_MENU_RIGHT]) sx = 1;
+ if (PulsedInputState.menu[KEY_MENU_UP]) sy = -1;
+ if (PulsedInputState.menu[KEY_MENU_DOWN]) sy = 1;
+
+ if (sx != 0 || sy != 0)
+ {
+ UpdateCursorLocation (sx, sy, NULL);
+ UpdateCursorInfo (last_buf);
+ UpdateFuelRequirement ();
+ }
+
+ SleepThreadUntil (TimeIn + MIN_ACCEL_DELAY);
+ }
+
+ flashCurrentLocation (NULL);
+
+ return !(GLOBAL (CurrentActivity) & CHECK_ABORT);
+}
+
+static void
+RepairMap (COUNT update_race, RECT *pLastRect, RECT *pNextRect)
+{
+ RECT r;
+
+ /* make a rect big enough for text */
+ r.extent.width = 50;
+ r.corner.x = (pNextRect->corner.x + (pNextRect->extent.width >> 1))
+ - (r.extent.width >> 1);
+ if (r.corner.x < 0)
+ r.corner.x = 0;
+ else if (r.corner.x + r.extent.width >= SIS_SCREEN_WIDTH)
+ r.corner.x = SIS_SCREEN_WIDTH - r.extent.width;
+ r.extent.height = 9;
+ r.corner.y = (pNextRect->corner.y + (pNextRect->extent.height >> 1))
+ - r.extent.height;
+ if (r.corner.y < 0)
+ r.corner.y = 0;
+ else if (r.corner.y + r.extent.height >= SIS_SCREEN_HEIGHT)
+ r.corner.y = SIS_SCREEN_HEIGHT - r.extent.height;
+ BoxUnion (pLastRect, &r, &r);
+ BoxUnion (pNextRect, &r, &r);
+ *pLastRect = *pNextRect;
+
+ if (r.corner.x < 0)
+ {
+ r.extent.width += r.corner.x;
+ r.corner.x = 0;
+ }
+ if (r.corner.x + r.extent.width > SIS_SCREEN_WIDTH)
+ r.extent.width = SIS_SCREEN_WIDTH - r.corner.x;
+ if (r.corner.y < 0)
+ {
+ r.extent.height += r.corner.y;
+ r.corner.y = 0;
+ }
+ if (r.corner.y + r.extent.height > SIS_SCREEN_HEIGHT)
+ r.extent.height = SIS_SCREEN_HEIGHT - r.corner.y;
+
+ r.extent.height += r.corner.y & 1;
+ r.corner.y &= ~1;
+
+ DrawStarMap (update_race, &r);
+}
+
+static void
+UpdateMap (void)
+{
+ BYTE ButtonState, VisibleChange;
+ BOOLEAN MapDrawn, Interrupted;
+ COUNT index;
+ HFLEETINFO hStarShip, hNextShip;
+
+ FlushInput ();
+ ButtonState = 1; /* assume a button down */
+
+ MapDrawn = Interrupted = FALSE;
+ for (index = 1,
+ hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; ++index, hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if (ButtonState)
+ {
+ if (!AnyButtonPress (TRUE))
+ ButtonState = 0;
+ }
+ else if ((Interrupted = (BOOLEAN)(
+ Interrupted || AnyButtonPress (TRUE)
+ )))
+ MapDrawn = TRUE;
+
+ if (FleetPtr->known_strength)
+ {
+ SIZE dx, dy, delta;
+ RECT r, last_r, temp_r0, temp_r1;
+
+ dx = FleetPtr->loc.x - FleetPtr->known_loc.x;
+ dy = FleetPtr->loc.y - FleetPtr->known_loc.y;
+ if (dx || dy)
+ {
+ SIZE xincr, yincr,
+ xerror, yerror,
+ cycle;
+
+ if (dx >= 0)
+ xincr = 1;
+ else
+ {
+ xincr = -1;
+ dx = -dx;
+ }
+ dx <<= 1;
+
+ if (dy >= 0)
+ yincr = 1;
+ else
+ {
+ yincr = -1;
+ dy = -dy;
+ }
+ dy <<= 1;
+
+ if (dx >= dy)
+ cycle = dx;
+ else
+ cycle = dy;
+ delta = xerror = yerror = cycle >> 1;
+
+ if (!MapDrawn)
+ {
+ DrawStarMap ((COUNT)~0, NULL);
+ MapDrawn = TRUE;
+ }
+
+ GetSphereRect (FleetPtr, &temp_r0, &last_r);
+ ++last_r.extent.width;
+ ++last_r.extent.height;
+ VisibleChange = FALSE;
+ do
+ {
+ do
+ {
+ if ((xerror -= dx) <= 0)
+ {
+ FleetPtr->known_loc.x += xincr;
+ xerror += cycle;
+ }
+ if ((yerror -= dy) <= 0)
+ {
+ FleetPtr->known_loc.y += yincr;
+ yerror += cycle;
+ }
+ GetSphereRect (FleetPtr, &temp_r1, &r);
+ } while (delta--
+ && ((delta & 0x1F)
+ || (temp_r0.corner.x == temp_r1.corner.x
+ && temp_r0.corner.y == temp_r1.corner.y)));
+
+ if (ButtonState)
+ {
+ if (!AnyButtonPress (TRUE))
+ ButtonState = 0;
+ }
+ else if ((Interrupted = (BOOLEAN)(
+ Interrupted || AnyButtonPress (TRUE)
+ )))
+ {
+ MapDrawn = TRUE;
+ goto DoneSphereMove;
+ }
+
+ ++r.extent.width;
+ ++r.extent.height;
+ if (temp_r0.corner.x != temp_r1.corner.x
+ || temp_r0.corner.y != temp_r1.corner.y)
+ {
+ VisibleChange = TRUE;
+ RepairMap (index, &last_r, &r);
+ }
+ } while (delta >= 0);
+ if (VisibleChange)
+ RepairMap ((COUNT)~0, &last_r, &r);
+
+DoneSphereMove:
+ FleetPtr->known_loc = FleetPtr->loc;
+ }
+
+ delta = FleetPtr->actual_strength - FleetPtr->known_strength;
+ if (delta)
+ {
+ if (!MapDrawn)
+ {
+ DrawStarMap ((COUNT)~0, NULL);
+ MapDrawn = TRUE;
+ }
+
+ if (delta > 0)
+ dx = 1;
+ else
+ {
+ delta = -delta;
+ dx = -1;
+ }
+ --delta;
+
+ GetSphereRect (FleetPtr, &temp_r0, &last_r);
+ ++last_r.extent.width;
+ ++last_r.extent.height;
+ VisibleChange = FALSE;
+ do
+ {
+ do
+ {
+ FleetPtr->known_strength += dx;
+ GetSphereRect (FleetPtr, &temp_r1, &r);
+ } while (delta--
+ && ((delta & 0xF)
+ || temp_r0.extent.height == temp_r1.extent.height));
+
+ if (ButtonState)
+ {
+ if (!AnyButtonPress (TRUE))
+ ButtonState = 0;
+ }
+ else if ((Interrupted = (BOOLEAN)(
+ Interrupted || AnyButtonPress (TRUE)
+ )))
+ {
+ MapDrawn = TRUE;
+ goto DoneSphereGrowth;
+ }
+ ++r.extent.width;
+ ++r.extent.height;
+ if (temp_r0.extent.height != temp_r1.extent.height)
+ {
+ VisibleChange = TRUE;
+ RepairMap (index, &last_r, &r);
+ }
+ } while (delta >= 0);
+ if (VisibleChange || temp_r0.extent.width != temp_r1.extent.width)
+ RepairMap ((COUNT)~0, &last_r, &r);
+
+DoneSphereGrowth:
+ FleetPtr->known_strength = FleetPtr->actual_strength;
+ }
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+}
+
+BOOLEAN
+StarMap (void)
+{
+ MENU_STATE MenuState;
+ POINT universe;
+ //FRAME OldFrame;
+ RECT clip_r;
+ CONTEXT OldContext;
+
+ memset (&MenuState, 0, sizeof (MenuState));
+
+ zoomLevel = 0;
+ mapOrigin.x = MAX_X_UNIVERSE >> 1;
+ mapOrigin.y = MAX_Y_UNIVERSE >> 1;
+ StarMapFrame = SetAbsFrameIndex (MiscDataFrame, 48);
+
+ if (!inHQSpace ())
+ universe = CurStarDescPtr->star_pt;
+ else
+ {
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ }
+
+ cursorLoc = GLOBAL (autopilot);
+ if (cursorLoc.x == ~0 && cursorLoc.y == ~0)
+ cursorLoc = universe;
+
+ MenuState.InputFunc = DoMoveCursor;
+ MenuState.Initialized = FALSE;
+
+ transition_pending = TRUE;
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ UpdateMap ();
+
+ DrawStarMap (0, (RECT*)-1);
+ transition_pending = FALSE;
+
+ BatchGraphics ();
+ OldContext = SetContext (SpaceContext);
+ GetContextClipRect (&clip_r);
+ SetContext (OldContext);
+ LoadIntoExtraScreen (&clip_r);
+ DrawCursor (UNIVERSE_TO_DISPX (cursorLoc.x),
+ UNIVERSE_TO_DISPY (cursorLoc.y));
+ UnbatchGraphics ();
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ SetMenuRepeatDelay (MIN_ACCEL_DELAY, MAX_ACCEL_DELAY, STEP_ACCEL_DELAY,
+ TRUE);
+ DoInput (&MenuState, FALSE);
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetDefaultMenuRepeatDelay ();
+
+ DrawHyperCoords (universe);
+ DrawSISMessage (NULL);
+ DrawStatusMessage (NULL);
+
+ if (GLOBAL (autopilot.x) == universe.x
+ && GLOBAL (autopilot.y) == universe.y)
+ GLOBAL (autopilot.x) = GLOBAL (autopilot.y) = ~0;
+
+ return (GLOBAL (autopilot.x) != ~0
+ && GLOBAL (autopilot.y) != ~0);
+}
+
diff --git a/src/uqm/planets/report.c b/src/uqm/planets/report.c
new file mode 100644
index 0000000..5defbe7
--- /dev/null
+++ b/src/uqm/planets/report.c
@@ -0,0 +1,271 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "lander.h"
+#include "scan.h"
+#include "planets.h"
+#include "../colors.h"
+#include "../controls.h"
+#include "../gamestr.h"
+#include "../setup.h"
+#include "../util.h"
+#include "../sounds.h"
+#include "../uqmdebug.h"
+#include "options.h"
+#include "libs/inplib.h"
+
+#include <ctype.h>
+#include <string.h>
+
+
+#define NUM_CELL_COLS MAP_WIDTH / 6
+#define NUM_CELL_ROWS MAP_HEIGHT / 6
+#define MAX_CELL_COLS 40
+
+extern FRAME SpaceJunkFrame;
+
+static void
+ClearReportArea (void)
+{
+ COUNT x, y;
+ RECT r;
+ STAMP s;
+ COORD startx;
+
+ if (optWhichFonts == OPT_PC)
+ s.frame = SetAbsFrameIndex (SpaceJunkFrame, 21);
+ else
+ s.frame = SetAbsFrameIndex (SpaceJunkFrame, 18);
+ GetFrameRect (s.frame, &r);
+
+ BatchGraphics ();
+
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x07, 0x00), 0x57));
+
+ startx = 1 + (r.extent.width >> 1) - 1;
+ s.origin.y = 1;
+ for (y = 0; y < NUM_CELL_ROWS; ++y)
+ {
+ s.origin.x = startx;
+ for (x = 0; x < NUM_CELL_COLS; ++x)
+ {
+ if (optWhichFonts == OPT_PC)
+ DrawStamp (&s);
+ else
+ DrawFilledStamp (&s);
+
+ s.origin.x += r.extent.width + 1;
+ }
+ s.origin.y += r.extent.height + 1;
+ }
+
+ UnbatchGraphics ();
+}
+
+static void
+MakeReport (SOUND ReadOutSounds, UNICODE *pStr, COUNT StrLen)
+{
+ BYTE ButtonState;
+ int end_page_len;
+ UNICODE end_page_buf[200];
+ UniChar last_c = 0;
+ COUNT row_cells;
+ BOOLEAN Sleepy;
+ RECT r;
+ TEXT t;
+
+ sprintf (end_page_buf, "%s\n", GAME_STRING (SCAN_STRING_BASE + NUM_SCAN_TYPES));
+ end_page_len = utf8StringCount (end_page_buf);
+
+ GetFrameRect (SetAbsFrameIndex (SpaceJunkFrame, 18), &r);
+
+ t.align = ALIGN_LEFT;
+ t.CharCount = 1;
+ t.pStr = pStr;
+
+ Sleepy = TRUE;
+
+ FlushInput ();
+ // XXX: this is a pretty ugly goto
+ goto InitPageCell;
+
+ while (StrLen)
+ {
+ COUNT col_cells;
+ const UNICODE *pLastStr;
+ const UNICODE *pNextStr;
+ COUNT lf_pos;
+
+ pLastStr = t.pStr;
+
+ // scan for LFs in the remaining string
+ // trailing LF will be ignored
+ for (lf_pos = StrLen, pNextStr = t.pStr;
+ lf_pos && getCharFromString (&pNextStr) != '\n';
+ --lf_pos)
+ ;
+
+ col_cells = 0;
+ // check if the remaining text fits on current screen
+ if (row_cells == NUM_CELL_ROWS - 1
+ && (StrLen > NUM_CELL_COLS || lf_pos > 1))
+ {
+ col_cells = (NUM_CELL_COLS >> 1) - (end_page_len >> 1);
+ t.pStr = end_page_buf;
+ StrLen += end_page_len;
+ }
+ t.baseline.x = 1 + (r.extent.width >> 1)
+ + (col_cells * (r.extent.width + 1)) - 1;
+ do
+ {
+ COUNT word_chars;
+ const UNICODE *pStr;
+ UniChar c;
+
+ pStr = t.pStr;
+ pNextStr = t.pStr;
+ while (UniChar_isGraph (getCharFromString (&pNextStr)))
+ pStr = pNextStr;
+
+ word_chars = utf8StringCountN (t.pStr, pStr);
+ if ((col_cells += word_chars) <= NUM_CELL_COLS)
+ {
+ TimeCount TimeOut;
+
+ if (StrLen -= word_chars)
+ --StrLen;
+ TimeOut = GetTimeCounter ();
+ while (word_chars--)
+ {
+ pNextStr = t.pStr;
+ c = getCharFromString (&pNextStr);
+
+ if (!Sleepy || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ font_DrawText (&t);
+ else
+ {
+ font_DrawText (&t);
+
+ PlaySound (ReadOutSounds, NotPositional (), NULL,
+ GAME_SOUND_PRIORITY);
+
+ if (c == ',')
+ TimeOut += ONE_SECOND / 4;
+ if (c == '.' || c == '!' || c == '?')
+ TimeOut += ONE_SECOND / 2;
+ else
+ TimeOut += ONE_SECOND / 20;
+ if (word_chars == 0)
+ TimeOut += ONE_SECOND / 20;
+
+ if (WaitForAnyButtonUntil (TRUE, TimeOut, FALSE))
+ {
+ Sleepy = FALSE;
+ // We draw the whole thing at once after this
+ BatchGraphics ();
+ }
+ }
+ t.pStr = pNextStr;
+ t.baseline.x += r.extent.width + 1;
+ }
+
+ ++col_cells;
+ last_c = getCharFromString (&t.pStr);
+ t.baseline.x += r.extent.width + 1;
+ }
+ } while (col_cells <= NUM_CELL_COLS && last_c != '\n' && StrLen);
+
+ t.baseline.y += r.extent.height + 1;
+ if (++row_cells == NUM_CELL_ROWS || StrLen == 0)
+ {
+ t.pStr = pLastStr;
+ if (!Sleepy)
+ {
+ UnbatchGraphics ();
+ }
+
+ if (!WaitForAnyButton (TRUE, WAIT_INFINITE, FALSE))
+ break;
+
+InitPageCell:
+ ButtonState = 1;
+ t.baseline.y = r.extent.height + 1;
+ row_cells = 0;
+ if (StrLen)
+ {
+ if (!Sleepy)
+ BatchGraphics ();
+ ClearReportArea();
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1F, 0x00), 0xFF));
+ }
+ }
+ }
+}
+
+void
+DoDiscoveryReport (SOUND ReadOutSounds)
+{
+ CONTEXT OldContext;
+ CONTEXT context;
+ BOOLEAN ownContext;
+ STAMP saveStamp;
+
+#ifdef DEBUG
+ if (disableInteractivity)
+ return;
+#endif
+
+ context = GetScanContext (&ownContext);
+ OldContext = SetContext (context);
+ saveStamp = SaveContextFrame (NULL);
+ {
+ FONT OldFont;
+ FRAME OldFontEffect;
+
+ OldFont = SetContextFont (
+ pSolarSysState->SysInfo.PlanetInfo.LanderFont);
+ if (optWhichFonts == OPT_PC)
+ OldFontEffect = SetContextFontEffect (
+ pSolarSysState->SysInfo.PlanetInfo.LanderFontEff);
+ else
+ OldFontEffect = SetContextFontEffect (NULL);
+
+ MakeReport (ReadOutSounds,
+ (UNICODE *)GetStringAddress (pSolarSysState->SysInfo.PlanetInfo.DiscoveryString),
+ GetStringLength (pSolarSysState->SysInfo.PlanetInfo.DiscoveryString));
+
+ SetContextFontEffect (OldFontEffect);
+ SetContextFont (OldFont);
+ }
+ // Restore previous screen
+ DrawStamp (&saveStamp);
+ SetContext (OldContext);
+ // TODO: Make CONTEXT ref-counted
+ if (ownContext)
+ DestroyScanContext ();
+
+ DestroyDrawable (ReleaseDrawable (saveStamp.frame));
+
+ WaitForNoInput (WAIT_INFINITE, TRUE);
+}
+
+
diff --git a/src/uqm/planets/roster.c b/src/uqm/planets/roster.c
new file mode 100644
index 0000000..663ac28
--- /dev/null
+++ b/src/uqm/planets/roster.c
@@ -0,0 +1,428 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../build.h"
+#include "../colors.h"
+#include "../controls.h"
+#include "../races.h"
+#include "../units.h"
+#include "../sis.h"
+#include "../shipcont.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "port.h"
+#include "libs/gfxlib.h"
+#include "libs/tasklib.h"
+
+#include <stdlib.h>
+
+// Ship icon positions in status display around the flagship
+static const POINT ship_pos[MAX_BUILT_SHIPS] =
+{
+ SUPPORT_SHIP_PTS
+};
+
+typedef struct
+{
+ // Ship icon positions split into (lower half) left and right (upper)
+ // and sorted in the Y coord. These are used for navigation around the
+ // escort positions.
+ POINT shipPos[MAX_BUILT_SHIPS];
+ COUNT count;
+ // Number of ships
+
+ POINT curShipPt;
+ // Location of the currently selected escort
+ FRAME curShipFrame;
+ // Icon of the currently selected escort
+ bool modifyingCrew;
+ // true when in crew modification "sub-menu". This is simple
+ // enough that it does not require a real sub-menu.
+} ROSTER_STATE;
+
+static SHIP_FRAGMENT* LockSupportShip (ROSTER_STATE *, HSHIPFRAG *phFrag);
+
+static void
+drawSupportShip (ROSTER_STATE *rosterState, bool filled)
+{
+ STAMP s;
+
+ if (!rosterState->curShipFrame)
+ return;
+
+ s.origin = rosterState->curShipPt;
+ s.frame = rosterState->curShipFrame;
+
+ if (filled)
+ DrawFilledStamp (&s);
+ else
+ DrawStamp (&s);
+}
+
+static void
+getSupportShipIcon (ROSTER_STATE *rosterState)
+{
+ HSHIPFRAG hShipFrag;
+ SHIP_FRAGMENT *ShipFragPtr;
+
+ rosterState->curShipFrame = NULL;
+ ShipFragPtr = LockSupportShip (rosterState, &hShipFrag);
+ if (!ShipFragPtr)
+ return;
+
+ rosterState->curShipFrame = ShipFragPtr->icons;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hShipFrag);
+}
+
+static void
+flashSupportShip (ROSTER_STATE *rosterState)
+{
+ static Color c = BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x24);
+ static TimeCount NextTime = 0;
+
+ if (GetTimeCounter () >= NextTime)
+ {
+ NextTime = GetTimeCounter () + (ONE_SECOND / 15);
+
+ /* The commented code out code is the old code before the switch
+ * to 24-bits colors. The current code produces very slightly
+ * different colors due to rounding errors, but the old code wasn't
+ * original anyhow, and you can't tell the difference visually.
+ * - SvdB
+ if (c >= BUILD_COLOR (MAKE_RGB15 (0x1F, 0x19, 0x19), 0x24))
+ c = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x00, 0x00), 0x24);
+ else
+ c += BUILD_COLOR (MAKE_RGB15 (0x00, 0x02, 0x02), 0x00);
+ */
+
+ if (c.g >= CC5TO8 (0x19))
+ {
+ c = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x00, 0x00), 0x24);
+ }
+ else
+ {
+ c.g += CC5TO8 (0x02);
+ c.b += CC5TO8 (0x02);
+ }
+ SetContextForeGroundColor (c);
+
+ drawSupportShip (rosterState, TRUE);
+ }
+}
+
+static SHIP_FRAGMENT *
+LockSupportShip (ROSTER_STATE *rosterState, HSHIPFRAG *phFrag)
+{
+ const POINT *pship_pos;
+ HSHIPFRAG hStarShip, hNextShip;
+
+ // Lookup the current escort's location in the unsorted points list
+ // to find the original escort index
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)),
+ pship_pos = ship_pos;
+ hStarShip; hStarShip = hNextShip, ++pship_pos)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (pointsEqual (*pship_pos, rosterState->curShipPt))
+ {
+ *phFrag = hStarShip;
+ return StarShipPtr;
+ }
+
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ return NULL;
+}
+
+static void
+flashSupportShipCrew (void)
+{
+ RECT r;
+
+ SetContext (StatusContext);
+ GetStatusMessageRect (&r);
+ SetFlashRect (&r);
+}
+
+static BOOLEAN
+DeltaSupportCrew (ROSTER_STATE *rosterState, SIZE crew_delta)
+{
+ BOOLEAN ret = FALSE;
+ UNICODE buf[40];
+ HFLEETINFO hTemplate;
+ HSHIPFRAG hShipFrag;
+ SHIP_FRAGMENT *StarShipPtr;
+ FLEET_INFO *TemplatePtr;
+
+ StarShipPtr = LockSupportShip (rosterState, &hShipFrag);
+ if (!StarShipPtr)
+ return FALSE;
+
+ hTemplate = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ StarShipPtr->race_id);
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+
+ StarShipPtr->crew_level += crew_delta;
+
+ if (StarShipPtr->crew_level == 0)
+ StarShipPtr->crew_level = 1;
+ else if (StarShipPtr->crew_level > TemplatePtr->crew_level &&
+ crew_delta > 0)
+ StarShipPtr->crew_level -= crew_delta;
+ else
+ {
+ if (StarShipPtr->crew_level >= TemplatePtr->crew_level)
+ sprintf (buf, "%u", StarShipPtr->crew_level);
+ else
+ sprintf (buf, "%u/%u",
+ StarShipPtr->crew_level,
+ TemplatePtr->crew_level);
+
+ PreUpdateFlashRect ();
+ DrawStatusMessage (buf);
+ PostUpdateFlashRect ();
+ DeltaSISGauges (-crew_delta, 0, 0);
+ if (crew_delta)
+ {
+ flashSupportShipCrew ();
+ }
+ ret = TRUE;
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hShipFrag);
+
+ return ret;
+}
+
+static void
+drawModifiedSupportShip (ROSTER_STATE *rosterState)
+{
+ SetContext (StatusContext);
+ SetContextForeGroundColor (ROSTER_MODIFY_SHIP_COLOR);
+ drawSupportShip (rosterState, TRUE);
+}
+
+static void
+selectSupportShip (ROSTER_STATE *rosterState, COUNT shipIndex)
+{
+ rosterState->curShipPt = rosterState->shipPos[shipIndex];
+ getSupportShipIcon (rosterState);
+ DeltaSupportCrew (rosterState, 0);
+}
+
+static BOOLEAN
+DoModifyRoster (MENU_STATE *pMS)
+{
+ ROSTER_STATE *rosterState = pMS->privData;
+ BOOLEAN select, cancel, up, down, horiz;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+ up = PulsedInputState.menu[KEY_MENU_UP];
+ down = PulsedInputState.menu[KEY_MENU_DOWN];
+ // Left or right produces the same effect because there are 2 columns
+ horiz = PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_RIGHT];
+
+ if (cancel && !rosterState->modifyingCrew)
+ {
+ return FALSE;
+ }
+ else if (select || cancel)
+ {
+ rosterState->modifyingCrew ^= true;
+ if (!rosterState->modifyingCrew)
+ {
+ SetFlashRect (NULL);
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ }
+ else
+ {
+ drawModifiedSupportShip (rosterState);
+ flashSupportShipCrew ();
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN,
+ MENU_SOUND_SELECT | MENU_SOUND_CANCEL);
+ }
+ }
+ else if (rosterState->modifyingCrew)
+ {
+ SIZE delta = 0;
+ BOOLEAN failed = FALSE;
+
+ if (up)
+ {
+ if (GLOBAL_SIS (CrewEnlisted))
+ delta = 1;
+ else
+ failed = TRUE;
+ }
+ else if (down)
+ {
+ if (GLOBAL_SIS (CrewEnlisted) < GetCrewPodCapacity ())
+ delta = -1;
+ else
+ failed = TRUE;
+ }
+
+ if (delta != 0)
+ {
+ failed = !DeltaSupportCrew (rosterState, delta);
+ }
+
+ if (failed)
+ { // not enough room or crew
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+ }
+ else
+ {
+ COUNT NewState;
+ POINT *pship_pos = rosterState->shipPos;
+ COUNT top_right = (rosterState->count + 1) >> 1;
+
+ NewState = pMS->CurState;
+
+ if (rosterState->count < 2)
+ {
+ // no navigation allowed
+ }
+ else if (horiz)
+ {
+ if (NewState == top_right - 1)
+ NewState = rosterState->count - 1;
+ else if (NewState >= top_right)
+ {
+ NewState -= top_right;
+ if (pship_pos[NewState].y < pship_pos[pMS->CurState].y)
+ ++NewState;
+ }
+ else
+ {
+ NewState += top_right;
+ if (NewState != top_right
+ && pship_pos[NewState].y > pship_pos[pMS->CurState].y)
+ --NewState;
+ }
+ }
+ else if (down)
+ {
+ ++NewState;
+ if (NewState == rosterState->count)
+ NewState = top_right;
+ else if (NewState == top_right)
+ NewState = 0;
+ }
+ else if (up)
+ {
+ if (NewState == 0)
+ NewState = top_right - 1;
+ else if (NewState == top_right)
+ NewState = rosterState->count - 1;
+ else
+ --NewState;
+ }
+
+ BatchGraphics ();
+ SetContext (StatusContext);
+
+ if (NewState != pMS->CurState)
+ {
+ // Draw the previous escort in unselected state
+ drawSupportShip (rosterState, FALSE);
+ // Select the new one
+ selectSupportShip (rosterState, NewState);
+ pMS->CurState = NewState;
+ }
+
+ flashSupportShip (rosterState);
+
+ UnbatchGraphics ();
+ }
+
+ SleepThread (ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static int
+compShipPos (const void *ptr1, const void *ptr2)
+{
+ const POINT *pt1 = (const POINT *) ptr1;
+ const POINT *pt2 = (const POINT *) ptr2;
+
+ // Ships on the left in the lower half
+ if (pt1->x < pt2->x)
+ return -1;
+ else if (pt1->x > pt2->x)
+ return 1;
+
+ // and ordered on Y
+ if (pt1->y < pt2->y)
+ return -1;
+ else if (pt1->y > pt2->y)
+ return 1;
+ else
+ return 0;
+}
+
+BOOLEAN
+RosterMenu (void)
+{
+ MENU_STATE MenuState;
+ ROSTER_STATE RosterState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.privData = &RosterState;
+
+ memset (&RosterState, 0, sizeof RosterState);
+
+ RosterState.count = CountLinks (&GLOBAL (built_ship_q));
+ if (!RosterState.count)
+ return FALSE;
+
+ // Get the escort positions we will use and sort on X then Y
+ assert (sizeof (RosterState.shipPos) == sizeof (ship_pos));
+ memcpy (RosterState.shipPos, ship_pos, sizeof (ship_pos));
+ qsort (RosterState.shipPos, RosterState.count,
+ sizeof (RosterState.shipPos[0]), compShipPos);
+
+ SetContext (StatusContext);
+ selectSupportShip (&RosterState, MenuState.CurState);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoModifyRoster;
+ DoInput (&MenuState, TRUE);
+
+ SetContext (StatusContext);
+ // unselect the last ship
+ drawSupportShip (&RosterState, FALSE);
+ DrawStatusMessage (NULL);
+
+ return TRUE;
+}
+
diff --git a/src/uqm/planets/scan.c b/src/uqm/planets/scan.c
new file mode 100644
index 0000000..3d5d9fd
--- /dev/null
+++ b/src/uqm/planets/scan.c
@@ -0,0 +1,1385 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "lander.h"
+#include "lifeform.h"
+#include "scan.h"
+#include "../build.h"
+#include "../colors.h"
+#include "../cons_res.h"
+#include "../controls.h"
+#include "../menustat.h"
+#include "../encount.h"
+ // for EncounterGroup
+#include "../gamestr.h"
+#include "../nameref.h"
+#include "../resinst.h"
+#include "../settings.h"
+#include "../util.h"
+#include "../process.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../state.h"
+#include "../sis.h"
+#include "../save.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/drawable.h"
+#include "libs/inplib.h"
+#include "libs/mathlib.h"
+
+extern FRAME SpaceJunkFrame;
+
+// define SPIN_ON_SCAN to allow the planet to spin
+// while scaning is going on
+#undef SPIN_ON_SCAN
+
+#define FLASH_INDEX 105
+
+static CONTEXT ScanContext;
+
+static POINT planetLoc;
+static RECT cursorRect;
+static FRAME eraseFrame;
+
+// ScanSystem() menu items
+// The first three are from enum PlanetScanTypes in planets.h
+enum ScanMenuItems
+{
+ EXIT_SCAN = NUM_SCAN_TYPES,
+ AUTO_SCAN,
+ DISPATCH_SHUTTLE,
+};
+
+
+void
+RepairBackRect (RECT *pRect)
+{
+ RECT new_r, old_r;
+
+ GetContextClipRect (&old_r);
+ new_r.corner.x = pRect->corner.x + old_r.corner.x;
+ new_r.corner.y = pRect->corner.y + old_r.corner.y;
+ new_r.extent = pRect->extent;
+
+ new_r.extent.height += new_r.corner.y & 1;
+ new_r.corner.y &= ~1;
+ DrawFromExtraScreen (&new_r);
+}
+
+static void
+EraseCoarseScan (void)
+{
+ SetContext (PlanetContext);
+
+ BatchGraphics ();
+ DrawStarBackGround ();
+ DrawDefaultPlanetSphere ();
+ UnbatchGraphics ();
+}
+
+static void
+PrintScanTitlePC (TEXT *t, RECT *r, const char *txt, int xpos)
+{
+ t->baseline.x = xpos;
+ SetContextForeGroundColor (SCAN_PC_TITLE_COLOR);
+ t->pStr = txt;
+ t->CharCount = (COUNT)~0;
+ font_DrawText (t);
+ TextRect (t, r, NULL);
+ t->baseline.x += r->extent.width;
+ SetContextForeGroundColor (SCAN_INFO_COLOR);
+}
+
+static void
+MakeScanValue (UNICODE *buf, long val, const UNICODE *extra)
+{
+ if (val >= 10 * 100)
+ { // 1 decimal place
+ sprintf (buf, "%ld.%ld%s", val / 100, (val / 10) % 10, extra);
+ }
+ else
+ { // 2 decimal places
+ sprintf (buf, "%ld.%02ld%s", val / 100, val % 100, extra);
+ }
+}
+
+static void
+GetPlanetTitle (UNICODE *buf, COUNT bufsize)
+{
+ int val;
+ UNICODE *named = GetNamedPlanetaryBody ();
+ if (named)
+ {
+ utf8StringCopy (buf, bufsize, named);
+ return;
+ }
+
+ // Unnamed body, use world type
+ val = pSolarSysState->pOrbitalDesc->data_index & ~PLANET_SHIELDED;
+ if (val >= FIRST_GAS_GIANT)
+ {
+ sprintf (buf, "%s", GAME_STRING (SCAN_STRING_BASE + 4 + 51));
+ // Gas Giant
+ }
+ else
+ {
+ sprintf (buf, "%s %s",
+ GAME_STRING (SCAN_STRING_BASE + 4 + val),
+ GAME_STRING (SCAN_STRING_BASE + 4 + 50));
+ // World
+ }
+}
+
+static void
+PrintCoarseScanPC (void)
+{
+#define SCAN_LEADING_PC 14
+ SDWORD val;
+ TEXT t;
+ RECT r;
+ UNICODE buf[200];
+
+ GetPlanetTitle (buf, sizeof (buf));
+
+ SetContext (PlanetContext);
+
+ t.align = ALIGN_CENTER;
+ t.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ t.baseline.y = 13;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (SCAN_PC_TITLE_COLOR);
+ SetContextFont (MicroFont);
+ font_DrawText (&t);
+
+ SetContextFont (TinyFont);
+
+#define LEFT_SIDE_BASELINE_X_PC 5
+#define RIGHT_SIDE_BASELINE_X_PC (SIS_SCREEN_WIDTH - 75)
+#define SCAN_BASELINE_Y_PC 40
+
+ t.baseline.y = SCAN_BASELINE_Y_PC;
+ t.align = ALIGN_LEFT;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE),
+ LEFT_SIDE_BASELINE_X_PC); // "Orbit: "
+ val = ((pSolarSysState->SysInfo.PlanetInfo.PlanetToSunDist * 100L
+ + (EARTH_RADIUS >> 1)) / EARTH_RADIUS);
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 1)); // " a.u."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 2),
+ LEFT_SIDE_BASELINE_X_PC); // "Atmo: "
+ if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == GAS_GIANT_ATMOSPHERE)
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (ORBITSCAN_STRING_BASE + 3)); // "Super Thick"
+ else if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == 0)
+ utf8StringCopy (buf, sizeof (buf),
+ GAME_STRING (ORBITSCAN_STRING_BASE + 4)); // "Vacuum"
+ else
+ {
+ val = (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity * 100
+ + (EARTH_ATMOSPHERE >> 1)) / EARTH_ATMOSPHERE;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 5)); // " atm"
+ }
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 6),
+ LEFT_SIDE_BASELINE_X_PC); // "Temp: "
+ sprintf (buf, "%d" STR_DEGREE_SIGN " c",
+ pSolarSysState->SysInfo.PlanetInfo.SurfaceTemperature);
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 7),
+ LEFT_SIDE_BASELINE_X_PC); // "Weather: "
+ if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == 0)
+ t.pStr = GAME_STRING (ORBITSCAN_STRING_BASE + 8); // "None"
+ else
+ {
+ sprintf (buf, "%s %u",
+ GAME_STRING (ORBITSCAN_STRING_BASE + 9), // "Class"
+ pSolarSysState->SysInfo.PlanetInfo.Weather + 1);
+ t.pStr = buf;
+ }
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 10),
+ LEFT_SIDE_BASELINE_X_PC); // "Tectonics: "
+ if (PLANSIZE (pSolarSysState->SysInfo.PlanetInfo.PlanDataPtr->Type) ==
+ GAS_GIANT)
+ t.pStr = GAME_STRING (ORBITSCAN_STRING_BASE + 8); // "None"
+ else
+ {
+ sprintf (buf, "%s %u",
+ GAME_STRING (ORBITSCAN_STRING_BASE + 9), // "Class"
+ pSolarSysState->SysInfo.PlanetInfo.Tectonics + 1);
+ t.pStr = buf;
+ }
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ t.baseline.y = SCAN_BASELINE_Y_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 11),
+ RIGHT_SIDE_BASELINE_X_PC); // "Mass: "
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ val = ((DWORD) val * (DWORD) val * (DWORD) val / 100L
+ * pSolarSysState->SysInfo.PlanetInfo.PlanetDensity
+ + ((100L * 100L) >> 1)) / (100L * 100L);
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 12)); // " e.s."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 13),
+ RIGHT_SIDE_BASELINE_X_PC); // "Radius: "
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 12)); // " e.s."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 14),
+ RIGHT_SIDE_BASELINE_X_PC); // "Gravity: "
+ val = pSolarSysState->SysInfo.PlanetInfo.SurfaceGravity;
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 15)); // " g."
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 16),
+ RIGHT_SIDE_BASELINE_X_PC); // "Day: "
+ val = (SDWORD)pSolarSysState->SysInfo.PlanetInfo.RotationPeriod
+ * 10 / 24;
+ MakeScanValue (buf, val,
+ GAME_STRING (ORBITSCAN_STRING_BASE + 17)); // " days"
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING_PC;
+
+ PrintScanTitlePC (&t, &r, GAME_STRING (ORBITSCAN_STRING_BASE + 18),
+ RIGHT_SIDE_BASELINE_X_PC); // "Tilt: "
+ val = pSolarSysState->SysInfo.PlanetInfo.AxialTilt;
+ if (val < 0)
+ val = -val;
+ t.pStr = buf;
+ sprintf (buf, "%d" STR_DEGREE_SIGN, val);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+PrintCoarseScan3DO (void)
+{
+#define SCAN_LEADING 19
+ SDWORD val;
+ TEXT t;
+ STAMP s;
+ UNICODE buf[200];
+
+ GetPlanetTitle (buf, sizeof (buf));
+
+ SetContext (PlanetContext);
+
+ t.align = ALIGN_CENTER;
+ t.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ t.baseline.y = 13;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (SCAN_INFO_COLOR);
+ SetContextFont (MicroFont);
+ font_DrawText (&t);
+
+ s.origin.x = s.origin.y = 0;
+ s.origin.x = 16 - SAFE_X;
+ s.frame = SetAbsFrameIndex (SpaceJunkFrame, 20);
+ DrawStamp (&s);
+
+#define LEFT_SIDE_BASELINE_X (27 + (16 - SAFE_X))
+#define RIGHT_SIDE_BASELINE_X (SIS_SCREEN_WIDTH - LEFT_SIDE_BASELINE_X)
+#define SCAN_BASELINE_Y 25
+
+ t.baseline.x = LEFT_SIDE_BASELINE_X;
+ t.baseline.y = SCAN_BASELINE_Y;
+ t.align = ALIGN_LEFT;
+
+ t.pStr = buf;
+ val = ((pSolarSysState->SysInfo.PlanetInfo.PlanetToSunDist * 100L
+ + (EARTH_RADIUS >> 1)) / EARTH_RADIUS);
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ if (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == GAS_GIANT_ATMOSPHERE)
+ strcpy (buf, STR_INFINITY_SIGN);
+ else
+ {
+ val = (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity * 100
+ + (EARTH_ATMOSPHERE >> 1)) / EARTH_ATMOSPHERE;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ }
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ sprintf (buf, "%d" STR_DEGREE_SIGN,
+ pSolarSysState->SysInfo.PlanetInfo.SurfaceTemperature);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ sprintf (buf, "<%u>", pSolarSysState->SysInfo.PlanetInfo.AtmoDensity == 0
+ ? 0 : (pSolarSysState->SysInfo.PlanetInfo.Weather + 1));
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ sprintf (buf, "<%u>",
+ PLANSIZE (
+ pSolarSysState->SysInfo.PlanetInfo.PlanDataPtr->Type
+ ) == GAS_GIANT
+ ? 0 : (pSolarSysState->SysInfo.PlanetInfo.Tectonics + 1));
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ t.baseline.x = RIGHT_SIDE_BASELINE_X;
+ t.baseline.y = SCAN_BASELINE_Y;
+ t.align = ALIGN_RIGHT;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ val = ((DWORD) val * (DWORD) val * (DWORD) val / 100L
+ * pSolarSysState->SysInfo.PlanetInfo.PlanetDensity
+ + ((100L * 100L) >> 1)) / (100L * 100L);
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.PlanetRadius;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.SurfaceGravity;
+ if (val == 0)
+ val = 1;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = pSolarSysState->SysInfo.PlanetInfo.AxialTilt;
+ if (val < 0)
+ val = -val;
+ sprintf (buf, "%d" STR_DEGREE_SIGN, val);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += SCAN_LEADING;
+
+ t.pStr = buf;
+ val = (SDWORD)pSolarSysState->SysInfo.PlanetInfo.RotationPeriod
+ * 10 / 24;
+ MakeScanValue (buf, val, STR_EARTH_SIGN);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+static void
+initPlanetLocationImage (void)
+{
+ FRAME cursorFrame;
+
+ // Get the cursor image
+ cursorFrame = SetAbsFrameIndex (MiscDataFrame, FLASH_INDEX);
+ cursorRect.extent = GetFrameBounds (cursorFrame);
+}
+
+static void
+savePlanetLocationImage (void)
+{
+ RECT r;
+ FRAME cursorFrame = SetAbsFrameIndex (MiscDataFrame, FLASH_INDEX);
+ HOT_SPOT hs = GetFrameHot (cursorFrame);
+
+ DestroyDrawable (ReleaseDrawable (eraseFrame));
+
+ r = cursorRect;
+ r.corner.x -= hs.x;
+ r.corner.y -= hs.y;
+ eraseFrame = CaptureDrawable (CopyContextRect (&r));
+ SetFrameHot (eraseFrame, hs);
+}
+
+static void
+restorePlanetLocationImage (void)
+{
+ STAMP s;
+
+ s.origin = cursorRect.corner;
+ s.frame = eraseFrame; // saved image
+ DrawStamp (&s);
+}
+
+static void
+drawPlanetCursor (BOOLEAN filled)
+{
+ STAMP s;
+
+ s.origin = cursorRect.corner;
+ s.frame = SetAbsFrameIndex (MiscDataFrame, FLASH_INDEX);
+ if (filled)
+ DrawFilledStamp (&s);
+ else
+ DrawStamp (&s);
+}
+
+static void
+setPlanetCursorLoc (POINT new_pt)
+{
+ new_pt.x >>= MAG_SHIFT;
+ new_pt.y >>= MAG_SHIFT;
+ cursorRect.corner = new_pt;
+}
+
+static void
+setPlanetLoc (POINT new_pt, BOOLEAN restoreOld)
+{
+ planetLoc = new_pt;
+
+ SetContext (ScanContext);
+ if (restoreOld)
+ restorePlanetLocationImage ();
+ setPlanetCursorLoc (new_pt);
+ savePlanetLocationImage ();
+}
+
+static void
+flashPlanetLocation (void)
+{
+#define FLASH_FRAME_DELAY (ONE_SECOND / 16)
+ static BYTE c = 0x00;
+ static int val = -2;
+ static POINT prevPt;
+ static TimeCount NextTime = 0;
+ BOOLEAN locChanged;
+ TimeCount Now = GetTimeCounter ();
+
+ locChanged = prevPt.x != cursorRect.corner.x
+ || prevPt.y != cursorRect.corner.y;
+
+ if (!locChanged && Now < NextTime)
+ return; // nothing to do
+
+ if (locChanged)
+ { // Reset the flashing cycle
+ c = 0x00;
+ val = -2;
+ prevPt = cursorRect.corner;
+
+ NextTime = Now + FLASH_FRAME_DELAY;
+ }
+ else
+ { // Continue the flashing cycle
+ if (c == 0x00 || c == 0x1A)
+ val = -val;
+ c += val;
+
+ if (Now - NextTime > FLASH_FRAME_DELAY)
+ NextTime = Now + FLASH_FRAME_DELAY; // missed timing by too much
+ else
+ NextTime += FLASH_FRAME_DELAY; // stable frame rate
+ }
+
+ SetContext (ScanContext);
+ SetContextForeGroundColor (BUILD_COLOR (MAKE_RGB15 (c, c, c), c));
+ drawPlanetCursor (TRUE);
+}
+
+void
+RedrawSurfaceScan (const POINT *newLoc)
+{
+ CONTEXT OldContext;
+
+ OldContext = SetContext (ScanContext);
+
+ BatchGraphics ();
+ DrawPlanet (0, BLACK_COLOR);
+ DrawScannedObjects (TRUE);
+ if (newLoc)
+ {
+ setPlanetLoc (*newLoc, FALSE);
+ drawPlanetCursor (FALSE);
+ }
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+static COUNT
+getLandingFuelNeeded (void)
+{
+ COUNT fuel;
+
+ fuel = pSolarSysState->SysInfo.PlanetInfo.SurfaceGravity << 1;
+ if (fuel > 3 * FUEL_TANK_SCALE)
+ fuel = 3 * FUEL_TANK_SCALE;
+
+ return fuel;
+}
+
+static void
+spawnFwiffo (void)
+{
+ HSHIPFRAG hStarShip;
+
+ EncounterGroup = 0;
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+ ReinitQueue (&GLOBAL (ip_group_q));
+ assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
+
+ hStarShip = CloneShipFragment (SPATHI_SHIP,
+ &GLOBAL (npc_built_ship_q), 1);
+ if (hStarShip)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (npc_built_ship_q),
+ hStarShip);
+ // Name Fwiffo
+ StarShipPtr->captains_name_index = NAME_OFFSET +
+ NUM_CAPTAINS_NAMES;
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ }
+}
+
+// Returns TRUE if the parent menu should remain
+static BOOLEAN
+DispatchLander (void)
+{
+ InputFrameCallback *oldCallback;
+ SIZE landingFuel = getLandingFuelNeeded ();
+
+ EraseCoarseScan ();
+
+ // Deactivate planet rotation callback
+ oldCallback = SetInputCallback (NULL);
+
+ DeltaSISGauges (0, -landingFuel, 0);
+ SetContext (ScanContext);
+ drawPlanetCursor (FALSE);
+
+ PlanetSide (planetLoc);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (GET_GAME_STATE (FOUND_PLUTO_SPATHI) == 1)
+ {
+ /* Create Fwiffo group and go into comm with it */
+ spawnFwiffo ();
+
+ NextActivity |= CHECK_LOAD; /* fake a load game */
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ SaveSolarSysLocation ();
+
+ return FALSE;
+ }
+
+ if (optWhichCoarseScan == OPT_PC)
+ PrintCoarseScanPC ();
+ else
+ PrintCoarseScan3DO ();
+
+ // Reactivate planet rotation callback
+ SetInputCallback (oldCallback);
+
+ return TRUE;
+}
+
+typedef struct
+{
+ bool success;
+ // true when player selected a location
+} PICK_PLANET_STATE;
+
+static BOOLEAN
+DoPickPlanetSide (MENU_STATE *pMS)
+{
+ PICK_PLANET_STATE *pickState = pMS->privData;
+ DWORD TimeIn = GetTimeCounter ();
+ BOOLEAN select, cancel;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pickState->success = false;
+ return FALSE;
+ }
+
+ if (cancel)
+ {
+ pickState->success = false;
+ return FALSE;
+ }
+ else if (select)
+ {
+ pickState->success = true;
+ return FALSE;
+ }
+ else
+ {
+ SIZE dx = 0;
+ SIZE dy = 0;
+ POINT new_pt;
+
+ new_pt = planetLoc;
+
+ if (CurrentInputState.menu[KEY_MENU_LEFT])
+ dx = -1;
+ if (CurrentInputState.menu[KEY_MENU_RIGHT])
+ dx = 1;
+ if (CurrentInputState.menu[KEY_MENU_UP])
+ dy = -1;
+ if (CurrentInputState.menu[KEY_MENU_DOWN])
+ dy = 1;
+
+ BatchGraphics ();
+
+ dx = dx << MAG_SHIFT;
+ if (dx)
+ {
+ new_pt.x += dx;
+ if (new_pt.x < 0)
+ new_pt.x += (MAP_WIDTH << MAG_SHIFT);
+ else if (new_pt.x >= (MAP_WIDTH << MAG_SHIFT))
+ new_pt.x -= (MAP_WIDTH << MAG_SHIFT);
+ }
+ dy = dy << MAG_SHIFT;
+ if (dy)
+ {
+ new_pt.y += dy;
+ if (new_pt.y < 0 || new_pt.y >= (MAP_HEIGHT << MAG_SHIFT))
+ new_pt.y = planetLoc.y;
+ }
+
+ if (!pointsEqual (new_pt, planetLoc))
+ {
+ setPlanetLoc (new_pt, TRUE);
+ }
+
+ flashPlanetLocation ();
+
+ UnbatchGraphics ();
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 40);
+ }
+
+ return TRUE;
+}
+
+static void
+drawLandingFuelUsage (COUNT fuel)
+{
+ UNICODE buf[100];
+
+ sprintf (buf, "%s%1.1f",
+ GAME_STRING (NAVIGATION_STRING_BASE + 5),
+ (float) fuel / FUEL_TANK_SCALE);
+ DrawStatusMessage (buf);
+}
+
+static void
+eraseLandingFuelUsage (void)
+{
+ DrawStatusMessage (NULL);
+}
+
+static BOOLEAN
+PickPlanetSide (void)
+{
+ MENU_STATE MenuState;
+ PICK_PLANET_STATE PickState;
+ COUNT fuel = getLandingFuelNeeded ();
+ BOOLEAN retval = TRUE;
+
+ memset (&MenuState, 0, sizeof MenuState);
+ MenuState.privData = &PickState;
+
+ ClearSISRect (CLEAR_SIS_RADAR);
+ SetContext (ScanContext);
+ BatchGraphics ();
+ DrawPlanet (0, BLACK_COLOR);
+ DrawScannedObjects (FALSE);
+ UnbatchGraphics ();
+
+ drawLandingFuelUsage (fuel);
+ // Set the current flash location
+ setPlanetCursorLoc (planetLoc);
+ savePlanetLocationImage ();
+
+ InitLander (0);
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_SELECT);
+
+ PickState.success = false;
+ MenuState.InputFunc = DoPickPlanetSide;
+ DoInput (&MenuState, TRUE);
+
+ eraseLandingFuelUsage ();
+ if (PickState.success)
+ { // player chose a location
+ retval = DispatchLander ();
+ }
+ else
+ { // player bailed out
+ restorePlanetLocationImage ();
+ }
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ return retval;
+}
+
+#define NUM_FLASH_COLORS 8
+
+static void
+DrawScannedStuff (COUNT y, COUNT scan)
+{
+ HELEMENT hElement, hNextElement;
+ Color OldColor;
+
+ OldColor = SetContextForeGroundColor (BLACK_COLOR);
+
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+ SIZE dy;
+ STAMP s;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+
+ dy = y - ElementPtr->current.location.y;
+ if (LOBYTE (ElementPtr->scan_node) != scan || dy < 0)
+ { // node of wrong type, or not time for it yet
+ UnlockElement (hElement);
+ continue;
+ }
+
+ // XXX: flag this as 'found' scanned object
+ ElementPtr->state_flags |= APPEARING;
+
+ s.origin = ElementPtr->current.location;
+
+ if (dy >= NUM_FLASH_COLORS)
+ { // flashing done for this node, draw normal
+ s.frame = ElementPtr->next.image.frame;
+ DrawStamp (&s);
+ }
+ else
+ {
+ BYTE grad;
+ Color c = WHITE_COLOR;
+ COUNT nodeSize;
+
+ // mineral -- white --> turquoise?? (contrasts with red)
+ // energy -- white --> red (contrasts with white)
+ // bio -- white --> violet (contrasts with green)
+ grad = 0xff - 0xff * dy / (NUM_FLASH_COLORS - 1);
+ switch (scan)
+ {
+ case MINERAL_SCAN:
+ c.r = grad;
+ break;
+ case ENERGY_SCAN:
+ c.g = grad;
+ c.b = grad;
+ break;
+ case BIOLOGICAL_SCAN:
+ c.g = grad;
+ break;
+ }
+
+ SetContextForeGroundColor (c);
+
+ // flash the node from the smallest size to node size
+ // Get the node size for mineral, or number of transitions
+ // for other scan types (was set by GeneratePlanetSide())
+ nodeSize = GetFrameIndex (ElementPtr->next.image.frame)
+ - GetFrameIndex (ElementPtr->current.image.frame);
+ if (dy > nodeSize)
+ dy = nodeSize;
+
+ s.frame = SetRelFrameIndex (ElementPtr->current.image.frame, dy);
+ DrawFilledStamp (&s);
+ }
+
+ UnlockElement (hElement);
+ }
+
+ SetContextForeGroundColor (OldColor);
+}
+
+COUNT
+callGenerateForScanType (const SOLARSYS_STATE *solarSys,
+ const PLANET_DESC *world, COUNT node, BYTE scanType, NODE_INFO *info)
+{
+ switch (scanType)
+ {
+ case MINERAL_SCAN:
+ return (*solarSys->genFuncs->generateMinerals) (
+ solarSys, world, node, info);
+ case ENERGY_SCAN:
+ return (*solarSys->genFuncs->generateEnergy) (
+ solarSys, world, node, info);
+ case BIOLOGICAL_SCAN:
+ return (*solarSys->genFuncs->generateLife) (
+ solarSys, world, node, info);
+ }
+
+ assert (false);
+ return 0;
+}
+
+bool
+callPickupForScanType (SOLARSYS_STATE *solarSys, PLANET_DESC *world,
+ COUNT node, BYTE scanType)
+{
+ switch (scanType)
+ {
+ case MINERAL_SCAN:
+ return (*solarSys->genFuncs->pickupMinerals) (
+ solarSys, world, node);
+ case ENERGY_SCAN:
+ return (*solarSys->genFuncs->pickupEnergy) (
+ solarSys, world, node);
+ case BIOLOGICAL_SCAN:
+ return (*solarSys->genFuncs->pickupLife) (
+ solarSys, world, node);
+ }
+
+ assert (false);
+ return false;
+}
+
+static void
+ScanPlanet (COUNT scanType)
+{
+#define SCAN_DURATION (ONE_SECOND * 7 / 4)
+// NUM_FLASH_COLORS for flashing blips; 1 for the final frame
+#define SCAN_LINES (MAP_HEIGHT + NUM_FLASH_COLORS + 1)
+#define SCAN_LINE_WAIT (SCAN_DURATION / SCAN_LINES)
+
+ COUNT startScan, endScan;
+ COUNT scan;
+ RECT r;
+ static const Color textColors[] =
+ {
+ SCAN_MINERAL_TEXT_COLOR,
+ SCAN_ENERGY_TEXT_COLOR,
+ SCAN_BIOLOGICAL_TEXT_COLOR,
+ };
+ static const Color tintColors[] =
+ {
+ SCAN_MINERAL_TINT_COLOR,
+ SCAN_ENERGY_TINT_COLOR,
+ SCAN_BIOLOGICAL_TINT_COLOR,
+ };
+
+ if (scanType == AUTO_SCAN)
+ {
+ startScan = MINERAL_SCAN;
+ endScan = BIOLOGICAL_SCAN;
+ }
+ else
+ {
+ startScan = scanType;
+ endScan = scanType;
+ }
+
+ for (scan = startScan; scan <= endScan; ++scan)
+ {
+ TEXT t;
+ SWORD i;
+ Color tintColor;
+ // Alpha value will be ignored.
+ TimeCount TimeOut;
+
+ t.baseline.x = SIS_SCREEN_WIDTH >> 1;
+ t.baseline.y = SIS_SCREEN_HEIGHT - MAP_HEIGHT - 7;
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+
+ t.pStr = GAME_STRING (SCAN_STRING_BASE + scan);
+
+ SetContext (PlanetContext);
+ r.corner.x = 0;
+ r.corner.y = t.baseline.y - 10;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = t.baseline.y - r.corner.y + 1;
+ // XXX: I do not know why we are repairing it here, as there
+ // should not be anything drawn over the stars at the moment
+ RepairBackRect (&r);
+
+ SetContextFont (MicroFont);
+ SetContextForeGroundColor (textColors[scan]);
+ font_DrawText (&t);
+
+ SetContext (ScanContext);
+
+ // Draw a virgin surface
+ BatchGraphics ();
+ DrawPlanet (0, BLACK_COLOR);
+ UnbatchGraphics ();
+
+ tintColor = tintColors[scan];
+
+ // Draw the scan slowly line by line
+ TimeOut = GetTimeCounter ();
+ for (i = 0; i < SCAN_LINES; i++)
+ {
+ TimeOut += SCAN_LINE_WAIT;
+ if (WaitForAnyButtonUntil (TRUE, TimeOut, FALSE))
+ break;
+
+ BatchGraphics ();
+ DrawPlanet (i, tintColor);
+ DrawScannedStuff (i, scan);
+ UnbatchGraphics ();
+#ifdef SPIN_ON_SCAN
+ RotatePlanetSphere (TRUE);
+#endif
+ }
+
+ if (i < SCAN_LINES)
+ { // Aborted by a keypress; draw in finished state
+ BatchGraphics ();
+ DrawPlanet (SCAN_LINES - 1, tintColor);
+ DrawScannedStuff (SCAN_LINES - 1, scan);
+ UnbatchGraphics ();
+ }
+ }
+
+ SetContext (PlanetContext);
+ RepairBackRect (&r);
+
+ SetContext (ScanContext);
+ if (scanType == AUTO_SCAN)
+ { // clear the last scan
+ DrawPlanet (0, BLACK_COLOR);
+ DrawScannedObjects (FALSE);
+ }
+
+ FlushInput ();
+}
+
+static BOOLEAN
+DoScan (MENU_STATE *pMS)
+{
+ BOOLEAN select, cancel;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ if (cancel || (select && pMS->CurState == EXIT_SCAN))
+ {
+ return FALSE;
+ }
+ else if (select)
+ {
+ if (pMS->CurState == DISPATCH_SHUTTLE)
+ {
+ COUNT fuel_required;
+
+ if ((pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ || (pSolarSysState->SysInfo.PlanetInfo.AtmoDensity ==
+ GAS_GIANT_ATMOSPHERE))
+ { // cannot dispatch to shielded planets or gas giants
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ fuel_required = getLandingFuelNeeded ();
+ if (GLOBAL_SIS (FuelOnBoard) < fuel_required
+ || GLOBAL_SIS (NumLanders) == 0
+ || GLOBAL_SIS (CrewEnlisted) == 0)
+ {
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ SetFlashRect (NULL);
+
+ if (!PickPlanetSide ())
+ return FALSE;
+
+ DrawMenuStateStrings (PM_MIN_SCAN, pMS->CurState);
+ SetFlashRect (SFR_MENU_3DO);
+
+ return TRUE;
+ }
+
+ // Various scans
+ if (pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ { // cannot scan shielded planets
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return TRUE;
+ }
+
+ ScanPlanet (pMS->CurState);
+ if (pMS->CurState == AUTO_SCAN)
+ {
+ pMS->CurState = DISPATCH_SHUTTLE;
+ DrawMenuStateStrings (PM_MIN_SCAN, pMS->CurState);
+ }
+ }
+ else if (optWhichMenu == OPT_PC ||
+ (!(pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ && pSolarSysState->SysInfo.PlanetInfo.AtmoDensity !=
+ GAS_GIANT_ATMOSPHERE))
+ {
+ DoMenuChooser (pMS, PM_MIN_SCAN);
+ }
+
+ return TRUE;
+}
+
+static CONTEXT
+CreateScanContext (void)
+{
+ CONTEXT oldContext;
+ CONTEXT context;
+ RECT r;
+
+ // ScanContext rect is relative to SpaceContext
+ oldContext = SetContext (SpaceContext);
+ GetContextClipRect (&r);
+
+ context = CreateContext ("ScanContext");
+ SetContext (context);
+ SetContextFGFrame (Screen);
+ r.corner.x += r.extent.width - MAP_WIDTH;
+ r.corner.y += r.extent.height - MAP_HEIGHT;
+ r.extent.width = MAP_WIDTH;
+ r.extent.height = MAP_HEIGHT;
+ SetContextClipRect (&r);
+
+ SetContext (oldContext);
+
+ return context;
+}
+
+CONTEXT
+GetScanContext (BOOLEAN *owner)
+{
+ // TODO: Make CONTEXT ref-counted
+ if (ScanContext)
+ {
+ if (owner)
+ *owner = FALSE;
+ }
+ else
+ {
+ if (owner)
+ *owner = TRUE;
+ ScanContext = CreateScanContext ();
+ }
+ return ScanContext;
+}
+
+void
+DestroyScanContext (void)
+{
+ if (ScanContext)
+ {
+ DestroyContext (ScanContext);
+ ScanContext = NULL;
+ }
+}
+
+void
+ScanSystem (void)
+{
+ MENU_STATE MenuState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ GetScanContext (NULL);
+
+ if (optWhichMenu == OPT_3DO &&
+ ((pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ || pSolarSysState->SysInfo.PlanetInfo.AtmoDensity ==
+ GAS_GIANT_ATMOSPHERE))
+ {
+ MenuState.CurState = EXIT_SCAN;
+ }
+ else
+ {
+ MenuState.CurState = AUTO_SCAN;
+ planetLoc.x = (MAP_WIDTH >> 1) << MAG_SHIFT;
+ planetLoc.y = (MAP_HEIGHT >> 1) << MAG_SHIFT;
+
+ initPlanetLocationImage ();
+ SetContext (ScanContext);
+ DrawScannedObjects (FALSE);
+ }
+
+ DrawMenuStateStrings (PM_MIN_SCAN, MenuState.CurState);
+ SetFlashRect (SFR_MENU_3DO);
+
+ if (optWhichCoarseScan == OPT_PC)
+ PrintCoarseScanPC ();
+ else
+ PrintCoarseScan3DO ();
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ MenuState.InputFunc = DoScan;
+ DoInput (&MenuState, FALSE);
+
+ SetFlashRect (NULL);
+
+ // cleanup scan graphics
+ BatchGraphics ();
+ SetContext (ScanContext);
+ DrawPlanet (0, BLACK_COLOR);
+ EraseCoarseScan ();
+ UnbatchGraphics ();
+
+ DestroyDrawable (ReleaseDrawable (eraseFrame));
+ eraseFrame = NULL;
+}
+
+static void
+generateBioNode (SOLARSYS_STATE *system, ELEMENT *NodeElementPtr,
+ BYTE *life_init_tab, COUNT creatureType)
+{
+ COUNT i;
+
+ // NOTE: TFB_Random() calls here are NOT part of the deterministic planet
+ // generation PRNG flow.
+ if (CreatureData[creatureType].Attributes & SPEED_MASK)
+ {
+ // Place moving creatures at a random location.
+ i = TFB_Random ();
+ NodeElementPtr->current.location.x =
+ (LOBYTE (i) % (MAP_WIDTH - (8 << 1))) + 8;
+ NodeElementPtr->current.location.y =
+ (HIBYTE (i) % (MAP_HEIGHT - (8 << 1))) + 8;
+ }
+
+ if (system->PlanetSideFrame[0] == 0)
+ system->PlanetSideFrame[0] =
+ CaptureDrawable (LoadGraphic (CANNISTER_MASK_PMAP_ANIM));
+
+ for (i = 0; i < MAX_LIFE_VARIATION
+ && life_init_tab[i] != (BYTE)(creatureType + 1);
+ ++i)
+ {
+ if (life_init_tab[i] != 0)
+ continue;
+
+ life_init_tab[i] = (BYTE)creatureType + 1;
+
+ system->PlanetSideFrame[i + 3] = load_life_form (creatureType);
+ break;
+ }
+
+ NodeElementPtr->mass_points = (BYTE)creatureType;
+ NodeElementPtr->hit_points = HINIBBLE (
+ CreatureData[creatureType].ValueAndHitPoints);
+ DisplayArray[NodeElementPtr->PrimIndex].
+ Object.Stamp.frame = SetAbsFrameIndex (
+ system->PlanetSideFrame[i + 3], (COUNT)TFB_Random ());
+}
+
+void
+GeneratePlanetSide (void)
+{
+ SIZE scan;
+ BYTE life_init_tab[MAX_LIFE_VARIATION];
+ // life_init_tab is filled with the creature types of already
+ // selected creatures. If an entry is 0, none has been selected
+ // yet, otherwise, it is 1 more than the creature type.
+
+ InitDisplayList ();
+ if (pSolarSysState->pOrbitalDesc->data_index & PLANET_SHIELDED)
+ return;
+
+ memset (life_init_tab, 0, sizeof life_init_tab);
+
+ for (scan = BIOLOGICAL_SCAN; scan >= MINERAL_SCAN; --scan)
+ {
+ COUNT num_nodes;
+ FRAME f;
+
+ f = SetAbsFrameIndex (MiscDataFrame,
+ NUM_SCANDOT_TRANSITIONS * (scan - ENERGY_SCAN));
+
+ num_nodes = callGenerateForScanType (pSolarSysState,
+ pSolarSysState->pOrbitalDesc, GENERATE_ALL, scan, NULL);
+
+ while (num_nodes--)
+ {
+ HELEMENT hNodeElement;
+ ELEMENT *NodeElementPtr;
+ NODE_INFO info;
+
+ if (isNodeRetrieved (&pSolarSysState->SysInfo.PlanetInfo,
+ scan, num_nodes))
+ continue;
+
+ hNodeElement = AllocElement ();
+ if (!hNodeElement)
+ continue;
+
+ LockElement (hNodeElement, &NodeElementPtr);
+
+ callGenerateForScanType (pSolarSysState,
+ pSolarSysState->pOrbitalDesc, num_nodes,
+ scan, &info);
+
+ NodeElementPtr->scan_node = MAKE_WORD (scan, num_nodes + 1);
+ NodeElementPtr->playerNr = PS_NON_PLAYER;
+ NodeElementPtr->current.location.x = info.loc_pt.x;
+ NodeElementPtr->current.location.y = info.loc_pt.y;
+
+ SetPrimType (&DisplayArray[NodeElementPtr->PrimIndex], STAMP_PRIM);
+ if (scan == MINERAL_SCAN)
+ {
+ NodeElementPtr->turn_wait = info.type;
+ NodeElementPtr->mass_points = HIBYTE (info.density);
+ NodeElementPtr->current.image.frame = SetAbsFrameIndex (
+ MiscDataFrame, (NUM_SCANDOT_TRANSITIONS * 2)
+ + ElementCategory (info.type) * 5);
+ NodeElementPtr->next.image.frame = SetRelFrameIndex (
+ NodeElementPtr->current.image.frame,
+ LOBYTE (info.density) + 1);
+ DisplayArray[NodeElementPtr->PrimIndex].Object.Stamp.frame =
+ IncFrameIndex (NodeElementPtr->next.image.frame);
+ }
+ else /* (scan == BIOLOGICAL_SCAN || scan == ENERGY_SCAN) */
+ {
+ NodeElementPtr->current.image.frame = f;
+ NodeElementPtr->next.image.frame = SetRelFrameIndex (
+ f, NUM_SCANDOT_TRANSITIONS - 1);
+ NodeElementPtr->turn_wait = MAKE_BYTE (4, 4);
+ NodeElementPtr->preprocess_func = object_animation;
+ if (scan == ENERGY_SCAN)
+ {
+ NodeElementPtr->mass_points = MAX_SCROUNGED;
+ DisplayArray[NodeElementPtr->PrimIndex].Object.Stamp.frame =
+ pSolarSysState->PlanetSideFrame[1];
+ }
+ else /* (scan == BIOLOGICAL_SCAN) */
+ {
+ generateBioNode (pSolarSysState, NodeElementPtr,
+ life_init_tab, info.type);
+ }
+ }
+
+ NodeElementPtr->next.location.x =
+ NodeElementPtr->current.location.x << MAG_SHIFT;
+ NodeElementPtr->next.location.y =
+ NodeElementPtr->current.location.y << MAG_SHIFT;
+ UnlockElement (hNodeElement);
+
+ PutElement (hNodeElement);
+ }
+ }
+}
+
+bool
+isNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr)
+{
+ return (planetInfo->ScanRetrieveMask[scanType] & ((DWORD) 1 << nodeNr))
+ != 0;
+}
+
+COUNT
+countNodesRetrieved (PLANET_INFO *planetInfo, BYTE scanType)
+{
+ COUNT count;
+ DWORD mask = planetInfo->ScanRetrieveMask[scanType];
+
+ // count the number of bits set
+ // Caution: 'mask' must be unsigned
+ for (count = 0; mask != 0; mask >>= 1)
+ {
+ if (mask & 1)
+ ++count;
+ }
+ return count;
+}
+
+void
+setNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr)
+{
+ planetInfo->ScanRetrieveMask[scanType] |= ((DWORD) 1 << nodeNr);
+}
+
+void
+setNodeNotRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr)
+{
+ planetInfo->ScanRetrieveMask[scanType] &= ~((DWORD) 1 << nodeNr);
+}
+
diff --git a/src/uqm/planets/scan.h b/src/uqm/planets/scan.h
new file mode 100644
index 0000000..f66fb0e
--- /dev/null
+++ b/src/uqm/planets/scan.h
@@ -0,0 +1,72 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_PLANETS_SCAN_H_
+#define UQM_PLANETS_SCAN_H_
+
+typedef struct scan_desc SCAN_DESC;
+typedef struct scan_block SCAN_BLOCK;
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "planets.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct scan_desc
+{
+ POINT start;
+ COUNT start_dot;
+ COUNT num_dots;
+ COUNT dots_per_semi;
+};
+
+struct scan_block
+{
+ POINT *line_base;
+ COUNT num_scans;
+ COUNT num_same_scans;
+ SCAN_DESC *scan_base;
+};
+
+extern void ScanSystem (void);
+
+extern void RepairBackRect (RECT *pRect);
+extern void GeneratePlanetSide (void);
+extern COUNT callGenerateForScanType (const SOLARSYS_STATE *,
+ const PLANET_DESC *world, COUNT node, BYTE scanType, NODE_INFO *);
+// Returns true if the node should be removed from the surface
+extern bool callPickupForScanType (SOLARSYS_STATE *solarSys,
+ PLANET_DESC *world, COUNT node, BYTE scanType);
+
+extern void RedrawSurfaceScan (const POINT *newLoc);
+extern CONTEXT GetScanContext (BOOLEAN *owner);
+extern void DestroyScanContext (void);
+
+bool isNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr);
+COUNT countNodesRetrieved (PLANET_INFO *planetInfo, BYTE scanType);
+void setNodeRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr);
+void setNodeNotRetrieved (PLANET_INFO *planetInfo, BYTE scanType, BYTE nodeNr);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_SCAN_H_ */
diff --git a/src/uqm/planets/solarsys.c b/src/uqm/planets/solarsys.c
new file mode 100644
index 0000000..11bd4c0
--- /dev/null
+++ b/src/uqm/planets/solarsys.c
@@ -0,0 +1,2021 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "solarsys.h"
+#include "lander.h"
+#include "../colors.h"
+#include "../controls.h"
+#include "../menustat.h"
+ // for DrawMenuStateStrings()
+#include "../starmap.h"
+#include "../races.h"
+#include "../gamestr.h"
+#include "../gendef.h"
+#include "../globdata.h"
+#include "../sis.h"
+#include "../init.h"
+#include "../shipcont.h"
+#include "../gameopt.h"
+#include "../nameref.h"
+#include "../resinst.h"
+#include "../settings.h"
+#include "../ipdisp.h"
+#include "../grpinfo.h"
+#include "../process.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../state.h"
+#include "../uqmdebug.h"
+#include "../save.h"
+#include "options.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+
+//#define DEBUG_SOLARSYS
+//#define SMOOTH_SYSTEM_ZOOM 1
+
+#define IP_FRAME_RATE (ONE_SECOND / 30)
+
+static BOOLEAN DoIpFlight (SOLARSYS_STATE *pSS);
+static void DrawSystem (SIZE radius, BOOLEAN IsInnerSystem);
+static FRAME CreateStarBackGround (void);
+static void DrawInnerSystem (void);
+static void DrawOuterSystem (void);
+static void ValidateOrbits (void);
+
+// SolarSysMenu() items
+enum SolarSysMenuMenuItems
+{
+ // XXX: Must match the enum in menustat.h
+ STARMAP = 1,
+ EQUIP_DEVICE,
+ CARGO,
+ ROSTER,
+ GAME_MENU,
+ NAVIGATION,
+};
+
+
+SOLARSYS_STATE *pSolarSysState;
+FRAME SISIPFrame;
+FRAME SunFrame;
+FRAME OrbitalFrame;
+FRAME SpaceJunkFrame;
+COLORMAP OrbitalCMap;
+COLORMAP SunCMap;
+MUSIC_REF SpaceMusic;
+
+SIZE EncounterRace;
+BYTE EncounterGroup;
+ // last encountered group info
+
+static FRAME StarsFrame;
+ // prepared star-field graphic
+static FRAME SolarSysFrame;
+ // saved solar system view graphic
+
+static RECT scaleRect;
+ // system zooms in when the flagship enters this rect
+
+RandomContext *SysGenRNG;
+
+#define DISPLAY_TO_LOC (DISPLAY_FACTOR >> 1)
+
+POINT
+locationToDisplay (POINT pt, SIZE scaleRadius)
+{
+ POINT out;
+
+ out.x = (SIS_SCREEN_WIDTH >> 1)
+ + (long)pt.x * DISPLAY_TO_LOC / scaleRadius;
+ out.y = (SIS_SCREEN_HEIGHT >> 1)
+ + (long)pt.y * DISPLAY_TO_LOC / scaleRadius;
+
+ return out;
+}
+
+POINT
+displayToLocation (POINT pt, SIZE scaleRadius)
+{
+ POINT out;
+
+ out.x = ((long)pt.x - (SIS_SCREEN_WIDTH >> 1))
+ * scaleRadius / DISPLAY_TO_LOC;
+ out.y = ((long)pt.y - (SIS_SCREEN_HEIGHT >> 1))
+ * scaleRadius / DISPLAY_TO_LOC;
+
+ return out;
+}
+
+POINT
+planetOuterLocation (COUNT planetI)
+{
+ SIZE scaleRadius = pSolarSysState->SunDesc[0].radius;
+ return displayToLocation (pSolarSysState->PlanetDesc[planetI].image.origin,
+ scaleRadius);
+}
+
+bool
+worldIsPlanet (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
+{
+ return world->pPrevDesc == solarSys->SunDesc;
+}
+
+bool
+worldIsMoon (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
+{
+ return world->pPrevDesc != solarSys->SunDesc;
+}
+
+// Returns the planet index of the world. If the world is a moon, then
+// this is the index of the planet it is orbiting.
+COUNT
+planetIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
+{
+ const PLANET_DESC *planet = worldIsPlanet (solarSys, world) ?
+ world : world->pPrevDesc;
+ return planet - solarSys->PlanetDesc;
+}
+
+COUNT
+moonIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *moon)
+{
+ assert (!worldIsPlanet (solarSys, moon));
+ return moon - solarSys->MoonDesc;
+}
+
+// Test whether 'world' is the planetI-th planet, and if moonI is not
+// set to MATCH_PLANET, also whether 'world' is the moonI-th moon.
+bool
+matchWorld (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world,
+ BYTE planetI, BYTE moonI)
+{
+ // Check whether we have the right planet.
+ if (planetIndex (solarSys, world) != planetI)
+ return false;
+
+ if (moonI == MATCH_PLANET)
+ {
+ // Only test whether we are at the planet.
+ if (!worldIsPlanet (solarSys, world))
+ return false;
+ }
+ else
+ {
+ // Test whether the moon matches too
+ if (!worldIsMoon (solarSys, world))
+ return false;
+
+ if (moonIndex (solarSys, world) != moonI)
+ return false;
+ }
+
+ return true;
+}
+
+bool
+playerInSolarSystem (void)
+{
+ return pSolarSysState != NULL;
+}
+
+bool
+playerInPlanetOrbit (void)
+{
+ return playerInSolarSystem () && pSolarSysState->InOrbit;
+}
+
+bool
+playerInInnerSystem (void)
+{
+ assert (playerInSolarSystem ());
+ assert (pSolarSysState->pBaseDesc == pSolarSysState->PlanetDesc
+ || pSolarSysState->pBaseDesc == pSolarSysState->MoonDesc);
+ return pSolarSysState->pBaseDesc != pSolarSysState->PlanetDesc;
+}
+
+// Sets the SysGenRNG to the required state first.
+static void
+GenerateMoons (SOLARSYS_STATE *system, PLANET_DESC *planet)
+{
+ COUNT i;
+ COUNT facing;
+ PLANET_DESC *pMoonDesc;
+
+ RandomContext_SeedRandom (SysGenRNG, planet->rand_seed);
+
+ (*system->genFuncs->generateName) (system, planet);
+ (*system->genFuncs->generateMoons) (system, planet);
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ ARCTAN (planet->location.x, planet->location.y)));
+ for (i = 0, pMoonDesc = &system->MoonDesc[0];
+ i < MAX_MOONS; ++i, ++pMoonDesc)
+ {
+ pMoonDesc->pPrevDesc = planet;
+ if (i >= planet->NumPlanets)
+ continue;
+
+ pMoonDesc->temp_color = planet->temp_color;
+ }
+}
+
+void
+FreeIPData (void)
+{
+ DestroyDrawable (ReleaseDrawable (SISIPFrame));
+ SISIPFrame = 0;
+ DestroyDrawable (ReleaseDrawable (SunFrame));
+ SunFrame = 0;
+ DestroyColorMap (ReleaseColorMap (SunCMap));
+ SunCMap = 0;
+ DestroyColorMap (ReleaseColorMap (OrbitalCMap));
+ OrbitalCMap = 0;
+ DestroyDrawable (ReleaseDrawable (OrbitalFrame));
+ OrbitalFrame = 0;
+ DestroyDrawable (ReleaseDrawable (SpaceJunkFrame));
+ SpaceJunkFrame = 0;
+ DestroyMusic (SpaceMusic);
+ SpaceMusic = 0;
+
+ RandomContext_Delete (SysGenRNG);
+ SysGenRNG = NULL;
+}
+
+void
+LoadIPData (void)
+{
+ if (SpaceJunkFrame == 0)
+ {
+ SpaceJunkFrame = CaptureDrawable (
+ LoadGraphic (IPBKGND_MASK_PMAP_ANIM));
+ SISIPFrame = CaptureDrawable (LoadGraphic (SISIP_MASK_PMAP_ANIM));
+
+ OrbitalCMap = CaptureColorMap (LoadColorMap (ORBPLAN_COLOR_MAP));
+ OrbitalFrame = CaptureDrawable (
+ LoadGraphic (ORBPLAN_MASK_PMAP_ANIM));
+ SunCMap = CaptureColorMap (LoadColorMap (IPSUN_COLOR_MAP));
+ SunFrame = CaptureDrawable (LoadGraphic (SUN_MASK_PMAP_ANIM));
+
+ SpaceMusic = LoadMusic (IP_MUSIC);
+ }
+
+ if (!SysGenRNG)
+ {
+ SysGenRNG = RandomContext_New ();
+ }
+}
+
+
+static void
+sortPlanetPositions (void)
+{
+ COUNT i;
+ SIZE sort_array[MAX_PLANETS + 1];
+
+ // When this part is done, sort_array will contain the indices to
+ // all planets, sorted on their y position.
+ // The sun itself, which has its data located at
+ // pSolarSysState->PlanetDesc[-1], is included in this array.
+ // Very ugly stuff, but it's correct.
+
+ // Initialise sort_array.
+ for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i)
+ sort_array[i] = i - 1;
+
+ // Sort sort_array, based on the positions of the planets/sun.
+ for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i)
+ {
+ COUNT j;
+
+ for (j = pSolarSysState->SunDesc[0].NumPlanets; j > i; --j)
+ {
+ SIZE real_i, real_j;
+
+ real_i = sort_array[i];
+ real_j = sort_array[j];
+ if (pSolarSysState->PlanetDesc[real_i].image.origin.y >
+ pSolarSysState->PlanetDesc[real_j].image.origin.y)
+ {
+ SIZE temp;
+
+ temp = sort_array[i];
+ sort_array[i] = sort_array[j];
+ sort_array[j] = temp;
+ }
+ }
+ }
+
+ // Put the results of the sorting in the solar system structure.
+ pSolarSysState->FirstPlanetIndex = sort_array[0];
+ pSolarSysState->LastPlanetIndex =
+ sort_array[pSolarSysState->SunDesc[0].NumPlanets];
+ for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i) {
+ PLANET_DESC *planet = &pSolarSysState->PlanetDesc[sort_array[i]];
+ planet->NextIndex = sort_array[i + 1];
+ }
+}
+
+static void
+initSolarSysSISCharacteristics (void)
+{
+ BYTE i;
+ BYTE num_thrusters;
+
+ num_thrusters = 0;
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (DriveSlots[i]) == FUSION_THRUSTER)
+ ++num_thrusters;
+ }
+ pSolarSysState->max_ship_speed = (BYTE)(
+ (num_thrusters + 5) * IP_SHIP_THRUST_INCREMENT);
+
+ pSolarSysState->turn_wait = IP_SHIP_TURN_WAIT;
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (JetSlots[i]) == TURNING_JETS)
+ pSolarSysState->turn_wait -= IP_SHIP_TURN_DECREMENT;
+ }
+}
+
+DWORD
+GetRandomSeedForStar (const STAR_DESC *star)
+{
+ return MAKE_DWORD (star->star_pt.x, star->star_pt.y);
+}
+
+// Returns an orbital PLANET_DESC when player is in orbit
+static PLANET_DESC *
+LoadSolarSys (void)
+{
+ COUNT i;
+ PLANET_DESC *orbital = NULL;
+ PLANET_DESC *pCurDesc;
+#define NUM_TEMP_RANGES 5
+ static const Color temp_color_array[NUM_TEMP_RANGES] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x0E), 0x54),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x06, 0x08), 0x62),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x0B, 0x00), 0x6D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x08, 0x00), 0x75),
+ };
+
+ RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (CurStarDescPtr));
+
+ SunFrame = SetAbsFrameIndex (SunFrame, STAR_TYPE (CurStarDescPtr->Type));
+
+ pCurDesc = &pSolarSysState->SunDesc[0];
+ pCurDesc->pPrevDesc = 0;
+ pCurDesc->rand_seed = RandomContext_Random (SysGenRNG);
+
+ pCurDesc->data_index = STAR_TYPE (CurStarDescPtr->Type);
+ pCurDesc->location.x = 0;
+ pCurDesc->location.y = 0;
+ pCurDesc->image.origin = pCurDesc->location;
+ pCurDesc->image.frame = SunFrame;
+
+ (*pSolarSysState->genFuncs->generatePlanets) (pSolarSysState);
+ if (GET_GAME_STATE (PLANETARY_CHANGE))
+ {
+ PutPlanetInfo ();
+ SET_GAME_STATE (PLANETARY_CHANGE, 0);
+ }
+
+ for (i = 0, pCurDesc = pSolarSysState->PlanetDesc;
+ i < MAX_PLANETS; ++i, ++pCurDesc)
+ {
+ pCurDesc->pPrevDesc = &pSolarSysState->SunDesc[0];
+ pCurDesc->image.origin = pCurDesc->location;
+ if (i >= pSolarSysState->SunDesc[0].NumPlanets)
+ {
+ pCurDesc->image.frame = 0;
+ }
+ else
+ {
+ COUNT index;
+ SYSTEM_INFO SysInfo;
+
+ DoPlanetaryAnalysis (&SysInfo, pCurDesc);
+ index = (SysInfo.PlanetInfo.SurfaceTemperature + 250) / 100;
+ if (index >= NUM_TEMP_RANGES)
+ index = NUM_TEMP_RANGES - 1;
+ pCurDesc->temp_color = temp_color_array[index];
+ }
+ }
+
+ sortPlanetPositions ();
+
+ if (!GLOBAL (ip_planet))
+ { // Outer system
+ pSolarSysState->pBaseDesc = pSolarSysState->PlanetDesc;
+ pSolarSysState->pOrbitalDesc = NULL;
+ }
+ else
+ { // Inner system
+ pSolarSysState->SunDesc[0].location = GLOBAL (ip_location);
+ GLOBAL (ip_location) = displayToLocation (
+ GLOBAL (ShipStamp.origin), MAX_ZOOM_RADIUS);
+
+ i = GLOBAL (ip_planet) - 1;
+ pSolarSysState->pOrbitalDesc = &pSolarSysState->PlanetDesc[i];
+ GenerateMoons (pSolarSysState, pSolarSysState->pOrbitalDesc);
+ pSolarSysState->pBaseDesc = pSolarSysState->MoonDesc;
+
+ SET_GAME_STATE (PLANETARY_LANDING, 0);
+ }
+
+ initSolarSysSISCharacteristics ();
+
+ if (GLOBAL (in_orbit))
+ { // Only when loading a game into orbital
+ i = GLOBAL (in_orbit) - 1;
+ if (i == 0)
+ { // Orbiting the planet itself
+ orbital = pSolarSysState->pBaseDesc->pPrevDesc;
+ }
+ else
+ { // Orbiting a moon
+ // -1 because planet itself is 1, and moons have to be 1-based
+ i -= 1;
+ orbital = &pSolarSysState->MoonDesc[i];
+ }
+ GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
+ GLOBAL (in_orbit) = 0;
+ }
+ else
+ {
+ i = GLOBAL (ShipFacing);
+ // XXX: Solar system reentry test depends on ShipFacing != 0
+ if (i == 0)
+ ++i;
+
+ GLOBAL (ShipStamp.frame) = SetAbsFrameIndex (SISIPFrame, i - 1);
+ }
+
+ return orbital;
+}
+
+static void
+saveNonOrbitalLocation (void)
+{
+ // XXX: Solar system reentry test depends on ShipFacing != 0
+ GLOBAL (ShipFacing) = GetFrameIndex (GLOBAL (ShipStamp.frame)) + 1;
+ GLOBAL (in_orbit) = 0;
+ if (!playerInInnerSystem ())
+ {
+ GLOBAL (ip_planet) = 0;
+ }
+ else
+ {
+ // ip_planet is 1-based because code tests for ip_planet!=0
+ GLOBAL (ip_planet) = 1 + planetIndex (pSolarSysState,
+ pSolarSysState->pOrbitalDesc);
+ GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
+ }
+}
+
+static void
+FreeSolarSys (void)
+{
+ if (pSolarSysState->InIpFlight)
+ {
+ pSolarSysState->InIpFlight = FALSE;
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ saveNonOrbitalLocation ();
+ }
+
+ DestroyDrawable (ReleaseDrawable (SolarSysFrame));
+ SolarSysFrame = NULL;
+
+ StopMusic ();
+
+// FreeIPData ();
+}
+
+static FRAME
+getCollisionFrame (PLANET_DESC *planet, COUNT WaitPlanet)
+{
+ if (pSolarSysState->WaitIntersect != (COUNT)~0
+ && pSolarSysState->WaitIntersect != WaitPlanet)
+ { // New collisions are with a single point (center of planet)
+ return DecFrameIndex (stars_in_space);
+ }
+ else
+ { // Existing collisions are cleared only once the ship does not
+ // intersect anymore with a full planet image
+ return planet->image.frame;
+ }
+}
+
+// Returns the planet with which the flagship is colliding
+static PLANET_DESC *
+CheckIntersect (void)
+{
+ COUNT i;
+ PLANET_DESC *pCurDesc;
+ INTERSECT_CONTROL ShipIntersect, PlanetIntersect;
+ COUNT NewWaitPlanet;
+ BYTE PlanetOffset, MoonOffset;
+
+ // Check collisions with the system center object
+ // This may be the planet in inner view, or the sun
+ pCurDesc = pSolarSysState->pBaseDesc->pPrevDesc;
+ PlanetOffset = pCurDesc - pSolarSysState->PlanetDesc + 1;
+ MoonOffset = 1; // the planet itself
+
+ ShipIntersect.IntersectStamp.origin = GLOBAL (ShipStamp.origin);
+ ShipIntersect.EndPoint = ShipIntersect.IntersectStamp.origin;
+ ShipIntersect.IntersectStamp.frame = GLOBAL (ShipStamp.frame);
+
+ PlanetIntersect.IntersectStamp.origin.x = SIS_SCREEN_WIDTH >> 1;
+ PlanetIntersect.IntersectStamp.origin.y = SIS_SCREEN_HEIGHT >> 1;
+ PlanetIntersect.EndPoint = PlanetIntersect.IntersectStamp.origin;
+
+ PlanetIntersect.IntersectStamp.frame = getCollisionFrame (pCurDesc,
+ MAKE_WORD (PlanetOffset, MoonOffset));
+
+ // Start with no collisions
+ NewWaitPlanet = 0;
+
+ if (pCurDesc != pSolarSysState->SunDesc /* can't intersect with sun */
+ && DrawablesIntersect (&ShipIntersect,
+ &PlanetIntersect, MAX_TIME_VALUE))
+ {
+#ifdef DEBUG_SOLARSYS
+ log_add (log_Debug, "0: Planet %d, Moon %d", PlanetOffset,
+ MoonOffset);
+#endif /* DEBUG_SOLARSYS */
+ NewWaitPlanet = MAKE_WORD (PlanetOffset, MoonOffset);
+ if (pSolarSysState->WaitIntersect != (COUNT)~0
+ && pSolarSysState->WaitIntersect != NewWaitPlanet)
+ {
+ pSolarSysState->WaitIntersect = NewWaitPlanet;
+#ifdef DEBUG_SOLARSYS
+ log_add (log_Debug, "Star index = %d, Planet index = %d, <%d, %d>",
+ CurStarDescPtr - star_array,
+ pCurDesc - pSolarSysState->PlanetDesc,
+ pSolarSysState->SunDesc[0].location.x,
+ pSolarSysState->SunDesc[0].location.y);
+#endif /* DEBUG_SOLARSYS */
+ return pCurDesc;
+ }
+ }
+
+ for (i = pCurDesc->NumPlanets,
+ pCurDesc = pSolarSysState->pBaseDesc; i; --i, ++pCurDesc)
+ {
+ PlanetIntersect.IntersectStamp.origin = pCurDesc->image.origin;
+ PlanetIntersect.EndPoint = PlanetIntersect.IntersectStamp.origin;
+ if (playerInInnerSystem ())
+ {
+ PlanetOffset = pCurDesc->pPrevDesc -
+ pSolarSysState->PlanetDesc;
+ MoonOffset = pCurDesc - pSolarSysState->MoonDesc + 2;
+ }
+ else
+ {
+ PlanetOffset = pCurDesc - pSolarSysState->PlanetDesc;
+ MoonOffset = 0;
+ }
+ ++PlanetOffset;
+ PlanetIntersect.IntersectStamp.frame = getCollisionFrame (pCurDesc,
+ MAKE_WORD (PlanetOffset, MoonOffset));
+
+ if (DrawablesIntersect (&ShipIntersect,
+ &PlanetIntersect, MAX_TIME_VALUE))
+ {
+#ifdef DEBUG_SOLARSYS
+ log_add (log_Debug, "1: Planet %d, Moon %d", PlanetOffset,
+ MoonOffset);
+#endif /* DEBUG_SOLARSYS */
+ NewWaitPlanet = MAKE_WORD (PlanetOffset, MoonOffset);
+
+ if (pSolarSysState->WaitIntersect == (COUNT)~0)
+ { // All collisions disallowed, but the ship is still colliding
+ // with something. Collisions will remain disabled.
+ break;
+ }
+ else if (pSolarSysState->WaitIntersect == NewWaitPlanet)
+ { // Existing and continued collision -- ignore
+ continue;
+ }
+
+ // Collision with a new planet/moon. This may cause a transition
+ // to an inner system or start an orbital view.
+ pSolarSysState->WaitIntersect = NewWaitPlanet;
+ return pCurDesc;
+ }
+ }
+
+ // This records the planet/moon with which the ship just collided
+ // It may be a previously existing collision also (the value won't change)
+ // If all collisions were disabled, this will reenable then once the ship
+ // stops colliding with any planets
+ if (pSolarSysState->WaitIntersect != (COUNT)~0 || NewWaitPlanet == 0)
+ pSolarSysState->WaitIntersect = NewWaitPlanet;
+
+ return NULL;
+}
+
+static void
+GetOrbitRect (RECT *pRect, COORD dx, COORD dy, SIZE radius,
+ int xnumer, int ynumer, int denom)
+{
+ pRect->corner.x = (SIS_SCREEN_WIDTH >> 1) + (long)-dx * xnumer / denom;
+ pRect->corner.y = (SIS_SCREEN_HEIGHT >> 1) + (long)-dy * ynumer / denom;
+ pRect->extent.width = (long)radius * (xnumer << 1) / denom;
+ pRect->extent.height = pRect->extent.width >> 1;
+}
+
+static void
+GetPlanetOrbitRect (RECT *r, PLANET_DESC *planet, int sizeNumer,
+ int dyNumer, int denom)
+{
+ COORD dx, dy;
+
+ dx = planet->radius;
+ dy = planet->radius;
+ if (sizeNumer > DISPLAY_FACTOR)
+ {
+ dx = dx + planet->location.x;
+ dy = (dy + planet->location.y) << 1;
+ }
+ GetOrbitRect (r, dx, dy, planet->radius, sizeNumer, dyNumer, denom);
+}
+
+static void
+ValidateOrbit (PLANET_DESC *planet, int sizeNumer, int dyNumer, int denom)
+{
+ COUNT index;
+
+ if (sizeNumer <= DISPLAY_FACTOR)
+ { // All planets in outer view, and moons in inner
+ RECT r;
+
+ GetPlanetOrbitRect (&r, planet, sizeNumer, dyNumer, denom);
+
+ // Calculate the location of the planet's image
+ r.corner.x += (r.extent.width >> 1);
+ r.corner.y += (r.extent.height >> 1);
+ r.corner.x += (long)planet->location.x * sizeNumer / denom;
+ // Ellipse function always has coefficients a^2 = 2 * b^2
+ r.corner.y += (long)planet->location.y * (sizeNumer / 2) / denom;
+
+ planet->image.origin = r.corner;
+ }
+
+ // Calculate the size and lighting angle of planet's image and
+ // set the image that will be drawn
+ index = planet->data_index & ~WORLD_TYPE_SPECIAL;
+ if (index < NUMBER_OF_PLANET_TYPES)
+ { // The world is a normal planetary body (planet or moon)
+ BYTE Type;
+ COUNT Size;
+ COUNT angle;
+
+ Type = PlanData[index].Type;
+ Size = PLANSIZE (Type);
+ if (sizeNumer > DISPLAY_FACTOR)
+ {
+ Size += 3;
+ }
+ else if (worldIsMoon (pSolarSysState, planet))
+ {
+ Size += 2;
+ }
+ else if (denom <= (MAX_ZOOM_RADIUS >> 2))
+ {
+ ++Size;
+ if (denom == MIN_ZOOM_RADIUS)
+ ++Size;
+ }
+
+ if (worldIsPlanet (pSolarSysState, planet))
+ { // Planet
+ angle = ARCTAN (planet->location.x, planet->location.y);
+ }
+ else
+ { // Moon
+ angle = ARCTAN (planet->pPrevDesc->location.x,
+ planet->pPrevDesc->location.y);
+ }
+ planet->image.frame = SetAbsFrameIndex (OrbitalFrame,
+ (Size << FACING_SHIFT) + NORMALIZE_FACING (
+ ANGLE_TO_FACING (angle)));
+ }
+ else if (planet->data_index == HIERARCHY_STARBASE)
+ {
+ planet->image.frame = SetAbsFrameIndex (SpaceJunkFrame, 16);
+ }
+ else if (planet->data_index == SA_MATRA)
+ {
+ planet->image.frame = SetAbsFrameIndex (SpaceJunkFrame, 19);
+ }
+}
+
+static void
+DrawOrbit (PLANET_DESC *planet, int sizeNumer, int dyNumer, int denom)
+{
+ RECT r;
+
+ GetPlanetOrbitRect (&r, planet, sizeNumer, dyNumer, denom);
+
+ SetContextForeGroundColor (planet->temp_color);
+ DrawOval (&r, 1);
+}
+
+static SIZE
+FindRadius (POINT shipLoc, SIZE fromRadius)
+{
+ SIZE nextRadius;
+ POINT displayLoc;
+
+ do
+ {
+ fromRadius >>= 1;
+ if (fromRadius > MIN_ZOOM_RADIUS)
+ nextRadius = fromRadius >> 1;
+ else
+ nextRadius = 0; // scaleRect will be nul
+
+ GetOrbitRect (&scaleRect, nextRadius, nextRadius, nextRadius,
+ DISPLAY_FACTOR, DISPLAY_FACTOR >> 2, fromRadius);
+ displayLoc = locationToDisplay (shipLoc, fromRadius);
+
+ } while (pointWithinRect (scaleRect, displayLoc));
+
+ return fromRadius;
+}
+
+static UWORD
+flagship_inertial_thrust (COUNT CurrentAngle)
+{
+ BYTE max_speed;
+ SIZE cur_delta_x, cur_delta_y;
+ COUNT TravelAngle;
+ VELOCITY_DESC *VelocityPtr;
+
+ max_speed = pSolarSysState->max_ship_speed;
+ VelocityPtr = &GLOBAL (velocity);
+ GetCurrentVelocityComponents (VelocityPtr, &cur_delta_x, &cur_delta_y);
+ TravelAngle = GetVelocityTravelAngle (VelocityPtr);
+ if (TravelAngle == CurrentAngle
+ && cur_delta_x == COSINE (CurrentAngle, max_speed)
+ && cur_delta_y == SINE (CurrentAngle, max_speed))
+ return (SHIP_AT_MAX_SPEED);
+ else
+ {
+ SIZE delta_x, delta_y;
+ DWORD desired_speed;
+
+ delta_x = cur_delta_x
+ + COSINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT);
+ delta_y = cur_delta_y
+ + SINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT);
+ desired_speed = (DWORD) ((long) delta_x * delta_x)
+ + (DWORD) ((long) delta_y * delta_y);
+ if (desired_speed <= (DWORD) ((UWORD) max_speed * max_speed))
+ SetVelocityComponents (VelocityPtr, delta_x, delta_y);
+ else if (TravelAngle == CurrentAngle)
+ {
+ SetVelocityComponents (VelocityPtr,
+ COSINE (CurrentAngle, max_speed),
+ SINE (CurrentAngle, max_speed));
+ return (SHIP_AT_MAX_SPEED);
+ }
+ else
+ {
+ VELOCITY_DESC v;
+
+ v = *VelocityPtr;
+
+ DeltaVelocityComponents (&v,
+ COSINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT >> 1)
+ - COSINE (TravelAngle, IP_SHIP_THRUST_INCREMENT),
+ SINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT >> 1)
+ - SINE (TravelAngle, IP_SHIP_THRUST_INCREMENT));
+ GetCurrentVelocityComponents (&v, &cur_delta_x, &cur_delta_y);
+ desired_speed =
+ (DWORD) ((long) cur_delta_x * cur_delta_x)
+ + (DWORD) ((long) cur_delta_y * cur_delta_y);
+ if (desired_speed > (DWORD) ((UWORD) max_speed * max_speed))
+ {
+ SetVelocityComponents (VelocityPtr,
+ COSINE (CurrentAngle, max_speed),
+ SINE (CurrentAngle, max_speed));
+ return (SHIP_AT_MAX_SPEED);
+ }
+
+ *VelocityPtr = v;
+ }
+
+ return 0;
+ }
+}
+
+static void
+ProcessShipControls (void)
+{
+ COUNT index;
+ SIZE delta_x, delta_y;
+
+ if (CurrentInputState.key[PlayerControls[0]][KEY_UP])
+ delta_y = -1;
+ else
+ delta_y = 0;
+
+ delta_x = 0;
+ if (CurrentInputState.key[PlayerControls[0]][KEY_LEFT])
+ delta_x -= 1;
+ if (CurrentInputState.key[PlayerControls[0]][KEY_RIGHT])
+ delta_x += 1;
+
+ if (delta_x || delta_y < 0)
+ {
+ GLOBAL (autopilot.x) = ~0;
+ GLOBAL (autopilot.y) = ~0;
+ }
+ else if (GLOBAL (autopilot.x) != ~0 && GLOBAL (autopilot.y) != ~0)
+ delta_y = -1;
+ else
+ delta_y = 0;
+
+ index = GetFrameIndex (GLOBAL (ShipStamp.frame));
+ if (pSolarSysState->turn_counter)
+ --pSolarSysState->turn_counter;
+ else if (delta_x)
+ {
+ if (delta_x < 0)
+ index = NORMALIZE_FACING (index - 1);
+ else
+ index = NORMALIZE_FACING (index + 1);
+
+ GLOBAL (ShipStamp.frame) =
+ SetAbsFrameIndex (GLOBAL (ShipStamp.frame), index);
+
+ pSolarSysState->turn_counter = pSolarSysState->turn_wait;
+ }
+ if (pSolarSysState->thrust_counter)
+ --pSolarSysState->thrust_counter;
+ else if (delta_y < 0)
+ {
+#define THRUST_WAIT 1
+ flagship_inertial_thrust (FACING_TO_ANGLE (index));
+
+ pSolarSysState->thrust_counter = THRUST_WAIT;
+ }
+}
+
+static void
+enterInnerSystem (PLANET_DESC *planet)
+{
+#define INNER_ENTRY_DISTANCE (MIN_MOON_RADIUS + ((MAX_MOONS - 1) \
+ * MOON_DELTA) + (MOON_DELTA / 4))
+ COUNT angle;
+
+ // Calculate the inner system entry location and facing
+ angle = FACING_TO_ANGLE (GetFrameIndex (GLOBAL (ShipStamp.frame)))
+ + HALF_CIRCLE;
+ GLOBAL (ShipStamp.origin.x) = (SIS_SCREEN_WIDTH >> 1)
+ + COSINE (angle, INNER_ENTRY_DISTANCE);
+ GLOBAL (ShipStamp.origin.y) = (SIS_SCREEN_HEIGHT >> 1)
+ + SINE (angle, INNER_ENTRY_DISTANCE);
+ if (GLOBAL (ShipStamp.origin.y) < 0)
+ GLOBAL (ShipStamp.origin.y) = 1;
+ else if (GLOBAL (ShipStamp.origin.y) >= SIS_SCREEN_HEIGHT)
+ GLOBAL (ShipStamp.origin.y) =
+ (SIS_SCREEN_HEIGHT - 1) - 1;
+
+ GLOBAL (ip_location) = displayToLocation (
+ GLOBAL (ShipStamp.origin), MAX_ZOOM_RADIUS);
+
+ pSolarSysState->SunDesc[0].location =
+ planetOuterLocation (planetIndex (pSolarSysState, planet));
+ ZeroVelocityComponents (&GLOBAL (velocity));
+
+ GenerateMoons (pSolarSysState, planet);
+ pSolarSysState->pBaseDesc = pSolarSysState->MoonDesc;
+ pSolarSysState->pOrbitalDesc = planet;
+}
+
+static void
+leaveInnerSystem (PLANET_DESC *planet)
+{
+ COUNT outerPlanetWait;
+
+ pSolarSysState->pBaseDesc = pSolarSysState->PlanetDesc;
+ pSolarSysState->pOrbitalDesc = NULL;
+
+ outerPlanetWait = MAKE_WORD (planet - pSolarSysState->PlanetDesc + 1, 0);
+ GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
+ XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
+ ZeroVelocityComponents (&GLOBAL (velocity));
+
+ // Now the ship is in outer system (as per game logic)
+
+ pSolarSysState->WaitIntersect = outerPlanetWait;
+ // See if we also intersect with another planet, and if we do,
+ // disable collisions comletely until we stop intersecting
+ // with any planet at all.
+ CheckIntersect ();
+ if (pSolarSysState->WaitIntersect != outerPlanetWait)
+ pSolarSysState->WaitIntersect = (COUNT)~0;
+}
+
+static void
+enterOrbital (PLANET_DESC *planet)
+{
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ pSolarSysState->pOrbitalDesc = planet;
+ pSolarSysState->InOrbit = TRUE;
+}
+
+static BOOLEAN
+CheckShipLocation (SIZE *newRadius)
+{
+ SIZE radius;
+
+ radius = pSolarSysState->SunDesc[0].radius;
+ *newRadius = pSolarSysState->SunDesc[0].radius;
+
+ if (GLOBAL (ShipStamp.origin.x) < 0
+ || GLOBAL (ShipStamp.origin.x) >= SIS_SCREEN_WIDTH
+ || GLOBAL (ShipStamp.origin.y) < 0
+ || GLOBAL (ShipStamp.origin.y) >= SIS_SCREEN_HEIGHT)
+ {
+ // The ship leaves the screen.
+ if (!playerInInnerSystem ())
+ { // Outer zoom-out transition
+ if (radius == MAX_ZOOM_RADIUS)
+ {
+ // The ship leaves IP.
+ GLOBAL (CurrentActivity) |= END_INTERPLANETARY;
+ return FALSE; // no location change
+ }
+
+ *newRadius = FindRadius (GLOBAL (ip_location),
+ MAX_ZOOM_RADIUS << 1);
+ }
+ else
+ {
+ leaveInnerSystem (pSolarSysState->pOrbitalDesc);
+ }
+
+ return TRUE;
+ }
+
+ if (!playerInInnerSystem ()
+ && pointWithinRect (scaleRect, GLOBAL (ShipStamp.origin)))
+ { // Outer zoom-in transition
+ *newRadius = FindRadius (GLOBAL (ip_location), radius);
+ return TRUE;
+ }
+
+ if (GLOBAL (autopilot.x) == ~0 && GLOBAL (autopilot.y) == ~0)
+ { // Not on autopilot -- may collide with a planet
+ PLANET_DESC *planet = CheckIntersect ();
+ if (planet)
+ { // Collision with a planet
+ if (playerInInnerSystem ())
+ { // Entering planet orbit (scans, etc.)
+ enterOrbital (planet);
+ return FALSE; // no location change
+ }
+ else
+ { // Transition to inner system
+ enterInnerSystem (planet);
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE; // no location change
+}
+
+static void
+DrawSystemTransition (BOOLEAN inner)
+{
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ if (inner)
+ DrawInnerSystem ();
+ else
+ DrawOuterSystem ();
+ RedrawQueue (FALSE);
+ ScreenTransition (3, NULL);
+ UnbatchGraphics ();
+}
+
+static void
+TransitionSystemIn (void)
+{
+ SetContext (SpaceContext);
+ DrawSystemTransition (playerInInnerSystem ());
+}
+
+static void
+ScaleSystem (SIZE new_radius)
+{
+#ifdef SMOOTH_SYSTEM_ZOOM
+ // XXX: This appears to have been an attempt to zoom the system view
+ // in a different way. This code zooms gradually instead of
+ // doing a crossfade from one zoom level to the other.
+ // TODO: Do not loop here, and instead increment the zoom level
+ // in IP_frame() with a function drawing the new zoom. The ship
+ // controls are not handled in the loop, and the flagship
+ // can collide with a group while zooming, and that is not handled
+ // 100% correctly.
+#define NUM_STEPS 10
+ COUNT i;
+ SIZE old_radius;
+ SIZE d, step;
+
+ old_radius = pSolarSysState->SunDesc[0].radius;
+
+ assert (old_radius != 0);
+ assert (old_radius != new_radius);
+
+ d = new_radius - old_radius;
+ step = d / NUM_STEPS;
+
+ for (i = 0; i < NUM_STEPS - 1; ++i)
+ {
+ pSolarSysState->SunDesc[0].radius += step;
+ XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
+
+ BatchGraphics ();
+ DrawOuterSystem ();
+ RedrawQueue (FALSE);
+ UnbatchGraphics ();
+
+ SleepThread (ONE_SECOND / 30);
+ }
+
+ // Final zoom step
+ pSolarSysState->SunDesc[0].radius = new_radius;
+ XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
+
+ BatchGraphics ();
+ DrawOuterSystem ();
+ RedrawQueue (FALSE);
+ UnbatchGraphics ();
+
+#else // !SMOOTH_SYSTEM_ZOOM
+ RECT r;
+
+ pSolarSysState->SunDesc[0].radius = new_radius;
+ XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
+
+ GetContextClipRect (&r);
+ SetTransitionSource (&r);
+ BatchGraphics ();
+ DrawOuterSystem ();
+ RedrawQueue (FALSE);
+ ScreenTransition (3, &r);
+ UnbatchGraphics ();
+#endif // SMOOTH_SYSTEM_ZOOM
+}
+
+static void
+RestoreSystemView (void)
+{
+ STAMP s;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SolarSysFrame;
+ DrawStamp (&s);
+}
+
+// Normally called by DoIpFlight() to process a frame
+static void
+IP_frame (void)
+{
+ BOOLEAN locChange;
+ SIZE newRadius;
+
+ SetContext (SpaceContext);
+
+ GameClockTick ();
+ ProcessShipControls ();
+
+ locChange = CheckShipLocation (&newRadius);
+ if (locChange)
+ {
+ if (playerInInnerSystem ())
+ { // Entering inner system
+ DrawSystemTransition (TRUE);
+ }
+ else if (pSolarSysState->SunDesc[0].radius == newRadius)
+ { // Leaving inner system to outer
+ DrawSystemTransition (FALSE);
+ }
+ else
+ { // Zooming outer system
+ ScaleSystem (newRadius);
+ }
+ }
+ else
+ { // Just flying around, minding own business..
+ BatchGraphics ();
+ RestoreSystemView ();
+ RedrawQueue (FALSE);
+ DrawAutoPilotMessage (FALSE);
+ UnbatchGraphics ();
+ }
+
+}
+
+static BOOLEAN
+CheckZoomLevel (void)
+{
+ BOOLEAN InnerSystem;
+ POINT shipLoc;
+
+ InnerSystem = playerInInnerSystem ();
+ if (InnerSystem)
+ shipLoc = pSolarSysState->SunDesc[0].location;
+ else
+ shipLoc = GLOBAL (ip_location);
+
+ pSolarSysState->SunDesc[0].radius = FindRadius (shipLoc,
+ MAX_ZOOM_RADIUS << 1);
+ if (!InnerSystem)
+ { // Update ship stamp since the radius probably changed
+ XFormIPLoc (&shipLoc, &GLOBAL (ShipStamp.origin), TRUE);
+ }
+
+ return InnerSystem;
+}
+
+static void
+ValidateOrbits (void)
+{
+ COUNT i;
+ PLANET_DESC *planet;
+
+ for (i = pSolarSysState->SunDesc[0].NumPlanets,
+ planet = &pSolarSysState->PlanetDesc[0]; i; --i, ++planet)
+ {
+ ValidateOrbit (planet, DISPLAY_FACTOR, DISPLAY_FACTOR / 4,
+ pSolarSysState->SunDesc[0].radius);
+ }
+}
+
+static void
+ValidateInnerOrbits (void)
+{
+ COUNT i;
+ PLANET_DESC *planet;
+
+ assert (playerInInnerSystem ());
+
+ planet = pSolarSysState->pOrbitalDesc;
+ ValidateOrbit (planet, DISPLAY_FACTOR * 4, DISPLAY_FACTOR,
+ planet->radius);
+
+ for (i = 0; i < planet->NumPlanets; ++i)
+ {
+ PLANET_DESC *moon = &pSolarSysState->MoonDesc[i];
+ ValidateOrbit (moon, 2, 1, 2);
+ }
+}
+
+static void
+DrawInnerSystem (void)
+{
+ ValidateInnerOrbits ();
+ DrawSystem (pSolarSysState->pOrbitalDesc->radius, TRUE);
+ DrawSISTitle (GLOBAL_SIS (PlanetName));
+}
+
+static void
+DrawOuterSystem (void)
+{
+ ValidateOrbits ();
+ DrawSystem (pSolarSysState->SunDesc[0].radius, FALSE);
+ DrawHyperCoords (CurStarDescPtr->star_pt);
+}
+
+static void
+ResetSolarSys (void)
+{
+ // Originally there was a flash_task test here, however, I found no cases
+ // where flash_task could be set at the time of call. The test was
+ // probably needed on 3DO when IP_frame() was a task.
+ assert (!pSolarSysState->InIpFlight);
+
+ DrawMenuStateStrings (PM_STARMAP, -(PM_NAVIGATE - PM_SCAN));
+
+ InitDisplayList ();
+ // This also spawns the flagship element
+ DoMissions ();
+
+ // Figure out and note which planet/moon we just left, if any
+ // This records any existing collision and prevents the ship
+ // from entering planets until a new collision occurs.
+ // TODO: this may need logic similar to one in leaveInnerSystem()
+ // for when the ship collides with more than one planet at
+ // the same time. While quite rare, it's still possible.
+ CheckIntersect ();
+
+ pSolarSysState->InIpFlight = TRUE;
+
+ // Do not start playing the music if we entered the solarsys only
+ // to load a game (load invoked from Main menu)
+ // XXX: This is quite hacky
+ if (!PLRPlaying ((MUSIC_REF)~0) &&
+ (LastActivity != CHECK_LOAD || NextActivity))
+ {
+ PlayMusic (SpaceMusic, TRUE, 1);
+ }
+}
+
+static void
+EnterPlanetOrbit (void)
+{
+ if (pSolarSysState->InIpFlight)
+ { // This means we hit a planet in IP flight; not a Load into orbit
+ FreeSolarSys ();
+
+ if (worldIsMoon (pSolarSysState, pSolarSysState->pOrbitalDesc))
+ { // Moon -- use its origin
+ // XXX: The conversion functions do not error-correct, so the
+ // point we set here will change once flag_ship_preprocess()
+ // in ipdisp.c starts over again.
+ GLOBAL (ShipStamp.origin) =
+ pSolarSysState->pOrbitalDesc->image.origin;
+ }
+ else
+ { // Planet -- its origin is for the outer view, so use mid-screen
+ GLOBAL (ShipStamp.origin.x) = SIS_SCREEN_WIDTH >> 1;
+ GLOBAL (ShipStamp.origin.y) = SIS_SCREEN_HEIGHT >> 1;
+ }
+ }
+
+ GetPlanetInfo ();
+ (*pSolarSysState->genFuncs->generateOrbital) (pSolarSysState,
+ pSolarSysState->pOrbitalDesc);
+ LastActivity &= ~(CHECK_LOAD | CHECK_RESTART);
+ if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD |
+ START_ENCOUNTER)) || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0
+ || GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ return;
+
+ // Implement a to-do in generate.h for a better test
+ if (pSolarSysState->TopoFrame)
+ { // We've entered orbit; LoadPlanet() called planet surface-gen code
+ PlanetOrbitMenu ();
+ FreePlanet ();
+ }
+ // Otherwise, generateOrbital function started a homeworld conversation,
+ // and we did not get to the planet no matter what.
+
+ // START_ENCOUNTER could be set by Devices menu a number of ways:
+ // Talking Pet, Sun Device or a Caster over Chmmr, or
+ // a Caster for Ilwrath
+ // Could also have blown self up with Utwig Bomb
+ if (!(GLOBAL (CurrentActivity) & (START_ENCOUNTER |
+ CHECK_ABORT | CHECK_LOAD))
+ && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
+ { // Reload the system and return to the inner view
+ PLANET_DESC *orbital = LoadSolarSys ();
+ assert (!orbital);
+ CheckZoomLevel ();
+ ValidateOrbits ();
+ ValidateInnerOrbits ();
+ ResetSolarSys ();
+
+ RepairSISBorder ();
+ TransitionSystemIn ();
+ }
+}
+
+static void
+InitSolarSys (void)
+{
+ BOOLEAN InnerSystem;
+ BOOLEAN Reentry;
+ PLANET_DESC *orbital;
+
+
+ LoadIPData ();
+ LoadLanderData ();
+
+ Reentry = (GLOBAL (ShipFacing) != 0);
+ if (!Reentry)
+ {
+ GLOBAL (autopilot.x) = ~0;
+ GLOBAL (autopilot.y) = ~0;
+
+ GLOBAL (ShipStamp.origin.x) = SIS_SCREEN_WIDTH >> 1;
+ GLOBAL (ShipStamp.origin.y) = SIS_SCREEN_HEIGHT - 2;
+
+ GLOBAL (ip_location) = displayToLocation (GLOBAL (ShipStamp.origin),
+ MAX_ZOOM_RADIUS);
+ }
+
+
+ StarsFrame = CreateStarBackGround ();
+
+ SetContext (SpaceContext);
+ SetContextFGFrame (Screen);
+ SetContextBackGroundColor (BLACK_COLOR);
+
+
+ orbital = LoadSolarSys ();
+ InnerSystem = CheckZoomLevel ();
+ ValidateOrbits ();
+ if (InnerSystem)
+ ValidateInnerOrbits ();
+
+ if (Reentry)
+ {
+ (*pSolarSysState->genFuncs->reinitNpcs) (pSolarSysState);
+ }
+ else
+ {
+ EncounterRace = -1;
+ EncounterGroup = 0;
+ GLOBAL (BattleGroupRef) = 0;
+ ReinitQueue (&GLOBAL (ip_group_q));
+ ReinitQueue (&GLOBAL (npc_built_ship_q));
+ (*pSolarSysState->genFuncs->initNpcs) (pSolarSysState);
+ }
+
+ if (orbital)
+ {
+ enterOrbital (orbital);
+ }
+ else
+ { // Draw the borders, the system (inner or outer) and fade/transition
+ SetContext (SpaceContext);
+
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+
+ DrawSISFrame ();
+ DrawSISMessage (NULL);
+
+ ResetSolarSys ();
+
+ if (LastActivity == (CHECK_LOAD | CHECK_RESTART))
+ { // Starting a new game, NOT from load!
+ // We have to fade the screen in from intro or menu
+ DrawOuterSystem ();
+ RedrawQueue (FALSE);
+ UnbatchGraphics ();
+ FadeScreen (FadeAllToColor, ONE_SECOND / 2);
+
+ LastActivity = 0;
+ }
+ else if (LastActivity == CHECK_LOAD && !NextActivity)
+ { // Called just to load a game; invoked from Main menu
+ // No point in drawing anything
+ UnbatchGraphics ();
+ }
+ else
+ { // Entered a new system, or loaded into inner or outer
+ if (InnerSystem)
+ DrawInnerSystem ();
+ else
+ DrawOuterSystem ();
+ RedrawQueue (FALSE);
+ ScreenTransition (3, NULL);
+ UnbatchGraphics ();
+
+ LastActivity &= ~CHECK_LOAD;
+ }
+ }
+}
+
+static void
+endInterPlanetary (void)
+{
+ GLOBAL (CurrentActivity) &= ~END_INTERPLANETARY;
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ // These are game state changing ops and so cannot be
+ // called once another game has been loaded!
+ (*pSolarSysState->genFuncs->uninitNpcs) (pSolarSysState);
+ SET_GAME_STATE (USED_BROADCASTER, 0);
+ }
+}
+
+// Find the closest planet to a point, in interplanetary.
+static PLANET_DESC *
+closestPlanetInterPlanetary (const POINT *point)
+{
+ BYTE i;
+ BYTE numPlanets;
+ DWORD bestDistSquared;
+ PLANET_DESC *bestPlanet = NULL;
+
+ assert(pSolarSysState != NULL);
+
+ numPlanets = pSolarSysState->SunDesc[0].NumPlanets;
+
+ bestDistSquared = (DWORD) -1; // Maximum value of DWORD.
+ for (i = 0; i < numPlanets; i++)
+ {
+ PLANET_DESC *planet = &pSolarSysState->PlanetDesc[i];
+
+ SIZE dx = point->x - planet->image.origin.x;
+ SIZE dy = point->y - planet->image.origin.y;
+
+ DWORD distSquared = (DWORD) ((long) dx * dx + (long) dy * dy);
+ if (distSquared < bestDistSquared)
+ {
+ bestDistSquared = distSquared;
+ bestPlanet = planet;
+ }
+ }
+
+ return bestPlanet;
+}
+
+static void
+UninitSolarSys (void)
+{
+ FreeSolarSys ();
+
+//FreeLanderData ();
+//FreeIPData ();
+
+ DestroyDrawable (ReleaseDrawable (StarsFrame));
+ StarsFrame = NULL;
+
+ if (GLOBAL (CurrentActivity) & END_INTERPLANETARY)
+ {
+ endInterPlanetary ();
+ return;
+ }
+
+ if ((GLOBAL (CurrentActivity) & START_ENCOUNTER) && EncounterGroup)
+ {
+ GetGroupInfo (GLOBAL (BattleGroupRef), EncounterGroup);
+ // Generate the encounter location name based on the closest planet
+
+ if (GLOBAL (ip_planet) == 0)
+ {
+ PLANET_DESC *planet =
+ closestPlanetInterPlanetary (&GLOBAL (ShipStamp.origin));
+
+ (*pSolarSysState->genFuncs->generateName) (
+ pSolarSysState, planet);
+ }
+ }
+}
+
+static void
+CalcSunSize (PLANET_DESC *pSunDesc, SIZE radius)
+{
+ SIZE index = 0;
+
+ if (radius <= (MAX_ZOOM_RADIUS >> 1))
+ {
+ ++index;
+ if (radius <= (MAX_ZOOM_RADIUS >> 2))
+ ++index;
+ }
+
+ pSunDesc->image.origin.x = SIS_SCREEN_WIDTH >> 1;
+ pSunDesc->image.origin.y = SIS_SCREEN_HEIGHT >> 1;
+ pSunDesc->image.frame = SetRelFrameIndex (SunFrame, index);
+}
+
+static void
+SetPlanetColorMap (PLANET_DESC *planet)
+{
+ COUNT index = planet->data_index & ~WORLD_TYPE_SPECIAL;
+ assert (index < NUMBER_OF_PLANET_TYPES);
+ SetColorMap (GetColorMapAddress (SetAbsColorMapIndex (OrbitalCMap,
+ PLANCOLOR (PlanData[index].Type))));
+}
+
+static void
+DrawInnerPlanets (PLANET_DESC *planet)
+{
+ STAMP s;
+ COUNT i;
+ PLANET_DESC *moon;
+
+ // Draw the planet image
+ SetPlanetColorMap (planet);
+ s.origin.x = SIS_SCREEN_WIDTH >> 1;
+ s.origin.y = SIS_SCREEN_HEIGHT >> 1;
+ s.frame = planet->image.frame;
+
+ i = planet->data_index & ~WORLD_TYPE_SPECIAL;
+ if (i < NUMBER_OF_PLANET_TYPES
+ && (planet->data_index & PLANET_SHIELDED))
+ { // Shielded world looks "shielded" in inner view
+ s.frame = SetAbsFrameIndex (SpaceJunkFrame, 17);
+ }
+ DrawStamp (&s);
+
+ // Draw the moon images
+ for (i = planet->NumPlanets, moon = pSolarSysState->MoonDesc;
+ i; --i, ++moon)
+ {
+ if (!(moon->data_index & WORLD_TYPE_SPECIAL))
+ SetPlanetColorMap (moon);
+ DrawStamp (&moon->image);
+ }
+}
+
+static void
+DrawSystem (SIZE radius, BOOLEAN IsInnerSystem)
+{
+ BYTE i;
+ PLANET_DESC *pCurDesc;
+ PLANET_DESC *pBaseDesc;
+ CONTEXT oldContext;
+ STAMP s;
+
+ if (!SolarSysFrame)
+ { // Create the saved view graphic
+ RECT clipRect;
+
+ GetContextClipRect (&clipRect);
+ SolarSysFrame = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
+ clipRect.extent.width, clipRect.extent.height, 1));
+ }
+
+ oldContext = SetContext (OffScreenContext);
+ SetContextFGFrame (SolarSysFrame);
+ SetContextClipRect (NULL);
+
+ DrawStarBackGround ();
+
+ pBaseDesc = pSolarSysState->pBaseDesc;
+ if (IsInnerSystem)
+ { // Draw the inner system view *planet's* orbit segment
+ pCurDesc = pSolarSysState->pOrbitalDesc;
+ DrawOrbit (pCurDesc, DISPLAY_FACTOR * 4, DISPLAY_FACTOR, radius);
+ }
+
+ // Draw the planet orbits or moon orbits
+ for (i = pBaseDesc->pPrevDesc->NumPlanets, pCurDesc = pBaseDesc;
+ i; --i, ++pCurDesc)
+ {
+ if (IsInnerSystem)
+ DrawOrbit (pCurDesc, 2, 1, 2);
+ else
+ DrawOrbit (pCurDesc, DISPLAY_FACTOR, DISPLAY_FACTOR / 4,
+ radius);
+ }
+
+ if (IsInnerSystem)
+ { // Draw the inner system view
+ DrawInnerPlanets (pSolarSysState->pOrbitalDesc);
+ }
+ else
+ { // Draw the outer system view
+ SIZE index;
+
+ CalcSunSize (&pSolarSysState->SunDesc[0], radius);
+
+ index = pSolarSysState->FirstPlanetIndex;
+ for (;;)
+ {
+ pCurDesc = &pSolarSysState->PlanetDesc[index];
+ if (pCurDesc == &pSolarSysState->SunDesc[0])
+ { // It's a sun
+ SetColorMap (GetColorMapAddress (SetAbsColorMapIndex (
+ SunCMap, STAR_COLOR (CurStarDescPtr->Type))));
+ }
+ else
+ { // It's a planet
+ SetPlanetColorMap (pCurDesc);
+ }
+ DrawStamp (&pCurDesc->image);
+
+ if (index == pSolarSysState->LastPlanetIndex)
+ break;
+ index = pCurDesc->NextIndex;
+ }
+ }
+
+ SetContext (oldContext);
+
+ // Draw the now-saved view graphic
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SolarSysFrame;
+ DrawStamp (&s);
+}
+
+void
+DrawStarBackGround (void)
+{
+ STAMP s;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = StarsFrame;
+ DrawStamp (&s);
+}
+
+static FRAME
+CreateStarBackGround (void)
+{
+ COUNT i, j;
+ DWORD rand_val;
+ STAMP s;
+ CONTEXT oldContext;
+ RECT clipRect;
+ FRAME frame;
+
+ // Use SpaceContext to find out the dimensions of the background
+ oldContext = SetContext (SpaceContext);
+ GetContextClipRect (&clipRect);
+
+ // Prepare a pre-drawn stars frame for this system
+ frame = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
+ clipRect.extent.width, clipRect.extent.height, 1));
+ SetContext (OffScreenContext);
+ SetContextFGFrame (frame);
+ SetContextClipRect (NULL);
+ SetContextBackGroundColor (BLACK_COLOR);
+
+ ClearDrawable ();
+
+ RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (CurStarDescPtr));
+
+#define NUM_DIM_PIECES 8
+ s.frame = SpaceJunkFrame;
+ for (i = 0; i < NUM_DIM_PIECES; ++i)
+ {
+#define NUM_DIM_DRAWN 5
+ for (j = 0; j < NUM_DIM_DRAWN; ++j)
+ {
+ rand_val = RandomContext_Random (SysGenRNG);
+ s.origin.x = LOWORD (rand_val) % SIS_SCREEN_WIDTH;
+ s.origin.y = HIWORD (rand_val) % SIS_SCREEN_HEIGHT;
+
+ DrawStamp (&s);
+ }
+ s.frame = IncFrameIndex (s.frame);
+ }
+#define NUM_BRT_PIECES 8
+ for (i = 0; i < NUM_BRT_PIECES; ++i)
+ {
+#define NUM_BRT_DRAWN 30
+ for (j = 0; j < NUM_BRT_DRAWN; ++j)
+ {
+ rand_val = RandomContext_Random (SysGenRNG);
+ s.origin.x = LOWORD (rand_val) % SIS_SCREEN_WIDTH;
+ s.origin.y = HIWORD (rand_val) % SIS_SCREEN_HEIGHT;
+
+ DrawStamp (&s);
+ }
+ s.frame = IncFrameIndex (s.frame);
+ }
+
+ SetContext (oldContext);
+
+ return frame;
+}
+
+void
+XFormIPLoc (POINT *pIn, POINT *pOut, BOOLEAN ToDisplay)
+{
+ if (ToDisplay)
+ *pOut = locationToDisplay (*pIn, pSolarSysState->SunDesc[0].radius);
+ else
+ *pOut = displayToLocation (*pIn, pSolarSysState->SunDesc[0].radius);
+}
+
+void
+ExploreSolarSys (void)
+{
+ SOLARSYS_STATE SolarSysState;
+
+ if (CurStarDescPtr == 0)
+ {
+ POINT universe;
+
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ CurStarDescPtr = FindStar (0, &universe, 1, 1);
+ if (!CurStarDescPtr)
+ {
+ log_add (log_Fatal, "ExploreSolarSys(): do not know where you are!");
+ explode ();
+ }
+ }
+ GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (CurStarDescPtr->star_pt.x);
+ GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (CurStarDescPtr->star_pt.y);
+
+ pSolarSysState = &SolarSysState;
+
+ memset (pSolarSysState, 0, sizeof (*pSolarSysState));
+
+ SolarSysState.genFuncs = getGenerateFunctions (CurStarDescPtr->Index);
+
+ InitSolarSys ();
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ SolarSysState.InputFunc = DoIpFlight;
+ DoInput (&SolarSysState, FALSE);
+ UninitSolarSys ();
+ pSolarSysState = 0;
+}
+
+UNICODE *
+GetNamedPlanetaryBody (void)
+{
+ if (!CurStarDescPtr || !playerInSolarSystem () || !playerInInnerSystem ())
+ return NULL; // Not inside an inner system, so no name
+
+ assert (pSolarSysState->pOrbitalDesc != NULL);
+
+ if (CurStarDescPtr->Index == SOL_DEFINED)
+ { // Planets and moons in Sol
+ int planet;
+ int moon;
+
+ planet = planetIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
+
+ if (worldIsPlanet (pSolarSysState, pSolarSysState->pOrbitalDesc))
+ { // A planet
+ return GAME_STRING (PLANET_NUMBER_BASE + planet);
+ }
+
+ // Moons
+ moon = moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
+ switch (planet)
+ {
+ case 2: // Earth
+ switch (moon)
+ {
+ case 0: // Starbase
+ return GAME_STRING (STARBASE_STRING_BASE + 0);
+ case 1: // Luna
+ return GAME_STRING (PLANET_NUMBER_BASE + 9);
+ }
+ break;
+ case 4: // Jupiter
+ switch (moon)
+ {
+ case 0: // Io
+ return GAME_STRING (PLANET_NUMBER_BASE + 10);
+ case 1: // Europa
+ return GAME_STRING (PLANET_NUMBER_BASE + 11);
+ case 2: // Ganymede
+ return GAME_STRING (PLANET_NUMBER_BASE + 12);
+ case 3: // Callisto
+ return GAME_STRING (PLANET_NUMBER_BASE + 13);
+ }
+ break;
+ case 5: // Saturn
+ if (moon == 0) // Titan
+ return GAME_STRING (PLANET_NUMBER_BASE + 14);
+ break;
+ case 7: // Neptune
+ if (moon == 0) // Triton
+ return GAME_STRING (PLANET_NUMBER_BASE + 15);
+ break;
+ }
+ }
+ else if (CurStarDescPtr->Index == SPATHI_DEFINED)
+ {
+ if (matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc,
+ 0, MATCH_PLANET))
+ {
+#ifdef NOTYET
+ return "Spathiwa";
+#endif // NOTYET
+ }
+ }
+ else if (CurStarDescPtr->Index == SAMATRA_DEFINED)
+ {
+ if (matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc, 4, 0))
+ { // Sa-Matra
+ return GAME_STRING (PLANET_NUMBER_BASE + 32);
+ }
+ }
+
+ return NULL;
+}
+
+void
+GetPlanetOrMoonName (UNICODE *buf, COUNT bufsize)
+{
+ UNICODE *named;
+ int moon;
+ int i;
+
+ named = GetNamedPlanetaryBody ();
+ if (named)
+ {
+ utf8StringCopy (buf, bufsize, named);
+ return;
+ }
+
+ // Either not named or we already have a name
+ utf8StringCopy (buf, bufsize, GLOBAL_SIS (PlanetName));
+
+ if (!playerInSolarSystem () || !playerInInnerSystem () ||
+ worldIsPlanet (pSolarSysState, pSolarSysState->pOrbitalDesc))
+ { // Outer or inner system or orbiting a planet
+ return;
+ }
+
+ // Orbiting an unnamed moon
+ i = strlen (buf);
+ buf += i;
+ bufsize -= i;
+ moon = moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
+ if (bufsize >= 3)
+ {
+ snprintf (buf, bufsize, "-%c", 'A' + moon);
+ buf[bufsize - 1] = '\0';
+ }
+}
+
+void
+SaveSolarSysLocation (void)
+{
+ assert (playerInSolarSystem ());
+
+ // This is a two-stage saving procedure
+ // Stage 1: called when saving from inner/outer view
+ // Stage 2: called when saving from orbital
+
+ if (!playerInPlanetOrbit ())
+ {
+ saveNonOrbitalLocation ();
+ }
+ else
+ { // In orbit around a planet.
+ BYTE moon;
+
+ // Update the starinfo.dat file if necessary.
+ if (GET_GAME_STATE (PLANETARY_CHANGE))
+ {
+ PutPlanetInfo ();
+ SET_GAME_STATE (PLANETARY_CHANGE, 0);
+ }
+
+ // GLOBAL (ip_planet) is already set
+ assert (GLOBAL (ip_planet) != 0);
+
+ // has to be at least 1 because code tests for in_orbit!=0
+ moon = 1; /* the planet itself */
+ if (worldIsMoon (pSolarSysState, pSolarSysState->pOrbitalDesc))
+ {
+ moon += moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
+ // +1 because moons have to be 1-based
+ moon += 1;
+ }
+ GLOBAL (in_orbit) = moon;
+ }
+}
+
+static BOOLEAN
+DoSolarSysMenu (MENU_STATE *pMS)
+{
+ BOOLEAN select = PulsedInputState.menu[KEY_MENU_SELECT];
+ BOOLEAN handled;
+
+ if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
+ return FALSE;
+
+ handled = DoMenuChooser (pMS, PM_STARMAP);
+ if (handled)
+ return TRUE;
+
+ if (LastActivity == CHECK_LOAD)
+ select = TRUE; // Selected LOAD from main menu
+
+ if (!select)
+ return TRUE;
+
+ SetFlashRect (NULL);
+
+ switch (pMS->CurState)
+ {
+ case EQUIP_DEVICE:
+ select = DevicesMenu ();
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ { // Invoked Talking Pet or a Caster for Ilwrath
+ // Going into conversation
+ return FALSE;
+ }
+ break;
+ case CARGO:
+ CargoMenu ();
+ break;
+ case ROSTER:
+ select = RosterMenu ();
+ break;
+ case GAME_MENU:
+ if (!GameOptions ())
+ return FALSE; // abort or load
+ break;
+ case STARMAP:
+ StarMap ();
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ TransitionSystemIn ();
+ // Fall through !!!
+ case NAVIGATION:
+ return FALSE;
+ }
+
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ if (select)
+ { // 3DO menu jumps to NAVIGATE after a successful submenu run
+ if (optWhichMenu != OPT_PC)
+ pMS->CurState = NAVIGATION;
+ DrawMenuStateStrings (PM_STARMAP, pMS->CurState);
+ }
+ SetFlashRect (SFR_MENU_3DO);
+ }
+
+ return TRUE;
+}
+
+static void
+SolarSysMenu (void)
+{
+ MENU_STATE MenuState;
+
+ memset (&MenuState, 0, sizeof MenuState);
+
+ if (LastActivity == CHECK_LOAD)
+ { // Selected LOAD from main menu
+ MenuState.CurState = GAME_MENU;
+ }
+ else
+ {
+ DrawMenuStateStrings (PM_STARMAP, STARMAP);
+ MenuState.CurState = STARMAP;
+ }
+
+ DrawStatusMessage (NULL);
+ SetFlashRect (SFR_MENU_3DO);
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ MenuState.InputFunc = DoSolarSysMenu;
+ DoInput (&MenuState, TRUE);
+
+ DrawMenuStateStrings (PM_STARMAP, -NAVIGATION);
+}
+
+static BOOLEAN
+DoIpFlight (SOLARSYS_STATE *pSS)
+{
+ static TimeCount NextTime;
+ BOOLEAN cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+
+ if (pSS->InOrbit)
+ { // CheckShipLocation() or InitSolarSys() sent us to orbital
+ EnterPlanetOrbit ();
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ pSS->InOrbit = FALSE;
+ }
+ else if (cancel || LastActivity == CHECK_LOAD)
+ {
+ SolarSysMenu ();
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ }
+ else
+ {
+ assert (pSS->InIpFlight);
+ IP_frame ();
+ SleepThreadUntil (NextTime);
+ NextTime = GetTimeCounter () + IP_FRAME_RATE;
+ }
+
+ return (!(GLOBAL (CurrentActivity)
+ & (START_ENCOUNTER | END_INTERPLANETARY
+ | CHECK_ABORT | CHECK_LOAD))
+ && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0);
+}
diff --git a/src/uqm/planets/solarsys.h b/src/uqm/planets/solarsys.h
new file mode 100644
index 0000000..0f86fdd
--- /dev/null
+++ b/src/uqm/planets/solarsys.h
@@ -0,0 +1,34 @@
+//Copyright (C) 2011, Scott A. Colcord
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SOLARSYS_H
+#define SOLARSYS_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void LoadIPData (void);
+extern void FreeIPData (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SOLARSYS_H */
+
diff --git a/src/uqm/planets/sundata.h b/src/uqm/planets/sundata.h
new file mode 100644
index 0000000..6d7888e
--- /dev/null
+++ b/src/uqm/planets/sundata.h
@@ -0,0 +1,73 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_PLANETS_SUNDATA_H_
+#define UQM_PLANETS_SUNDATA_H_
+
+#include "plandata.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*------------------------------ Global Data ------------------------------ */
+
+#define NUMBER_OF_SUN_SIZES (SUPER_GIANT_STAR - DWARF_STAR + 1)
+
+#define DWARF_ENERGY 1
+#define GIANT_ENERGY 5
+#define SUPERGIANT_ENERGY 20
+
+typedef struct
+{
+ BYTE StarSize;
+ BYTE StarIntensity;
+ UWORD StarEnergy;
+
+ PLANET_INFO PlanetInfo;
+} SYSTEM_INFO;
+
+#define GENERATE_ALL ((COUNT)~0)
+
+extern COUNT GenerateMineralDeposits (const SYSTEM_INFO *, COUNT whichDeposit,
+ NODE_INFO *info);
+extern COUNT GenerateLifeForms (const SYSTEM_INFO *, COUNT whichLife,
+ NODE_INFO *info);
+extern void GenerateRandomLocation (POINT *loc);
+extern COUNT GenerateRandomNodes (const SYSTEM_INFO *, COUNT scan, COUNT numNodes,
+ COUNT type, COUNT whichNode, NODE_INFO *info);
+// Generate lifeforms from a preset lifeTypes[] array
+extern COUNT GeneratePresetLife (const SYSTEM_INFO *,
+ const SBYTE *lifeTypes, COUNT whichLife, NODE_INFO *info);
+
+#define DWARF_ELEMENT_DENSITY 1
+#define GIANT_ELEMENT_DENSITY 3
+#define SUPERGIANT_ELEMENT_DENSITY 8
+
+#define MAX_ELEMENT_DENSITY ((MAX_ELEMENT_UNITS * SUPERGIANT_ELEMENT_DENSITY) << 1)
+
+extern void DoPlanetaryAnalysis (SYSTEM_INFO *SysInfoPtr,
+ PLANET_DESC *pPlanetDesc);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PLANETS_SUNDATA_H_ */
+
diff --git a/src/uqm/planets/surface.c b/src/uqm/planets/surface.c
new file mode 100644
index 0000000..79f9e75
--- /dev/null
+++ b/src/uqm/planets/surface.c
@@ -0,0 +1,251 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "lifeform.h"
+#include "planets.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+//#define DEBUG_SURFACE
+
+const BYTE *Elements;
+const PlanetFrame *PlanData;
+
+static COUNT
+CalcMineralDeposits (const SYSTEM_INFO *SysInfoPtr, COUNT which_deposit,
+ NODE_INFO *info)
+{
+ BYTE j;
+ COUNT num_deposits;
+ const ELEMENT_ENTRY *eptr;
+
+ eptr = &SysInfoPtr->PlanetInfo.PlanDataPtr->UsefulElements[0];
+ num_deposits = 0;
+ j = NUM_USEFUL_ELEMENTS;
+ do
+ {
+ BYTE num_possible;
+
+ num_possible = LOBYTE (RandomContext_Random (SysGenRNG))
+ % (DEPOSIT_QUANTITY (eptr->Density) + 1);
+ while (num_possible--)
+ {
+#define MEDIUM_DEPOSIT_THRESHOLD 150
+#define LARGE_DEPOSIT_THRESHOLD 225
+ COUNT deposit_quality_fine;
+ COUNT deposit_quality_gross;
+
+ deposit_quality_fine = (LOWORD (RandomContext_Random (SysGenRNG)) % 100)
+ + (
+ DEPOSIT_QUALITY (eptr->Density)
+ + SysInfoPtr->StarSize
+ ) * 50;
+ if (deposit_quality_fine < MEDIUM_DEPOSIT_THRESHOLD)
+ deposit_quality_gross = 0;
+ else if (deposit_quality_fine < LARGE_DEPOSIT_THRESHOLD)
+ deposit_quality_gross = 1;
+ else
+ deposit_quality_gross = 2;
+
+ GenerateRandomLocation (&info->loc_pt);
+
+ info->density = MAKE_WORD (
+ deposit_quality_gross, deposit_quality_fine / 10 + 1);
+ info->type = eptr->ElementType;
+#ifdef DEBUG_SURFACE
+ log_add (log_Debug, "\t\t%d units of %Fs",
+ info->density,
+ Elements[eptr->ElementType].name);
+#endif /* DEBUG_SURFACE */
+ if (num_deposits >= which_deposit
+ || ++num_deposits == sizeof (DWORD) * 8)
+ { // reached the maximum or the requested node
+ return num_deposits;
+ }
+ }
+ ++eptr;
+ } while (--j);
+
+ return num_deposits;
+}
+
+// Returns:
+// for whichLife==~0 : the number of nodes generated
+// for whichLife<32 : the index of the last node (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+COUNT
+GenerateMineralDeposits (const SYSTEM_INFO *SysInfoPtr, COUNT whichDeposit,
+ NODE_INFO *info)
+{
+ NODE_INFO temp_info;
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[MINERAL_SCAN]);
+ return CalcMineralDeposits (SysInfoPtr, whichDeposit, info);
+}
+
+static COUNT
+CalcLifeForms (const SYSTEM_INFO *SysInfoPtr, COUNT which_life,
+ NODE_INFO *info)
+{
+ COUNT num_life_forms;
+
+ num_life_forms = 0;
+ if (PLANSIZE (SysInfoPtr->PlanetInfo.PlanDataPtr->Type) != GAS_GIANT)
+ {
+#define MIN_LIFE_CHANCE 10
+ SIZE life_var;
+
+ life_var = RandomContext_Random (SysGenRNG) & 1023;
+ if (life_var < SysInfoPtr->PlanetInfo.LifeChance
+ || (SysInfoPtr->PlanetInfo.LifeChance < MIN_LIFE_CHANCE
+ && life_var < MIN_LIFE_CHANCE))
+ {
+ BYTE num_types;
+
+ num_types = 1 + LOBYTE (RandomContext_Random (SysGenRNG))
+ % MAX_LIFE_VARIATION;
+ do
+ {
+ BYTE index, num_creatures;
+ UWORD rand_val;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ index = LOBYTE (rand_val) % NUM_CREATURE_TYPES;
+ num_creatures = 1 + HIBYTE (rand_val) % 10;
+ do
+ {
+ GenerateRandomLocation (&info->loc_pt);
+ info->type = index;
+ info->density = 0;
+
+ if (num_life_forms >= which_life
+ || ++num_life_forms == sizeof (DWORD) * 8)
+ { // reached the maximum or the requested node
+ return num_life_forms;
+ }
+ } while (--num_creatures);
+ } while (--num_types);
+ }
+#ifdef DEBUG_SURFACE
+ else
+ {
+ log_add (log_Debug, "It's dead, Jim! (%d >= %d)", life_var,
+ SysInfoPtr->PlanetInfo.LifeChance);
+ }
+#endif /* DEBUG_SURFACE */
+ }
+
+ return num_life_forms;
+}
+
+// Returns:
+// for whichLife==~0 : the number of lifeforms generated
+// for whichLife<32 : the index of the last lifeform (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+COUNT
+GenerateLifeForms (const SYSTEM_INFO *SysInfoPtr, COUNT whichLife,
+ NODE_INFO *info)
+{
+ NODE_INFO temp_info;
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[BIOLOGICAL_SCAN]);
+ return CalcLifeForms (SysInfoPtr, whichLife, info);
+}
+
+// Returns:
+// for whichLife==~0 : the number of lifeforms generated
+// for whichLife<32 : the index of the last lifeform (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+// lifeTypes[] is terminated with -1
+COUNT
+GeneratePresetLife (const SYSTEM_INFO *SysInfoPtr, const SBYTE *lifeTypes,
+ COUNT whichLife, NODE_INFO *info)
+{
+ COUNT i;
+ NODE_INFO temp_info;
+
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+
+ // This function may look unnecessarily complicated, but it must be
+ // kept this way to preserve the universe. That is done by preserving
+ // the order and number of Random() calls.
+
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[BIOLOGICAL_SCAN]);
+
+ for (i = 0; lifeTypes[i] >= 0; ++i)
+ {
+ GenerateRandomLocation (&info->loc_pt);
+ info->type = lifeTypes[i];
+ // density is irrelevant for bio nodes
+ info->density = 0;
+
+ if (i >= whichLife)
+ break;
+ }
+
+ return i;
+}
+
+void
+GenerateRandomLocation (POINT *loc)
+{
+ UWORD rand_val;
+
+ rand_val = RandomContext_Random (SysGenRNG);
+ loc->x = 8 + LOBYTE (rand_val) % (MAP_WIDTH - (8 << 1));
+ loc->y = 8 + HIBYTE (rand_val) % (MAP_HEIGHT - (8 << 1));
+}
+
+// Returns:
+// for whichNode==~0 : the number of nodes generated
+// for whichNode<32 : the index of the last node (no known usage exists)
+// Sets the SysGenRNG to the required state first.
+COUNT
+GenerateRandomNodes (const SYSTEM_INFO *SysInfoPtr, COUNT scan, COUNT numNodes,
+ COUNT type, COUNT whichNode, NODE_INFO *info)
+{
+ COUNT i;
+ NODE_INFO temp_info;
+
+ if (!info) // user not interested in info but we need space for it
+ info = &temp_info;
+
+ RandomContext_SeedRandom (SysGenRNG,
+ SysInfoPtr->PlanetInfo.ScanSeed[scan]);
+
+ for (i = 0; i < numNodes; ++i)
+ {
+ GenerateRandomLocation (&info->loc_pt);
+ // type is irrelevant for energy nodes
+ info->type = type;
+ // density is irrelevant for energy and bio nodes
+ info->density = 0;
+
+ if (i >= whichNode)
+ break;
+ }
+
+ return i;
+}
diff --git a/src/uqm/process.c b/src/uqm/process.c
new file mode 100644
index 0000000..142c58e
--- /dev/null
+++ b/src/uqm/process.c
@@ -0,0 +1,1108 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "process.h"
+
+#include "races.h"
+#include "collide.h"
+#include "options.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "hyper.h"
+#include "element.h"
+#include "battle.h"
+#include "weapon.h"
+#include "libs/graphics/drawable.h"
+#include "libs/graphics/drawcmd.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+
+//#define DEBUG_PROCESS
+
+COUNT DisplayFreeList;
+PRIMITIVE DisplayArray[MAX_DISPLAY_PRIMS];
+extern POINT SpaceOrg;
+
+SIZE zoom_out = 1 << ZOOM_SHIFT;
+static SIZE opt_max_zoom_out;
+
+#if 0
+static inline void
+CALC_ZOOM_STUFF (COUNT* idx, COUNT* sc)
+{
+ int i, z;
+
+ z = 1 << ZOOM_SHIFT;
+ for (i = 0; (z <<= 1) <= zoom_out; i++)
+ ;
+ *idx = i;
+ *sc = ((1 << i) << (ZOOM_SHIFT + 8)) / zoom_out;
+}
+#else
+static inline void
+CALC_ZOOM_STUFF (COUNT* idx, COUNT* sc)
+{
+ int i;
+
+ if (zoom_out < 2 << ZOOM_SHIFT)
+ i = 0;
+ else if (zoom_out < 4 << ZOOM_SHIFT)
+ i = 1;
+ else
+ i = 2;
+ *idx = i;
+ *sc = (1 << (i + ZOOM_SHIFT + 8)) / zoom_out;
+}
+#endif
+
+HELEMENT
+AllocElement (void)
+{
+ HELEMENT hElement;
+
+ hElement = AllocLink (&disp_q);
+ if (hElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ memset (ElementPtr, 0, sizeof (*ElementPtr));
+ ElementPtr->PrimIndex = AllocDisplayPrim ();
+ if (ElementPtr->PrimIndex == END_OF_LIST)
+ {
+ log_add (log_Error, "AllocElement: Out of display prims!");
+ explode ();
+ }
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM);
+ UnlockElement (hElement);
+ }
+
+ return (hElement);
+}
+
+void
+FreeElement (HELEMENT hElement)
+{
+ if (hElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ FreeDisplayPrim (ElementPtr->PrimIndex);
+ UnlockElement (hElement);
+
+ FreeLink (&disp_q, hElement);
+ }
+}
+
+void
+SetUpElement (ELEMENT *ElementPtr)
+{
+ ElementPtr->next = ElementPtr->current;
+ if (CollidingElement (ElementPtr))
+ {
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectFrame (ElementPtr);
+ }
+}
+
+static void
+PreProcess (ELEMENT *ElementPtr)
+{
+ ELEMENT_FLAGS state_flags;
+
+ if (ElementPtr->life_span == 0)
+ {
+ if (ElementPtr->pParent) /* untarget this dead element */
+ Untarget (ElementPtr);
+
+ ElementPtr->state_flags |= DISAPPEARING;
+ if (ElementPtr->death_func)
+ (*ElementPtr->death_func) (ElementPtr);
+ }
+
+ state_flags = ElementPtr->state_flags;
+ if (!(state_flags & DISAPPEARING))
+ {
+ if (state_flags & APPEARING)
+ {
+ SetUpElement (ElementPtr);
+
+ if (state_flags & PLAYER_SHIP)
+ state_flags &= ~APPEARING; /* want to preprocess ship */
+ }
+
+ if (ElementPtr->preprocess_func && !(state_flags & APPEARING))
+ {
+ (*ElementPtr->preprocess_func) (ElementPtr);
+
+ state_flags = ElementPtr->state_flags;
+ if ((state_flags & CHANGING) && CollidingElement (ElementPtr))
+ InitIntersectFrame (ElementPtr);
+ }
+
+ if (!(state_flags & IGNORE_VELOCITY))
+ {
+ SIZE delta_x, delta_y;
+
+ GetNextVelocityComponents (&ElementPtr->velocity,
+ &delta_x, &delta_y, 1);
+ if (delta_x != 0 || delta_y != 0)
+ {
+ state_flags |= CHANGING;
+ ElementPtr->next.location.x += delta_x;
+ ElementPtr->next.location.y += delta_y;
+ }
+ }
+
+ if (CollidingElement (ElementPtr))
+ InitIntersectEndPoint (ElementPtr);
+
+ if (state_flags & FINITE_LIFE)
+ --ElementPtr->life_span;
+ }
+
+ ElementPtr->state_flags = (state_flags & ~(POST_PROCESS | COLLISION))
+ | PRE_PROCESS;
+}
+
+static void
+PostProcess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->postprocess_func)
+ (*ElementPtr->postprocess_func) (ElementPtr);
+ ElementPtr->current = ElementPtr->next;
+
+ if (CollidingElement (ElementPtr))
+ {
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ }
+
+ ElementPtr->state_flags = (ElementPtr->state_flags
+ & ~(PRE_PROCESS | CHANGING | APPEARING))
+ | POST_PROCESS;
+}
+
+static COUNT
+CalcReduction (SIZE dx, SIZE dy)
+{
+ COUNT next_reduction;
+
+#ifdef KDEBUG
+ log_add (log_Debug, "CalcReduction:");
+#endif
+
+ if (optMeleeScale == TFB_SCALE_STEP)
+ {
+ SIZE sdx, sdy;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) > IN_ENCOUNTER)
+ return (0);
+
+ sdx = dx;
+ sdy = dy;
+ for (next_reduction = MAX_VIS_REDUCTION;
+ (dx <<= REDUCTION_SHIFT) <= TRANSITION_WIDTH
+ && (dy <<= REDUCTION_SHIFT) <= TRANSITION_HEIGHT
+ && next_reduction > 0;
+ next_reduction -= REDUCTION_SHIFT)
+ ;
+
+ /* check for "real" zoom in */
+ if (next_reduction < zoom_out
+ && zoom_out <= MAX_VIS_REDUCTION)
+ {
+#define HYSTERESIS_X DISPLAY_TO_WORLD(24)
+#define HYSTERESIS_Y DISPLAY_TO_WORLD(20)
+ if (((sdx + HYSTERESIS_X)
+ << (MAX_VIS_REDUCTION - next_reduction)) > TRANSITION_WIDTH
+ || ((sdy + HYSTERESIS_Y)
+ << (MAX_VIS_REDUCTION - next_reduction)) > TRANSITION_HEIGHT)
+ /* if we don't zoom in, we want to stay at next+1 */
+ next_reduction += REDUCTION_SHIFT;
+ }
+
+ if (next_reduction == 0
+ && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ next_reduction += REDUCTION_SHIFT;
+ }
+ else
+ {
+ if (LOBYTE (GLOBAL (CurrentActivity)) > IN_ENCOUNTER)
+ return (1 << ZOOM_SHIFT);
+
+ dx = (dx * MAX_ZOOM_OUT) / (LOG_SPACE_WIDTH >> 2);
+ if (dx < (1 << ZOOM_SHIFT))
+ dx = 1 << ZOOM_SHIFT;
+ else if (dx > MAX_ZOOM_OUT)
+ dx = MAX_ZOOM_OUT;
+
+ dy = (dy * MAX_ZOOM_OUT) / (LOG_SPACE_HEIGHT >> 2);
+ if (dy < (1 << ZOOM_SHIFT))
+ dy = 1 << ZOOM_SHIFT;
+ else if (dy > MAX_ZOOM_OUT)
+ dy = MAX_ZOOM_OUT;
+
+ if (dy > dx)
+ next_reduction = dy;
+ else
+ next_reduction = dx;
+
+ if (next_reduction < (2 << ZOOM_SHIFT)
+ && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ next_reduction = (2 << ZOOM_SHIFT);
+ }
+
+#ifdef KDEBUG
+ log_add (log_Debug, "CalcReduction: exit");
+#endif
+
+ return (next_reduction);
+}
+
+static VIEW_STATE
+CalcView (POINT *pNewScrollPt, SIZE next_reduction,
+ SIZE *pdx, SIZE *pdy, COUNT ships_alive)
+{
+ SIZE dx, dy;
+ VIEW_STATE view_state;
+
+#ifdef KDEBUG
+ log_add (log_Debug, "CalcView:");
+#endif
+ dx = ((COORD)(LOG_SPACE_WIDTH >> 1) - pNewScrollPt->x);
+ dy = ((COORD)(LOG_SPACE_HEIGHT >> 1) - pNewScrollPt->y);
+ dx = WRAP_DELTA_X (dx);
+ dy = WRAP_DELTA_Y (dy);
+ if (ships_alive == 1)
+ {
+#define ORG_JUMP_X ((SIZE)DISPLAY_ALIGN(LOG_SPACE_WIDTH / 75))
+#define ORG_JUMP_Y ((SIZE)DISPLAY_ALIGN(LOG_SPACE_HEIGHT / 75))
+ if (dx > ORG_JUMP_X)
+ dx = ORG_JUMP_X;
+ else if (dx < -ORG_JUMP_X)
+ dx = -ORG_JUMP_X;
+ if (dy > ORG_JUMP_Y)
+ dy = ORG_JUMP_Y;
+ else if (dy < -ORG_JUMP_Y)
+ dy = -ORG_JUMP_Y;
+ }
+
+ if ((dx || dy) && inHQSpace ())
+ MoveSIS (&dx, &dy);
+
+ if (zoom_out == next_reduction)
+ view_state = dx == 0 && dy == 0 && !inHQSpace ()
+ ? VIEW_STABLE : VIEW_SCROLL;
+ else
+ {
+ if (optMeleeScale == TFB_SCALE_STEP)
+ {
+ SpaceOrg.x = (COORD)(LOG_SPACE_WIDTH >> 1)
+ - (LOG_SPACE_WIDTH >> ((MAX_REDUCTION + 1)
+ - next_reduction));
+ SpaceOrg.y = (COORD)(LOG_SPACE_HEIGHT >> 1)
+ - (LOG_SPACE_HEIGHT >> ((MAX_REDUCTION + 1)
+ - next_reduction));
+ }
+ else
+ {
+#define ZOOM_JUMP ((1 << ZOOM_SHIFT) >> 3)
+ if (ships_alive == 1
+ && zoom_out > next_reduction
+ && zoom_out <= MAX_ZOOM_OUT
+ && zoom_out - next_reduction > ZOOM_JUMP)
+ next_reduction = zoom_out - ZOOM_JUMP;
+
+ // Always align the origin on a whole pixel to reduce the
+ // amount of object positioning jitter
+ SpaceOrg.x = DISPLAY_ALIGN((int)(LOG_SPACE_WIDTH >> 1) -
+ (LOG_SPACE_WIDTH * next_reduction / (MAX_ZOOM_OUT << 2)));
+ SpaceOrg.y = DISPLAY_ALIGN((int)(LOG_SPACE_HEIGHT >> 1) -
+ (LOG_SPACE_HEIGHT * next_reduction / (MAX_ZOOM_OUT << 2)));
+ }
+ zoom_out = next_reduction;
+ view_state = VIEW_CHANGE;
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_HYPERSPACE)
+ MoveGalaxy (view_state, dx, dy);
+
+ *pdx = dx;
+ *pdy = dy;
+
+#ifdef KDEBUG
+ log_add (log_Debug, "CalcView: exit");
+#endif
+ return (view_state);
+}
+
+
+static ELEMENT_FLAGS
+ProcessCollisions (HELEMENT hSuccElement, ELEMENT *ElementPtr,
+ TIME_VALUE min_time, ELEMENT_FLAGS process_flags)
+{
+ HELEMENT hTestElement;
+
+ while ((hTestElement = hSuccElement) != 0)
+ {
+ ELEMENT *TestElementPtr;
+
+ LockElement (hTestElement, &TestElementPtr);
+ if (!(TestElementPtr->state_flags & process_flags))
+ PreProcess (TestElementPtr);
+ hSuccElement = GetSuccElement (TestElementPtr);
+
+ if (TestElementPtr == ElementPtr)
+ {
+ UnlockElement (hTestElement);
+ continue;
+ }
+
+ if (CollisionPossible (TestElementPtr, ElementPtr))
+ {
+ ELEMENT_FLAGS state_flags, test_state_flags;
+ TIME_VALUE time_val;
+
+ state_flags = ElementPtr->state_flags;
+ test_state_flags = TestElementPtr->state_flags;
+ if (((state_flags | test_state_flags) & FINITE_LIFE)
+ && (((state_flags & APPEARING)
+ && ElementPtr->life_span > 1)
+ || ((test_state_flags & APPEARING)
+ && TestElementPtr->life_span > 1)))
+ time_val = 0;
+ else
+ {
+ while ((time_val = DrawablesIntersect (&ElementPtr->IntersectControl,
+ &TestElementPtr->IntersectControl, min_time)) == 1
+ && !((state_flags | test_state_flags) & FINITE_LIFE))
+ {
+#ifdef DEBUG_PROCESS
+ log_add (log_Debug, "BAD NEWS 0x%x <--> 0x%x", ElementPtr,
+ TestElementPtr);
+#endif /* DEBUG_PROCESS */
+ if (state_flags & COLLISION)
+ {
+ InitIntersectEndPoint (TestElementPtr);
+ TestElementPtr->IntersectControl.IntersectStamp.origin =
+ TestElementPtr->IntersectControl.EndPoint;
+ time_val = DrawablesIntersect (&ElementPtr->IntersectControl,
+ &TestElementPtr->IntersectControl, 1);
+ InitIntersectStartPoint (TestElementPtr);
+ }
+
+ if (time_val == 1)
+ {
+ FRAME CurFrame, NextFrame,
+ TestCurFrame, TestNextFrame;
+
+ CurFrame = ElementPtr->current.image.frame;
+ NextFrame = ElementPtr->next.image.frame;
+ TestCurFrame = TestElementPtr->current.image.frame;
+ TestNextFrame = TestElementPtr->next.image.frame;
+ if (NextFrame == CurFrame
+ && TestNextFrame == TestCurFrame)
+ {
+ if (test_state_flags & APPEARING)
+ {
+ do_damage (TestElementPtr, TestElementPtr->hit_points);
+ if (TestElementPtr->pParent) /* untarget this dead element */
+ Untarget (TestElementPtr);
+
+ TestElementPtr->state_flags |= (COLLISION | DISAPPEARING);
+ if (TestElementPtr->death_func)
+ (*TestElementPtr->death_func) (TestElementPtr);
+ }
+ if (state_flags & APPEARING)
+ {
+ do_damage (ElementPtr, ElementPtr->hit_points);
+ if (ElementPtr->pParent) /* untarget this dead element */
+ Untarget (ElementPtr);
+
+ ElementPtr->state_flags |= (COLLISION | DISAPPEARING);
+ if (ElementPtr->death_func)
+ (*ElementPtr->death_func) (ElementPtr);
+
+ UnlockElement (hTestElement);
+ return (COLLISION);
+ }
+
+ time_val = 0;
+ }
+ else
+ {
+ if (GetFrameIndex (CurFrame) !=
+ GetFrameIndex (NextFrame))
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (NextFrame,
+ CurFrame);
+ else if (NextFrame != CurFrame)
+ {
+ ElementPtr->next.image =
+ ElementPtr->current.image;
+ if (ElementPtr->life_span > NORMAL_LIFE)
+ ElementPtr->life_span = NORMAL_LIFE;
+ }
+
+ if (GetFrameIndex (TestCurFrame) !=
+ GetFrameIndex (TestNextFrame))
+ TestElementPtr->next.image.frame =
+ SetEquFrameIndex (TestNextFrame,
+ TestCurFrame);
+ else if (TestNextFrame != TestCurFrame)
+ {
+ TestElementPtr->next.image =
+ TestElementPtr->current.image;
+ if (TestElementPtr->life_span > NORMAL_LIFE)
+ TestElementPtr->life_span = NORMAL_LIFE;
+ }
+
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectFrame (ElementPtr);
+ if (state_flags & PLAYER_SHIP)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ StarShipPtr->ShipFacing =
+ GetFrameIndex (
+ ElementPtr->next.image.frame);
+ }
+
+ InitIntersectStartPoint (TestElementPtr);
+ InitIntersectEndPoint (TestElementPtr);
+ InitIntersectFrame (TestElementPtr);
+ if (test_state_flags & PLAYER_SHIP)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (TestElementPtr, &StarShipPtr);
+ StarShipPtr->ShipFacing =
+ GetFrameIndex (
+ TestElementPtr->next.image.frame);
+ }
+ }
+ }
+
+ if (time_val == 0)
+ {
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectEndPoint (TestElementPtr);
+
+ break;
+ }
+ }
+ }
+
+ if (time_val > 0)
+ {
+ POINT SavePt, TestSavePt;
+
+#ifdef DEBUG_PROCESS
+ log_add (log_Debug, "0x%x <--> 0x%x at %u", ElementPtr,
+ TestElementPtr, time_val);
+#endif /* DEBUG_PROCESS */
+ SavePt = ElementPtr->IntersectControl.EndPoint;
+ TestSavePt = TestElementPtr->IntersectControl.EndPoint;
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectEndPoint (TestElementPtr);
+ if (time_val == 1
+ || (((state_flags & COLLISION)
+ || !ProcessCollisions (hSuccElement, ElementPtr,
+ time_val - 1, process_flags))
+ && ((test_state_flags & COLLISION)
+ || !ProcessCollisions (
+ !(TestElementPtr->state_flags & APPEARING) ?
+ GetSuccElement (ElementPtr) :
+ GetHeadElement (), TestElementPtr,
+ time_val - 1, process_flags))))
+ {
+ state_flags = ElementPtr->state_flags;
+ test_state_flags = TestElementPtr->state_flags;
+
+#ifdef DEBUG_PROCESS
+ log_add (log_Debug, "PROCESSING 0x%x <--> 0x%x at %u",
+ ElementPtr, TestElementPtr, time_val);
+#endif /* DEBUG_PROCESS */
+ if (test_state_flags & PLAYER_SHIP)
+ {
+ (*TestElementPtr->collision_func) (
+ TestElementPtr, &TestSavePt,
+ ElementPtr, &SavePt
+ );
+ (*ElementPtr->collision_func) (
+ ElementPtr, &SavePt,
+ TestElementPtr, &TestSavePt
+ );
+ }
+ else
+ {
+ (*ElementPtr->collision_func) (
+ ElementPtr, &SavePt,
+ TestElementPtr, &TestSavePt
+ );
+ (*TestElementPtr->collision_func) (
+ TestElementPtr, &TestSavePt,
+ ElementPtr, &SavePt
+ );
+ }
+
+ if (TestElementPtr->state_flags & COLLISION)
+ {
+ if (!(test_state_flags & COLLISION))
+ {
+ TestElementPtr->IntersectControl.IntersectStamp.origin =
+ TestSavePt;
+ TestElementPtr->next.location.x =
+ DISPLAY_TO_WORLD (TestSavePt.x);
+ TestElementPtr->next.location.y =
+ DISPLAY_TO_WORLD (TestSavePt.y);
+ InitIntersectEndPoint (TestElementPtr);
+ }
+ }
+
+ if (ElementPtr->state_flags & COLLISION)
+ {
+ if (!(state_flags & COLLISION))
+ {
+ ElementPtr->IntersectControl.IntersectStamp.origin =
+ SavePt;
+ ElementPtr->next.location.x =
+ DISPLAY_TO_WORLD (SavePt.x);
+ ElementPtr->next.location.y =
+ DISPLAY_TO_WORLD (SavePt.y);
+ InitIntersectEndPoint (ElementPtr);
+
+ if (!(state_flags & FINITE_LIFE) &&
+ !(test_state_flags & FINITE_LIFE))
+ {
+ collide (ElementPtr, TestElementPtr);
+
+ ProcessCollisions (GetHeadElement (), ElementPtr,
+ MAX_TIME_VALUE, process_flags);
+ ProcessCollisions (GetHeadElement (), TestElementPtr,
+ MAX_TIME_VALUE, process_flags);
+ }
+ }
+ UnlockElement (hTestElement);
+ return (COLLISION);
+ }
+
+ if (!CollidingElement (ElementPtr))
+ {
+ ElementPtr->state_flags |= COLLISION;
+ UnlockElement (hTestElement);
+ return (COLLISION);
+ }
+ }
+ }
+ }
+
+ UnlockElement (hTestElement);
+ }
+
+ return (ElementPtr->state_flags & COLLISION);
+}
+
+static VIEW_STATE
+PreProcessQueue (SIZE *pscroll_x, SIZE *pscroll_y)
+{
+ SIZE min_reduction, max_reduction;
+ COUNT sides_active;
+ POINT Origin;
+ HELEMENT hElement;
+ COUNT ships_alive;
+
+#ifdef KDEBUG
+ log_add (log_Debug, "PreProcess:");
+#endif
+ sides_active = (battle_counter[0] ? 1 : 0)
+ + (battle_counter[1] ? 1 : 0);
+
+ if (optMeleeScale == TFB_SCALE_STEP)
+ min_reduction = max_reduction = MAX_VIS_REDUCTION + 1;
+ else
+ min_reduction = max_reduction = MAX_ZOOM_OUT + (1 << ZOOM_SHIFT);
+
+ Origin.x = (COORD)(LOG_SPACE_WIDTH >> 1);
+ Origin.y = (COORD)(LOG_SPACE_HEIGHT >> 1);
+
+ hElement = GetHeadElement ();
+ ships_alive = 0;
+ while (hElement != 0)
+ {
+ ELEMENT *ElementPtr;
+ HELEMENT hNextElement;
+
+ LockElement (hElement, &ElementPtr);
+
+ if (!(ElementPtr->state_flags & PRE_PROCESS))
+ PreProcess (ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+
+ if (CollidingElement (ElementPtr)
+ && !(ElementPtr->state_flags & COLLISION))
+ ProcessCollisions (hNextElement, ElementPtr,
+ MAX_TIME_VALUE, PRE_PROCESS);
+
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ SIZE dx, dy;
+
+ ships_alive++;
+ if (max_reduction > opt_max_zoom_out
+ && min_reduction > opt_max_zoom_out)
+ {
+ Origin.x = DISPLAY_ALIGN (ElementPtr->next.location.x);
+ Origin.y = DISPLAY_ALIGN (ElementPtr->next.location.y);
+ }
+
+ dx = DISPLAY_ALIGN (ElementPtr->next.location.x) - Origin.x;
+ dx = WRAP_DELTA_X (dx);
+ dy = DISPLAY_ALIGN (ElementPtr->next.location.y) - Origin.y;
+ dy = WRAP_DELTA_Y (dy);
+
+ if (sides_active <= 2 || ElementPtr->playerNr == 0)
+ {
+ Origin.x = DISPLAY_ALIGN (Origin.x + (dx >> 1));
+ Origin.y = DISPLAY_ALIGN (Origin.y + (dy >> 1));
+
+ if (dx < 0)
+ dx = -dx;
+ if (dy < 0)
+ dy = -dy;
+ max_reduction = CalcReduction (dx, dy);
+ }
+ else if (max_reduction > opt_max_zoom_out
+ && min_reduction <= opt_max_zoom_out)
+ {
+ Origin.x = DISPLAY_ALIGN (Origin.x + (dx >> 1));
+ Origin.y = DISPLAY_ALIGN (Origin.y + (dy >> 1));
+
+ if (dx < 0)
+ dx = -dx;
+ if (dy < 0)
+ dy = -dy;
+ min_reduction = CalcReduction (dx, dy);
+ }
+ else
+ {
+ SIZE reduction;
+
+ if (dx < 0)
+ dx = -dx;
+ if (dy < 0)
+ dy = -dy;
+ reduction = CalcReduction (dx << 1, dy << 1);
+
+ if (min_reduction > opt_max_zoom_out
+ || reduction < min_reduction)
+ min_reduction = reduction;
+ }
+// log_add (log_Debug, "dx = %d dy = %d min_red = %d max_red = %d",
+// dx, dy, min_reduction, max_reduction);
+ }
+
+ UnlockElement (hElement);
+ hElement = hNextElement;
+ }
+
+ if ((min_reduction > opt_max_zoom_out || min_reduction <= max_reduction)
+ && (min_reduction = max_reduction) > opt_max_zoom_out
+ && (min_reduction = zoom_out) > opt_max_zoom_out)
+ {
+ if (optMeleeScale == TFB_SCALE_STEP)
+ min_reduction = 0;
+ else
+ min_reduction = 1 << ZOOM_SHIFT;
+ }
+
+#ifdef KDEBUG
+ log_add (log_Debug, "PreProcess: exit");
+#endif
+ return (CalcView (&Origin, min_reduction, pscroll_x, pscroll_y, ships_alive));
+}
+
+void
+InsertPrim (PRIM_LINKS *pLinks, COUNT primIndex, COUNT iPI)
+{
+ COUNT Link;
+ PRIM_LINKS PL;
+
+ if (iPI == END_OF_LIST)
+ {
+ Link = GetSuccLink (*pLinks); /* get tail */
+ if (Link == END_OF_LIST)
+ *pLinks = MakeLinks (primIndex, primIndex);
+ else
+ *pLinks = MakeLinks (GetPredLink (*pLinks), primIndex);
+ }
+ else
+ {
+ PL = GetPrimLinks (&DisplayArray[iPI]);
+ if (iPI != GetPredLink (*pLinks)) /* if not the head */
+ Link = GetPredLink (PL);
+ else
+ {
+ Link = END_OF_LIST;
+ *pLinks = MakeLinks (primIndex, GetSuccLink (*pLinks));
+ }
+ SetPrimLinks (&DisplayArray[iPI], primIndex, GetSuccLink (PL));
+ }
+
+ if (Link != END_OF_LIST)
+ {
+ PL = GetPrimLinks (&DisplayArray[Link]);
+ SetPrimLinks (&DisplayArray[Link], GetPredLink (PL), primIndex);
+ }
+ SetPrimLinks (&DisplayArray[primIndex], Link, iPI);
+}
+
+PRIM_LINKS DisplayLinks;
+
+static inline COORD
+CalcDisplayCoord (COORD c, COORD orgc, SIZE reduction)
+{
+ if (optMeleeScale == TFB_SCALE_STEP)
+ { /* old fixed-step zoom style */
+ return (c - orgc) >> reduction;
+ }
+ else
+ { /* new continuous zoom style */
+ return ((c - orgc) << ZOOM_SHIFT) / reduction;
+ }
+}
+
+static void
+PostProcessQueue (VIEW_STATE view_state, SIZE scroll_x,
+ SIZE scroll_y)
+{
+ POINT delta;
+ SIZE reduction;
+ HELEMENT hElement;
+
+#ifdef KDEBUG
+ log_add (log_Debug, "PostProcess:");
+#endif
+ if (optMeleeScale == TFB_SCALE_STEP)
+ reduction = zoom_out + ONE_SHIFT;
+ else
+ reduction = zoom_out << ONE_SHIFT;
+
+ hElement = GetHeadElement ();
+ while (hElement != 0)
+ {
+ ELEMENT_FLAGS state_flags;
+ ELEMENT *ElementPtr;
+ HELEMENT hNextElement;
+
+ LockElement (hElement, &ElementPtr);
+
+ state_flags = ElementPtr->state_flags;
+ if (state_flags & PRE_PROCESS)
+ {
+ if (!(state_flags & COLLISION))
+ ElementPtr->state_flags &= ~DEFY_PHYSICS;
+ else
+ ElementPtr->state_flags &= ~COLLISION;
+
+ if (state_flags & POST_PROCESS)
+ {
+ delta.x = 0;
+ delta.y = 0;
+ }
+ else
+ {
+ delta.x = scroll_x;
+ delta.y = scroll_y;
+ }
+ }
+ else
+ {
+ HELEMENT hPostElement;
+
+ hPostElement = hElement;
+ do
+ {
+ ELEMENT *PostElementPtr;
+
+ LockElement (hPostElement, &PostElementPtr);
+ if (!(PostElementPtr->state_flags & PRE_PROCESS))
+ PreProcess (PostElementPtr);
+ hNextElement = GetSuccElement (PostElementPtr);
+
+ if (CollidingElement (PostElementPtr)
+ && !(PostElementPtr->state_flags & COLLISION))
+ ProcessCollisions (GetHeadElement (), PostElementPtr,
+ MAX_TIME_VALUE, PRE_PROCESS | POST_PROCESS);
+ UnlockElement (hPostElement);
+ hPostElement = hNextElement;
+ } while (hPostElement != 0);
+
+ scroll_x = 0;
+ scroll_y = 0;
+ delta.x = 0;
+ delta.y = 0;
+ /* because these are newly added elements that are
+ * already in adjusted coordinates */
+ state_flags = ElementPtr->state_flags;
+ }
+
+ if (state_flags & DISAPPEARING)
+ {
+ hNextElement = GetSuccElement (ElementPtr);
+ UnlockElement (hElement);
+ RemoveElement (hElement);
+ FreeElement (hElement);
+ }
+ else
+ {
+ GRAPHICS_PRIM ObjType;
+
+ ObjType = GetPrimType (&DisplayArray[ElementPtr->PrimIndex]);
+ if (view_state != VIEW_STABLE
+ || (state_flags & (APPEARING | CHANGING)))
+ {
+ POINT next;
+
+ if (ObjType == LINE_PRIM)
+ {
+ SIZE dx, dy;
+
+ dx = ElementPtr->next.location.x
+ - ElementPtr->current.location.x;
+ dy = ElementPtr->next.location.y
+ - ElementPtr->current.location.y;
+
+ next.x = WRAP_X (ElementPtr->current.location.x + delta.x);
+ next.y = WRAP_Y (ElementPtr->current.location.y + delta.y);
+ DisplayArray[ElementPtr->PrimIndex].Object.Line.first.x =
+ CalcDisplayCoord (next.x, SpaceOrg.x, reduction);
+ DisplayArray[ElementPtr->PrimIndex].Object.Line.first.y =
+ CalcDisplayCoord (next.y, SpaceOrg.y, reduction);
+
+ next.x += dx;
+ next.y += dy;
+ DisplayArray[ElementPtr->PrimIndex].Object.Line.second.x =
+ CalcDisplayCoord (next.x, SpaceOrg.x, reduction);
+ DisplayArray[ElementPtr->PrimIndex].Object.Line.second.y =
+ CalcDisplayCoord (next.y, SpaceOrg.y, reduction);
+ }
+ else
+ {
+ next.x = WRAP_X (ElementPtr->next.location.x + delta.x);
+ next.y = WRAP_Y (ElementPtr->next.location.y + delta.y);
+ DisplayArray[ElementPtr->PrimIndex].Object.Point.x =
+ CalcDisplayCoord (next.x, SpaceOrg.x, reduction);
+ DisplayArray[ElementPtr->PrimIndex].Object.Point.y =
+ CalcDisplayCoord (next.y, SpaceOrg.y, reduction);
+
+ if (ObjType == STAMP_PRIM || ObjType == STAMPFILL_PRIM)
+ {
+ if (view_state == VIEW_CHANGE
+ || (state_flags & (APPEARING | CHANGING)))
+ {
+ COUNT index, scale = GSCALE_IDENTITY;
+
+ if (optMeleeScale == TFB_SCALE_STEP)
+ index = zoom_out;
+ else
+ CALC_ZOOM_STUFF (&index, &scale);
+
+ ElementPtr->next.image.frame = SetEquFrameIndex (
+ ElementPtr->next.image.farray[index],
+ ElementPtr->next.image.frame);
+
+ if (optMeleeScale == TFB_SCALE_TRILINEAR &&
+ index < 2 && scale != GSCALE_IDENTITY)
+ {
+ // enqueues drawcommand to assign next
+ // (smaller) zoom level image as mipmap,
+ // needed for trilinear scaling
+
+ FRAME frame = ElementPtr->next.image.frame;
+ FRAME mmframe = SetEquFrameIndex (
+ ElementPtr->next.image.farray[
+ index + 1], frame);
+
+ // TODO: This is currently hacky, this code
+ // really should not dereference FRAME.
+ // Perhaps make mipmap part of STAMP prim?
+ if (frame && mmframe)
+ {
+ HOT_SPOT mmhs = GetFrameHot (mmframe);
+ TFB_DrawScreen_SetMipmap (frame->image,
+ mmframe->image, mmhs.x, mmhs.y);
+ }
+ }
+ }
+ DisplayArray[ElementPtr->PrimIndex].Object.Stamp.frame =
+ ElementPtr->next.image.frame;
+ }
+ }
+
+ ElementPtr->next.location = next;
+ }
+
+ PostProcess (ElementPtr);
+
+ if (ObjType < NUM_PRIMS)
+ InsertPrim (&DisplayLinks, ElementPtr->PrimIndex, END_OF_LIST);
+
+ hNextElement = GetSuccElement (ElementPtr);
+ UnlockElement (hElement);
+ }
+
+ hElement = hNextElement;
+ }
+#ifdef KDEBUG
+ log_add (log_Debug, "PostProcess: exit");
+#endif
+}
+
+void
+InitDisplayList (void)
+{
+ COUNT i;
+
+ if (optMeleeScale == TFB_SCALE_STEP)
+ {
+ zoom_out = MAX_VIS_REDUCTION + 1;
+ opt_max_zoom_out = MAX_VIS_REDUCTION;
+ }
+ else
+ {
+ zoom_out = MAX_ZOOM_OUT + (1 << ZOOM_SHIFT);
+ opt_max_zoom_out = MAX_ZOOM_OUT;
+ }
+
+ ReinitQueue (&disp_q);
+
+ for (i = 0; i < MAX_DISPLAY_PRIMS; ++i)
+ SetPrimLinks (&DisplayArray[i], END_OF_LIST, i + 1);
+ SetPrimLinks (&DisplayArray[i - 1], END_OF_LIST, END_OF_LIST);
+ DisplayFreeList = 0;
+ DisplayLinks = MakeLinks (END_OF_LIST, END_OF_LIST);
+}
+
+UWORD nth_frame = 0;
+
+void
+RedrawQueue (BOOLEAN clear)
+{
+ SIZE scroll_x, scroll_y;
+ VIEW_STATE view_state;
+
+ SetContext (StatusContext);
+
+ view_state = PreProcessQueue (&scroll_x, &scroll_y);
+ PostProcessQueue (view_state, scroll_x, scroll_y);
+
+ if (optStereoSFX)
+ UpdateSoundPositions ();
+
+ SetContext (SpaceContext);
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE
+ || !(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ BYTE skip_frames;
+
+ skip_frames = HIBYTE (nth_frame);
+ if (skip_frames != (BYTE)~0
+ && (skip_frames == 0 || (--nth_frame & 0x00FF) == 0))
+ {
+ nth_frame += skip_frames;
+ if (clear)
+ ClearDrawable (); // this is for BATCH_BUILD_PAGE effect, but not scaled by SetGraphicScale
+
+ if (optMeleeScale != TFB_SCALE_STEP)
+ {
+ COUNT index, scale;
+
+ CALC_ZOOM_STUFF (&index, &scale);
+ SetGraphicScale (scale);
+ }
+
+ DrawBatch (DisplayArray, DisplayLinks, 0);//BATCH_BUILD_PAGE);
+ SetGraphicScale (0);
+ }
+
+ FlushSounds ();
+ }
+ else
+ { // sfx queue needs to be flushed when aborting
+ ProcessSound ((SOUND)~0, NULL);
+ FlushSounds ();
+ }
+
+ DisplayLinks = MakeLinks (END_OF_LIST, END_OF_LIST);
+}
+
+// Set the hTarget field to 0 for all elements in the display list that
+// have hTarget set to ElementPtr.
+void
+Untarget (ELEMENT *ElementPtr)
+{
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ HELEMENT hTarget;
+ ELEMENT *ListPtr;
+
+ LockElement (hElement, &ListPtr);
+ hNextElement = GetSuccElement (ListPtr);
+
+ hTarget = ListPtr->hTarget;
+ if (hTarget)
+ {
+ ELEMENT *TargetElementPtr;
+
+ LockElement (hTarget, &TargetElementPtr);
+ if (TargetElementPtr == ElementPtr)
+ ListPtr->hTarget = 0;
+ UnlockElement (hTarget);
+ }
+
+ UnlockElement (hElement);
+ }
+}
+
+void
+RemoveElement (HLINK hLink)
+{
+ if (optStereoSFX)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hLink, &ElementPtr);
+ if (ElementPtr != NULL)
+ RemoveSoundsForObject(ElementPtr);
+ UnlockElement (hLink);
+ }
+ RemoveQueue (&disp_q, hLink);
+}
+
+
diff --git a/src/uqm/process.h b/src/uqm/process.h
new file mode 100644
index 0000000..d794a2e
--- /dev/null
+++ b/src/uqm/process.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_PROCESS_H_INCL_
+#define UQM_PROCESS_H_INCL_
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "element.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void RedrawQueue (BOOLEAN clear);
+extern void InitDisplayList (void);
+extern void SetUpElement (ELEMENT *ElementPtr);
+extern void InsertPrim (PRIM_LINKS *pLinks, COUNT primIndex, COUNT iPI);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_PROCESS_H_INCL_ */
diff --git a/src/uqm/races.h b/src/uqm/races.h
new file mode 100644
index 0000000..b1a1617
--- /dev/null
+++ b/src/uqm/races.h
@@ -0,0 +1,675 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_RACES_H_
+#define UQM_RACES_H_
+
+#include "types.h"
+#include "libs/compiler.h"
+#include "units.h"
+#include "displist.h"
+
+typedef struct STARSHIP STARSHIP;
+typedef HLINK HSTARSHIP;
+
+#include "element.h"
+#include "libs/sndlib.h"
+#include "libs/reslib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// TODO: remove RACES_PER_PLAYER remnant of SC1
+#define RACES_PER_PLAYER 7
+#define MAX_SHIPS_PER_SIDE 14
+
+/* SHIP_INFO.ship_flags - ship specific flags */
+/* bits 0 and 1 are now available */
+#define SEEKING_WEAPON (1 << 2)
+#define SEEKING_SPECIAL (1 << 3)
+#define POINT_DEFENSE (1 << 4)
+ /* Ship has some point-defense capabilities */
+#define IMMEDIATE_WEAPON (1 << 5)
+#define CREW_IMMUNE (1 << 6)
+#define FIRES_FORE (1 << 7)
+#define FIRES_RIGHT (1 << 8)
+#define FIRES_AFT (1 << 9)
+#define FIRES_LEFT (1 << 10)
+#define SHIELD_DEFENSE (1 << 11)
+#define DONT_CHASE (1 << 12)
+#define PLAYER_CAPTAIN (1 << 13)
+ /* The protagonist himself is on board. He gets a different color. */
+
+typedef UWORD STATUS_FLAGS;
+
+/* STATUS_FLAGS - heat of battle specific flags */
+#define LEFT (1 << 0)
+#define RIGHT (1 << 1)
+#define THRUST (1 << 2)
+#define WEAPON (1 << 3)
+#define SPECIAL (1 << 4)
+#define LOW_ON_ENERGY (1 << 5)
+#define SHIP_BEYOND_MAX_SPEED (1 << 6)
+#define SHIP_AT_MAX_SPEED (1 << 7)
+#define SHIP_IN_GRAVITY_WELL (1 << 8)
+#define PLAY_VICTORY_DITTY (1 << 9)
+
+/* These track the old resource package orderings for the ship resource indices */
+typedef enum
+{
+ NO_ID,
+ ARILOU_ID,
+ CHMMR_ID,
+ EARTHLING_ID,
+ ORZ_ID,
+ PKUNK_ID,
+ SHOFIXTI_ID,
+ SPATHI_ID,
+ SUPOX_ID,
+ THRADDASH_ID,
+ UTWIG_ID,
+ VUX_ID,
+ YEHAT_ID,
+ MELNORME_ID,
+ DRUUGE_ID,
+ ILWRATH_ID,
+ MYCON_ID,
+ SLYLANDRO_ID,
+ UMGAH_ID,
+ UR_QUAN_ID,
+ ZOQFOTPIK_ID,
+ SYREEN_ID,
+ KOHR_AH_ID,
+ ANDROSYNTH_ID,
+ CHENJESU_ID,
+ MMRNMHRM_ID,
+ LAST_MELEE_ID = MMRNMHRM_ID,
+ SIS_SHIP_ID,
+ SA_MATRA_ID,
+ UR_QUAN_PROBE_ID,
+ NUM_SPECIES_ID
+} SPECIES_ID;
+
+typedef struct captain_stuff
+{
+ RESOURCE captain_rsc;
+ FRAME background;
+ FRAME turn;
+ FRAME thrust;
+ FRAME weapon;
+ FRAME special;
+} CAPTAIN_STUFF;
+
+typedef enum
+{
+ PURSUE = 0,
+ AVOID,
+ ENTICE,
+ NO_MOVEMENT
+} MOVEMENT_STATE;
+
+typedef struct
+{
+ ELEMENT *ObjectPtr;
+ COUNT facing;
+ COUNT which_turn;
+ MOVEMENT_STATE MoveState;
+} EVALUATE_DESC;
+
+typedef void (IntelligenceFunc) (ELEMENT *ShipPtr,
+ EVALUATE_DESC *ObjectsOfConcern, COUNT ConcernCounter);
+typedef struct
+{
+ COUNT ManeuverabilityIndex;
+ COUNT WeaponRange;
+ IntelligenceFunc *intelligence_func;
+} INTEL_STUFF;
+
+typedef struct
+{
+ COUNT max_thrust;
+ COUNT thrust_increment;
+ BYTE energy_regeneration;
+ BYTE weapon_energy_cost;
+ BYTE special_energy_cost;
+ BYTE energy_wait;
+ BYTE turn_wait;
+ BYTE thrust_wait;
+ BYTE weapon_wait;
+ BYTE special_wait;
+ BYTE ship_mass;
+} CHARACTERISTIC_STUFF;
+
+typedef struct
+{
+ UWORD ship_flags;
+ BYTE ship_cost;
+
+ COUNT crew_level;
+ COUNT max_crew;
+ BYTE energy_level;
+ BYTE max_energy;
+
+ RESOURCE race_strings_rsc;
+ RESOURCE icons_rsc;
+ RESOURCE melee_icon_rsc;
+
+ STRING race_strings;
+ FRAME icons;
+ FRAME melee_icon;
+} SHIP_INFO;
+
+typedef struct
+{
+ COUNT strength;
+ POINT known_loc;
+
+#define INFINITE_RADIUS ((COUNT) ~0)
+} FLEET_STUFF;
+
+typedef struct
+{
+ RESOURCE ship_rsc[NUM_VIEWS];
+ RESOURCE weapon_rsc[NUM_VIEWS];
+ RESOURCE special_rsc[NUM_VIEWS];
+ CAPTAIN_STUFF captain_control;
+ RESOURCE victory_ditty_rsc;
+ RESOURCE ship_sounds_rsc;
+
+ FRAME ship[NUM_VIEWS];
+ FRAME weapon[NUM_VIEWS];
+ FRAME special[NUM_VIEWS];
+ MUSIC_REF victory_ditty;
+ SOUND ship_sounds;
+} DATA_STUFF;
+
+
+typedef struct race_desc RACE_DESC;
+
+typedef void (PREPROCESS_FUNC) (ELEMENT *ElementPtr);
+typedef void (POSTPROCESS_FUNC) (ELEMENT *ElementPtr);
+typedef COUNT (INIT_WEAPON_FUNC) (ELEMENT *ElementPtr, HELEMENT Weapon[]);
+typedef void (UNINIT_FUNC) (RACE_DESC *pRaceDesc);
+
+struct race_desc
+{
+ SHIP_INFO ship_info _ALIGNED_ANY;
+ FLEET_STUFF fleet _ALIGNED_ANY;
+ CHARACTERISTIC_STUFF characteristics _ALIGNED_ANY;
+ DATA_STUFF ship_data _ALIGNED_ANY;
+ INTEL_STUFF cyborg_control _ALIGNED_ANY;
+
+ UNINIT_FUNC *uninit_func;
+ PREPROCESS_FUNC *preprocess_func;
+ POSTPROCESS_FUNC *postprocess_func;
+ INIT_WEAPON_FUNC *init_weapon_func;
+
+ void* data; // private ship data, ship code owns this
+
+ void *CodeRef;
+};
+
+#define SHIP_BASE_COMMON \
+ /* LINK elements; must be first */ \
+ HLINK pred; \
+ HLINK succ; \
+ \
+ SPECIES_ID SpeciesID; \
+ BYTE captains_name_index \
+ /* Also used in full-game to detect if a STARSHIP is an escort
+ * or the flagship (captains_name_index == 0) */
+
+typedef struct
+{
+ SHIP_BASE_COMMON;
+} SHIP_BASE;
+
+
+struct STARSHIP
+{
+ SHIP_BASE_COMMON;
+
+ RACE_DESC *RaceDescPtr;
+
+ // Ship information
+ COUNT crew_level;
+ // In full-game battles: crew left
+ // In SuperMelee: irrelevant
+ COUNT max_crew;
+ BYTE ship_cost;
+ // In Super Melee ship queue: ship cost
+ // In full-game: irrelevant
+ COUNT index;
+ // original queue index
+ STRING race_strings;
+ FRAME icons;
+
+ // Battle states
+ BYTE weapon_counter;
+ // In battle: frames left before primary weapon can be used
+ BYTE special_counter;
+ // In battle: frames left before special can be used
+ BYTE energy_counter;
+ // In battle: frames left before energy regeneration
+
+ BYTE ship_input_state;
+ STATUS_FLAGS cur_status_flags;
+ STATUS_FLAGS old_status_flags;
+
+ HELEMENT hShip;
+ COUNT ShipFacing;
+
+ SIZE playerNr;
+ // 0: bottom player; In full-game: the human player (RPG)
+ // 1: top player; In full-game: the NPC opponent
+ // -1: neutral; this should currently never happen (asserts)
+ BYTE control;
+ // HUMAN, COMPUTER or NETWORK control flags, see intel.h
+};
+
+#define RPG_PLAYER_NUM 0
+#define NPC_PLAYER_NUM 1
+
+static inline STARSHIP *
+LockStarShip (const QUEUE *pq, HSTARSHIP h)
+{
+ assert (GetLinkSize (pq) == sizeof (STARSHIP));
+ return (STARSHIP *) LockLink (pq, h);
+}
+
+#define UnlockStarShip(pq, h) UnlockLink (pq, h)
+#define FreeStarShip(pq, h) FreeLink (pq, h)
+
+
+typedef HLINK HSHIPFRAG;
+
+typedef struct
+{
+ SHIP_BASE_COMMON;
+
+ BYTE race_id;
+ BYTE index;
+ COUNT crew_level;
+ /* For ships in npc_built_ship_q, the value INFINITE_FLEET for
+ * crew_level indicates an infinite number of ships. */
+ COUNT max_crew;
+
+ BYTE energy_level;
+ BYTE max_energy;
+ // XXX: energy_level and max_energy are unused. We save and load
+ // them, but otherwise nothing needs them atm.
+
+ STRING race_strings;
+ FRAME icons;
+ FRAME melee_icon; /* Only used by Shipyard */
+
+#define INFINITE_FLEET ((COUNT) ~0)
+} SHIP_FRAGMENT;
+
+static inline SHIP_FRAGMENT *
+LockShipFrag (const QUEUE *pq, HSHIPFRAG h)
+{
+ assert (GetLinkSize (pq) == sizeof (SHIP_FRAGMENT));
+ return (SHIP_FRAGMENT *) LockLink (pq, h);
+}
+
+#define UnlockShipFrag(pq, h) UnlockLink (pq, h)
+#define FreeShipFrag(pq, h) FreeLink (pq, h)
+
+
+typedef HLINK HFLEETINFO;
+
+typedef struct
+{
+ // LINK elements; must be first
+ HFLEETINFO pred;
+ HFLEETINFO succ;
+
+ SPECIES_ID SpeciesID;
+
+ UWORD allied_state; /* GOOD_GUY, BAD_GUY or DEAD_GUY */
+ BYTE days_left; /* Days left before the fleet reachers 'dest_loc'. */
+ BYTE growth_fract;
+ COUNT crew_level;
+ COUNT max_crew;
+ BYTE growth;
+ BYTE max_energy;
+ POINT loc; /* Location of the fleet (center) */
+
+ STRING race_strings;
+ /* Race specific strings, see doc/devel/racestrings. */
+ FRAME icons;
+ FRAME melee_icon;
+
+ COUNT actual_strength;
+ /* Measure for the size of the sphere of influence.
+ * 0 if there is none and no ships will be generated.
+ * '(COUNT) ~0' if there is none, and the ship generation
+ * is handled separately. */
+ COUNT known_strength;
+ /* Measure for the size of the sphere of influence when last
+ * checked the starmap.
+ * 0 if the race's SoI is not known. */
+ POINT known_loc;
+ /* Location of the SoI (center) when last checked
+ * the starmap. */
+
+ BYTE growth_err_term;
+ BYTE func_index;
+ /* Function index defined in clock.h (the same as in SetEvent())
+ * for the function to call when the fleet reaches 'dest_loc'.
+ * '(BYTE) ~0' means no function to call. */
+ POINT dest_loc;
+ /* Location to which the fleet (center) is moving. */
+
+} FLEET_INFO;
+
+// Values for FLEET_INFO.allied_state
+enum
+{
+ DEAD_GUY = 0, // Race is extinct
+ GOOD_GUY, // Race is allied with the player
+ BAD_GUY, // Race is not allied with the player
+};
+
+static inline FLEET_INFO *
+LockFleetInfo (const QUEUE *pq, HFLEETINFO h)
+{
+ assert (GetLinkSize (pq) == sizeof (FLEET_INFO));
+ return (FLEET_INFO *) LockLink (pq, h);
+}
+
+#define UnlockFleetInfo(pq, h) UnlockLink (pq, h)
+
+enum
+{
+ ARILOU_SHIP,
+ CHMMR_SHIP,
+ HUMAN_SHIP,
+ ORZ_SHIP,
+ PKUNK_SHIP,
+ SHOFIXTI_SHIP,
+ SPATHI_SHIP,
+ SUPOX_SHIP,
+ THRADDASH_SHIP,
+ UTWIG_SHIP,
+ VUX_SHIP,
+ YEHAT_SHIP,
+ MELNORME_SHIP,
+ DRUUGE_SHIP,
+ ILWRATH_SHIP,
+ MYCON_SHIP,
+ SLYLANDRO_SHIP,
+ UMGAH_SHIP,
+ URQUAN_SHIP,
+ ZOQFOTPIK_SHIP,
+
+ SYREEN_SHIP,
+ BLACK_URQUAN_SHIP,
+ YEHAT_REBEL_SHIP,
+ URQUAN_DRONE_SHIP,
+ SAMATRA_SHIP = URQUAN_DRONE_SHIP,
+
+ NUM_AVAILABLE_RACES
+};
+
+#define RACE_COMMUNICATION \
+ ARILOU_CONVERSATION, /* ARILOU_SHIP */ \
+ CHMMR_CONVERSATION, /* CHMMR_SHIP */ \
+ INVALID_CONVERSATION, /* HUMAN_SHIP */ \
+ ORZ_CONVERSATION, /* ORZ_SHIP */ \
+ PKUNK_CONVERSATION, /* PKUNK_SHIP */ \
+ SHOFIXTI_CONVERSATION, /* SHOFIXTI_SHIP */ \
+ SPATHI_CONVERSATION, /* SPATHI_SHIP */ \
+ SUPOX_CONVERSATION, /* SUPOX_SHIP */ \
+ THRADD_CONVERSATION, /* THRADDASH_SHIP */ \
+ UTWIG_CONVERSATION, /* UTWIG_SHIP */ \
+ VUX_CONVERSATION, /* VUX_SHIP */ \
+ YEHAT_CONVERSATION, /* YEHAT_SHIP */ \
+ MELNORME_CONVERSATION, /* MELNORME_SHIP */ \
+ DRUUGE_CONVERSATION, /* DRUUGE_SHIP */ \
+ ILWRATH_CONVERSATION, /* ILWRATH_SHIP */ \
+ MYCON_CONVERSATION, /* MYCON_SHIP */ \
+ SLYLANDRO_CONVERSATION, /* SLYLANDRO_SHIP */ \
+ UMGAH_CONVERSATION, /* UMGAH_SHIP */ \
+ URQUAN_CONVERSATION, /* URQUAN_SHIP */ \
+ ZOQFOTPIK_CONVERSATION, /* ZOQFOTPIK_SHIP */ \
+ INVALID_CONVERSATION, /* SYREEN_SHIP */ \
+ BLACKURQ_CONVERSATION, /* BLACK_URQUAN_SHIP */ \
+ YEHAT_REBEL_CONVERSATION, /* YEHAT_REBEL_SHIP */ \
+ URQUAN_DRONE_CONVERSATION, /* URQUAN_DRONE_SHIP */
+
+#define RACE_SHIP_FOR_COMM \
+ ARILOU_SHIP, /* ARILOU_CONVERSATION */ \
+ CHMMR_SHIP, /* CHMMR_CONVERSATION */ \
+ HUMAN_SHIP, /* COMMANDER_CONVERSATION */ \
+ ORZ_SHIP, /* ORZ_CONVERSATION */ \
+ PKUNK_SHIP, /* PKUNK_CONVERSATION */ \
+ SHOFIXTI_SHIP, /* SHOFIXTI_CONVERSATION */ \
+ SPATHI_SHIP, /* SPATHI_CONVERSATION */ \
+ SUPOX_SHIP, /* SUPOX_CONVERSATION */ \
+ THRADDASH_SHIP, /* THRADD_CONVERSATION */ \
+ UTWIG_SHIP, /* UTWIG_CONVERSATION */ \
+ VUX_SHIP, /* VUX_CONVERSATION */ \
+ YEHAT_SHIP, /* YEHAT_CONVERSATION */ \
+ MELNORME_SHIP, /* MELNORME_CONVERSATION */ \
+ DRUUGE_SHIP, /* DRUUGE_CONVERSATION */ \
+ ILWRATH_SHIP, /* ILWRATH_CONVERSATION */ \
+ MYCON_SHIP, /* MYCON_CONVERSATION */ \
+ SLYLANDRO_SHIP, /* SLYLANDRO_CONVERSATION */ \
+ UMGAH_SHIP, /* UMGAH_CONVERSATION */ \
+ URQUAN_SHIP, /* URQUAN_CONVERSATION */ \
+ ZOQFOTPIK_SHIP, /* ZOQFOTPIK_CONVERSATION */ \
+ SYREEN_SHIP, /* SYREEN_CONVERSATION */ \
+ BLACK_URQUAN_SHIP, /* BLACKURQ_CONVERSATION */ \
+ UMGAH_SHIP, /* TALKING_PET_CONVERSATION */ \
+ SLYLANDRO_SHIP, /* SLYLANDRO_HOME_CONVERSATION */ \
+ URQUAN_DRONE_SHIP, /* URQUAN_DRONE_CONVERSATION */ \
+ YEHAT_SHIP, /* YEHAT_REBEL_CONVERSATION */ \
+ HUMAN_SHIP /* INVALID_CONVERSATION */
+
+#define RACE_SHIP_COST \
+ 1600, /* ARILOU_SHIP */ \
+ 3000, /* CHMMR_SHIP */ \
+ 1100, /* HUMAN_SHIP */ \
+ 2300, /* ORZ_SHIP */ \
+ 2000, /* PKUNK_SHIP */ \
+ 500, /* SHOFIXTI_SHIP */ \
+ 1800, /* SPATHI_SHIP */ \
+ 1600, /* SUPOX_SHIP */ \
+ 1000, /* THRADDASH_SHIP */ \
+ 2200, /* UTWIG_SHIP */ \
+ 1200, /* VUX_SHIP */ \
+ 2300, /* YEHAT_SHIP */ \
+ 3600, /* MELNORME_SHIP */ \
+ 1700, /* DRUUGE_SHIP */ \
+ 1000, /* ILWRATH_SHIP */ \
+ 2100, /* MYCON_SHIP */ \
+ 4400, /* SLYLANDRO_SHIP */ \
+ 700, /* UMGAH_SHIP */ \
+ 3000, /* URQUAN_SHIP */ \
+ 600, /* ZOQFOTPIK_SHIP */ \
+ 1300, /* SYREEN_SHIP */ \
+ 3000, /* BLACK_URQUAN_SHIP */ \
+ 2300, /* YEHAT_REBEL_SHIP */
+
+#define LOG_TO_IP(s) ((s) << 1)
+#define RACE_IP_SPEED \
+ LOG_TO_IP (40), /* ARILOU_SHIP */ \
+ LOG_TO_IP (27), /* CHMMR_SHIP */ \
+ LOG_TO_IP (24), /* HUMAN_SHIP */ \
+ LOG_TO_IP (40), /* ORZ_SHIP */ \
+ LOG_TO_IP (40), /* PKUNK_SHIP */ \
+ LOG_TO_IP (35), /* SHOFIXTI_SHIP */ \
+ LOG_TO_IP (48), /* SPATHI_SHIP */ \
+ LOG_TO_IP (40), /* SUPOX_SHIP */ \
+ LOG_TO_IP (28), /* THRADDASH_SHIP */ \
+ LOG_TO_IP (30), /* UTWIG_SHIP */ \
+ LOG_TO_IP (21), /* VUX_SHIP */ \
+ LOG_TO_IP (30), /* YEHAT_SHIP */ \
+ LOG_TO_IP (40), /* MELNORME_SHIP */ \
+ LOG_TO_IP (20), /* DRUUGE_SHIP */ \
+ LOG_TO_IP (25), /* ILWRATH_SHIP */ \
+ LOG_TO_IP (27), /* MYCON_SHIP */ \
+ LOG_TO_IP (60), /* SLYLANDRO_SHIP */ \
+ LOG_TO_IP (18), /* UMGAH_SHIP */ \
+ LOG_TO_IP (30), /* URQUAN_SHIP */ \
+ LOG_TO_IP (40), /* ZOQFOTPIK_SHIP */ \
+ LOG_TO_IP (36), /* SYREEN_SHIP */ \
+ LOG_TO_IP (30), /* BLACK_URQUAN_SHIP */ \
+ LOG_TO_IP (30), /* YEHAT_REBEL_SHIP */ \
+ LOG_TO_IP (90), /* URQUAN_DRONE_SHIP */
+
+#define LOG_TO_HYPER(s) (WORLD_TO_VELOCITY (s) >> 1)
+#define RACE_HYPER_SPEED \
+ LOG_TO_HYPER (40), /* ARILOU_SHIP */ \
+ LOG_TO_HYPER (27), /* CHMMR_SHIP */ \
+ LOG_TO_HYPER (24), /* HUMAN_SHIP */ \
+ LOG_TO_HYPER (40), /* ORZ_SHIP */ \
+ LOG_TO_HYPER (40), /* PKUNK_SHIP */ \
+ LOG_TO_HYPER (35), /* SHOFIXTI_SHIP */ \
+ LOG_TO_HYPER (48), /* SPATHI_SHIP */ \
+ LOG_TO_HYPER (40), /* SUPOX_SHIP */ \
+ LOG_TO_HYPER (50), /* THRADDASH_SHIP */ \
+ LOG_TO_HYPER (30), /* UTWIG_SHIP */ \
+ LOG_TO_HYPER (21), /* VUX_SHIP */ \
+ LOG_TO_HYPER (30), /* YEHAT_SHIP */ \
+ LOG_TO_HYPER (40), /* MELNORME_SHIP */ \
+ LOG_TO_HYPER (20), /* DRUUGE_SHIP */ \
+ LOG_TO_HYPER (25), /* ILWRATH_SHIP */ \
+ LOG_TO_HYPER (27), /* MYCON_SHIP */ \
+ LOG_TO_HYPER (60), /* SLYLANDRO_SHIP */ \
+ LOG_TO_HYPER (18), /* UMGAH_SHIP */ \
+ LOG_TO_HYPER (30), /* URQUAN_SHIP */ \
+ LOG_TO_HYPER (40), /* ZOQFOTPIK_SHIP */ \
+ LOG_TO_HYPER (36), /* SYREEN_SHIP */ \
+ LOG_TO_HYPER (30), /* BLACK_URQUAN_SHIP */ \
+ LOG_TO_HYPER (30), /* YEHAT_REBEL_SHIP */
+
+#define RACE_HYPERSPACE_PERCENT \
+ 20, /* ARILOU_SHIP */ \
+ 0, /* CHMMR_SHIP */ \
+ 0, /* HUMAN_SHIP */ \
+ 20, /* ORZ_SHIP */ \
+ 40, /* PKUNK_SHIP */ \
+ 0, /* SHOFIXTI_SHIP */ \
+ 20, /* SPATHI_SHIP */ \
+ 40, /* SUPOX_SHIP */ \
+ 60, /* THRADDASH_SHIP */ \
+ 40, /* UTWIG_SHIP */ \
+ 40, /* VUX_SHIP */ \
+ 60, /* YEHAT_SHIP */ \
+ 0, /* MELNORME_SHIP */ \
+ 30, /* DRUUGE_SHIP */ \
+ 60, /* ILWRATH_SHIP */ \
+ 40, /* MYCON_SHIP */ \
+ 2, /* SLYLANDRO_SHIP */ \
+ 30, /* UMGAH_SHIP */ \
+ 70, /* URQUAN_SHIP */ \
+ 0, /* ZOQFOTPIK_SHIP */ \
+ 0, /* SYREEN_SHIP */ \
+ 70, /* BLACK_URQUAN_SHIP */ \
+ 60, /* YEHAT_REBEL_SHIP */ \
+ 0, /* URQUAN_DRONE_SHIP */
+
+#define RACE_INTERPLANETARY_PERCENT \
+ 0, /* ARILOU_SHIP */ \
+ 0, /* CHMMR_SHIP */ \
+ 0, /* HUMAN_SHIP */ \
+ 20, /* ORZ_SHIP */ \
+ 20, /* PKUNK_SHIP */ \
+ 0, /* SHOFIXTI_SHIP */ \
+ 10, /* SPATHI_SHIP */ \
+ 20, /* SUPOX_SHIP */ \
+ 20, /* THRADDASH_SHIP */ \
+ 20, /* UTWIG_SHIP */ \
+ 20, /* VUX_SHIP */ \
+ 40, /* YEHAT_SHIP */ \
+ 0, /* MELNORME_SHIP */ \
+ 20, /* DRUUGE_SHIP */ \
+ 60, /* ILWRATH_SHIP */ \
+ 20, /* MYCON_SHIP */ \
+ 5, /* SLYLANDRO_SHIP */ \
+ 20, /* UMGAH_SHIP */ \
+ 40, /* URQUAN_SHIP */ \
+ 0, /* ZOQFOTPIK_SHIP */ \
+ 0, /* SYREEN_SHIP */ \
+ 40, /* BLACK_URQUAN_SHIP */ \
+ 40, /* YEHAT_REBEL_SHIP */ \
+ 0, /* URQUAN_DRONE_SHIP */
+
+// How many ships will an encounter consist of.
+// The first number specifies the minimum, the second the maximum.
+// The chance is 50% for each ship past the minimum to be present.
+#define RACE_ENCOUNTER_MAKEUP \
+ MAKE_BYTE (1, 5), /* ARILOU_SHIP */ \
+ 0, /* CHMMR_SHIP */ \
+ 0, /* HUMAN_SHIP */ \
+ MAKE_BYTE (1, 5), /* ORZ_SHIP */ \
+ MAKE_BYTE (1, 5), /* PKUNK_SHIP */ \
+ 0, /* SHOFIXTI_SHIP */ \
+ MAKE_BYTE (1, 5), /* SPATHI_SHIP */ \
+ MAKE_BYTE (1, 5), /* SUPOX_SHIP */ \
+ MAKE_BYTE (1, 5), /* THRADDASH_SHIP */ \
+ MAKE_BYTE (1, 5), /* UTWIG_SHIP */ \
+ MAKE_BYTE (1, 5), /* VUX_SHIP */ \
+ MAKE_BYTE (1, 5), /* YEHAT_SHIP */ \
+ MAKE_BYTE (1, 1), /* MELNORME_SHIP */ \
+ MAKE_BYTE (1, 5), /* DRUUGE_SHIP */ \
+ MAKE_BYTE (1, 5), /* ILWRATH_SHIP */ \
+ MAKE_BYTE (1, 5), /* MYCON_SHIP */ \
+ MAKE_BYTE (1, 1), /* SLYLANDRO_SHIP */ \
+ MAKE_BYTE (1, 5), /* UMGAH_SHIP */ \
+ MAKE_BYTE (1, 5), /* URQUAN_SHIP */ \
+ MAKE_BYTE (1, 5), /* ZOQFOTPIK_SHIP */ \
+ 0, /* SYREEN_SHIP */ \
+ MAKE_BYTE (1, 5), /* BLACK_URQUAN_SHIP */ \
+ MAKE_BYTE (1, 5), /* YEHAT_REBEL_SHIP */
+
+#define RACE_COLORS \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x10), 0x53), /* ARILOU_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x00), 0x00), /* CHMMR_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x01, 0x1f), 0x4D), /* HUMAN_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0E, 0x00, 0x0E), 0x36), /* ORZ_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x06, 0x08), 0x62), /* PKUNK_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x00), 0x00), /* SHOFIXTI_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0C, 0x05, 0x00), 0x76), /* SPATHI_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0C, 0x05, 0x00), 0x76), /* SUPOX_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x06, 0x08), 0x62), /* THRADDASH_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x06, 0x08), 0x62), /* UTWIG_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x10), 0x53), /* VUX_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0A, 0x00, 0x11), 0x3D), /* YEHAT_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x06, 0x08), 0x62), /* MELNORME_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D), /* DRUUGE_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0E, 0x00, 0x0E), 0x36), /* ILWRATH_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0E, 0x00, 0x0E), 0x36), /* MYCON_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0C, 0x05, 0x00), 0x76), /* SLYLANDRO_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0A, 0x00, 0x11), 0x3D), /* UMGAH_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x08, 0x00), 0x6E), /* URQUAN_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D), /* ZOQFOTPIK_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x00), 0x00), /* SYREEN_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x06, 0x06, 0x06), 0x20), /* BLACK_URQUAN_SHIP */ \
+ BUILD_COLOR (MAKE_RGB15_INIT (0x14, 0x07, 0x1F), 0x39), /* YEHAT_REBEL_SHIP */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_RACES_H_ */
diff --git a/src/uqm/resinst.h b/src/uqm/resinst.h
new file mode 100644
index 0000000..c876abc
--- /dev/null
+++ b/src/uqm/resinst.h
@@ -0,0 +1,24 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "ikey_con.h"
+#include "igfxres.h"
+#include "ifontres.h"
+#include "istrtab.h"
+#include "isndres.h"
+#include "imusicre.h"
diff --git a/src/uqm/restart.c b/src/uqm/restart.c
new file mode 100644
index 0000000..f52e753
--- /dev/null
+++ b/src/uqm/restart.c
@@ -0,0 +1,413 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "restart.h"
+
+#include "colors.h"
+#include "controls.h"
+#include "credits.h"
+#include "starmap.h"
+#include "fmv.h"
+#include "menustat.h"
+#include "gamestr.h"
+#include "globdata.h"
+#include "intel.h"
+#include "supermelee/melee.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "save.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "setupmenu.h"
+#include "util.h"
+#include "starcon.h"
+#include "uqmversion.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/inplib.h"
+
+
+enum
+{
+ START_NEW_GAME = 0,
+ LOAD_SAVED_GAME,
+ PLAY_SUPER_MELEE,
+ SETUP_GAME,
+ QUIT_GAME
+};
+
+// Draw the full restart menu. Nothing is done with selections.
+static void
+DrawRestartMenuGraphic (MENU_STATE *pMS)
+{
+ RECT r;
+ STAMP s;
+ TEXT t;
+ UNICODE buf[64];
+
+ s.frame = pMS->CurFrame;
+ GetFrameRect (s.frame, &r);
+ s.origin.x = (SCREEN_WIDTH - r.extent.width) >> 1;
+ s.origin.y = (SCREEN_HEIGHT - r.extent.height) >> 1;
+
+ SetContextBackGroundColor (BLACK_COLOR);
+ BatchGraphics ();
+ ClearDrawable ();
+ FlushColorXForms ();
+ DrawStamp (&s);
+
+ // Put the version number in the bottom right corner.
+ SetContextFont (TinyFont);
+ t.pStr = buf;
+ t.baseline.x = SCREEN_WIDTH - 3;
+ t.baseline.y = SCREEN_HEIGHT - 2;
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ sprintf (buf, "v%d.%d.%d%s", UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ SetContextForeGroundColor (WHITE_COLOR);
+ font_DrawText (&t);
+
+ UnbatchGraphics ();
+}
+
+static void
+DrawRestartMenu (MENU_STATE *pMS, BYTE NewState, FRAME f)
+{
+ POINT origin;
+ origin.x = 0;
+ origin.y = 0;
+ Flash_setOverlay(pMS->flashContext,
+ &origin, SetAbsFrameIndex (f, NewState + 1));
+}
+
+static BOOLEAN
+DoRestart (MENU_STATE *pMS)
+{
+ static TimeCount LastInputTime;
+ static TimeCount InactTimeOut;
+ TimeCount TimeIn = GetTimeCounter ();
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (pMS->Initialized)
+ Flash_process(pMS->flashContext);
+
+ if (!pMS->Initialized)
+ {
+ if (pMS->hMusic)
+ {
+ StopMusic ();
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+ pMS->hMusic = LoadMusic (MAINMENU_MUSIC);
+ InactTimeOut = (pMS->hMusic ? 120 : 20) * ONE_SECOND;
+ pMS->flashContext = Flash_createOverlay (ScreenContext,
+ NULL, NULL);
+ Flash_setMergeFactors (pMS->flashContext, -3, 3, 16);
+ Flash_setSpeed (pMS->flashContext, (6 * ONE_SECOND) / 16, 0,
+ (6 * ONE_SECOND) / 16, 0);
+ Flash_setFrameTime (pMS->flashContext, ONE_SECOND / 16);
+ Flash_setState(pMS->flashContext, FlashState_fadeIn,
+ (3 * ONE_SECOND) / 16);
+ DrawRestartMenu (pMS, pMS->CurState, pMS->CurFrame);
+ Flash_start (pMS->flashContext);
+ PlayMusic (pMS->hMusic, TRUE, 1);
+ LastInputTime = GetTimeCounter ();
+ pMS->Initialized = TRUE;
+
+ SleepThreadUntil (FadeScreen (FadeAllToColor, ONE_SECOND / 2));
+ }
+ else if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ switch (pMS->CurState)
+ {
+ case LOAD_SAVED_GAME:
+ LastActivity = CHECK_LOAD;
+ GLOBAL (CurrentActivity) = IN_INTERPLANETARY;
+ break;
+ case START_NEW_GAME:
+ LastActivity = CHECK_LOAD | CHECK_RESTART;
+ GLOBAL (CurrentActivity) = IN_INTERPLANETARY;
+ break;
+ case PLAY_SUPER_MELEE:
+ GLOBAL (CurrentActivity) = SUPER_MELEE;
+ break;
+ case SETUP_GAME:
+ Flash_pause(pMS->flashContext);
+ Flash_setState(pMS->flashContext, FlashState_fadeIn,
+ (3 * ONE_SECOND) / 16);
+ SetupMenu ();
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN,
+ MENU_SOUND_SELECT);
+ LastInputTime = GetTimeCounter ();
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ DrawRestartMenuGraphic (pMS);
+ ScreenTransition (3, NULL);
+ DrawRestartMenu (pMS, pMS->CurState, pMS->CurFrame);
+ Flash_continue(pMS->flashContext);
+ UnbatchGraphics ();
+ return TRUE;
+ case QUIT_GAME:
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+ GLOBAL (CurrentActivity) = CHECK_ABORT;
+ break;
+ }
+
+ Flash_pause(pMS->flashContext);
+
+ return FALSE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_UP] ||
+ PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ BYTE NewState;
+
+ NewState = pMS->CurState;
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (NewState == START_NEW_GAME)
+ NewState = QUIT_GAME;
+ else
+ --NewState;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ if (NewState == QUIT_GAME)
+ NewState = START_NEW_GAME;
+ else
+ ++NewState;
+ }
+ if (NewState != pMS->CurState)
+ {
+ BatchGraphics ();
+ DrawRestartMenu (pMS, NewState, pMS->CurFrame);
+ UnbatchGraphics ();
+ pMS->CurState = NewState;
+ }
+
+ LastInputTime = GetTimeCounter ();
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_RIGHT])
+ { // Does nothing, but counts as input for timeout purposes
+ LastInputTime = GetTimeCounter ();
+ }
+ else if (MouseButtonDown)
+ {
+ Flash_pause(pMS->flashContext);
+ DoPopupWindow (GAME_STRING (MAINMENU_STRING_BASE + 54));
+ // Mouse not supported message
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN, MENU_SOUND_SELECT);
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ DrawRestartMenuGraphic (pMS);
+ DrawRestartMenu (pMS, pMS->CurState, pMS->CurFrame);
+ ScreenTransition (3, NULL);
+ UnbatchGraphics ();
+ Flash_continue(pMS->flashContext);
+
+ LastInputTime = GetTimeCounter ();
+ }
+ else
+ { // No input received, check if timed out
+ if (GetTimeCounter () - LastInputTime > InactTimeOut)
+ {
+ SleepThreadUntil (FadeMusic (0, ONE_SECOND));
+ StopMusic ();
+ FadeMusic (NORMAL_VOLUME, 0);
+
+ GLOBAL (CurrentActivity) = (ACTIVITY)~0;
+ return FALSE;
+ }
+ }
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static BOOLEAN
+RestartMenu (MENU_STATE *pMS)
+{
+ TimeCount TimeOut;
+
+ ReinitQueue (&race_q[0]);
+ ReinitQueue (&race_q[1]);
+
+ SetContext (ScreenContext);
+
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ if (GLOBAL_SIS (CrewEnlisted) == (COUNT)~0
+ && GET_GAME_STATE (UTWIG_BOMB_ON_SHIP)
+ && !GET_GAME_STATE (UTWIG_BOMB))
+ { // player blew himself up with Utwig bomb
+ SET_GAME_STATE (UTWIG_BOMB_ON_SHIP, 0);
+
+ SleepThreadUntil (FadeScreen (FadeAllToWhite, ONE_SECOND / 8)
+ + ONE_SECOND / 60);
+ SetContextBackGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F));
+ ClearDrawable ();
+ FlushColorXForms ();
+
+ TimeOut = ONE_SECOND / 8;
+ }
+ else
+ {
+ TimeOut = ONE_SECOND / 2;
+
+ if (LOBYTE (LastActivity) == WON_LAST_BATTLE)
+ {
+ GLOBAL (CurrentActivity) = WON_LAST_BATTLE;
+ Victory ();
+ Credits (TRUE);
+
+ FreeGameData ();
+
+ GLOBAL (CurrentActivity) = CHECK_ABORT;
+ }
+ }
+
+ LastActivity = 0;
+ NextActivity = 0;
+
+ // TODO: This fade is not always necessary, especially after a splash
+ // screen. It only makes a user wait.
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, TimeOut));
+ if (TimeOut == ONE_SECOND / 8)
+ SleepThread (ONE_SECOND * 3);
+
+ pMS->CurFrame = CaptureDrawable (LoadGraphic (RESTART_PMAP_ANIM));
+
+ DrawRestartMenuGraphic (pMS);
+ GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN, MENU_SOUND_SELECT);
+ SetDefaultMenuRepeatDelay ();
+ DoInput (pMS, TRUE);
+
+ StopMusic ();
+ if (pMS->hMusic)
+ {
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+
+ Flash_terminate (pMS->flashContext);
+ pMS->flashContext = 0;
+ DestroyDrawable (ReleaseDrawable (pMS->CurFrame));
+ pMS->CurFrame = 0;
+
+ if (GLOBAL (CurrentActivity) == (ACTIVITY)~0)
+ return (FALSE); // timed out
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return (FALSE); // quit
+
+ TimeOut = FadeScreen (FadeAllToBlack, ONE_SECOND / 2);
+
+ SleepThreadUntil (TimeOut);
+ FlushColorXForms ();
+
+ SeedRandomNumbers ();
+
+ return (LOBYTE (GLOBAL (CurrentActivity)) != SUPER_MELEE);
+}
+
+static BOOLEAN
+TryStartGame (void)
+{
+ MENU_STATE MenuState;
+
+ LastActivity = GLOBAL (CurrentActivity);
+ GLOBAL (CurrentActivity) = 0;
+
+ memset (&MenuState, 0, sizeof (MenuState));
+ MenuState.InputFunc = DoRestart;
+
+ while (!RestartMenu (&MenuState))
+ { // spin until a game is started or loaded
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE &&
+ !(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ FreeGameData ();
+ Melee ();
+ MenuState.Initialized = FALSE;
+ }
+ else if (GLOBAL (CurrentActivity) == (ACTIVITY)~0)
+ { // timed out
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2));
+ return (FALSE);
+ }
+ else if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ { // quit
+ return (FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+StartGame (void)
+{
+ do
+ {
+ while (!TryStartGame ())
+ {
+ if (GLOBAL (CurrentActivity) == (ACTIVITY)~0)
+ { // timed out
+ GLOBAL (CurrentActivity) = 0;
+ SplashScreen (0);
+ Credits (FALSE);
+ }
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return (FALSE); // quit
+ }
+
+ if (LastActivity & CHECK_RESTART)
+ { // starting a new game
+ Introduction ();
+ }
+
+ } while (GLOBAL (CurrentActivity) & CHECK_ABORT);
+
+ {
+ extern STAR_DESC starmap_array[];
+ extern const BYTE element_array[];
+ extern const PlanetFrame planet_array[];
+
+ star_array = starmap_array;
+ Elements = element_array;
+ PlanData = planet_array;
+ }
+
+ PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
+ PlayerControl[1] = COMPUTER_CONTROL | AWESOME_RATING;
+
+ return (TRUE);
+}
+
diff --git a/src/uqm/restart.h b/src/uqm/restart.h
new file mode 100644
index 0000000..0eef97f
--- /dev/null
+++ b/src/uqm/restart.h
@@ -0,0 +1,33 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_RESTART_H_
+#define UQM_RESTART_H_
+
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+extern BOOLEAN StartGame (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_RESTART_H_ */
diff --git a/src/uqm/save.c b/src/uqm/save.c
new file mode 100644
index 0000000..8e39401
--- /dev/null
+++ b/src/uqm/save.c
@@ -0,0 +1,813 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <assert.h>
+
+#include "save.h"
+
+#include "build.h"
+#include "controls.h"
+#include "starmap.h"
+#include "encount.h"
+#include "libs/file.h"
+#include "gamestr.h"
+#include "globdata.h"
+#include "options.h"
+#include "races.h"
+#include "shipcont.h"
+#include "setup.h"
+#include "state.h"
+#include "grpintrn.h"
+#include "util.h"
+#include "hyper.h"
+ // for SaveSisHyperState()
+#include "planets/planets.h"
+ // for SaveSolarSysLocation() and tests
+#include "libs/inplib.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+// Status boolean. If for some insane reason you need to
+// save games in different threads, you'll need to
+// protect your calls to SaveGame with a mutex.
+
+// It's arguably over-paranoid to check for error on
+// every single write, but this preserves the older
+// behavior.
+
+static BOOLEAN io_ok = TRUE;
+
+// XXX: these should handle endian conversions later
+static inline void
+write_8 (void *fp, BYTE v)
+{
+ if (io_ok)
+ if (WriteResFile (&v, 1, 1, fp) != 1)
+ io_ok = FALSE;
+}
+
+static inline void
+write_16 (void *fp, UWORD v)
+{
+ write_8 (fp, (BYTE)( v & 0xff));
+ write_8 (fp, (BYTE)((v >> 8) & 0xff));
+}
+
+static inline void
+write_32 (void *fp, DWORD v)
+{
+ write_8 (fp, (BYTE)( v & 0xff));
+ write_8 (fp, (BYTE)((v >> 8) & 0xff));
+ write_8 (fp, (BYTE)((v >> 16) & 0xff));
+ write_8 (fp, (BYTE)((v >> 24) & 0xff));
+}
+
+static inline void
+write_a8 (void *fp, const BYTE *ar, COUNT count)
+{
+ if (io_ok)
+ if (WriteResFile (ar, 1, count, fp) != count)
+ io_ok = FALSE;
+}
+
+static inline void
+write_str (void *fp, const char *str, COUNT count)
+{
+ // no type conversion needed for strings
+ write_a8 (fp, (const BYTE *)str, count);
+}
+
+static inline void
+write_a16 (void *fp, const UWORD *ar, COUNT count)
+{
+ for ( ; count > 0; --count, ++ar)
+ {
+ if (!io_ok)
+ break;
+ write_16 (fp, *ar);
+ }
+}
+
+static void
+SaveShipQueue (uio_Stream *fh, QUEUE *pQueue, DWORD tag)
+{
+ COUNT num_links;
+ HSHIPFRAG hStarShip;
+
+ num_links = CountLinks (pQueue);
+ if (num_links == 0)
+ return;
+ write_32 (fh, tag);
+ write_32 (fh, num_links * 11); // Size of chunk: each entry is 11 bytes long.
+
+ hStarShip = GetHeadLink (pQueue);
+ while (num_links--)
+ {
+ HSHIPFRAG hNextShip;
+ SHIP_FRAGMENT *FragPtr;
+ COUNT Index;
+
+ FragPtr = LockShipFrag (pQueue, hStarShip);
+ hNextShip = _GetSuccLink (FragPtr);
+
+ Index = FragPtr->race_id;
+ // Write the number identifying this ship type.
+ // See races.h; look for the enum containing NUM_AVAILABLE_RACES.
+ write_16 (fh, Index);
+
+ // Write SHIP_FRAGMENT elements
+ write_8 (fh, FragPtr->captains_name_index);
+ write_8 (fh, FragPtr->race_id);
+ write_8 (fh, FragPtr->index);
+ write_16 (fh, FragPtr->crew_level);
+ write_16 (fh, FragPtr->max_crew);
+ write_8 (fh, FragPtr->energy_level);
+ write_8 (fh, FragPtr->max_energy);
+
+ UnlockShipFrag (pQueue, hStarShip);
+ hStarShip = hNextShip;
+ }
+}
+
+static void
+SaveRaceQueue (uio_Stream *fh, QUEUE *pQueue)
+{
+ COUNT num_links;
+ HFLEETINFO hFleet;
+
+ num_links = CountLinks (pQueue);
+ if (num_links == 0)
+ return;
+ write_32 (fh, RACE_Q_TAG);
+ // Write chunk size: 30 bytes per entry
+ write_32 (fh, num_links * 30);
+
+ hFleet = GetHeadLink (pQueue);
+ while (num_links--)
+ {
+ HFLEETINFO hNextFleet;
+ FLEET_INFO *FleetPtr;
+ COUNT Index;
+
+ FleetPtr = LockFleetInfo (pQueue, hFleet);
+ hNextFleet = _GetSuccLink (FleetPtr);
+
+ Index = GetIndexFromStarShip (pQueue, hFleet);
+ // The index is the position in the queue.
+ write_16 (fh, Index);
+
+ // Write FLEET_INFO elements
+ write_16 (fh, FleetPtr->allied_state);
+ write_8 (fh, FleetPtr->days_left);
+ write_8 (fh, FleetPtr->growth_fract);
+ write_16 (fh, FleetPtr->crew_level);
+ write_16 (fh, FleetPtr->max_crew);
+ write_8 (fh, FleetPtr->growth);
+ write_8 (fh, FleetPtr->max_energy);
+ write_16 (fh, FleetPtr->loc.x);
+ write_16 (fh, FleetPtr->loc.y);
+
+ write_16 (fh, FleetPtr->actual_strength);
+ write_16 (fh, FleetPtr->known_strength);
+ write_16 (fh, FleetPtr->known_loc.x);
+ write_16 (fh, FleetPtr->known_loc.y);
+ write_8 (fh, FleetPtr->growth_err_term);
+ write_8 (fh, FleetPtr->func_index);
+ write_16 (fh, FleetPtr->dest_loc.x);
+ write_16 (fh, FleetPtr->dest_loc.y);
+
+ UnlockFleetInfo (pQueue, hFleet);
+ hFleet = hNextFleet;
+ }
+}
+
+static void
+SaveGroupQueue (uio_Stream *fh, QUEUE *pQueue)
+{
+ HIPGROUP hGroup, hNextGroup;
+ COUNT num_links;
+
+ num_links = CountLinks (pQueue);
+ if (num_links == 0)
+ return;
+ write_32 (fh, IP_GRP_Q_TAG);
+ write_32 (fh, num_links * 13); // 13 bytes per element right now
+
+ for (hGroup = GetHeadLink (pQueue); hGroup; hGroup = hNextGroup)
+ {
+ IP_GROUP *GroupPtr;
+
+ GroupPtr = LockIpGroup (pQueue, hGroup);
+ hNextGroup = _GetSuccLink (GroupPtr);
+
+ write_16 (fh, GroupPtr->group_counter);
+ write_8 (fh, GroupPtr->race_id);
+ write_8 (fh, GroupPtr->sys_loc);
+ write_8 (fh, GroupPtr->task);
+ write_8 (fh, GroupPtr->in_system); /* was crew_level */
+ write_8 (fh, GroupPtr->dest_loc);
+ write_8 (fh, GroupPtr->orbit_pos);
+ write_8 (fh, GroupPtr->group_id); /* was max_energy */
+ write_16 (fh, GroupPtr->loc.x);
+ write_16 (fh, GroupPtr->loc.y);
+
+ UnlockIpGroup (pQueue, hGroup);
+ }
+}
+
+static void
+SaveEncounters (uio_Stream *fh)
+{
+ COUNT num_links;
+ HENCOUNTER hEncounter;
+ num_links = CountLinks (&GLOBAL (encounter_q));
+ if (num_links == 0)
+ return;
+ write_32 (fh, ENCOUNTERS_TAG);
+ write_32 (fh, 65 * num_links);
+
+ hEncounter = GetHeadLink (&GLOBAL (encounter_q));
+ while (num_links--)
+ {
+ HENCOUNTER hNextEncounter;
+ ENCOUNTER *EncounterPtr;
+ COUNT i;
+
+ LockEncounter (hEncounter, &EncounterPtr);
+ hNextEncounter = GetSuccEncounter (EncounterPtr);
+
+ write_16 (fh, EncounterPtr->transition_state);
+ write_16 (fh, EncounterPtr->origin.x);
+ write_16 (fh, EncounterPtr->origin.y);
+ write_16 (fh, EncounterPtr->radius);
+ // former STAR_DESC fields
+ write_16 (fh, EncounterPtr->loc_pt.x);
+ write_16 (fh, EncounterPtr->loc_pt.y);
+ write_8 (fh, EncounterPtr->race_id);
+ write_8 (fh, EncounterPtr->num_ships);
+ write_8 (fh, EncounterPtr->flags);
+
+ // Save each entry in the BRIEF_SHIP_INFO array
+ for (i = 0; i < MAX_HYPER_SHIPS; i++)
+ {
+ const BRIEF_SHIP_INFO *ShipInfo = &EncounterPtr->ShipList[i];
+
+ write_8 (fh, ShipInfo->race_id);
+ write_16 (fh, ShipInfo->crew_level);
+ write_16 (fh, ShipInfo->max_crew);
+ write_8 (fh, ShipInfo->max_energy);
+ }
+
+ // Save the stuff after the BRIEF_SHIP_INFO array
+ write_32 (fh, EncounterPtr->log_x);
+ write_32 (fh, EncounterPtr->log_y);
+
+ UnlockEncounter (hEncounter);
+ hEncounter = hNextEncounter;
+ }
+}
+
+static void
+SaveEvents (uio_Stream *fh)
+{
+ COUNT num_links;
+ HEVENT hEvent;
+ num_links = CountLinks (&GLOBAL (GameClock.event_q));
+ if (num_links == 0)
+ return;
+ write_32 (fh, EVENTS_TAG);
+ write_32 (fh, num_links * 5); /* Event chunks are five bytes each */
+
+ hEvent = GetHeadLink (&GLOBAL (GameClock.event_q));
+ while (num_links--)
+ {
+ HEVENT hNextEvent;
+ EVENT *EventPtr;
+
+ LockEvent (hEvent, &EventPtr);
+ hNextEvent = GetSuccEvent (EventPtr);
+
+ write_8 (fh, EventPtr->day_index);
+ write_8 (fh, EventPtr->month_index);
+ write_16 (fh, EventPtr->year_index);
+ write_8 (fh, EventPtr->func_index);
+
+ UnlockEvent (hEvent);
+ hEvent = hNextEvent;
+ }
+}
+
+/* The clock state is folded in with the game state chunk. */
+static void
+SaveClockState (const CLOCK_STATE *ClockPtr, uio_Stream *fh)
+{
+ write_8 (fh, ClockPtr->day_index);
+ write_8 (fh, ClockPtr->month_index);
+ write_16 (fh, ClockPtr->year_index);
+ write_16 (fh, ClockPtr->tick_count);
+ write_16 (fh, ClockPtr->day_in_ticks);
+}
+
+/* Save out the game state chunks. There are two of these; the Global
+ * State chunk is fixed size, but the Game State tag can be extended
+ * by modders. */
+static void
+SaveGameState (const GAME_STATE *GSPtr, uio_Stream *fh)
+{
+ write_32 (fh, GLOBAL_STATE_TAG);
+ write_32 (fh, 75);
+ write_8 (fh, GSPtr->glob_flags);
+ write_8 (fh, GSPtr->CrewCost);
+ write_8 (fh, GSPtr->FuelCost);
+ write_a8 (fh, GSPtr->ModuleCost, NUM_MODULES);
+ write_a8 (fh, GSPtr->ElementWorth, NUM_ELEMENT_CATEGORIES);
+ write_16 (fh, GSPtr->CurrentActivity);
+
+ SaveClockState (&GSPtr->GameClock, fh);
+
+ write_16 (fh, GSPtr->autopilot.x);
+ write_16 (fh, GSPtr->autopilot.y);
+ write_16 (fh, GSPtr->ip_location.x);
+ write_16 (fh, GSPtr->ip_location.y);
+ /* STAMP ShipStamp */
+ write_16 (fh, GSPtr->ShipStamp.origin.x);
+ write_16 (fh, GSPtr->ShipStamp.origin.y);
+ write_16 (fh, GSPtr->ShipFacing);
+ write_8 (fh, GSPtr->ip_planet);
+ write_8 (fh, GSPtr->in_orbit);
+
+ /* VELOCITY_DESC velocity */
+ write_16 (fh, GSPtr->velocity.TravelAngle);
+ write_16 (fh, GSPtr->velocity.vector.width);
+ write_16 (fh, GSPtr->velocity.vector.height);
+ write_16 (fh, GSPtr->velocity.fract.width);
+ write_16 (fh, GSPtr->velocity.fract.height);
+ write_16 (fh, GSPtr->velocity.error.width);
+ write_16 (fh, GSPtr->velocity.error.height);
+ write_16 (fh, GSPtr->velocity.incr.width);
+ write_16 (fh, GSPtr->velocity.incr.height);
+
+ /* The Game state bits. Vanilla UQM uses 155 bytes here at
+ * present. Only the first 99 bytes are significant, though;
+ * the rest will be overwritten by the BtGp chunks. */
+ write_32 (fh, GAME_STATE_TAG);
+ write_32 (fh, sizeof (GSPtr->GameState));
+ write_a8 (fh, GSPtr->GameState, sizeof (GSPtr->GameState));
+}
+
+/* This is folded into the Summary chunk */
+static void
+SaveSisState (const SIS_STATE *SSPtr, void *fp)
+{
+ write_32 (fp, SSPtr->log_x);
+ write_32 (fp, SSPtr->log_y);
+ write_32 (fp, SSPtr->ResUnits);
+ write_32 (fp, SSPtr->FuelOnBoard);
+ write_16 (fp, SSPtr->CrewEnlisted);
+ write_16 (fp, SSPtr->TotalElementMass);
+ write_16 (fp, SSPtr->TotalBioMass);
+ write_a8 (fp, SSPtr->ModuleSlots, NUM_MODULE_SLOTS);
+ write_a8 (fp, SSPtr->DriveSlots, NUM_DRIVE_SLOTS);
+ write_a8 (fp, SSPtr->JetSlots, NUM_JET_SLOTS);
+ write_8 (fp, SSPtr->NumLanders);
+ write_a16 (fp, SSPtr->ElementAmounts, NUM_ELEMENT_CATEGORIES);
+
+ write_str (fp, SSPtr->ShipName, SIS_NAME_SIZE);
+ write_str (fp, SSPtr->CommanderName, SIS_NAME_SIZE);
+ write_str (fp, SSPtr->PlanetName, SIS_NAME_SIZE);
+}
+
+/* Write out the Summary Chunk. This is variable length because of the
+ savegame name */
+static void
+SaveSummary (const SUMMARY_DESC *SummPtr, void *fp)
+{
+ write_32 (fp, SUMMARY_TAG);
+ write_32 (fp, 160 + strlen(SummPtr->SaveName));
+ SaveSisState (&SummPtr->SS, fp);
+
+ write_8 (fp, SummPtr->Activity);
+ write_8 (fp, SummPtr->Flags);
+ write_8 (fp, SummPtr->day_index);
+ write_8 (fp, SummPtr->month_index);
+ write_16 (fp, SummPtr->year_index);
+ write_8 (fp, SummPtr->MCreditLo);
+ write_8 (fp, SummPtr->MCreditHi);
+ write_8 (fp, SummPtr->NumShips);
+ write_8 (fp, SummPtr->NumDevices);
+ write_a8 (fp, SummPtr->ShipList, MAX_BUILT_SHIPS);
+ write_a8 (fp, SummPtr->DeviceList, MAX_EXCLUSIVE_DEVICES);
+ write_a8 (fp, (BYTE *) SummPtr->SaveName, strlen(SummPtr->SaveName));
+}
+
+/* Save the Star Description chunk. This is not to be confused with
+ * the Star *Info* chunk, which records which planetary features you
+ * have exploited with your lander */
+static void
+SaveStarDesc (const STAR_DESC *SDPtr, uio_Stream *fh)
+{
+ write_32 (fh, STAR_TAG);
+ write_32 (fh, 8);
+ write_16 (fh, SDPtr->star_pt.x);
+ write_16 (fh, SDPtr->star_pt.y);
+ write_8 (fh, SDPtr->Type);
+ write_8 (fh, SDPtr->Index);
+ write_8 (fh, SDPtr->Prefix);
+ write_8 (fh, SDPtr->Postfix);
+}
+
+static void
+PrepareSummary (SUMMARY_DESC *SummPtr, const char *name)
+{
+ SummPtr->SS = GlobData.SIS_state;
+
+ SummPtr->Activity = LOBYTE (GLOBAL (CurrentActivity));
+ switch (SummPtr->Activity)
+ {
+ case IN_HYPERSPACE:
+ if (inQuasiSpace ())
+ SummPtr->Activity = IN_QUASISPACE;
+ break;
+ case IN_INTERPLANETARY:
+ // Get a better planet name for summary
+ GetPlanetOrMoonName (SummPtr->SS.PlanetName,
+ sizeof (SummPtr->SS.PlanetName));
+ if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) == (BYTE)~0)
+ SummPtr->Activity = IN_STARBASE;
+ else if (playerInPlanetOrbit ())
+ SummPtr->Activity = IN_PLANET_ORBIT;
+ break;
+ case IN_LAST_BATTLE:
+ utf8StringCopy (SummPtr->SS.PlanetName,
+ sizeof (SummPtr->SS.PlanetName),
+ GAME_STRING (PLANET_NUMBER_BASE + 32)); // Sa-Matra
+ break;
+ }
+
+ SummPtr->MCreditLo = GET_GAME_STATE (MELNORME_CREDIT0);
+ SummPtr->MCreditHi = GET_GAME_STATE (MELNORME_CREDIT1);
+
+ {
+ HSHIPFRAG hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)), SummPtr->NumShips = 0;
+ hStarShip; hStarShip = hNextShip, ++SummPtr->NumShips)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+ SummPtr->ShipList[SummPtr->NumShips] = StarShipPtr->race_id;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+ }
+
+ SummPtr->NumDevices = InventoryDevices (SummPtr->DeviceList,
+ MAX_EXCLUSIVE_DEVICES);
+
+ SummPtr->Flags = GET_GAME_STATE (LANDER_SHIELDS)
+ | (GET_GAME_STATE (IMPROVED_LANDER_SPEED) << (4 + 0))
+ | (GET_GAME_STATE (IMPROVED_LANDER_CARGO) << (4 + 1))
+ | (GET_GAME_STATE (IMPROVED_LANDER_SHOT) << (4 + 2))
+ | ((GET_GAME_STATE (CHMMR_BOMB_STATE) < 2 ? 0 : 1) << (4 + 3));
+
+ SummPtr->day_index = GLOBAL (GameClock.day_index);
+ SummPtr->month_index = GLOBAL (GameClock.month_index);
+ SummPtr->year_index = GLOBAL (GameClock.year_index);
+ SummPtr->SaveName[SAVE_NAME_SIZE-1] = 0;
+ strncpy (SummPtr->SaveName, name, SAVE_NAME_SIZE-1);
+}
+
+static void
+SaveProblemMessage (STAMP *MsgStamp)
+{
+#define MAX_MSG_LINES 1
+ RECT r = {{0, 0}, {0, 0}};
+ COUNT i;
+ TEXT t;
+ UNICODE *ppStr[MAX_MSG_LINES];
+
+ // TODO: This should probably just use DoPopupWindow()
+
+ ppStr[0] = GAME_STRING (SAVEGAME_STRING_BASE + 2);
+
+ SetContextFont (StarConFont);
+
+ t.baseline.x = t.baseline.y = 0;
+ t.align = ALIGN_CENTER;
+ for (i = 0; i < MAX_MSG_LINES; ++i)
+ {
+ RECT tr;
+
+ t.pStr = ppStr[i];
+ if (*t.pStr == '\0')
+ break;
+ t.CharCount = (COUNT)~0;
+ TextRect (&t, &tr, NULL);
+ if (i == 0)
+ r = tr;
+ else
+ BoxUnion (&tr, &r, &r);
+ t.baseline.y += 11;
+ }
+ t.baseline.x = ((SIS_SCREEN_WIDTH >> 1) - (r.extent.width >> 1))
+ - r.corner.x;
+ t.baseline.y = ((SIS_SCREEN_HEIGHT >> 1) - (r.extent.height >> 1))
+ - r.corner.y;
+ r.corner.x += t.baseline.x - 4;
+ r.corner.y += t.baseline.y - 4;
+ r.extent.width += 8;
+ r.extent.height += 8;
+
+ *MsgStamp = SaveContextFrame (&r);
+
+ BatchGraphics ();
+ DrawStarConBox (&r, 2,
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19),
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F),
+ TRUE, BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x14, 0x14), 0x0F));
+
+ for (i = 0; i < MAX_MSG_LINES; ++i)
+ {
+ t.pStr = ppStr[i];
+ if (*t.pStr == '\0')
+ break;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.baseline.y += 11;
+ }
+ UnbatchGraphics ();
+}
+
+void
+SaveProblem (void)
+{
+ STAMP s;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (SpaceContext);
+ SaveProblemMessage (&s);
+ FlushGraphics ();
+
+ WaitForAnyButton (TRUE, WAIT_INFINITE, FALSE);
+
+ // Restore the screen under the message
+ DrawStamp (&s);
+ SetContext (OldContext);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+}
+
+static void
+SaveFlagshipState (void)
+{
+ if (inHQSpace ())
+ {
+ // Player is in HyperSpace or QuasiSpace.
+ SaveSisHyperState ();
+ }
+ else if (playerInSolarSystem ())
+ {
+ SaveSolarSysLocation ();
+ }
+}
+
+static void
+SaveStarInfo (uio_Stream *fh)
+{
+ GAME_STATE_FILE *fp;
+ fp = OpenStateFile (STARINFO_FILE, "rb");
+ if (fp)
+ {
+ DWORD flen = LengthStateFile (fp);
+ if (flen % 4)
+ {
+ log_add (log_Warning, "Unexpected Star Info length! Expected an integral number of DWORDS.\n");
+ }
+ else
+ {
+ write_32 (fh, SCAN_TAG);
+ write_32 (fh, flen);
+ while (flen)
+ {
+ DWORD val;
+ sread_32 (fp, &val);
+ write_32 (fh, val);
+ flen -= 4;
+ }
+ }
+ CloseStateFile (fp);
+ }
+}
+
+static void
+SaveBattleGroup (GAME_STATE_FILE *fp, DWORD encounter_id, DWORD grpoffs, uio_Stream *fh)
+{
+ GROUP_HEADER h;
+ DWORD size = 12;
+ int i;
+ SeekStateFile (fp, grpoffs, SEEK_SET);
+ ReadGroupHeader (fp, &h);
+ for (i = 1; i <= h.NumGroups; ++i)
+ {
+ BYTE NumShips;
+ SeekStateFile (fp, h.GroupOffset[i], SEEK_SET);
+ sread_8 (fp, NULL);
+ sread_8 (fp, &NumShips);
+ size += 2 + 10 * NumShips;
+ }
+ write_32 (fh, BATTLE_GROUP_TAG);
+ write_32 (fh, size);
+ write_32 (fh, encounter_id);
+ write_8 (fh, (grpoffs && (GLOBAL (BattleGroupRef) == grpoffs)) ? 1 : 0); // current
+ write_16 (fh, h.star_index);
+ write_8 (fh, h.day_index);
+ write_8 (fh, h.month_index);
+ write_16 (fh, h.year_index);
+ write_8 (fh, h.NumGroups);
+ for (i = 1; i <= h.NumGroups; ++i)
+ {
+ int j;
+ BYTE b;
+ SeekStateFile (fp, h.GroupOffset[i], SEEK_SET);
+ sread_8 (fp, &b); // Group race icon
+ write_8 (fh, b);
+ sread_8 (fp, &b); // NumShips
+ write_8 (fh, b);
+ for (j = 0; j < b; ++j)
+ {
+ BYTE race_outer;
+ SHIP_FRAGMENT sf;
+ sread_8 (fp, &race_outer);
+ ReadShipFragment (fp, &sf);
+ write_8 (fh, race_outer);
+ write_8 (fh, sf.captains_name_index);
+ write_8 (fh, sf.race_id);
+ write_8 (fh, sf.index);
+ write_16 (fh, sf.crew_level);
+ write_16 (fh, sf.max_crew);
+ write_8 (fh, sf.energy_level);
+ write_8 (fh, sf.max_energy);
+ }
+ }
+}
+
+static void
+SaveGroups (uio_Stream *fh)
+{
+ GAME_STATE_FILE *fp;
+ fp = OpenStateFile (RANDGRPINFO_FILE, "rb");
+ if (fp && LengthStateFile (fp) > 0)
+ {
+ GROUP_HEADER h;
+ BYTE lastenc, count;
+ int i;
+ ReadGroupHeader (fp, &h);
+ /* Group List */
+ SeekStateFile (fp, h.GroupOffset[0], SEEK_SET);
+ sread_8 (fp, &lastenc);
+ sread_8 (fp, &count);
+ write_32 (fh, GROUP_LIST_TAG);
+ write_32 (fh, 1 + 14 * count); // Chunk size
+ write_8 (fh, lastenc);
+ for (i = 0; i < count; ++i)
+ {
+ BYTE race_outer;
+ IP_GROUP ip;
+ sread_8 (fp, &race_outer);
+ ReadIpGroup (fp, &ip);
+
+ write_8 (fh, race_outer);
+ write_16 (fh, ip.group_counter);
+ write_8 (fh, ip.race_id);
+ write_8 (fh, ip.sys_loc);
+ write_8 (fh, ip.task);
+ write_8 (fh, ip.in_system);
+ write_8 (fh, ip.dest_loc);
+ write_8 (fh, ip.orbit_pos);
+ write_8 (fh, ip.group_id);
+ write_16 (fh, ip.loc.x);
+ write_16 (fh, ip.loc.y);
+ }
+ SaveBattleGroup (fp, 0, 0, fh);
+ CloseStateFile (fp);
+ }
+ fp = OpenStateFile (DEFGRPINFO_FILE, "rb");
+ if (fp && LengthStateFile (fp) > 0)
+ {
+ int state_index = SHOFIXTI_GRPOFFS0;
+ int encounter_index = 1;
+ while (state_index < NUM_GAME_STATE_BITS)
+ {
+ DWORD grpoffs = GET_GAME_STATE_32 (state_index);
+ if (grpoffs)
+ {
+ SaveBattleGroup (fp, encounter_index, grpoffs, fh);
+ }
+ ++encounter_index;
+ state_index += 32;
+ }
+ CloseStateFile (fp);
+ }
+}
+
+// This function first writes to a memory file, and then writes the whole
+// lot to the actual save file at once.
+BOOLEAN
+SaveGame (COUNT which_game, SUMMARY_DESC *SummPtr, const char *name)
+{
+ uio_Stream *out_fp;
+ POINT pt;
+ STAR_DESC SD;
+ char file[PATH_MAX];
+ if (CurStarDescPtr)
+ SD = *CurStarDescPtr;
+ else
+ memset (&SD, 0, sizeof (SD));
+
+ // XXX: Backup: SaveFlagshipState() overwrites ip_location
+ pt = GLOBAL (ip_location);
+ SaveFlagshipState ();
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
+ && !(GLOBAL (CurrentActivity)
+ & (START_ENCOUNTER | START_INTERPLANETARY)))
+ PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
+
+ // Write the memory file to the actual savegame file.
+ sprintf (file, "uqmsave.%02u", which_game);
+ if ((out_fp = res_OpenResFile (saveDir, file, "wb")))
+ {
+ io_ok = TRUE;
+ write_32 (out_fp, SAVEFILE_TAG);
+
+ PrepareSummary (SummPtr, name);
+ SaveSummary (SummPtr, out_fp);
+
+ SaveGameState (&GlobData.Game_state, out_fp);
+
+ // XXX: Restore
+ GLOBAL (ip_location) = pt;
+ // Only relevant when loading a game and must be cleaned
+ GLOBAL (in_orbit) = 0;
+
+ SaveRaceQueue (out_fp, &GLOBAL (avail_race_q));
+ // START_INTERPLANETARY is only set when saving from Homeworld
+ // encounter screen. When the game is loaded, the
+ // GenerateOrbitalFunction for the current star system
+ // create the encounter anew and populate the npc queue.
+ if (!(GLOBAL (CurrentActivity) & START_INTERPLANETARY))
+ {
+ if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ SaveShipQueue (out_fp, &GLOBAL (npc_built_ship_q), NPC_SHIP_Q_TAG);
+ else if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
+ // XXX: Technically, this queue does not need to be
+ // saved/loaded at all. IP groups will be reloaded
+ // from group state files. But the original code did,
+ // and so will we until we can prove we do not need to.
+ SaveGroupQueue (out_fp, &GLOBAL (ip_group_q));
+ }
+ SaveShipQueue (out_fp, &GLOBAL (built_ship_q), SHIP_Q_TAG);
+
+ // Save the game event chunk
+ SaveEvents (out_fp);
+
+ // Save the encounter chunk (black globes in HS/QS)
+ SaveEncounters (out_fp);
+
+ // Save out the data that used to be in state files
+ SaveStarInfo (out_fp);
+ SaveGroups (out_fp);
+
+ // Save out the Star Descriptor
+ SaveStarDesc (&SD, out_fp);
+
+ res_CloseResFile (out_fp);
+ if (!io_ok)
+ {
+ DeleteResFile(saveDir, file);
+ return FALSE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/uqm/save.h b/src/uqm/save.h
new file mode 100644
index 0000000..28852b8
--- /dev/null
+++ b/src/uqm/save.h
@@ -0,0 +1,78 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SAVE_H_
+#define UQM_SAVE_H_
+
+#include "sis.h" // SUMMARY_DESC includes SIS_STATE in it
+#include "globdata.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// XXX: Theoretically, a player can have 17 devices on board without
+// cheating. We only provide
+// room for 16 below, which is not really a problem since this
+// is only used for displaying savegame summaries. There is also
+// room for only 16 devices on screen.
+#define MAX_EXCLUSIVE_DEVICES 16
+#define SAVE_NAME_SIZE 64
+
+// The savefile tag numbers.
+#define SAVEFILE_TAG 0x01534d55 // "UMS\x01": UQM Save version 1
+#define SUMMARY_TAG 0x6d6d7553 // "Summ": Summary. Must be first!
+#define GLOBAL_STATE_TAG 0x74536c47 // "GlSt": Global State. Must be 2nd!
+#define GAME_STATE_TAG 0x74536d47 // "GmSt": Game State Bits. Must be 3rd!
+#define EVENTS_TAG 0x73747645 // "Evts": Events
+#define ENCOUNTERS_TAG 0x74636e45 // "Enct": Encounters
+#define RACE_Q_TAG 0x51636152 // "RacQ": avail_race_q
+#define IP_GRP_Q_TAG 0x51704749 // "IGpQ": ip_group_q
+#define NPC_SHIP_Q_TAG 0x5163704e // "NpcQ": npc_built_ship_q
+#define SHIP_Q_TAG 0x51706853 // "ShpQ": built_ship_q
+#define STAR_TAG 0x72617453 // "Star": STAR_DESC
+#define SCAN_TAG 0x6e616353 // "Scan": Scan Masks (stuff picked up)
+#define BATTLE_GROUP_TAG 0x70477442 // "BtGp": Battle Group definition
+#define GROUP_LIST_TAG 0x73707247 // "Grps": Group List
+
+typedef struct
+{
+ SIS_STATE SS;
+ BYTE Activity;
+ BYTE Flags;
+ BYTE day_index, month_index;
+ COUNT year_index;
+ BYTE MCreditLo, MCreditHi;
+ BYTE NumShips, NumDevices;
+ BYTE ShipList[MAX_BUILT_SHIPS];
+ BYTE DeviceList[MAX_EXCLUSIVE_DEVICES];
+ UNICODE SaveName[SAVE_NAME_SIZE];
+} SUMMARY_DESC;
+
+extern ACTIVITY NextActivity;
+
+extern BOOLEAN LoadGame (COUNT which_game, SUMMARY_DESC *summary_desc);
+extern BOOLEAN LoadLegacyGame (COUNT which_game, SUMMARY_DESC *summary_desc);
+
+extern void SaveProblem (void);
+extern BOOLEAN SaveGame (COUNT which_game, SUMMARY_DESC *summary_desc, const char *name);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SAVE_H_ */
diff --git a/src/uqm/settings.c b/src/uqm/settings.c
new file mode 100644
index 0000000..3e959a1
--- /dev/null
+++ b/src/uqm/settings.c
@@ -0,0 +1,97 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "settings.h"
+
+#include "globdata.h"
+#include "libs/compiler.h"
+
+
+static MUSIC_REF LastMusicRef;
+static BOOLEAN LastContinuous;
+static BYTE LastPriority;
+
+void
+ToggleMusic (void)
+{
+ GLOBAL (glob_flags) ^= MUSIC_DISABLED;
+ if (LastPriority <= 1)
+ {
+ if (GLOBAL (glob_flags) & MUSIC_DISABLED)
+ PLRStop (LastMusicRef);
+ else if (LastMusicRef)
+ PLRPlaySong (LastMusicRef, LastContinuous, LastPriority);
+ }
+}
+
+void
+PlayMusic (MUSIC_REF MusicRef, BOOLEAN Continuous, BYTE Priority)
+{
+ LastMusicRef = MusicRef;
+ LastContinuous = Continuous;
+ LastPriority = Priority;
+
+ if (
+#ifdef NEVER
+ Priority > 1
+ ||
+#endif /* NEVER */
+ !(GLOBAL (glob_flags) & MUSIC_DISABLED)
+ )
+ {
+ PLRPlaySong (MusicRef, Continuous, Priority);
+ }
+}
+
+void
+StopMusic (void)
+{
+ PLRStop (LastMusicRef);
+ LastMusicRef = 0;
+}
+
+void
+ResumeMusic (void)
+{
+ PLRResume (LastMusicRef);
+}
+
+void
+PauseMusic (void)
+{
+ PLRPause (LastMusicRef);
+}
+
+void
+ToggleSoundEffect (void)
+{
+ GLOBAL (glob_flags) ^= SOUND_DISABLED;
+}
+
+void
+PlaySoundEffect (SOUND S, COUNT Channel, SoundPosition Pos,
+ void *PositionalObject, BYTE Priority)
+{
+ if (!(GLOBAL (glob_flags) & SOUND_DISABLED))
+ {
+ SetChannelVolume (Channel, MAX_VOLUME >> 1, Priority);
+ //SetChannelRate (Channel, GetSampleRate (S), Priority);
+ PlayChannel (Channel, S, Pos, PositionalObject, Priority);
+ }
+}
+
diff --git a/src/uqm/settings.h b/src/uqm/settings.h
new file mode 100644
index 0000000..f903bc8
--- /dev/null
+++ b/src/uqm/settings.h
@@ -0,0 +1,41 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SETTINGS_H_
+#define UQM_SETTINGS_H_
+
+#include "libs/sndlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void ToggleMusic (void);
+extern void StopMusic (void);
+extern void ResumeMusic (void);
+extern void PauseMusic (void);
+extern void ToggleSoundEffect (void);
+
+extern void PlayMusic (MUSIC_REF MusicRef, BOOLEAN Continuous, BYTE Priority);
+extern void PlaySoundEffect (SOUND S, COUNT Channel, SoundPosition Pos,
+ void *PositionalObject, BYTE Priority);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SETTINGS_H_ */
diff --git a/src/uqm/setup.c b/src/uqm/setup.c
new file mode 100644
index 0000000..bfdf90a
--- /dev/null
+++ b/src/uqm/setup.c
@@ -0,0 +1,332 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "setup.h"
+
+#include "coderes.h"
+#include "controls.h"
+#include "options.h"
+#include "nameref.h"
+#ifdef NETPLAY
+# include "supermelee/netplay/netmelee.h"
+#endif
+#include "init.h"
+#include "intel.h"
+#include "status.h"
+#include "resinst.h"
+#include "sounds.h"
+#include "libs/compiler.h"
+#include "libs/uio.h"
+#include "libs/file.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/sound/sound.h"
+#include "libs/threadlib.h"
+#include "libs/vidlib.h"
+#include "libs/log.h"
+#include "libs/misc.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+
+ACTIVITY LastActivity;
+BYTE PlayerControl[NUM_PLAYERS];
+
+// XXX: These declarations should really go to the file they belong to.
+RESOURCE_INDEX hResIndex;
+CONTEXT ScreenContext;
+CONTEXT SpaceContext;
+CONTEXT StatusContext;
+CONTEXT OffScreenContext;
+SIZE screen_width, screen_height;
+FRAME Screen;
+FONT StarConFont;
+FONT MicroFont;
+FONT TinyFont;
+QUEUE race_q[NUM_PLAYERS];
+FRAME ActivityFrame;
+FRAME StatusFrame;
+FRAME FlagStatFrame;
+FRAME MiscDataFrame;
+FRAME FontGradFrame;
+STRING GameStrings;
+QUEUE disp_q;
+
+uio_Repository *repository;
+uio_DirHandle *rootDir;
+
+BOOLEAN usingSpeech;
+
+
+static void
+InitPlayerInput (void)
+{
+}
+
+void
+UninitPlayerInput (void)
+{
+#if DEMO_MODE
+ DestroyInputDevice (ReleaseInputDevice (DemoInput));
+#endif /* DEMO_MODE */
+}
+
+BOOLEAN
+LoadKernel (int argc, char *argv[])
+{
+ InitSound (argc, argv);
+ InitVideoPlayer (TRUE);
+
+ ScreenContext = CreateContext ("ScreenContext");
+ if (ScreenContext == NULL)
+ return FALSE;
+
+ Screen = CaptureDrawable (CreateDisplay (WANT_MASK | WANT_PIXMAP,
+ &screen_width, &screen_height));
+ if (Screen == NULL)
+ return FALSE;
+
+ SetContext (ScreenContext);
+ SetContextFGFrame (Screen);
+ SetContextOrigin (MAKE_POINT (0, 0));
+
+ hResIndex = (RESOURCE_INDEX) InitResourceSystem ();
+ if (hResIndex == 0)
+ return FALSE;
+
+ /* Load base content. */
+ if (loadIndices (contentDir) == 0)
+ return FALSE; // Must have at least one index in content dir
+
+ /* Load addons demanded by the current configuration. */
+ if (opt3doMusic)
+ {
+ loadAddon ("3domusic");
+ }
+
+ usingSpeech = optSpeech;
+ if (optSpeech && !loadAddon ("3dovoice"))
+ {
+ usingSpeech = FALSE;
+ }
+
+ if (optRemixMusic)
+ {
+ loadAddon ("remix");
+ }
+
+ if (optWhichIntro == OPT_3DO)
+ {
+ loadAddon ("3dovideo");
+ }
+
+ /* Now load the rest of the addons, in order. */
+ prepareAddons (optAddons);
+
+ {
+ COLORMAP ColorMapTab;
+
+ ColorMapTab = CaptureColorMap (LoadColorMap (STARCON_COLOR_MAP));
+ if (ColorMapTab == NULL)
+ return FALSE; // The most basic resource is missing
+ SetColorMap (GetColorMapAddress (ColorMapTab));
+ DestroyColorMap (ReleaseColorMap (ColorMapTab));
+ }
+
+ InitPlayerInput ();
+
+ GLOBAL (CurrentActivity) = (ACTIVITY)~0;
+ return TRUE;
+}
+
+BOOLEAN
+InitContexts (void)
+{
+ RECT r;
+
+ StatusContext = CreateContext ("StatusContext");
+ if (StatusContext == NULL)
+ return FALSE;
+
+ SetContext (StatusContext);
+ SetContextFGFrame (Screen);
+ r.corner.x = SPACE_WIDTH + SAFE_X;
+ r.corner.y = SAFE_Y;
+ r.extent.width = STATUS_WIDTH;
+ r.extent.height = STATUS_HEIGHT;
+ SetContextClipRect (&r);
+
+ SpaceContext = CreateContext ("SpaceContext");
+ if (SpaceContext == NULL)
+ return FALSE;
+
+ OffScreenContext = CreateContext ("OffScreenContext");
+ if (OffScreenContext == NULL)
+ return FALSE;
+
+ if (!InitQueue (&disp_q, MAX_DISPLAY_ELEMENTS, sizeof (ELEMENT)))
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOLEAN
+InitKernel (void)
+{
+ COUNT counter;
+
+ for (counter = 0; counter < NUM_PLAYERS; ++counter)
+ InitQueue (&race_q[counter], MAX_SHIPS_PER_SIDE, sizeof (STARSHIP));
+
+ StarConFont = LoadFont (STARCON_FONT);
+ if (StarConFont == NULL)
+ return FALSE;
+
+ TinyFont = LoadFont (TINY_FONT);
+ if (TinyFont == NULL)
+ return FALSE;
+
+ ActivityFrame = CaptureDrawable (LoadGraphic (ACTIVITY_ANIM));
+ if (ActivityFrame == NULL)
+ return FALSE;
+
+ StatusFrame = CaptureDrawable (LoadGraphic (STATUS_MASK_PMAP_ANIM));
+ if (StatusFrame == NULL)
+ return FALSE;
+
+ GameStrings = CaptureStringTable (LoadStringTable (STARCON_GAME_STRINGS));
+ if (GameStrings == 0)
+ return FALSE;
+
+ MicroFont = LoadFont (MICRO_FONT);
+ if (MicroFont == NULL)
+ return FALSE;
+
+ MenuSounds = CaptureSound (LoadSound (MENU_SOUNDS));
+ if (MenuSounds == 0)
+ return FALSE;
+
+ InitStatusOffsets ();
+ InitSpace ();
+
+ return TRUE;
+}
+
+BOOLEAN
+InitGameKernel (void)
+{
+ if (ActivityFrame == 0)
+ {
+ InitKernel ();
+ InitContexts ();
+ }
+ return TRUE;
+}
+
+bool
+SetPlayerInput (COUNT playerI)
+{
+ assert (PlayerInput[playerI] == NULL);
+
+ switch (PlayerControl[playerI] & CONTROL_MASK) {
+ case HUMAN_CONTROL:
+ PlayerInput[playerI] =
+ (InputContext *) HumanInputContext_new (playerI);
+ break;
+ case COMPUTER_CONTROL:
+ case CYBORG_CONTROL:
+ // COMPUTER_CONTROL is used in SuperMelee; the computer chooses
+ // the ships and fights the battles.
+ // CYBORG_CONTROL is used in the full game; the computer only
+ // fights the battles. XXX: This will need to be handled
+ // separately in the future if we want to remove the special
+ // cases for ship selection with CYBORG_CONTROL from the
+ // computer handlers.
+ PlayerInput[playerI] =
+ (InputContext *) ComputerInputContext_new (playerI);
+ break;
+#ifdef NETPLAY
+ case NETWORK_CONTROL:
+ PlayerInput[playerI] =
+ (InputContext *) NetworkInputContext_new (playerI);
+ break;
+#endif
+ default:
+ log_add (log_Fatal,
+ "Invalid control method in SetPlayerInput().");
+ explode (); /* Does not return */
+ }
+
+ return PlayerInput[playerI] != NULL;
+}
+
+bool
+SetPlayerInputAll (void)
+{
+ COUNT playerI;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ if (!SetPlayerInput (playerI))
+ return false;
+ return true;
+}
+
+void
+ClearPlayerInput (COUNT playerI)
+{
+ if (PlayerInput[playerI] == NULL) {
+ log_add (log_Debug, "ClearPlayerInput(): PlayerInput[%d] was NULL.",
+ playerI);
+ return;
+ }
+
+ PlayerInput[playerI]->handlers->deleteContext (PlayerInput[playerI]);
+ PlayerInput[playerI] = NULL;
+}
+
+void
+ClearPlayerInputAll (void)
+{
+ COUNT playerI;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ ClearPlayerInput (playerI);
+}
+
+int
+initIO (void)
+{
+ uio_init ();
+ repository = uio_openRepository (0);
+
+ rootDir = uio_openDir (repository, "/", 0);
+ if (rootDir == NULL)
+ {
+ log_add (log_Fatal, "Could not open '/' dir.");
+ return -1;
+ }
+ return 0;
+}
+
+void
+uninitIO (void)
+{
+ uio_closeDir (rootDir);
+ uio_closeRepository (repository);
+ uio_unInit ();
+}
+
diff --git a/src/uqm/setup.h b/src/uqm/setup.h
new file mode 100644
index 0000000..ffdbff3
--- /dev/null
+++ b/src/uqm/setup.h
@@ -0,0 +1,89 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_SETUP_H_
+#define UQM_SETUP_H_
+
+#include "displist.h"
+#include "globdata.h"
+#include "libs/reslib.h"
+#include "libs/sndlib.h"
+#include "libs/gfxlib.h"
+#include "libs/threadlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern RESOURCE_INDEX hResIndex;
+
+extern FRAME Screen;
+extern FRAME ActivityFrame;
+extern FRAME StatusFrame;
+extern FRAME FlagStatFrame;
+extern FRAME MiscDataFrame;
+extern FRAME FontGradFrame;
+
+extern CONTEXT OffScreenContext;
+ // OffScreenContext can often refer to a deleted ForeGroundFrame
+ // Always call SetContextFGFrame() before drawing anything to it
+ // Neither is the state of its ClipRect guaranteed.
+extern CONTEXT ScreenContext;
+extern CONTEXT SpaceContext;
+extern CONTEXT StatusContext;
+
+extern SIZE screen_width, screen_height;
+
+extern FONT StarConFont;
+extern FONT MicroFont;
+extern FONT TinyFont;
+
+extern CondVar RenderingCond;
+
+extern QUEUE race_q[];
+ /* Array of lists of ships involved in a battle, one queue per side;
+ * queue element is STARSHIP */
+
+extern ACTIVITY LastActivity;
+
+extern BYTE PlayerControl[];
+
+extern BOOLEAN usingSpeech;
+ // Actual speech presence indicator which decouples reality from
+ // the user option, thus the user option remains as pure intent
+
+BOOLEAN InitContexts (void);
+void UninitPlayerInput (void);
+BOOLEAN InitGameKernel (void);
+void UninitGameKernel (void);
+
+extern BOOLEAN LoadKernel (int argc, char *argv[]);
+extern void FreeKernel (void);
+
+int initIO (void);
+void uninitIO (void);
+
+bool SetPlayerInput (COUNT playerI);
+bool SetPlayerInputAll (void);
+void ClearPlayerInput (COUNT playerI);
+void ClearPlayerInputAll (void);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SETUP_H_ */
diff --git a/src/uqm/setupmenu.c b/src/uqm/setupmenu.c
new file mode 100644
index 0000000..b858617
--- /dev/null
+++ b/src/uqm/setupmenu.c
@@ -0,0 +1,1613 @@
+// Copyright Michael Martin, 2004.
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "setupmenu.h"
+
+#include "controls.h"
+#include "options.h"
+#include "setup.h"
+#include "sounds.h"
+#include "colors.h"
+#include "libs/gfxlib.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/widgets.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/strlib.h"
+#include "libs/reslib.h"
+#include "libs/inplib.h"
+#include "libs/vidlib.h"
+#include "libs/sound/sound.h"
+#include "libs/resource/stringbank.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+#include "resinst.h"
+#include "nameref.h"
+#include <math.h>
+
+
+static STRING SetupTab;
+
+typedef struct setup_menu_state {
+ BOOLEAN (*InputFunc) (struct setup_menu_state *pInputState);
+
+ BOOLEAN initialized;
+ int anim_frame_count;
+ DWORD NextTime;
+} SETUP_MENU_STATE;
+
+static BOOLEAN DoSetupMenu (SETUP_MENU_STATE *pInputState);
+static BOOLEAN done;
+static WIDGET *current, *next;
+
+static int quit_main_menu (WIDGET *self, int event);
+static int quit_sub_menu (WIDGET *self, int event);
+static int do_graphics (WIDGET *self, int event);
+static int do_audio (WIDGET *self, int event);
+static int do_engine (WIDGET *self, int event);
+static int do_resources (WIDGET *self, int event);
+static int do_keyconfig (WIDGET *self, int event);
+static int do_advanced (WIDGET *self, int event);
+static int do_editkeys (WIDGET *self, int event);
+static void change_template (WIDGET_CHOICE *self, int oldval);
+static void rename_template (WIDGET_TEXTENTRY *self);
+static void rebind_control (WIDGET_CONTROLENTRY *widget);
+static void clear_control (WIDGET_CONTROLENTRY *widget);
+
+#define MENU_COUNT 8
+#define CHOICE_COUNT 24
+#define SLIDER_COUNT 4
+#define BUTTON_COUNT 10
+#define LABEL_COUNT 4
+#define TEXTENTRY_COUNT 1
+#define CONTROLENTRY_COUNT 7
+
+/* The space for our widgets */
+static WIDGET_MENU_SCREEN menus[MENU_COUNT];
+static WIDGET_CHOICE choices[CHOICE_COUNT];
+static WIDGET_SLIDER sliders[SLIDER_COUNT];
+static WIDGET_BUTTON buttons[BUTTON_COUNT];
+static WIDGET_LABEL labels[LABEL_COUNT];
+static WIDGET_TEXTENTRY textentries[TEXTENTRY_COUNT];
+static WIDGET_CONTROLENTRY controlentries[CONTROLENTRY_COUNT];
+
+/* The hardcoded data that isn't strings */
+
+typedef int (*HANDLER)(WIDGET *, int);
+
+static int choice_widths[CHOICE_COUNT] = {
+ 3, 2, 3, 3, 2, 2, 2, 2, 2, 2,
+ 2, 2, 3, 2, 2, 3, 3, 2, 3, 3,
+ 3, 2, 2, 2 };
+
+static HANDLER button_handlers[BUTTON_COUNT] = {
+ quit_main_menu, quit_sub_menu, do_graphics, do_engine,
+ do_audio, do_resources, do_keyconfig, do_advanced, do_editkeys,
+ do_keyconfig };
+
+/* These refer to uninitialized widgets, but that's OK; we'll fill
+ * them in before we touch them */
+static WIDGET *main_widgets[] = {
+ (WIDGET *)(&buttons[2]),
+ (WIDGET *)(&buttons[3]),
+ (WIDGET *)(&buttons[4]),
+ (WIDGET *)(&buttons[5]),
+ (WIDGET *)(&buttons[6]),
+ (WIDGET *)(&buttons[7]),
+ (WIDGET *)(&buttons[0]),
+ NULL };
+
+static WIDGET *graphics_widgets[] = {
+ (WIDGET *)(&choices[0]),
+ (WIDGET *)(&choices[23]),
+ (WIDGET *)(&choices[10]),
+ (WIDGET *)(&sliders[3]),
+ (WIDGET *)(&choices[2]),
+ (WIDGET *)(&choices[3]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *audio_widgets[] = {
+ (WIDGET *)(&sliders[0]),
+ (WIDGET *)(&sliders[1]),
+ (WIDGET *)(&sliders[2]),
+ (WIDGET *)(&choices[14]),
+ (WIDGET *)(&choices[9]),
+ (WIDGET *)(&choices[21]),
+ (WIDGET *)(&choices[22]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *engine_widgets[] = {
+ (WIDGET *)(&choices[4]),
+ (WIDGET *)(&choices[5]),
+ (WIDGET *)(&choices[6]),
+ (WIDGET *)(&choices[7]),
+ (WIDGET *)(&choices[8]),
+ (WIDGET *)(&choices[13]),
+ (WIDGET *)(&choices[11]),
+ (WIDGET *)(&choices[17]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *advanced_widgets[] = {
+#ifdef HAVE_OPENGL
+ (WIDGET *)(&choices[1]),
+#endif
+ (WIDGET *)(&choices[12]),
+ (WIDGET *)(&choices[15]),
+ (WIDGET *)(&choices[16]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *keyconfig_widgets[] = {
+ (WIDGET *)(&choices[18]),
+ (WIDGET *)(&choices[19]),
+ (WIDGET *)(&labels[1]),
+ (WIDGET *)(&buttons[8]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static WIDGET *editkeys_widgets[] = {
+ (WIDGET *)(&choices[20]),
+ (WIDGET *)(&labels[2]),
+ (WIDGET *)(&textentries[0]),
+ (WIDGET *)(&controlentries[0]),
+ (WIDGET *)(&controlentries[1]),
+ (WIDGET *)(&controlentries[2]),
+ (WIDGET *)(&controlentries[3]),
+ (WIDGET *)(&controlentries[4]),
+ (WIDGET *)(&controlentries[5]),
+ (WIDGET *)(&controlentries[6]),
+ (WIDGET *)(&buttons[9]),
+ NULL };
+
+static WIDGET *incomplete_widgets[] = {
+ (WIDGET *)(&labels[0]),
+ (WIDGET *)(&buttons[1]),
+ NULL };
+
+static const struct
+{
+ WIDGET **widgets;
+ int bgIndex;
+}
+menu_defs[] =
+{
+ {main_widgets, 0},
+ {graphics_widgets, 1},
+ {audio_widgets, 1},
+ {engine_widgets, 2},
+ {incomplete_widgets, 3},
+ {keyconfig_widgets, 1},
+ {advanced_widgets, 2},
+ {editkeys_widgets, 1},
+ {NULL, 0}
+};
+
+// Start with reasonable gamma bounds. These will get updated
+// as we find out the actual bounds.
+static float minGamma = 0.4f;
+static float maxGamma = 2.5f;
+// The gamma slider uses an exponential curve
+// We use y = e^(2.1972*(x-1)) curve to give us a nice spread of
+// gamma values 0.11 < g < 9.0 centered at g=1.0
+#define GAMMA_CURVE_B 2.1972f
+static float minGammaX;
+static float maxGammaX;
+
+
+static int
+number_res_options (void)
+{
+ if (TFB_SupportsHardwareScaling ())
+ {
+ return 5;
+ }
+ else
+ {
+ return 2;
+ }
+}
+
+static int
+quit_main_menu (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = NULL;
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+quit_sub_menu (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[0]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_SELECT);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_graphics (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[1]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_audio (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[2]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_engine (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[3]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_resources (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[4]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_keyconfig (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[5]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static int
+do_advanced (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[6]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static void
+populate_editkeys (int templat)
+{
+ int i, j;
+
+ strncpy (textentries[0].value, input_templates[templat].name, textentries[0].maxlen);
+ textentries[0].value[textentries[0].maxlen-1] = 0;
+
+ for (i = 0; i < NUM_KEYS; i++)
+ {
+ for (j = 0; j < 2; j++)
+ {
+ InterrogateInputState (templat, i, j, controlentries[i].controlname[j], WIDGET_CONTROLENTRY_WIDTH);
+ }
+ }
+}
+
+static int
+do_editkeys (WIDGET *self, int event)
+{
+ if (event == WIDGET_EVENT_SELECT)
+ {
+ next = (WIDGET *)(&menus[7]);
+ /* Prepare the components */
+ choices[20].selected = 0;
+
+ populate_editkeys (0);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+ return TRUE;
+ }
+ (void)self;
+ return FALSE;
+}
+
+static void
+change_template (WIDGET_CHOICE *self, int oldval)
+{
+ (void) oldval;
+ populate_editkeys (self->selected);
+}
+
+static void
+rename_template (WIDGET_TEXTENTRY *self)
+{
+ /* TODO: This will have to change if the size of the
+ input_templates name is changed. It would probably be nice
+ to track this symbolically or ensure that self->value's
+ buffer is always at least this big; this will require some
+ reworking of widgets */
+ strncpy (input_templates[choices[20].selected].name, self->value, 30);
+ input_templates[choices[20].selected].name[29] = 0;
+}
+
+#define NUM_STEPS 20
+#define X_STEP (SCREEN_WIDTH / NUM_STEPS)
+#define Y_STEP (SCREEN_HEIGHT / NUM_STEPS)
+#define MENU_FRAME_RATE (ONE_SECOND / 20)
+
+static void
+SetDefaults (void)
+{
+ GLOBALOPTS opts;
+
+ GetGlobalOptions (&opts);
+ if (opts.res == OPTVAL_CUSTOM)
+ {
+ choices[0].numopts = number_res_options () + 1;
+ }
+ else
+ {
+ choices[0].numopts = number_res_options ();
+ }
+ choices[0].selected = opts.res;
+ choices[1].selected = opts.driver;
+ choices[2].selected = opts.scaler;
+ choices[3].selected = opts.scanlines;
+ choices[4].selected = opts.menu;
+ choices[5].selected = opts.text;
+ choices[6].selected = opts.cscan;
+ choices[7].selected = opts.scroll;
+ choices[8].selected = opts.subtitles;
+ choices[9].selected = opts.music3do;
+ choices[10].selected = opts.fullscreen;
+ choices[11].selected = opts.intro;
+ choices[12].selected = opts.fps;
+ choices[13].selected = opts.meleezoom;
+ choices[14].selected = opts.stereo;
+ choices[15].selected = opts.adriver;
+ choices[16].selected = opts.aquality;
+ choices[17].selected = opts.shield;
+ choices[18].selected = opts.player1;
+ choices[19].selected = opts.player2;
+ choices[20].selected = 0;
+ choices[21].selected = opts.musicremix;
+ choices[22].selected = opts.speech;
+ choices[23].selected = opts.keepaspect;
+
+ sliders[0].value = opts.musicvol;
+ sliders[1].value = opts.sfxvol;
+ sliders[2].value = opts.speechvol;
+ sliders[3].value = opts.gamma;
+}
+
+static void
+PropagateResults (void)
+{
+ GLOBALOPTS opts;
+ opts.res = choices[0].selected;
+ opts.driver = choices[1].selected;
+ opts.scaler = choices[2].selected;
+ opts.scanlines = choices[3].selected;
+ opts.menu = choices[4].selected;
+ opts.text = choices[5].selected;
+ opts.cscan = choices[6].selected;
+ opts.scroll = choices[7].selected;
+ opts.subtitles = choices[8].selected;
+ opts.music3do = choices[9].selected;
+ opts.fullscreen = choices[10].selected;
+ opts.intro = choices[11].selected;
+ opts.fps = choices[12].selected;
+ opts.meleezoom = choices[13].selected;
+ opts.stereo = choices[14].selected;
+ opts.adriver = choices[15].selected;
+ opts.aquality = choices[16].selected;
+ opts.shield = choices[17].selected;
+ opts.player1 = choices[18].selected;
+ opts.player2 = choices[19].selected;
+ opts.musicremix = choices[21].selected;
+ opts.speech = choices[22].selected;
+ opts.keepaspect = choices[23].selected;
+
+ opts.musicvol = sliders[0].value;
+ opts.sfxvol = sliders[1].value;
+ opts.speechvol = sliders[2].value;
+ opts.gamma = sliders[3].value;
+ SetGlobalOptions (&opts);
+}
+
+static BOOLEAN
+DoSetupMenu (SETUP_MENU_STATE *pInputState)
+{
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (!pInputState->initialized)
+ {
+ SetDefaultMenuRepeatDelay ();
+ pInputState->NextTime = GetTimeCounter ();
+ SetDefaults ();
+ Widget_SetFont (StarConFont);
+ Widget_SetWindowColors (SHADOWBOX_BACKGROUND_COLOR,
+ SHADOWBOX_DARK_COLOR, SHADOWBOX_MEDIUM_COLOR);
+
+ current = NULL;
+ next = (WIDGET *)(&menus[0]);
+ (*next->receiveFocus) (next, WIDGET_EVENT_DOWN);
+
+ pInputState->initialized = TRUE;
+ }
+ if (current != next)
+ {
+ SetTransitionSource (NULL);
+ }
+
+ BatchGraphics ();
+ (*next->draw)(next, 0, 0);
+
+ if (current != next)
+ {
+ ScreenTransition (3, NULL);
+ current = next;
+ }
+
+ UnbatchGraphics ();
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ Widget_Event (WIDGET_EVENT_UP);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ Widget_Event (WIDGET_EVENT_DOWN);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ Widget_Event (WIDGET_EVENT_LEFT);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ Widget_Event (WIDGET_EVENT_RIGHT);
+ }
+ if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ Widget_Event (WIDGET_EVENT_SELECT);
+ }
+ if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ Widget_Event (WIDGET_EVENT_CANCEL);
+ }
+ if (PulsedInputState.menu[KEY_MENU_DELETE])
+ {
+ Widget_Event (WIDGET_EVENT_DELETE);
+ }
+
+ SleepThreadUntil (pInputState->NextTime + MENU_FRAME_RATE);
+ pInputState->NextTime = GetTimeCounter ();
+ return !((GLOBAL (CurrentActivity) & CHECK_ABORT) ||
+ (next == NULL));
+}
+
+static void
+redraw_menu (void)
+{
+ BatchGraphics ();
+ (*next->draw)(next, 0, 0);
+ UnbatchGraphics ();
+}
+
+static BOOLEAN
+OnTextEntryChange (TEXTENTRY_STATE *pTES)
+{
+ WIDGET_TEXTENTRY *widget = (WIDGET_TEXTENTRY *) pTES->CbParam;
+
+ widget->cursor_pos = pTES->CursorPos;
+ if (pTES->JoystickMode)
+ widget->state |= WTE_BLOCKCUR;
+ else
+ widget->state &= ~WTE_BLOCKCUR;
+
+ // XXX TODO: Here, we can examine the text entered so far
+ // to make sure it fits on the screen, for example,
+ // and return FALSE to disallow the last change
+
+ return TRUE; // allow change
+}
+
+static BOOLEAN
+OnTextEntryFrame (TEXTENTRY_STATE *pTES)
+{
+ redraw_menu ();
+
+ SleepThreadUntil (pTES->NextTime);
+ pTES->NextTime = GetTimeCounter () + MENU_FRAME_RATE;
+
+ return TRUE; // continue
+}
+
+static int
+OnTextEntryEvent (WIDGET_TEXTENTRY *widget)
+{ // Going to edit the text
+ TEXTENTRY_STATE tes;
+ UNICODE revert_buf[256];
+
+ // position cursor at the end of text
+ widget->cursor_pos = utf8StringCount (widget->value);
+ widget->state = WTE_EDITING;
+ redraw_menu ();
+
+ // make a backup copy for revert on cancel
+ utf8StringCopy (revert_buf, sizeof (revert_buf), widget->value);
+
+ // text entry setup
+ tes.Initialized = FALSE;
+ tes.NextTime = GetTimeCounter () + MENU_FRAME_RATE;
+ tes.BaseStr = widget->value;
+ tes.MaxSize = widget->maxlen;
+ tes.CursorPos = widget->cursor_pos;
+ tes.CbParam = widget;
+ tes.ChangeCallback = OnTextEntryChange;
+ tes.FrameCallback = OnTextEntryFrame;
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_SELECT);
+ if (!DoTextEntry (&tes))
+ { // editing failed (canceled) -- revert the changes
+ utf8StringCopy (widget->value, widget->maxlen, revert_buf);
+ }
+ else
+ {
+ if (widget->onChange)
+ {
+ (*(widget->onChange))(widget);
+ }
+ }
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ widget->state = WTE_NORMAL;
+ redraw_menu ();
+
+ return TRUE; // event handled
+}
+
+static inline float
+gammaCurve (float x)
+{
+ // The slider uses an exponential curve
+ return exp ((x - 1) * GAMMA_CURVE_B);
+}
+
+static inline float
+solveGammaCurve (float y)
+{
+ return log (y) / GAMMA_CURVE_B + 1;
+}
+
+static int
+gammaToSlider (float gamma)
+{
+ const float x = solveGammaCurve (gamma);
+ const float step = (maxGammaX - minGammaX) / 100;
+ return (int) ((x - minGammaX) / step + 0.5);
+}
+
+static float
+sliderToGamma (int value)
+{
+ const float step = (maxGammaX - minGammaX) / 100;
+ const float x = minGammaX + step * value;
+ const float g = gammaCurve (x);
+ // report any value that is close enough as 1.0
+ return (fabs (g - 1.0f) < 0.001f) ? 1.0f : g;
+}
+
+static void
+updateGammaBounds (bool useUpper)
+{
+ float g, x;
+ int slider;
+
+ // The slider uses an exponential curve.
+ // Calculate where on the curve the min and max gamma values are
+ minGammaX = solveGammaCurve (minGamma);
+ maxGammaX = solveGammaCurve (maxGamma);
+
+ // We have 100 discrete steps through the range, so the slider may
+ // skip over a 1.0 gamma. We need to ensure that there always is
+ // a 1.0 on the slider by tweaking the range (expanding/contracting).
+ slider = gammaToSlider (1.0f);
+ g = sliderToGamma (slider);
+ if (g == 1.0f)
+ return; // no adjustment needed
+
+ x = solveGammaCurve (g);
+ if (useUpper)
+ { // Move the upper bound up or down to land on 1.0
+ const float d = (x - 1.0f) * 100 / slider;
+ maxGammaX -= d;
+ maxGamma = gammaCurve (maxGammaX);
+ }
+ else
+ { // Move the lower bound up or down to land on 1.0
+ const float d = (x - 1.0f) * 100 / (100 - slider);
+ minGammaX -= d;
+ minGamma = gammaCurve (minGammaX);
+ }
+}
+
+static int
+gamma_HandleEventSlider (WIDGET *_self, int event)
+{
+ WIDGET_SLIDER *self = (WIDGET_SLIDER *)_self;
+ int prevValue = self->value;
+ float gamma;
+ bool set;
+
+ switch (event)
+ {
+ case WIDGET_EVENT_LEFT:
+ self->value -= self->step;
+ break;
+ case WIDGET_EVENT_RIGHT:
+ self->value += self->step;
+ break;
+ default:
+ return FALSE;
+ }
+
+ // Limit the slider to values accepted by gfx subsys
+ gamma = sliderToGamma (self->value);
+ set = TFB_SetGamma (gamma);
+ if (!set)
+ { // revert
+ self->value = prevValue;
+ gamma = sliderToGamma (self->value);
+ }
+
+ // Grow or shrink the range based on accepted values
+ if (gamma < minGamma || (!set && event == WIDGET_EVENT_LEFT))
+ {
+ minGamma = gamma;
+ updateGammaBounds (true);
+ // at the lowest end
+ self->value = 0;
+ }
+ else if (gamma > maxGamma || (!set && event == WIDGET_EVENT_RIGHT))
+ {
+ maxGamma = gamma;
+ updateGammaBounds (false);
+ // at the highest end
+ self->value = 100;
+ }
+ return TRUE;
+}
+
+static void
+gamma_DrawValue (WIDGET_SLIDER *self, int x, int y)
+{
+ TEXT t;
+ char buf[16];
+ float gamma = sliderToGamma (self->value);
+ snprintf (buf, sizeof buf, "%.4f", gamma);
+
+ t.baseline.x = x;
+ t.baseline.y = y;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ t.pStr = buf;
+
+ font_DrawText (&t);
+}
+
+static void
+rebind_control (WIDGET_CONTROLENTRY *widget)
+{
+ int templat = choices[20].selected;
+ int control = widget->controlindex;
+ int index = widget->highlighted;
+
+ FlushInput ();
+ DrawLabelAsWindow (&labels[3], NULL);
+ RebindInputState (templat, control, index);
+ populate_editkeys (templat);
+ FlushInput ();
+}
+
+static void
+clear_control (WIDGET_CONTROLENTRY *widget)
+{
+ int templat = choices[20].selected;
+ int control = widget->controlindex;
+ int index = widget->highlighted;
+
+ RemoveInputState (templat, control, index);
+ populate_editkeys (templat);
+}
+
+static int
+count_widgets (WIDGET **widgets)
+{
+ int count;
+
+ for (count = 0; *widgets != NULL; ++widgets, ++count)
+ ;
+ return count;
+}
+
+static stringbank *bank = NULL;
+static FRAME setup_frame = NULL;
+
+static void
+init_widgets (void)
+{
+ const char *buffer[100], *str, *title;
+ int count, i, index;
+
+ if (bank == NULL)
+ {
+ bank = StringBank_Create ();
+ }
+
+ if (setup_frame == NULL)
+ {
+ setup_frame = CaptureDrawable (LoadGraphic (MENUBKG_PMAP_ANIM));
+ }
+
+ count = GetStringTableCount (SetupTab);
+
+ if (count < 3)
+ {
+ log_add (log_Fatal, "PANIC: Setup string table too short to even hold all indices!");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Menus */
+ title = StringBank_AddOrFindString (bank, GetStringAddress (SetAbsStringTableIndex (SetupTab, 0)));
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, 1)), '\n', 100, buffer, bank) != MENU_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Menu Subtitles");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < MENU_COUNT; i++)
+ {
+ menus[i].tag = WIDGET_TYPE_MENU_SCREEN;
+ menus[i].parent = NULL;
+ menus[i].handleEvent = Widget_HandleEventMenuScreen;
+ menus[i].receiveFocus = Widget_ReceiveFocusMenuScreen;
+ menus[i].draw = Widget_DrawMenuScreen;
+ menus[i].height = Widget_HeightFullScreen;
+ menus[i].width = Widget_WidthFullScreen;
+ menus[i].title = title;
+ menus[i].subtitle = buffer[i];
+ menus[i].bgStamp.origin.x = 0;
+ menus[i].bgStamp.origin.y = 0;
+ menus[i].bgStamp.frame = SetAbsFrameIndex (setup_frame, menu_defs[i].bgIndex);
+ menus[i].num_children = count_widgets (menu_defs[i].widgets);
+ menus[i].child = menu_defs[i].widgets;
+ menus[i].highlighted = 0;
+ }
+ if (menu_defs[i].widgets != NULL)
+ {
+ log_add (log_Error, "Menu definition array has more items!");
+ }
+
+ /* Options */
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, 2)), '\n', 100, buffer, bank) != CHOICE_COUNT)
+ {
+ log_add (log_Fatal, "PANIC: Incorrect number of Choice Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < CHOICE_COUNT; i++)
+ {
+ choices[i].tag = WIDGET_TYPE_CHOICE;
+ choices[i].parent = NULL;
+ choices[i].handleEvent = Widget_HandleEventChoice;
+ choices[i].receiveFocus = Widget_ReceiveFocusChoice;
+ choices[i].draw = Widget_DrawChoice;
+ choices[i].height = Widget_HeightChoice;
+ choices[i].width = Widget_WidthFullScreen;
+ choices[i].category = buffer[i];
+ choices[i].numopts = 0;
+ choices[i].options = NULL;
+ choices[i].selected = 0;
+ choices[i].highlighted = 0;
+ choices[i].maxcolumns = choice_widths[i];
+ choices[i].onChange = NULL;
+ }
+
+ /* Fill in the options now */
+ index = 3; /* Index into string table */
+ for (i = 0; i < CHOICE_COUNT; i++)
+ {
+ int j, optcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading choices");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ optcount = SplitString (str, '\n', 100, buffer, bank);
+ choices[i].numopts = optcount;
+ choices[i].options = HMalloc (optcount * sizeof (CHOICE_OPTION));
+ for (j = 0; j < optcount; j++)
+ {
+ choices[i].options[j].optname = buffer[j];
+ choices[i].options[j].tooltip[0] = "";
+ choices[i].options[j].tooltip[1] = "";
+ choices[i].options[j].tooltip[2] = "";
+ }
+ for (j = 0; j < optcount; j++)
+ {
+ int k, tipcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading choices");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ tipcount = SplitString (str, '\n', 100, buffer, bank);
+ if (tipcount > 3)
+ {
+ tipcount = 3;
+ }
+ for (k = 0; k < tipcount; k++)
+ {
+ choices[i].options[j].tooltip[k] = buffer[k];
+ }
+ }
+ }
+
+ /* The first choice is resolution, and is handled specially */
+ choices[0].numopts = number_res_options ();
+
+ /* Choices 18-20 are also special, being the names of the key configurations */
+ for (i = 0; i < 6; i++)
+ {
+ choices[18].options[i].optname = input_templates[i].name;
+ choices[19].options[i].optname = input_templates[i].name;
+ choices[20].options[i].optname = input_templates[i].name;
+ }
+
+ /* Choice 20 has a special onChange handler, too. */
+ choices[20].onChange = change_template;
+
+ /* Sliders */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading sliders");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != SLIDER_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Slider Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < SLIDER_COUNT; i++)
+ {
+ sliders[i].tag = WIDGET_TYPE_SLIDER;
+ sliders[i].parent = NULL;
+ sliders[i].handleEvent = Widget_HandleEventSlider;
+ sliders[i].receiveFocus = Widget_ReceiveFocusSimple;
+ sliders[i].draw = Widget_DrawSlider;
+ sliders[i].height = Widget_HeightOneLine;
+ sliders[i].width = Widget_WidthFullScreen;
+ sliders[i].draw_value = Widget_Slider_DrawValue;
+ sliders[i].min = 0;
+ sliders[i].max = 100;
+ sliders[i].step = 5;
+ sliders[i].value = 75;
+ sliders[i].category = buffer[i];
+ sliders[i].tooltip[0] = "";
+ sliders[i].tooltip[1] = "";
+ sliders[i].tooltip[2] = "";
+ }
+ // gamma is a special case
+ sliders[3].step = 1;
+ sliders[3].handleEvent = gamma_HandleEventSlider;
+ sliders[3].draw_value = gamma_DrawValue;
+
+ for (i = 0; i < SLIDER_COUNT; i++)
+ {
+ int j, tipcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading sliders");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ tipcount = SplitString (str, '\n', 100, buffer, bank);
+ if (tipcount > 3)
+ {
+ tipcount = 3;
+ }
+ for (j = 0; j < tipcount; j++)
+ {
+ sliders[i].tooltip[j] = buffer[j];
+ }
+ }
+
+ /* Buttons */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading buttons");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != BUTTON_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Button Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < BUTTON_COUNT; i++)
+ {
+ buttons[i].tag = WIDGET_TYPE_BUTTON;
+ buttons[i].parent = NULL;
+ buttons[i].handleEvent = button_handlers[i];
+ buttons[i].receiveFocus = Widget_ReceiveFocusSimple;
+ buttons[i].draw = Widget_DrawButton;
+ buttons[i].height = Widget_HeightOneLine;
+ buttons[i].width = Widget_WidthFullScreen;
+ buttons[i].name = buffer[i];
+ buttons[i].tooltip[0] = "";
+ buttons[i].tooltip[1] = "";
+ buttons[i].tooltip[2] = "";
+ }
+
+ for (i = 0; i < BUTTON_COUNT; i++)
+ {
+ int j, tipcount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading buttons");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ tipcount = SplitString (str, '\n', 100, buffer, bank);
+ if (tipcount > 3)
+ {
+ tipcount = 3;
+ }
+ for (j = 0; j < tipcount; j++)
+ {
+ buttons[i].tooltip[j] = buffer[j];
+ }
+ }
+
+ /* Labels */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading labels");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != LABEL_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Label Options");
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < LABEL_COUNT; i++)
+ {
+ labels[i].tag = WIDGET_TYPE_LABEL;
+ labels[i].parent = NULL;
+ labels[i].handleEvent = Widget_HandleEventIgnoreAll;
+ labels[i].receiveFocus = Widget_ReceiveFocusRefuseFocus;
+ labels[i].draw = Widget_DrawLabel;
+ labels[i].height = Widget_HeightLabel;
+ labels[i].width = Widget_WidthFullScreen;
+ labels[i].line_count = 0;
+ labels[i].lines = NULL;
+ }
+
+ for (i = 0; i < LABEL_COUNT; i++)
+ {
+ int j, linecount;
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading labels");
+ exit (EXIT_FAILURE);
+ }
+ str = GetStringAddress (SetAbsStringTableIndex (SetupTab, index++));
+ linecount = SplitString (str, '\n', 100, buffer, bank);
+ labels[i].line_count = linecount;
+ labels[i].lines = (const char **)HMalloc(linecount * sizeof(const char *));
+ for (j = 0; j < linecount; j++)
+ {
+ labels[i].lines[j] = buffer[j];
+ }
+ }
+
+ /* Text Entry boxes */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading text entries");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != TEXTENTRY_COUNT)
+ {
+ log_add (log_Fatal, "PANIC: Incorrect number of Text Entries");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < TEXTENTRY_COUNT; i++)
+ {
+ textentries[i].tag = WIDGET_TYPE_TEXTENTRY;
+ textentries[i].parent = NULL;
+ textentries[i].handleEvent = Widget_HandleEventTextEntry;
+ textentries[i].receiveFocus = Widget_ReceiveFocusSimple;
+ textentries[i].draw = Widget_DrawTextEntry;
+ textentries[i].height = Widget_HeightOneLine;
+ textentries[i].width = Widget_WidthFullScreen;
+ textentries[i].handleEventSelect = OnTextEntryEvent;
+ textentries[i].category = buffer[i];
+ textentries[i].value[0] = 0;
+ textentries[i].maxlen = WIDGET_TEXTENTRY_WIDTH-1;
+ textentries[i].state = WTE_NORMAL;
+ textentries[i].cursor_pos = 0;
+ }
+
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading text entries");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != TEXTENTRY_COUNT)
+ {
+ /* TODO: Ignore extras instead of dying. */
+ log_add (log_Fatal, "PANIC: Incorrect number of Text Entries");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < TEXTENTRY_COUNT; i++)
+ {
+ strncpy (textentries[i].value, buffer[i], textentries[i].maxlen);
+ textentries[i].value[textentries[i].maxlen] = 0;
+ }
+ textentries[0].onChange = rename_template;
+
+ /* Control Entry boxes */
+ if (index >= count)
+ {
+ log_add (log_Fatal, "PANIC: String table cut short while reading control entries");
+ exit (EXIT_FAILURE);
+ }
+
+ if (SplitString (GetStringAddress (SetAbsStringTableIndex (SetupTab, index++)), '\n', 100, buffer, bank) != CONTROLENTRY_COUNT)
+ {
+ log_add (log_Fatal, "PANIC: Incorrect number of Control Entries");
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < CONTROLENTRY_COUNT; i++)
+ {
+ controlentries[i].tag = WIDGET_TYPE_CONTROLENTRY;
+ controlentries[i].parent = NULL;
+ controlentries[i].handleEvent = Widget_HandleEventControlEntry;
+ controlentries[i].receiveFocus = Widget_ReceiveFocusControlEntry;
+ controlentries[i].draw = Widget_DrawControlEntry;
+ controlentries[i].height = Widget_HeightOneLine;
+ controlentries[i].width = Widget_WidthFullScreen;
+ controlentries[i].category = buffer[i];
+ controlentries[i].highlighted = 0;
+ controlentries[i].controlname[0][0] = 0;
+ controlentries[i].controlname[1][0] = 0;
+ controlentries[i].controlindex = i;
+ controlentries[i].onChange = rebind_control;
+ controlentries[i].onDelete = clear_control;
+ }
+
+ /* Check for garbage at the end */
+ if (index < count)
+ {
+ log_add (log_Warning, "WARNING: Setup strings had %d garbage entries at the end.",
+ count - index);
+ }
+}
+
+static void
+clean_up_widgets (void)
+{
+ int i;
+
+ for (i = 0; i < CHOICE_COUNT; i++)
+ {
+ if (choices[i].options)
+ {
+ HFree (choices[i].options);
+ }
+ }
+
+ for (i = 0; i < LABEL_COUNT; i++)
+ {
+ if (labels[i].lines)
+ {
+ HFree ((void *)labels[i].lines);
+ }
+ }
+
+ /* Clear out the master tables */
+
+ if (SetupTab)
+ {
+ DestroyStringTable (ReleaseStringTable (SetupTab));
+ SetupTab = 0;
+ }
+ if (bank)
+ {
+ StringBank_Free (bank);
+ bank = NULL;
+ }
+ if (setup_frame)
+ {
+ DestroyDrawable (ReleaseDrawable (setup_frame));
+ setup_frame = NULL;
+ }
+}
+
+void
+SetupMenu (void)
+{
+ SETUP_MENU_STATE s;
+
+ s.InputFunc = DoSetupMenu;
+ s.initialized = FALSE;
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetupTab = CaptureStringTable (LoadStringTable (SETUP_MENU_STRTAB));
+ if (SetupTab)
+ {
+ init_widgets ();
+ }
+ else
+ {
+ log_add (log_Fatal, "PANIC: Could not find strings for the setup menu!");
+ exit (EXIT_FAILURE);
+ }
+ done = FALSE;
+
+ DoInput (&s, TRUE);
+ GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
+ PropagateResults ();
+ if (SetupTab)
+ {
+ clean_up_widgets ();
+ }
+}
+
+void
+GetGlobalOptions (GLOBALOPTS *opts)
+{
+ bool whichBound;
+
+ if (GfxFlags & TFB_GFXFLAGS_SCALE_BILINEAR)
+ {
+ opts->scaler = OPTVAL_BILINEAR_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_BIADAPT)
+ {
+ opts->scaler = OPTVAL_BIADAPT_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_BIADAPTADV)
+ {
+ opts->scaler = OPTVAL_BIADV_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_TRISCAN)
+ {
+ opts->scaler = OPTVAL_TRISCAN_SCALE;
+ }
+ else if (GfxFlags & TFB_GFXFLAGS_SCALE_HQXX)
+ {
+ opts->scaler = OPTVAL_HQXX_SCALE;
+ }
+ else
+ {
+ opts->scaler = OPTVAL_NO_SCALE;
+ }
+ opts->fullscreen = (GfxFlags & TFB_GFXFLAGS_FULLSCREEN) ?
+ OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->subtitles = optSubtitles ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->scanlines = (GfxFlags & TFB_GFXFLAGS_SCANLINES) ?
+ OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->menu = (optWhichMenu == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->text = (optWhichFonts == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->cscan = (optWhichCoarseScan == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->scroll = (optSmoothScroll == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->intro = (optWhichIntro == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->shield = (optWhichShield == OPT_3DO) ? OPTVAL_3DO : OPTVAL_PC;
+ opts->fps = (GfxFlags & TFB_GFXFLAGS_SHOWFPS) ?
+ OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->meleezoom = (optMeleeScale == TFB_SCALE_STEP) ?
+ OPTVAL_PC : OPTVAL_3DO;
+ opts->stereo = optStereoSFX ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ /* These values are read in, but won't change during a run. */
+ opts->music3do = opt3doMusic ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->musicremix = optRemixMusic ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->speech = optSpeech ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ opts->keepaspect = optKeepAspectRatio ? OPTVAL_ENABLED : OPTVAL_DISABLED;
+ switch (snddriver) {
+ case audio_DRIVER_OPENAL:
+ opts->adriver = OPTVAL_OPENAL;
+ break;
+ case audio_DRIVER_MIXSDL:
+ opts->adriver = OPTVAL_MIXSDL;
+ break;
+ default:
+ opts->adriver = OPTVAL_SILENCE;
+ break;
+ }
+ if (soundflags & audio_QUALITY_HIGH)
+ {
+ opts->aquality = OPTVAL_HIGH;
+ }
+ else if (soundflags & audio_QUALITY_LOW)
+ {
+ opts->aquality = OPTVAL_LOW;
+ }
+ else
+ {
+ opts->aquality = OPTVAL_MEDIUM;
+ }
+
+ /* Work out resolution. On the way, try to guess a good default
+ * for config.alwaysgl, then overwrite it if it was set previously. */
+ opts->driver = OPTVAL_PURE_IF_POSSIBLE;
+ switch (ScreenWidthActual)
+ {
+ case 320:
+ if (GraphicsDriver == TFB_GFXDRIVER_SDL_PURE)
+ {
+ opts->res = OPTVAL_320_240;
+ }
+ else
+ {
+ if (ScreenHeightActual != 240)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_320_240;
+ opts->driver = OPTVAL_ALWAYS_GL;
+ }
+ }
+ break;
+ case 640:
+ if (GraphicsDriver == TFB_GFXDRIVER_SDL_PURE)
+ {
+ opts->res = OPTVAL_640_480;
+ }
+ else
+ {
+ if (ScreenHeightActual != 480)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_640_480;
+ opts->driver = OPTVAL_ALWAYS_GL;
+ }
+ }
+ break;
+ case 800:
+ if (ScreenHeightActual != 600)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_800_600;
+ }
+ break;
+ case 1024:
+ if (ScreenHeightActual != 768)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_1024_768;
+ }
+ break;
+ case 1280:
+ if (ScreenHeightActual != 960)
+ {
+ opts->res = OPTVAL_CUSTOM;
+ }
+ else
+ {
+ opts->res = OPTVAL_1280_960;
+ }
+ break;
+ default:
+ opts->res = OPTVAL_CUSTOM;
+ break;
+ }
+
+ if (res_IsBoolean ("config.alwaysgl"))
+ {
+ if (res_GetBoolean ("config.alwaysgl"))
+ {
+ opts->driver = OPTVAL_ALWAYS_GL;
+ }
+ else
+ {
+ opts->driver = OPTVAL_PURE_IF_POSSIBLE;
+ }
+ }
+
+ whichBound = (optGamma < maxGamma);
+ // The option supplied by the user may be beyond our starting range
+ // but valid nonetheless. We need to account for that.
+ if (optGamma <= minGamma)
+ minGamma = optGamma - 0.03f;
+ else if (optGamma >= maxGamma)
+ maxGamma = optGamma + 0.3f;
+ updateGammaBounds (whichBound);
+ opts->gamma = gammaToSlider (optGamma);
+
+ opts->player1 = PlayerControls[0];
+ opts->player2 = PlayerControls[1];
+
+ opts->musicvol = (((int)(musicVolumeScale * 100.0f) + 2) / 5) * 5;
+ opts->sfxvol = (((int)(sfxVolumeScale * 100.0f) + 2) / 5) * 5;
+ opts->speechvol = (((int)(speechVolumeScale * 100.0f) + 2) / 5) * 5;
+}
+
+void
+SetGlobalOptions (GLOBALOPTS *opts)
+{
+ int NewGfxFlags = GfxFlags;
+ int NewWidth = ScreenWidthActual;
+ int NewHeight = ScreenHeightActual;
+ int NewDriver = GraphicsDriver;
+
+ NewGfxFlags &= ~TFB_GFXFLAGS_SCALE_ANY;
+
+ switch (opts->res) {
+ case OPTVAL_320_240:
+ NewWidth = 320;
+ NewHeight = 240;
+#ifdef HAVE_OPENGL
+ NewDriver = (opts->driver == OPTVAL_ALWAYS_GL ? TFB_GFXDRIVER_SDL_OPENGL : TFB_GFXDRIVER_SDL_PURE);
+#else
+ NewDriver = TFB_GFXDRIVER_SDL_PURE;
+#endif
+ break;
+ case OPTVAL_640_480:
+ NewWidth = 640;
+ NewHeight = 480;
+#ifdef HAVE_OPENGL
+ NewDriver = (opts->driver == OPTVAL_ALWAYS_GL ? TFB_GFXDRIVER_SDL_OPENGL : TFB_GFXDRIVER_SDL_PURE);
+#else
+ NewDriver = TFB_GFXDRIVER_SDL_PURE;
+#endif
+ break;
+ case OPTVAL_800_600:
+ NewWidth = 800;
+ NewHeight = 600;
+ NewDriver = TFB_GFXDRIVER_SDL_OPENGL;
+ break;
+ case OPTVAL_1024_768:
+ NewWidth = 1024;
+ NewHeight = 768;
+ NewDriver = TFB_GFXDRIVER_SDL_OPENGL;
+ break;
+ case OPTVAL_1280_960:
+ NewWidth = 1280;
+ NewHeight = 960;
+ NewDriver = TFB_GFXDRIVER_SDL_OPENGL;
+ break;
+ default:
+ /* Don't mess with the custom value */
+ break;
+ }
+
+ res_PutInteger ("config.reswidth", NewWidth);
+ res_PutInteger ("config.resheight", NewHeight);
+ res_PutBoolean ("config.alwaysgl", opts->driver == OPTVAL_ALWAYS_GL);
+ res_PutBoolean ("config.usegl", NewDriver == TFB_GFXDRIVER_SDL_OPENGL);
+
+ switch (opts->scaler) {
+ case OPTVAL_BILINEAR_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_BILINEAR;
+ res_PutString ("config.scaler", "bilinear");
+ break;
+ case OPTVAL_BIADAPT_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_BIADAPT;
+ res_PutString ("config.scaler", "biadapt");
+ break;
+ case OPTVAL_BIADV_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_BIADAPTADV;
+ res_PutString ("config.scaler", "biadv");
+ break;
+ case OPTVAL_TRISCAN_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_TRISCAN;
+ res_PutString ("config.scaler", "triscan");
+ break;
+ case OPTVAL_HQXX_SCALE:
+ NewGfxFlags |= TFB_GFXFLAGS_SCALE_HQXX;
+ res_PutString ("config.scaler", "hq");
+ break;
+ default:
+ /* OPTVAL_NO_SCALE has no equivalent in gfxflags. */
+ res_PutString ("config.scaler", "no");
+ break;
+ }
+ if (opts->scanlines) {
+ NewGfxFlags |= TFB_GFXFLAGS_SCANLINES;
+ } else {
+ NewGfxFlags &= ~TFB_GFXFLAGS_SCANLINES;
+ }
+ if (opts->fullscreen)
+ NewGfxFlags |= TFB_GFXFLAGS_FULLSCREEN;
+ else
+ NewGfxFlags &= ~TFB_GFXFLAGS_FULLSCREEN;
+
+ res_PutBoolean ("config.scanlines", (BOOLEAN)opts->scanlines);
+ res_PutBoolean ("config.fullscreen", (BOOLEAN)opts->fullscreen);
+
+
+ if ((NewWidth != ScreenWidthActual) ||
+ (NewHeight != ScreenHeightActual) ||
+ (NewDriver != GraphicsDriver) ||
+ (NewGfxFlags != GfxFlags))
+ {
+ FlushGraphics ();
+ UninitVideoPlayer ();
+ TFB_DrawScreen_ReinitVideo (NewDriver, NewGfxFlags, NewWidth, NewHeight);
+ FlushGraphics ();
+ InitVideoPlayer (TRUE);
+ }
+
+ // Avoid setting gamma when it is not necessary
+ if (optGamma != 1.0f || sliderToGamma (opts->gamma) != 1.0f)
+ {
+ optGamma = sliderToGamma (opts->gamma);
+ setGammaCorrection (optGamma);
+ }
+
+ optSubtitles = (opts->subtitles == OPTVAL_ENABLED) ? TRUE : FALSE;
+ optWhichMenu = (opts->menu == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optWhichFonts = (opts->text == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optWhichCoarseScan = (opts->cscan == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optSmoothScroll = (opts->scroll == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optWhichShield = (opts->shield == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optMeleeScale = (opts->meleezoom == OPTVAL_3DO) ? TFB_SCALE_TRILINEAR : TFB_SCALE_STEP;
+ opt3doMusic = (opts->music3do == OPTVAL_ENABLED);
+ optRemixMusic = (opts->musicremix == OPTVAL_ENABLED);
+ optSpeech = (opts->speech == OPTVAL_ENABLED);
+ optWhichIntro = (opts->intro == OPTVAL_3DO) ? OPT_3DO : OPT_PC;
+ optStereoSFX = (opts->stereo == OPTVAL_ENABLED);
+ optKeepAspectRatio = (opts->keepaspect == OPTVAL_ENABLED);
+ PlayerControls[0] = opts->player1;
+ PlayerControls[1] = opts->player2;
+
+ res_PutBoolean ("config.subtitles", opts->subtitles == OPTVAL_ENABLED);
+ res_PutBoolean ("config.textmenu", opts->menu == OPTVAL_PC);
+ res_PutBoolean ("config.textgradients", opts->text == OPTVAL_PC);
+ res_PutBoolean ("config.iconicscan", opts->cscan == OPTVAL_3DO);
+ res_PutBoolean ("config.smoothscroll", opts->scroll == OPTVAL_3DO);
+
+ res_PutBoolean ("config.3domusic", opts->music3do == OPTVAL_ENABLED);
+ res_PutBoolean ("config.remixmusic", opts->musicremix == OPTVAL_ENABLED);
+ res_PutBoolean ("config.speech", opts->speech == OPTVAL_ENABLED);
+ res_PutBoolean ("config.3domovies", opts->intro == OPTVAL_3DO);
+ res_PutBoolean ("config.showfps", opts->fps == OPTVAL_ENABLED);
+ res_PutBoolean ("config.smoothmelee", opts->meleezoom == OPTVAL_3DO);
+ res_PutBoolean ("config.positionalsfx", opts->stereo == OPTVAL_ENABLED);
+ res_PutBoolean ("config.pulseshield", opts->shield == OPTVAL_3DO);
+ res_PutBoolean ("config.keepaspectratio", opts->keepaspect == OPTVAL_ENABLED);
+ res_PutInteger ("config.gamma", (int) (optGamma * GAMMA_SCALE + 0.5));
+ res_PutInteger ("config.player1control", opts->player1);
+ res_PutInteger ("config.player2control", opts->player2);
+
+ switch (opts->adriver) {
+ case OPTVAL_SILENCE:
+ res_PutString ("config.audiodriver", "none");
+ break;
+ case OPTVAL_MIXSDL:
+ res_PutString ("config.audiodriver", "mixsdl");
+ break;
+ case OPTVAL_OPENAL:
+ res_PutString ("config.audiodriver", "openal");
+ default:
+ /* Shouldn't happen; leave config untouched */
+ break;
+ }
+
+ switch (opts->aquality) {
+ case OPTVAL_LOW:
+ res_PutString ("config.audioquality", "low");
+ break;
+ case OPTVAL_MEDIUM:
+ res_PutString ("config.audioquality", "medium");
+ break;
+ case OPTVAL_HIGH:
+ res_PutString ("config.audioquality", "high");
+ break;
+ default:
+ /* Shouldn't happen; leave config untouched */
+ break;
+ }
+
+ res_PutInteger ("config.musicvol", opts->musicvol);
+ res_PutInteger ("config.sfxvol", opts->sfxvol);
+ res_PutInteger ("config.speechvol", opts->speechvol);
+ musicVolumeScale = opts->musicvol / 100.0f;
+ sfxVolumeScale = opts->sfxvol / 100.0f;
+ speechVolumeScale = opts->speechvol / 100.0f;
+ // update actual volumes
+ SetMusicVolume (musicVolume);
+ SetSpeechVolume (speechVolumeScale);
+
+ res_PutString ("keys.1.name", input_templates[0].name);
+ res_PutString ("keys.2.name", input_templates[1].name);
+ res_PutString ("keys.3.name", input_templates[2].name);
+ res_PutString ("keys.4.name", input_templates[3].name);
+ res_PutString ("keys.5.name", input_templates[4].name);
+ res_PutString ("keys.6.name", input_templates[5].name);
+
+ SaveResourceIndex (configDir, "uqm.cfg", "config.", TRUE);
+ SaveKeyConfiguration (configDir, "flight.cfg");
+}
diff --git a/src/uqm/setupmenu.h b/src/uqm/setupmenu.h
new file mode 100644
index 0000000..4dc74c0
--- /dev/null
+++ b/src/uqm/setupmenu.h
@@ -0,0 +1,100 @@
+// Copyright Michael Martin, 2004.
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_SETUPMENU_H_
+#define UQM_SETUPMENU_H_
+
+#include "controls.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef enum {
+ OPTVAL_DISABLED,
+ OPTVAL_ENABLED
+} OPT_ENABLABLE;
+
+typedef enum {
+ OPTVAL_PC,
+ OPTVAL_3DO
+} OPT_CONSOLETYPE;
+
+typedef enum {
+ OPTVAL_NO_SCALE,
+ OPTVAL_BILINEAR_SCALE,
+ OPTVAL_BIADAPT_SCALE,
+ OPTVAL_BIADV_SCALE,
+ OPTVAL_TRISCAN_SCALE,
+ OPTVAL_HQXX_SCALE,
+} OPT_SCALETYPE;
+
+typedef enum {
+ OPTVAL_320_240,
+ OPTVAL_640_480,
+ OPTVAL_800_600,
+ OPTVAL_1024_768,
+ OPTVAL_1280_960,
+ OPTVAL_CUSTOM
+} OPT_RESTYPE;
+
+typedef enum {
+ OPTVAL_PURE_IF_POSSIBLE,
+ OPTVAL_ALWAYS_GL
+} OPT_DRIVERTYPE;
+
+typedef enum {
+ OPTVAL_SILENCE,
+ OPTVAL_MIXSDL,
+ OPTVAL_OPENAL
+} OPT_ADRIVERTYPE;
+
+typedef enum {
+ OPTVAL_LOW,
+ OPTVAL_MEDIUM,
+ OPTVAL_HIGH
+} OPT_AQUALITYTYPE;
+
+/* At the moment, CONTROL_TEMPLATE is directly in this structure. If
+ * CONTROL_TEMPLATE and the options available diverge, this will need
+ * to change */
+typedef struct globalopts_struct {
+ OPT_SCALETYPE scaler;
+ OPT_RESTYPE res;
+ OPT_DRIVERTYPE driver;
+ OPT_ADRIVERTYPE adriver;
+ OPT_AQUALITYTYPE aquality;
+ OPT_ENABLABLE fullscreen, subtitles, scanlines, fps, stereo;
+ OPT_ENABLABLE music3do, musicremix, speech;
+ OPT_ENABLABLE keepaspect;
+ OPT_CONSOLETYPE menu, text, cscan, scroll, intro, meleezoom, shield;
+ CONTROL_TEMPLATE player1, player2;
+ int speechvol, musicvol, sfxvol;
+ int gamma;
+} GLOBALOPTS;
+
+void SetupMenu (void);
+
+void GetGlobalOptions (GLOBALOPTS *opts);
+void SetGlobalOptions (GLOBALOPTS *opts);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // UQM_SETUPMENU_H_
diff --git a/src/uqm/ship.c b/src/uqm/ship.c
new file mode 100644
index 0000000..747c929
--- /dev/null
+++ b/src/uqm/ship.c
@@ -0,0 +1,574 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "ship.h"
+
+#include "build.h"
+#include "collide.h"
+#include "colors.h"
+#include "globdata.h"
+#include "status.h"
+#include "hyper.h"
+#include "tactrans.h"
+#include "pickship.h"
+#include "intel.h"
+#include "init.h"
+#include "battle.h"
+#include "supermelee/pickmele.h"
+#include "races.h"
+#include "setup.h"
+#include "sounds.h"
+#include "libs/mathlib.h"
+
+
+void
+animation_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+
+STATUS_FLAGS
+inertial_thrust (ELEMENT *ElementPtr)
+{
+#define MAX_ALLOWED_SPEED WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (18))
+#define MAX_ALLOWED_SPEED_SQR ((DWORD)MAX_ALLOWED_SPEED * MAX_ALLOWED_SPEED)
+
+ COUNT CurrentAngle, TravelAngle;
+ COUNT max_thrust, thrust_increment;
+ VELOCITY_DESC *VelocityPtr;
+ STARSHIP *StarShipPtr;
+
+ VelocityPtr = &ElementPtr->velocity;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ CurrentAngle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ TravelAngle = GetVelocityTravelAngle (VelocityPtr);
+ thrust_increment = StarShipPtr->RaceDescPtr->characteristics.thrust_increment;
+ max_thrust = StarShipPtr->RaceDescPtr->characteristics.max_thrust;
+ if (thrust_increment == max_thrust)
+ { // inertialess acceleration (Skiff)
+ SetVelocityVector (VelocityPtr,
+ max_thrust, StarShipPtr->ShipFacing);
+ return (SHIP_AT_MAX_SPEED);
+ }
+ else if (TravelAngle == CurrentAngle
+ && (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
+ && !(StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL))
+ { // already maxed-out acceleration
+ return (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED));
+ }
+ else
+ {
+ SIZE delta_x, delta_y;
+ SIZE cur_delta_x, cur_delta_y;
+ DWORD desired_speed, max_speed;
+ DWORD current_speed;
+
+ thrust_increment = WORLD_TO_VELOCITY (thrust_increment);
+ GetCurrentVelocityComponents (VelocityPtr, &cur_delta_x, &cur_delta_y);
+ current_speed = VelocitySquared (cur_delta_x, cur_delta_y);
+ delta_x = cur_delta_x + COSINE (CurrentAngle, thrust_increment);
+ delta_y = cur_delta_y + SINE (CurrentAngle, thrust_increment);
+ desired_speed = VelocitySquared (delta_x, delta_y);
+ max_speed = VelocitySquared (WORLD_TO_VELOCITY (max_thrust), 0);
+
+ if (desired_speed <= max_speed)
+ { // normal acceleration
+ SetVelocityComponents (VelocityPtr, delta_x, delta_y);
+ }
+ else if (((StarShipPtr->cur_status_flags & SHIP_IN_GRAVITY_WELL)
+ && desired_speed <= MAX_ALLOWED_SPEED_SQR)
+ || desired_speed < current_speed)
+ { // acceleration in a gravity well within max allowed
+ // deceleration after gravity whip
+ SetVelocityComponents (VelocityPtr, delta_x, delta_y);
+ return (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ }
+ else if (TravelAngle == CurrentAngle)
+ { // normal max acceleration, same vector
+ if (current_speed <= max_speed)
+ SetVelocityVector (VelocityPtr, max_thrust,
+ StarShipPtr->ShipFacing);
+ return (SHIP_AT_MAX_SPEED);
+ }
+ else
+ { // maxed-out acceleration at an angle to current travel vector
+ // thrusting at an angle while at max velocity only changes
+ // the travel vector, but does not really change the velocity
+
+ VELOCITY_DESC v = *VelocityPtr;
+
+ DeltaVelocityComponents (&v,
+ COSINE (CurrentAngle, thrust_increment >> 1)
+ - COSINE (TravelAngle, thrust_increment),
+ SINE (CurrentAngle, thrust_increment >> 1)
+ - SINE (TravelAngle, thrust_increment));
+ GetCurrentVelocityComponents (&v, &cur_delta_x, &cur_delta_y);
+ desired_speed = VelocitySquared (cur_delta_x, cur_delta_y);
+ if (desired_speed > max_speed)
+ {
+ if (desired_speed < current_speed)
+ *VelocityPtr = v;
+ return (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ }
+
+ *VelocityPtr = v;
+ }
+
+ return (0);
+ }
+}
+
+void
+ship_preprocess (ELEMENT *ElementPtr)
+{
+ STATUS_FLAGS cur_status_flags;
+ STARSHIP *StarShipPtr;
+ RACE_DESC *RDPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ RDPtr = StarShipPtr->RaceDescPtr;
+
+ cur_status_flags =
+ StarShipPtr->cur_status_flags
+ & ~(LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+ if (!(ElementPtr->state_flags & APPEARING))
+ {
+ cur_status_flags |= StarShipPtr->ship_input_state
+ & (LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+ }
+ else
+ { // Preprocessing for the first time
+ ElementPtr->crew_level = RDPtr->ship_info.crew_level;
+
+ if (ElementPtr->playerNr == NPC_PLAYER_NUM
+ && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ { // Sa-Matra
+ STAMP s;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ s.origin.x = s.origin.y = 0;
+ s.frame = RDPtr->ship_data.captain_control.background;
+ DrawStamp (&s);
+ DestroyDrawable (ReleaseDrawable (s.frame));
+ RDPtr->ship_data.captain_control.background = 0;
+ SetContext (OldContext);
+ }
+ else if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ {
+ CONTEXT OldContext;
+
+ InitShipStatus (&RDPtr->ship_info, StarShipPtr, NULL);
+ OldContext = SetContext (StatusContext);
+ DrawCaptainsWindow (StarShipPtr);
+ SetContext (OldContext);
+ if (RDPtr->preprocess_func)
+ (*RDPtr->preprocess_func) (ElementPtr);
+
+ // XXX: Hack: Pkunk sets hTarget!=0 when it reincarnates. In that
+ // case there is no ship_transition() but a Phoenix transition
+ // instead.
+ if (ElementPtr->hTarget == 0)
+ {
+ ship_transition (ElementPtr);
+ }
+ else
+ {
+ ElementPtr->hTarget = 0;
+ if (!PLRPlaying ((MUSIC_REF)~0) && OpponentAlive (StarShipPtr))
+ BattleSong (TRUE);
+ }
+ return;
+ }
+ else
+ {
+ ElementPtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ ElementPtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ ElementPtr->next.location = ElementPtr->current.location;
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+
+ if (hyper_transition (ElementPtr))
+ return;
+ }
+ }
+ StarShipPtr->cur_status_flags = cur_status_flags;
+
+ if (StarShipPtr->energy_counter)
+ --StarShipPtr->energy_counter;
+ else if (RDPtr->ship_info.energy_level < (BYTE)RDPtr->ship_info.max_energy
+ || (SBYTE)RDPtr->characteristics.energy_regeneration < 0)
+ DeltaEnergy (ElementPtr,
+ (SBYTE)RDPtr->characteristics.energy_regeneration);
+
+ if (RDPtr->preprocess_func)
+ {
+ (*RDPtr->preprocess_func) (ElementPtr);
+ cur_status_flags = StarShipPtr->cur_status_flags;
+ }
+
+ if (ElementPtr->turn_wait)
+ --ElementPtr->turn_wait;
+ else if (cur_status_flags & (LEFT | RIGHT))
+ {
+ if (cur_status_flags & LEFT)
+ StarShipPtr->ShipFacing =
+ NORMALIZE_FACING (StarShipPtr->ShipFacing - 1);
+ else
+ StarShipPtr->ShipFacing =
+ NORMALIZE_FACING (StarShipPtr->ShipFacing + 1);
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->next.image.frame,
+ StarShipPtr->ShipFacing);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = RDPtr->characteristics.turn_wait;
+ }
+
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else if (cur_status_flags & THRUST)
+ {
+ STATUS_FLAGS thrust_status;
+
+ thrust_status = inertial_thrust (ElementPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED
+ | SHIP_BEYOND_MAX_SPEED
+ | SHIP_IN_GRAVITY_WELL);
+ StarShipPtr->cur_status_flags |= thrust_status;
+
+ ElementPtr->thrust_wait = RDPtr->characteristics.thrust_wait;
+
+ if (!OBJECT_CLOAKED (ElementPtr)
+ && LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ {
+ spawn_ion_trail (ElementPtr);
+ }
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ PreProcessStatus (ElementPtr);
+}
+
+void
+ship_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ RACE_DESC *RDPtr;
+
+ if (ElementPtr->crew_level == 0)
+ return;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ RDPtr = StarShipPtr->RaceDescPtr;
+
+ if (StarShipPtr->weapon_counter)
+ --StarShipPtr->weapon_counter;
+ else if ((StarShipPtr->cur_status_flags
+ & WEAPON) && DeltaEnergy (ElementPtr,
+ -RDPtr->characteristics.weapon_energy_cost))
+ {
+ COUNT num_weapons;
+ HELEMENT Weapon[6];
+
+ num_weapons = (*RDPtr->init_weapon_func) (ElementPtr, Weapon);
+
+ if (num_weapons)
+ {
+ HELEMENT *WeaponPtr;
+ STARSHIP *StarShipPtr;
+ BOOLEAN played_sfx = FALSE;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ WeaponPtr = &Weapon[0];
+ do
+ {
+ HELEMENT w;
+ w = *WeaponPtr++;
+ if (w)
+ {
+ ELEMENT *EPtr;
+
+ LockElement (w, &EPtr);
+ SetElementStarShip (EPtr, StarShipPtr);
+ if (!played_sfx)
+ {
+ ProcessSound (RDPtr->ship_data.ship_sounds, EPtr);
+ played_sfx = TRUE;
+ }
+ UnlockElement (w);
+
+ PutElement (w);
+ }
+ } while (--num_weapons);
+
+ if (!played_sfx)
+ ProcessSound (RDPtr->ship_data.ship_sounds, ElementPtr);
+ }
+
+ StarShipPtr->weapon_counter =
+ RDPtr->characteristics.weapon_wait;
+ }
+
+ if (StarShipPtr->special_counter)
+ --StarShipPtr->special_counter;
+
+ if (RDPtr->postprocess_func)
+ (*RDPtr->postprocess_func) (ElementPtr);
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER)
+ PostProcessStatus (ElementPtr);
+}
+
+void
+collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (!(ElementPtr1->state_flags & FINITE_LIFE))
+ {
+ ElementPtr0->state_flags |= COLLISION;
+ if (GRAVITY_MASS (ElementPtr1->mass_points))
+ {
+ // Collision with a planet.
+ SIZE damage;
+
+ damage = ElementPtr0->hit_points >> 2;
+ if (damage == 0)
+ damage = 1;
+ do_damage (ElementPtr0, damage);
+
+ damage = TARGET_DAMAGED_FOR_1_PT + (damage >> 1);
+ if (damage > TARGET_DAMAGED_FOR_6_PLUS_PT)
+ damage = TARGET_DAMAGED_FOR_6_PLUS_PT;
+ ProcessSound (SetAbsSoundIndex (GameSounds, damage), ElementPtr0);
+ }
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static BOOLEAN
+spawn_ship (STARSHIP *StarShipPtr)
+{
+ HELEMENT hShip;
+ RACE_DESC *RDPtr;
+
+ RDPtr = load_ship (StarShipPtr->SpeciesID, TRUE);
+ if (!RDPtr)
+ return FALSE;
+
+ StarShipPtr->RaceDescPtr = RDPtr;
+
+ StarShipPtr->ship_input_state = 0;
+ StarShipPtr->cur_status_flags = 0;
+ StarShipPtr->old_status_flags = 0;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER
+ || LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ {
+ if (StarShipPtr->crew_level == 0)
+ {
+ // SIS, already handled from sis_ship.c.
+ // RDPtr->ship_info.crew_level = GLOBAL_SIS (CrewEnlisted);
+ }
+ else
+ RDPtr->ship_info.crew_level = StarShipPtr->crew_level;
+
+ if (RDPtr->ship_info.crew_level > RDPtr->ship_info.max_crew)
+ RDPtr->ship_info.crew_level = RDPtr->ship_info.max_crew;
+ }
+
+ StarShipPtr->energy_counter = 0;
+ StarShipPtr->weapon_counter = 0;
+ StarShipPtr->special_counter = 0;
+
+ hShip = StarShipPtr->hShip;
+ if (hShip == 0)
+ {
+ hShip = AllocElement ();
+ if (hShip != 0)
+ InsertElement (hShip, GetHeadElement ());
+ }
+
+ StarShipPtr->hShip = hShip;
+ if (StarShipPtr->hShip != 0)
+ {
+ // Construct an ELEMENT for the STARSHIP
+ ELEMENT *ShipElementPtr;
+
+ LockElement (hShip, &ShipElementPtr);
+
+ ShipElementPtr->playerNr = StarShipPtr->playerNr;
+ ShipElementPtr->crew_level = 0;
+ ShipElementPtr->mass_points = RDPtr->characteristics.ship_mass;
+ ShipElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR;
+ ShipElementPtr->turn_wait = 0;
+ ShipElementPtr->thrust_wait = 0;
+ ShipElementPtr->life_span = NORMAL_LIFE;
+ ShipElementPtr->colorCycleIndex = 0;
+
+ SetPrimType (&DisplayArray[ShipElementPtr->PrimIndex], STAMP_PRIM);
+ ShipElementPtr->current.image.farray = RDPtr->ship_data.ship;
+
+ if (ShipElementPtr->playerNr == NPC_PLAYER_NUM
+ && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
+ {
+ // This is the Sa-Matra
+ StarShipPtr->ShipFacing = 0;
+ ShipElementPtr->current.image.frame =
+ SetAbsFrameIndex (RDPtr->ship_data.ship[0],
+ StarShipPtr->ShipFacing);
+ ShipElementPtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ ShipElementPtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ ++ShipElementPtr->life_span;
+ }
+ else
+ {
+ StarShipPtr->ShipFacing = NORMALIZE_FACING (TFB_Random ());
+ if (inHQSpace ())
+ { // Only one ship is ever spawned in HyperSpace -- flagship
+ COUNT facing = GLOBAL (ShipFacing);
+ // XXX: Solar system reentry test depends on ShipFacing != 0
+ if (facing > 0)
+ --facing;
+
+ // XXX: This appears to set the facing to a random value
+ // for when the ship returns from an encounter back
+ // to HyperSpace. However, it is overwritten later
+ // in sis.c. See also r1614.
+ //GLOBAL (ShipFacing) = StarShipPtr->ShipFacing + 1;
+ StarShipPtr->ShipFacing = facing;
+ }
+ ShipElementPtr->current.image.frame =
+ SetAbsFrameIndex (RDPtr->ship_data.ship[0],
+ StarShipPtr->ShipFacing);
+ do
+ {
+ ShipElementPtr->current.location.x =
+ WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
+ ShipElementPtr->current.location.y =
+ WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
+ } while (CalculateGravity (ShipElementPtr)
+ || TimeSpaceMatterConflict (ShipElementPtr));
+ }
+
+ ShipElementPtr->preprocess_func = ship_preprocess;
+ ShipElementPtr->postprocess_func = ship_postprocess;
+ ShipElementPtr->death_func = ship_death;
+ ShipElementPtr->collision_func = collision;
+ ZeroVelocityComponents (&ShipElementPtr->velocity);
+
+ SetElementStarShip (ShipElementPtr, StarShipPtr);
+ ShipElementPtr->hTarget = 0;
+
+ UnlockElement (hShip);
+ }
+
+ return (hShip != 0);
+}
+
+// Select a new ship and spawn it.
+BOOLEAN
+GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side)
+{
+ HSTARSHIP hBattleShip;
+
+ hBattleShip = GetEncounterStarShip (LastStarShipPtr, which_side);
+ if (hBattleShip)
+ {
+ STARSHIP *StarShipPtr;
+
+ StarShipPtr = LockStarShip (&race_q[which_side], hBattleShip);
+ if (LastStarShipPtr)
+ {
+ if (StarShipPtr == LastStarShipPtr)
+ {
+ // Ship has been recycled (on infinite ship worlds).
+ LastStarShipPtr = 0;
+ }
+ else
+ StarShipPtr->hShip = LastStarShipPtr->hShip;
+ }
+
+ if (!spawn_ship (StarShipPtr))
+ {
+ UnlockStarShip (&race_q[which_side], hBattleShip);
+ return (FALSE);
+ }
+ UnlockStarShip (&race_q[which_side], hBattleShip);
+ }
+
+ if (LastStarShipPtr)
+ LastStarShipPtr->hShip = 0;
+
+ return (hBattleShip != 0);
+}
+
+BOOLEAN
+GetInitialStarShips (void)
+{
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ {
+ HSTARSHIP ships[NUM_PLAYERS];
+ COUNT i;
+
+ if (!GetInitialMeleeStarShips (ships))
+ return FALSE;
+
+ for (i = 0; i < NUM_PLAYERS; i++)
+ {
+ STARSHIP *StarShipPtr;
+ COUNT playerI = GetPlayerOrder (i);
+
+ StarShipPtr = LockStarShip (&race_q[playerI], ships[playerI]);
+ if (!spawn_ship (StarShipPtr))
+ {
+ UnlockStarShip (&race_q[playerI], ships[playerI]);
+ return FALSE;
+ }
+ UnlockStarShip (&race_q[playerI], ships[playerI]);
+ }
+ return TRUE;
+ }
+ else
+ {
+ int i;
+
+ for (i = NUM_PLAYERS; i > 0; --i)
+ {
+ if (!GetNextStarShip (NULL, i - 1))
+ return FALSE;
+ }
+ return TRUE;
+ }
+}
+
diff --git a/src/uqm/ship.h b/src/uqm/ship.h
new file mode 100644
index 0000000..8f0c72c
--- /dev/null
+++ b/src/uqm/ship.h
@@ -0,0 +1,43 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SHIP_H_INCL_
+#define UQM_SHIP_H_INCL_
+
+#include "libs/compiler.h"
+#include "races.h"
+#include "element.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern BOOLEAN GetNextStarShip (STARSHIP *LastStarShipPtr, COUNT which_side);
+extern BOOLEAN GetInitialStarShips (void);
+
+extern void animation_preprocess (ELEMENT *ElementPtr);
+extern void ship_preprocess (ELEMENT *ElementPtr);
+extern void ship_postprocess (ELEMENT *ElementPtr);
+extern void collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1);
+
+extern STATUS_FLAGS inertial_thrust (ELEMENT *ElementPtr);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SHIP_H_INCL_ */
diff --git a/src/uqm/shipcont.h b/src/uqm/shipcont.h
new file mode 100644
index 0000000..e265e7d
--- /dev/null
+++ b/src/uqm/shipcont.h
@@ -0,0 +1,44 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_SHIPCONT_H_
+#define UQM_SHIPCONT_H_
+
+#include "menustat.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define FIELD_WIDTH (STATUS_WIDTH - 5)
+
+extern void CargoMenu (void);
+extern BOOLEAN RosterMenu (void);
+extern BOOLEAN DevicesMenu (void);
+extern BOOLEAN StarMap (void);
+
+extern void DrawCargoStrings (BYTE OldElement, BYTE NewElement);
+extern void ShowRemainingCapacity (void);
+
+extern SIZE InventoryDevices (BYTE *pDeviceMap, COUNT Size);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SHIPCONT_H_ */
diff --git a/src/uqm/ships/Makeinfo b/src/uqm/ships/Makeinfo
new file mode 100644
index 0000000..e77d2db
--- /dev/null
+++ b/src/uqm/ships/Makeinfo
@@ -0,0 +1,5 @@
+uqm_SUBDIRS="androsyn arilou blackurq chenjesu chmmr druuge human ilwrath
+ lastbat melnorme mmrnmhrm mycon orz pkunk probe shofixti sis_ship
+ slylandr spathi supox syreen thradd umgah urquan utwig vux yehat
+ zoqfot"
+uqm_HFILES="ship.h"
diff --git a/src/uqm/ships/androsyn/Makeinfo b/src/uqm/ships/androsyn/Makeinfo
new file mode 100644
index 0000000..2f2f511
--- /dev/null
+++ b/src/uqm/ships/androsyn/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="androsyn.c"
+uqm_HFILES="androsyn.h icode.h resinst.h"
diff --git a/src/uqm/ships/androsyn/androsyn.c b/src/uqm/ships/androsyn/androsyn.c
new file mode 100644
index 0000000..1139d8d
--- /dev/null
+++ b/src/uqm/ships/androsyn/androsyn.c
@@ -0,0 +1,528 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "androsyn.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 24
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 8
+#define MAX_THRUST 24
+#define THRUST_INCREMENT 3
+#define TURN_WAIT 4
+#define THRUST_WAIT 0
+#define SHIP_MASS 6
+
+// Bubbles
+#define WEAPON_ENERGY_COST 3
+#define WEAPON_WAIT 0
+#define ANDROSYNTH_OFFSET 14
+#define MISSILE_OFFSET 3
+#define MISSILE_SPEED DISPLAY_TO_WORLD (8)
+#define MISSILE_LIFE 200
+#define MISSILE_HITS 3
+#define MISSILE_DAMAGE 2
+#define TRACK_WAIT 2
+
+// Blazer
+#define SPECIAL_ENERGY_COST 2
+#define BLAZER_DEGENERATION (-1)
+#define SPECIAL_WAIT 0
+#define BLAZER_OFFSET 10
+#define BLAZER_THRUST 60
+#define BLAZER_TURN_WAIT 1
+#define BLAZER_DAMAGE 3
+#define BLAZER_MASS 1
+
+static RACE_DESC androsynth_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_WEAPON,
+ 15, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ ANDROSYNTH_RACE_STRINGS,
+ ANDROSYNTH_ICON_MASK_PMAP_ANIM,
+ ANDROSYNTH_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ INFINITE_RADIUS, /* Initial sphere of influence radius */
+ // XXX: Why infinite radius? Bug?
+ { /* Known location (center of SoI) */
+ MAX_X_UNIVERSE >> 1, MAX_Y_UNIVERSE >> 1,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ ANDROSYNTH_BIG_MASK_PMAP_ANIM,
+ ANDROSYNTH_MED_MASK_PMAP_ANIM,
+ ANDROSYNTH_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BUBBLE_BIG_MASK_PMAP_ANIM,
+ BUBBLE_MED_MASK_PMAP_ANIM,
+ BUBBLE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BLAZER_BIG_MASK_PMAP_ANIM,
+ BLAZER_MED_MASK_PMAP_ANIM,
+ BLAZER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ ANDROSYNTH_CAPT_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ ANDROSYNTH_VICTORY_SONG,
+ ANDROSYNTH_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ LONG_RANGE_WEAPON >> 2,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+
+// Private per-instance ship data
+typedef struct
+{
+ ElementCollisionFunc* collision_func;
+} ANDROSYNTH_DATA;
+
+// Local typedef
+typedef ANDROSYNTH_DATA CustomShipData_t;
+
+// Retrieve race-specific ship data from a race desc
+static CustomShipData_t *
+GetCustomShipData (RACE_DESC *pRaceDesc)
+{
+ return pRaceDesc->data;
+}
+
+// Set the race-specific data in a race desc
+// (Re)Allocates its own storage for the data.
+static void
+SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data)
+{
+ if (pRaceDesc->data == data)
+ return; // no-op
+
+ if (pRaceDesc->data) // Out with the old
+ {
+ HFree (pRaceDesc->data);
+ pRaceDesc->data = NULL;
+ }
+
+ if (data) // In with the new
+ {
+ CustomShipData_t* newData = HMalloc (sizeof (*data));
+ *newData = *data;
+ pRaceDesc->data = newData;
+ }
+}
+
+static void
+blazer_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ BYTE old_offs;
+ COUNT old_crew_level;
+ COUNT old_life;
+
+ old_crew_level = ElementPtr0->crew_level;
+ old_life = ElementPtr0->life_span;
+ old_offs = ElementPtr0->blast_offset;
+ ElementPtr0->blast_offset = BLAZER_OFFSET;
+ ElementPtr0->mass_points = BLAZER_DAMAGE;
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->mass_points = BLAZER_MASS;
+ ElementPtr0->blast_offset = old_offs;
+ ElementPtr0->life_span = old_life;
+ ElementPtr0->crew_level = old_crew_level;
+
+ ElementPtr0->state_flags &= ~(DISAPPEARING | NONSOLID);
+ collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+}
+
+static void
+bubble_preprocess (ELEMENT *ElementPtr)
+{
+ BYTE thrust_wait, turn_wait;
+
+ thrust_wait = HINIBBLE (ElementPtr->turn_wait);
+ turn_wait = LONIBBLE (ElementPtr->turn_wait);
+ if (thrust_wait > 0)
+ --thrust_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ thrust_wait = (BYTE)((COUNT)TFB_Random () & 3);
+ }
+
+ if (turn_wait > 0)
+ --turn_wait;
+ else
+ {
+ COUNT facing;
+ SIZE delta_facing;
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)));
+ if ((delta_facing = TrackShip (ElementPtr, &facing)) == -1)
+ facing = (COUNT)TFB_Random ();
+ else if (delta_facing <= ANGLE_TO_FACING (HALF_CIRCLE))
+ facing += (COUNT)TFB_Random () & (ANGLE_TO_FACING (HALF_CIRCLE) - 1);
+ else
+ facing -= (COUNT)TFB_Random () & (ANGLE_TO_FACING (HALF_CIRCLE) - 1);
+ SetVelocityVector (&ElementPtr->velocity,
+ MISSILE_SPEED, facing);
+ turn_wait = TRACK_WAIT;
+ }
+
+ ElementPtr->turn_wait = MAKE_BYTE (turn_wait, thrust_wait);
+}
+
+static COUNT
+initialize_bubble (ELEMENT *ShipPtr, HELEMENT BubbleArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = ANDROSYNTH_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = bubble_preprocess;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ BubbleArray[0] = initialize_missile (&MissileBlock);
+
+ if (BubbleArray[0])
+ {
+ ELEMENT *BubblePtr;
+
+ LockElement (BubbleArray[0], &BubblePtr);
+ BubblePtr->turn_wait = 0;
+ UnlockElement (BubbleArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+androsynth_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ /* in blazer form */
+ if (ShipPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
+ {
+ ObjectsOfConcern[CREW_OBJECT_INDEX].ObjectPtr = 0;
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE)
+ {
+ if ((lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE)
+ && !(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
+ lpEvalDesc->MoveState = AVOID;
+ else
+ lpEvalDesc->ObjectPtr = 0;
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+ }
+ else
+ {
+ STARSHIP *pEnemyStarShip = NULL;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &pEnemyStarShip);
+ if (lpEvalDesc->which_turn <= 16
+ && (StarShipPtr->special_counter > 0
+ || StarShipPtr->RaceDescPtr->ship_info.energy_level < MAX_ENERGY / 3
+ || ((WEAPON_RANGE (&pEnemyStarShip->RaceDescPtr->cyborg_control) <= CLOSE_RANGE_WEAPON
+ && lpEvalDesc->ObjectPtr->crew_level > BLAZER_DAMAGE)
+ || (lpEvalDesc->ObjectPtr->crew_level > (BLAZER_DAMAGE * 3)
+ && MANEUVERABILITY (&pEnemyStarShip->RaceDescPtr->cyborg_control) > SLOW_SHIP))))
+ lpEvalDesc->MoveState = ENTICE;
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ if (StarShipPtr->special_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if ((ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 4)
+ || (lpEvalDesc->ObjectPtr
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >= MAX_ENERGY / 3
+ && (WEAPON_RANGE (&pEnemyStarShip->RaceDescPtr->cyborg_control) >=
+ WEAPON_RANGE (&StarShipPtr->RaceDescPtr->cyborg_control) << 1
+ || (lpEvalDesc->which_turn < 16
+ && (WEAPON_RANGE (&pEnemyStarShip->RaceDescPtr->cyborg_control) > CLOSE_RANGE_WEAPON
+ || lpEvalDesc->ObjectPtr->crew_level <= BLAZER_DAMAGE)
+ && (lpEvalDesc->ObjectPtr->crew_level <= (BLAZER_DAMAGE * 3)
+ || MANEUVERABILITY (&pEnemyStarShip->RaceDescPtr->cyborg_control) <=
+ SLOW_SHIP)))))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+
+ if (!(StarShipPtr->ship_input_state & SPECIAL)
+ && StarShipPtr->weapon_counter == 0
+ && lpEvalDesc->ObjectPtr)
+ {
+ if (lpEvalDesc->which_turn <= 4)
+ StarShipPtr->ship_input_state |= WEAPON;
+ else if (lpEvalDesc->MoveState != PURSUE
+ && lpEvalDesc->which_turn <= 12)
+ {
+ COUNT travel_facing, direction_facing;
+ SIZE delta_x, delta_y,
+ ship_delta_x, ship_delta_y,
+ other_delta_x, other_delta_y;
+
+ GetCurrentVelocityComponents (&ShipPtr->velocity,
+ &ship_delta_x, &ship_delta_y);
+ GetCurrentVelocityComponents (&lpEvalDesc->ObjectPtr->velocity,
+ &other_delta_x, &other_delta_y);
+ delta_x = ship_delta_x - other_delta_x;
+ delta_y = ship_delta_y - other_delta_y;
+ travel_facing = ARCTAN (delta_x, delta_y);
+
+ delta_x =
+ lpEvalDesc->ObjectPtr->next.location.x -
+ ShipPtr->next.location.x;
+ delta_y =
+ lpEvalDesc->ObjectPtr->next.location.y -
+ ShipPtr->next.location.y;
+ direction_facing = ARCTAN (delta_x, delta_y);
+
+ if (NORMALIZE_ANGLE (travel_facing
+ - direction_facing + OCTANT) <= QUADRANT)
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+ }
+ }
+}
+
+static void
+androsynth_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ /* take care of blazer effect */
+ if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
+ {
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ || StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
+ {
+ StarShipPtr->RaceDescPtr->characteristics.energy_regeneration =
+ (BYTE)BLAZER_DEGENERATION;
+ StarShipPtr->energy_counter = ENERGY_WAIT;
+
+ if (StarShipPtr->cur_status_flags & SPECIAL)
+ {
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
+ ElementPtr); /* COMET_ON */
+ ElementPtr->turn_wait = 0;
+ ElementPtr->thrust_wait = 0;
+ StarShipPtr->RaceDescPtr->characteristics.special_wait =
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait;
+ ElementPtr->mass_points = BLAZER_MASS;
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait
+ = BLAZER_TURN_WAIT;
+
+ /* Save the current collision func because we were not the
+ * ones who set it */
+ {
+ const ANDROSYNTH_DATA shipData = { ElementPtr->collision_func };
+ SetCustomShipData (StarShipPtr->RaceDescPtr, &shipData);
+ ElementPtr->collision_func = blazer_collision;
+ }
+ }
+ }
+
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
+ /* if blazer wasn't able to change back into ship
+ * give it a little more juice to try to get out
+ * of its predicament.
+ */
+ {
+ DeltaEnergy (ElementPtr, -BLAZER_DEGENERATION);
+ StarShipPtr->energy_counter = 1;
+ }
+ }
+}
+
+static void
+androsynth_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ STATUS_FLAGS cur_status_flags;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ cur_status_flags = StarShipPtr->cur_status_flags;
+ if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
+ {
+ if (cur_status_flags & SPECIAL)
+ {
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level < SPECIAL_ENERGY_COST)
+ DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST); /* so text will flash */
+ else
+ {
+ cur_status_flags &= ~WEAPON;
+
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0],
+ ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+ }
+ }
+ else
+ {
+ cur_status_flags &= ~(THRUST | WEAPON | SPECIAL);
+
+ /* protection against vux */
+ if (StarShipPtr->RaceDescPtr->characteristics.turn_wait > BLAZER_TURN_WAIT)
+ {
+ StarShipPtr->RaceDescPtr->characteristics.special_wait +=
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait
+ - BLAZER_TURN_WAIT;
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait = BLAZER_TURN_WAIT;
+ }
+
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ cur_status_flags &= ~(LEFT | RIGHT
+ | SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ StarShipPtr->RaceDescPtr->characteristics.energy_regeneration = ENERGY_REGENERATION;
+ ElementPtr->mass_points = SHIP_MASS;
+ ElementPtr->collision_func =
+ GetCustomShipData (StarShipPtr->RaceDescPtr)->collision_func;
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (StarShipPtr->RaceDescPtr->ship_data.ship[0],
+ ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+ else
+ {
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else
+ {
+ COUNT facing;
+
+ facing = StarShipPtr->ShipFacing;
+ if (ElementPtr->turn_wait == 0
+ && (cur_status_flags & (LEFT | RIGHT)))
+ {
+ if (cur_status_flags & LEFT)
+ --facing;
+ else
+ ++facing;
+ }
+
+ SetVelocityVector (&ElementPtr->velocity,
+ BLAZER_THRUST, NORMALIZE_FACING (facing));
+ cur_status_flags |= SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED;
+ }
+ }
+ }
+ StarShipPtr->cur_status_flags = cur_status_flags;
+}
+
+static void
+uninit_androsynth (RACE_DESC *pRaceDesc)
+{
+ SetCustomShipData (pRaceDesc, NULL);
+}
+
+
+RACE_DESC*
+init_androsynth (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ androsynth_desc.uninit_func = uninit_androsynth;
+ androsynth_desc.preprocess_func = androsynth_preprocess;
+ androsynth_desc.postprocess_func = androsynth_postprocess;
+ androsynth_desc.init_weapon_func = initialize_bubble;
+ androsynth_desc.cyborg_control.intelligence_func = androsynth_intelligence;
+
+ RaceDescPtr = &androsynth_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/androsyn/androsyn.h b/src/uqm/ships/androsyn/androsyn.h
new file mode 100644
index 0000000..43fe88d
--- /dev/null
+++ b/src/uqm/ships/androsyn/androsyn.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ANDROSYN_H
+#define ANDROSYN_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_androsynth (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ANDROSYN_H */
+
diff --git a/src/uqm/ships/androsyn/icode.h b/src/uqm/ships/androsyn/icode.h
new file mode 100644
index 0000000..67f053a
--- /dev/null
+++ b/src/uqm/ships/androsyn/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ANDROSYNTH_CODE "ship.androsynth.code"
diff --git a/src/uqm/ships/androsyn/resinst.h b/src/uqm/ships/androsyn/resinst.h
new file mode 100644
index 0000000..94b4a3f
--- /dev/null
+++ b/src/uqm/ships/androsyn/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ANDROSYNTH_BIG_MASK_PMAP_ANIM "ship.androsynth.graphics.guardian.large"
+#define ANDROSYNTH_CAPT_MASK_PMAP_ANIM "ship.androsynth.graphics.captain"
+#define ANDROSYNTH_ICON_MASK_PMAP_ANIM "ship.androsynth.icons"
+#define ANDROSYNTH_MED_MASK_PMAP_ANIM "ship.androsynth.graphics.guardian.medium"
+#define ANDROSYNTH_MICON_MASK_PMAP_ANIM "ship.androsynth.meleeicons"
+#define ANDROSYNTH_RACE_STRINGS "ship.androsynth.text"
+#define ANDROSYNTH_SHIP_SOUNDS "ship.androsynth.sounds"
+#define ANDROSYNTH_SML_MASK_PMAP_ANIM "ship.androsynth.graphics.guardian.small"
+#define ANDROSYNTH_VICTORY_SONG "ship.androsynth.ditty"
+#define BLAZER_BIG_MASK_PMAP_ANIM "ship.androsynth.graphics.blazer.large"
+#define BLAZER_MED_MASK_PMAP_ANIM "ship.androsynth.graphics.blazer.medium"
+#define BLAZER_SML_MASK_PMAP_ANIM "ship.androsynth.graphics.blazer.small"
+#define BUBBLE_BIG_MASK_PMAP_ANIM "ship.androsynth.graphics.bubble.large"
+#define BUBBLE_MED_MASK_PMAP_ANIM "ship.androsynth.graphics.bubble.medium"
+#define BUBBLE_SML_MASK_PMAP_ANIM "ship.androsynth.graphics.bubble.small"
diff --git a/src/uqm/ships/arilou/Makeinfo b/src/uqm/ships/arilou/Makeinfo
new file mode 100644
index 0000000..f132bdd
--- /dev/null
+++ b/src/uqm/ships/arilou/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="arilou.c"
+uqm_HFILES="arilou.h icode.h resinst.h"
diff --git a/src/uqm/ships/arilou/arilou.c b/src/uqm/ships/arilou/arilou.c
new file mode 100644
index 0000000..78340a3
--- /dev/null
+++ b/src/uqm/ships/arilou/arilou.c
@@ -0,0 +1,303 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "arilou.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 6
+#define MAX_ENERGY 20
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 6
+#define MAX_THRUST /* DISPLAY_TO_WORLD (10) */ 40
+#define THRUST_INCREMENT MAX_THRUST
+#define THRUST_WAIT 0
+#define TURN_WAIT 0
+#define SHIP_MASS 1
+
+// Tracking Laser
+#define WEAPON_ENERGY_COST 2
+#define WEAPON_WAIT 1
+#define ARILOU_OFFSET 9
+#define LASER_RANGE DISPLAY_TO_WORLD (100 + ARILOU_OFFSET)
+
+// Teleporter
+#define SPECIAL_ENERGY_COST 3
+#define SPECIAL_WAIT 2
+#define HYPER_LIFE 5
+
+static RACE_DESC arilou_desc =
+{
+ { /* SHIP_INFO */
+ /* FIRES_FORE | */ IMMEDIATE_WEAPON,
+ 16, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ ARILOU_RACE_STRINGS,
+ ARILOU_ICON_MASK_PMAP_ANIM,
+ ARILOU_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 250 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 438, 6372,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ ARILOU_BIG_MASK_PMAP_ANIM,
+ ARILOU_MED_MASK_PMAP_ANIM,
+ ARILOU_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ WARP_BIG_MASK_PMAP_ANIM,
+ WARP_MED_MASK_PMAP_ANIM,
+ WARP_SML_MASK_PMAP_ANIM,
+ },
+ {
+ ARILOU_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ ARILOU_VICTORY_SONG,
+ ARILOU_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ LASER_RANGE >> 1,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT
+initialize_autoaim_laser (ELEMENT *ShipPtr, HELEMENT LaserArray[])
+{
+ COUNT orig_facing;
+ SIZE delta_facing;
+ STARSHIP *StarShipPtr;
+ LASER_BLOCK LaserBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ LaserBlock.face = orig_facing = StarShipPtr->ShipFacing;
+ if ((delta_facing = TrackShip (ShipPtr, &LaserBlock.face)) > 0)
+ LaserBlock.face = NORMALIZE_FACING (orig_facing + delta_facing);
+ ShipPtr->hTarget = 0;
+
+ LaserBlock.cx = ShipPtr->next.location.x;
+ LaserBlock.cy = ShipPtr->next.location.y;
+ LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = ARILOU_OFFSET;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E);
+ LaserArray[0] = initialize_laser (&LaserBlock);
+
+ return (1);
+}
+
+static void
+arilou_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ StarShipPtr->ship_input_state |= THRUST;
+
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = ENTICE;
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ if (StarShipPtr->special_counter == 0)
+ {
+ EVALUATE_DESC *lpEvalDesc;
+
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->which_turn <= 6)
+ {
+ BOOLEAN IsTrackingWeapon;
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (((EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & SEEKING_WEAPON) &&
+ lpEvalDesc->ObjectPtr->next.image.farray ==
+ EnemyStarShipPtr->RaceDescPtr->ship_data.weapon) ||
+ ((EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & SEEKING_SPECIAL) &&
+ lpEvalDesc->ObjectPtr->next.image.farray ==
+ EnemyStarShipPtr->RaceDescPtr->ship_data.special))
+ IsTrackingWeapon = TRUE;
+ else
+ IsTrackingWeapon = FALSE;
+
+ if (((lpEvalDesc->ObjectPtr->state_flags & PLAYER_SHIP) /* means IMMEDIATE WEAPON */
+ || (IsTrackingWeapon && (lpEvalDesc->which_turn == 1
+ || (lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))) /* FIGHTERS!!! */
+ || PlotIntercept (lpEvalDesc->ObjectPtr, ShipPtr, 3, 0))
+ && !(TFB_Random () & 3))
+ {
+ StarShipPtr->ship_input_state &= ~(LEFT | RIGHT | THRUST | WEAPON);
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ }
+ }
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level <= SPECIAL_ENERGY_COST << 1)
+ StarShipPtr->ship_input_state &= ~WEAPON;
+}
+
+static void
+arilou_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (!(ElementPtr->state_flags & NONSOLID))
+ {
+ if (ElementPtr->thrust_wait == 0)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED;
+ }
+
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ /* Special key is pressed; start teleport */
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | LEFT | RIGHT | THRUST | WEAPON);
+
+ ElementPtr->state_flags |= NONSOLID | FINITE_LIFE | CHANGING;
+ ElementPtr->life_span = HYPER_LIFE;
+
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ ElementPtr->next.image.frame =
+ StarShipPtr->RaceDescPtr->ship_data.special[0];
+
+ ProcessSound (SetAbsSoundIndex (
+ /* HYPERJUMP */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+ }
+ else if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
+ {
+ COUNT life_span;
+
+ StarShipPtr->cur_status_flags =
+ (StarShipPtr->cur_status_flags
+ & ~(LEFT | RIGHT | THRUST | WEAPON | SPECIAL))
+ | (StarShipPtr->old_status_flags
+ & (LEFT | RIGHT | THRUST | WEAPON | SPECIAL));
+ ++StarShipPtr->weapon_counter;
+ ++StarShipPtr->special_counter;
+ ++StarShipPtr->energy_counter;
+ ++ElementPtr->turn_wait;
+ ++ElementPtr->thrust_wait;
+
+ if ((life_span = ElementPtr->life_span) == NORMAL_LIFE)
+ {
+ /* Ending teleport */
+ ElementPtr->state_flags &= ~(NONSOLID | FINITE_LIFE);
+ ElementPtr->state_flags |= APPEARING;
+ ElementPtr->current.image.farray =
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ ElementPtr->current.image.frame =
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (StarShipPtr->RaceDescPtr->ship_data.ship[0],
+ StarShipPtr->ShipFacing);
+ InitIntersectStartPoint (ElementPtr);
+ }
+ else
+ {
+ /* Teleporting in progress */
+ --life_span;
+ if (life_span != 2)
+ {
+ if (life_span < 2)
+ ElementPtr->next.image.frame =
+ DecFrameIndex (ElementPtr->next.image.frame);
+ else
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->next.image.frame);
+ }
+ else
+ {
+ ElementPtr->next.location.x =
+ WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
+ ElementPtr->next.location.y =
+ WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
+ }
+ }
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+RACE_DESC*
+init_arilou (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ arilou_desc.preprocess_func = arilou_preprocess;
+ arilou_desc.init_weapon_func = initialize_autoaim_laser;
+ arilou_desc.cyborg_control.intelligence_func = arilou_intelligence;
+
+ RaceDescPtr = &arilou_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/arilou/arilou.h b/src/uqm/ships/arilou/arilou.h
new file mode 100644
index 0000000..8e65d58
--- /dev/null
+++ b/src/uqm/ships/arilou/arilou.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ARILOU_H
+#define ARILOU_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_arilou (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ARILOU_H */
+
diff --git a/src/uqm/ships/arilou/icode.h b/src/uqm/ships/arilou/icode.h
new file mode 100644
index 0000000..81d4fff
--- /dev/null
+++ b/src/uqm/ships/arilou/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ARILOU_CODE "ship.arilou.code"
diff --git a/src/uqm/ships/arilou/resinst.h b/src/uqm/ships/arilou/resinst.h
new file mode 100644
index 0000000..173e222
--- /dev/null
+++ b/src/uqm/ships/arilou/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ARILOU_BIG_MASK_PMAP_ANIM "ship.arilou.graphics.skiff.large"
+#define ARILOU_CAPTAIN_MASK_PMAP_ANIM "ship.arilou.graphics.captain"
+#define ARILOU_ICON_MASK_PMAP_ANIM "ship.arilou.icons"
+#define ARILOU_MED_MASK_PMAP_ANIM "ship.arilou.graphics.skiff.medium"
+#define ARILOU_MICON_MASK_PMAP_ANIM "ship.arilou.meleeicons"
+#define ARILOU_RACE_STRINGS "ship.arilou.text"
+#define ARILOU_SHIP_SOUNDS "ship.arilou.sounds"
+#define ARILOU_SML_MASK_PMAP_ANIM "ship.arilou.graphics.skiff.small"
+#define ARILOU_VICTORY_SONG "ship.arilou.ditty"
+#define WARP_BIG_MASK_PMAP_ANIM "ship.arilou.graphics.warp.large"
+#define WARP_MED_MASK_PMAP_ANIM "ship.arilou.graphics.warp.medium"
+#define WARP_SML_MASK_PMAP_ANIM "ship.arilou.graphics.warp.small"
diff --git a/src/uqm/ships/blackurq/Makeinfo b/src/uqm/ships/blackurq/Makeinfo
new file mode 100644
index 0000000..62ca12e
--- /dev/null
+++ b/src/uqm/ships/blackurq/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="blackurq.c"
+uqm_HFILES="blackurq.h icode.h resinst.h"
diff --git a/src/uqm/ships/blackurq/blackurq.c b/src/uqm/ships/blackurq/blackurq.c
new file mode 100644
index 0000000..286191d
--- /dev/null
+++ b/src/uqm/ships/blackurq/blackurq.c
@@ -0,0 +1,567 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "blackurq.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+
+// Core characteristics
+#define MAX_CREW MAX_CREW_SIZE
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST 30
+#define THRUST_INCREMENT 6
+#define TURN_WAIT 4
+#define THRUST_WAIT 6
+#define SHIP_MASS 10
+
+// Buzzsaw
+#define WEAPON_ENERGY_COST 6
+#define WEAPON_WAIT 6
+#define MISSILE_OFFSET 9
+#define KOHR_AH_OFFSET 28
+#define MISSILE_SPEED 64
+#define MISSILE_LIFE 64
+ /* actually, it's as long as you hold the button down.*/
+#define MISSILE_HITS 10
+#define MISSILE_DAMAGE 4
+#define SAW_RATE 0
+#define MAX_SAWS 8
+#define ACTIVATE_RANGE 224
+ /* Originally SPACE_WIDTH - the distance within which
+ * stationary sawblades will home */
+#define TRACK_WAIT 4
+#define FRAGMENT_SPEED MISSILE_SPEED
+#define FRAGMENT_LIFE 10
+#define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED)
+
+// F.R.I.E.D.
+#define SPECIAL_ENERGY_COST (MAX_ENERGY_SIZE / 2)
+#define SPECIAL_WAIT 9
+#define GAS_OFFSET 2
+#define GAS_SPEED 16
+#define GAS_RATE 2 /* Controls animation of the gas cloud decay - the decay
+ * animation advances one frame every GAS_RATE frames. */
+#define GAS_HITS 100
+#define GAS_DAMAGE 3
+#define GAS_ALT_DAMAGE 50
+#define NUM_GAS_CLOUDS 16
+
+static RACE_DESC black_urquan_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 30, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ KOHR_AH_RACE_STRINGS,
+ KOHR_AH_ICON_MASK_PMAP_ANIM,
+ KOHR_AH_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 2666 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 6000, 6250,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ KOHR_AH_BIG_MASK_PMAP_ANIM,
+ KOHR_AH_MED_MASK_PMAP_ANIM,
+ KOHR_AH_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BUZZSAW_BIG_MASK_PMAP_ANIM,
+ BUZZSAW_MED_MASK_PMAP_ANIM,
+ BUZZSAW_SML_MASK_PMAP_ANIM,
+ },
+ {
+ GAS_BIG_MASK_PMAP_ANIM,
+ GAS_MED_MASK_PMAP_ANIM,
+ GAS_SML_MASK_PMAP_ANIM,
+ },
+ {
+ KOHR_AH_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ KOHR_AH_VICTORY_SONG,
+ KOHR_AH_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+spin_preprocess (ELEMENT *ElementPtr)
+{
+ ELEMENT *ShipPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ if (ShipPtr->crew_level
+ && ++StarShipPtr->RaceDescPtr->characteristics.special_wait > MAX_SAWS)
+ {
+ ElementPtr->life_span = 1;
+ ElementPtr->state_flags |= DISAPPEARING;
+ }
+ else
+ {
+ ++ElementPtr->life_span;
+ if (ElementPtr->turn_wait)
+ --ElementPtr->turn_wait;
+ else
+ {
+#define LAST_SPIN_INDEX 1
+ if (GetFrameIndex (
+ ElementPtr->current.image.frame
+ ) < LAST_SPIN_INDEX)
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ else
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame, 0);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = SAW_RATE;
+ }
+ }
+ UnlockElement (StarShipPtr->hShip);
+}
+
+static void
+buzztrack_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else
+ {
+ COUNT facing = 0;
+
+ if (ElementPtr->hTarget == 0
+ && TrackShip (ElementPtr, &facing) < 0)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ }
+ else
+ {
+ SIZE delta_x, delta_y;
+ ELEMENT *eptr;
+
+ LockElement (ElementPtr->hTarget, &eptr);
+ delta_x = eptr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_y = eptr->current.location.y
+ - ElementPtr->current.location.y;
+ UnlockElement (ElementPtr->hTarget);
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+ facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x = WORLD_TO_DISPLAY (delta_x);
+ delta_y = WORLD_TO_DISPLAY (delta_y);
+ if (delta_x >= ACTIVATE_RANGE
+ || delta_y >= ACTIVATE_RANGE
+ || (DWORD)((UWORD)delta_x * delta_x)
+ + (DWORD)((UWORD)delta_y * delta_y) >=
+ (DWORD)ACTIVATE_RANGE * ACTIVATE_RANGE)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ }
+ else
+ {
+ ElementPtr->thrust_wait = TRACK_WAIT;
+ SetVelocityVector (&ElementPtr->velocity,
+ DISPLAY_TO_WORLD (2), facing);
+ }
+ }
+ }
+
+ spin_preprocess (ElementPtr);
+}
+
+static void
+decelerate_preprocess (ELEMENT *ElementPtr)
+{
+ SIZE dx, dy;
+
+ GetCurrentVelocityComponents (&ElementPtr->velocity, &dx, &dy);
+ dx /= 2;
+ dy /= 2;
+ SetVelocityComponents (&ElementPtr->velocity, dx, dy);
+ if (dx == 0 && dy == 0)
+ {
+ ElementPtr->preprocess_func = buzztrack_preprocess;
+ }
+
+ spin_preprocess (ElementPtr);
+}
+
+static void
+splinter_preprocess (ELEMENT *ElementPtr)
+{
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+}
+
+static void
+buzzsaw_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+
+ if (ElementPtr0->state_flags & DISAPPEARING)
+ {
+ ElementPtr0->state_flags &= ~DISAPPEARING;
+ ElementPtr0->state_flags |= NONSOLID | CHANGING;
+ ElementPtr0->life_span = 5;
+ ElementPtr0->next.image.frame =
+ SetAbsFrameIndex (ElementPtr0->current.image.frame, 2);
+
+ ElementPtr0->preprocess_func = splinter_preprocess;
+ }
+}
+
+static void
+buzzsaw_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (!(StarShipPtr->cur_status_flags & WEAPON))
+ {
+ ElementPtr->life_span >>= 1;
+ ElementPtr->preprocess_func = decelerate_preprocess;
+ }
+
+ spin_preprocess (ElementPtr);
+}
+
+static void
+buzzsaw_postprocess (ELEMENT *ElementPtr)
+{
+ HELEMENT hElement;
+
+ ElementPtr->postprocess_func = 0;
+ hElement = AllocElement ();
+ if (hElement)
+ {
+ COUNT primIndex;
+ ELEMENT *ListElementPtr;
+ STARSHIP *StarShipPtr;
+
+ LockElement (hElement, &ListElementPtr);
+ primIndex = ListElementPtr->PrimIndex;
+ *ListElementPtr = *ElementPtr;
+ ListElementPtr->PrimIndex = primIndex;
+ (GLOBAL (DisplayArray))[primIndex] =
+ (GLOBAL (DisplayArray))[ElementPtr->PrimIndex];
+ ListElementPtr->current = ListElementPtr->next;
+ InitIntersectStartPoint (ListElementPtr);
+ InitIntersectEndPoint (ListElementPtr);
+ ListElementPtr->state_flags = (ListElementPtr->state_flags
+ & ~(PRE_PROCESS | CHANGING | APPEARING))
+ | POST_PROCESS;
+ UnlockElement (hElement);
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LockElement (StarShipPtr->hShip, &ListElementPtr);
+ InsertElement (hElement, GetSuccElement (ListElementPtr));
+ UnlockElement (StarShipPtr->hShip);
+
+ ElementPtr->life_span = 0;
+ }
+}
+
+static COUNT
+initialize_buzzsaw (ELEMENT *ShipPtr, HELEMENT SawArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = KOHR_AH_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = buzzsaw_preprocess;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ SawArray[0] = initialize_missile (&MissileBlock);
+
+ if (SawArray[0])
+ {
+ ELEMENT *SawPtr;
+
+ LockElement (SawArray[0], &SawPtr);
+ SawPtr->turn_wait = SAW_RATE;
+ SawPtr->thrust_wait = 0;
+ SawPtr->postprocess_func = buzzsaw_postprocess;
+ SawPtr->collision_func = buzzsaw_collision;
+ UnlockElement (SawArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+black_urquan_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr
+ && lpEvalDesc->MoveState == ENTICE
+ && (lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT)
+ && lpEvalDesc->which_turn <= 8)
+ lpEvalDesc->MoveState = PURSUE;
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+
+ if (StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST
+ && lpEvalDesc->ObjectPtr
+ && lpEvalDesc->which_turn <= 8)
+ StarShipPtr->ship_input_state |= SPECIAL;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ HELEMENT h, hNext;
+ ELEMENT *BuzzSawPtr;
+
+ h = (StarShipPtr->old_status_flags & WEAPON) ?
+ GetSuccElement (ShipPtr) : (HELEMENT)0;
+ for (; h; h = hNext)
+ {
+ LockElement (h, &BuzzSawPtr);
+ hNext = GetSuccElement (BuzzSawPtr);
+ if (!(BuzzSawPtr->state_flags & NONSOLID)
+ && BuzzSawPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.weapon
+ && BuzzSawPtr->life_span > MISSILE_LIFE * 3 / 4
+ && elementsOfSamePlayer (BuzzSawPtr, ShipPtr))
+ {
+ {
+ //COUNT which_turn;
+
+ if (!PlotIntercept (BuzzSawPtr,
+ lpEvalDesc->ObjectPtr, BuzzSawPtr->life_span,
+ FRAGMENT_RANGE / 2))
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ else if (StarShipPtr->weapon_counter == 0)
+ StarShipPtr->ship_input_state |= WEAPON;
+
+ UnlockElement (h);
+ break;
+ }
+ hNext = 0;
+ }
+ UnlockElement (h);
+ }
+
+ if (h == 0)
+ {
+ if (StarShipPtr->old_status_flags & WEAPON)
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ else if (StarShipPtr->weapon_counter == 0
+ && ship_weapons (ShipPtr, lpEvalDesc->ObjectPtr, FRAGMENT_RANGE / 2))
+ StarShipPtr->ship_input_state |= WEAPON;
+
+ if (StarShipPtr->special_counter == 0
+ && !(StarShipPtr->ship_input_state & WEAPON)
+ && lpEvalDesc->which_turn <= 8
+ && (StarShipPtr->ship_input_state & (LEFT | RIGHT))
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
+ SPECIAL_ENERGY_COST)
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ }
+}
+
+static void
+gas_cloud_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = GAS_RATE;
+ }
+}
+
+static void
+gas_cloud_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ ElementPtr0->mass_points = GAS_DAMAGE;
+ else
+ ElementPtr0->mass_points = GAS_ALT_DAMAGE;
+
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+}
+
+static void
+spawn_gas_cloud (ELEMENT *ElementPtr)
+{
+ SIZE dx, dy;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = 20;
+ MissileBlock.speed = GAS_SPEED;
+ MissileBlock.hit_points = GAS_HITS;
+ MissileBlock.damage = GAS_DAMAGE;
+ MissileBlock.life =
+ GetFrameCount (MissileBlock.farray[0]) * (GAS_RATE + 1) - 1;
+ MissileBlock.preprocess_func = gas_cloud_preprocess;
+ MissileBlock.blast_offs = GAS_OFFSET;
+
+ GetCurrentVelocityComponents (&ElementPtr->velocity, &dx, &dy);
+ for (MissileBlock.face = 0;
+ MissileBlock.face < ANGLE_TO_FACING (FULL_CIRCLE);
+ MissileBlock.face +=
+ (ANGLE_TO_FACING (FULL_CIRCLE) / NUM_GAS_CLOUDS))
+ {
+ HELEMENT hGasCloud;
+
+ hGasCloud = initialize_missile (&MissileBlock);
+ if (hGasCloud)
+ {
+ ELEMENT *GasCloudPtr;
+
+ LockElement (hGasCloud, &GasCloudPtr);
+ SetElementStarShip (GasCloudPtr, StarShipPtr);
+ GasCloudPtr->hTarget = 0;
+ GasCloudPtr->turn_wait = GAS_RATE - 1;
+ GasCloudPtr->collision_func = gas_cloud_collision;
+ DeltaVelocityComponents (&GasCloudPtr->velocity, dx, dy);
+ UnlockElement (hGasCloud);
+ PutElement (hGasCloud);
+ }
+ }
+}
+
+static void
+black_urquan_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ spawn_gas_cloud (ElementPtr);
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+
+ StarShipPtr->special_counter = SPECIAL_WAIT;
+ }
+}
+
+static void
+black_urquan_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ /* no spinning disks */
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
+ if (StarShipPtr->weapon_counter == 0
+ && (StarShipPtr->cur_status_flags
+ & StarShipPtr->old_status_flags & WEAPON))
+ ++StarShipPtr->weapon_counter;
+}
+
+RACE_DESC*
+init_black_urquan (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ black_urquan_desc.preprocess_func = black_urquan_preprocess;
+ black_urquan_desc.postprocess_func = black_urquan_postprocess;
+ black_urquan_desc.init_weapon_func = initialize_buzzsaw;
+ black_urquan_desc.cyborg_control.intelligence_func = black_urquan_intelligence;
+
+ RaceDescPtr = &black_urquan_desc;
+
+ return (RaceDescPtr);
+}
diff --git a/src/uqm/ships/blackurq/blackurq.h b/src/uqm/ships/blackurq/blackurq.h
new file mode 100644
index 0000000..4b2b5d9
--- /dev/null
+++ b/src/uqm/ships/blackurq/blackurq.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef BLACKURQ_H
+#define BLACKURQ_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_black_urquan (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* BLACKURQ_H */
+
diff --git a/src/uqm/ships/blackurq/icode.h b/src/uqm/ships/blackurq/icode.h
new file mode 100644
index 0000000..99c0ca2
--- /dev/null
+++ b/src/uqm/ships/blackurq/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define KOHR_AH_CODE "ship.kohrah.code"
diff --git a/src/uqm/ships/blackurq/resinst.h b/src/uqm/ships/blackurq/resinst.h
new file mode 100644
index 0000000..840f519
--- /dev/null
+++ b/src/uqm/ships/blackurq/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define BUZZSAW_BIG_MASK_PMAP_ANIM "ship.kohrah.graphics.buzzsaw.large"
+#define BUZZSAW_MED_MASK_PMAP_ANIM "ship.kohrah.graphics.buzzsaw.medium"
+#define BUZZSAW_SML_MASK_PMAP_ANIM "ship.kohrah.graphics.buzzsaw.small"
+#define GAS_BIG_MASK_PMAP_ANIM "ship.kohrah.graphics.gas.large"
+#define GAS_MED_MASK_PMAP_ANIM "ship.kohrah.graphics.gas.medium"
+#define GAS_SML_MASK_PMAP_ANIM "ship.kohrah.graphics.gas.small"
+#define KOHR_AH_BIG_MASK_PMAP_ANIM "ship.kohrah.graphics.marauder.large"
+#define KOHR_AH_CAPTAIN_MASK_PMAP_ANIM "ship.kohrah.graphics.captain"
+#define KOHR_AH_ICON_MASK_PMAP_ANIM "ship.kohrah.icons"
+#define KOHR_AH_MED_MASK_PMAP_ANIM "ship.kohrah.graphics.marauder.medium"
+#define KOHR_AH_MICON_MASK_PMAP_ANIM "ship.kohrah.meleeicons"
+#define KOHR_AH_RACE_STRINGS "ship.kohrah.text"
+#define KOHR_AH_SHIP_SOUNDS "ship.kohrah.sounds"
+#define KOHR_AH_SML_MASK_PMAP_ANIM "ship.kohrah.graphics.marauder.small"
+#define KOHR_AH_VICTORY_SONG "ship.kohrah.ditty"
diff --git a/src/uqm/ships/chenjesu/Makeinfo b/src/uqm/ships/chenjesu/Makeinfo
new file mode 100644
index 0000000..3f10d6a
--- /dev/null
+++ b/src/uqm/ships/chenjesu/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="chenjesu.c"
+uqm_HFILES="chenjesu.h icode.h resinst.h"
diff --git a/src/uqm/ships/chenjesu/chenjesu.c b/src/uqm/ships/chenjesu/chenjesu.c
new file mode 100644
index 0000000..85a1014
--- /dev/null
+++ b/src/uqm/ships/chenjesu/chenjesu.c
@@ -0,0 +1,588 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "chenjesu.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 36
+#define MAX_ENERGY 30
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST /* DISPLAY_TO_WORLD (7) */ 27
+#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 3
+#define THRUST_WAIT 4
+#define TURN_WAIT 6
+#define SHIP_MASS 10
+
+// Photon Shard
+#define WEAPON_ENERGY_COST 5
+#define WEAPON_WAIT 0
+#define CHENJESU_OFFSET 16
+#define MISSILE_OFFSET 0
+#define MISSILE_SPEED DISPLAY_TO_WORLD (16)
+#define MISSILE_LIFE 90
+ /* actually, it's as long as you hold the button down. */
+#define MISSILE_HITS 10
+#define MISSILE_DAMAGE 6
+#define NUM_SPARKLES 8
+
+// Shrapnel
+#define FRAGMENT_OFFSET 2
+#define NUM_FRAGMENTS 8
+#define FRAGMENT_LIFE 10
+#define FRAGMENT_SPEED MISSILE_SPEED
+#define FRAGMENT_RANGE (FRAGMENT_LIFE * FRAGMENT_SPEED)
+ /* This bit is for the cyborg only. */
+#define FRAGMENT_HITS 1
+#define FRAGMENT_DAMAGE 2
+
+// DOGI
+#define SPECIAL_ENERGY_COST MAX_ENERGY
+#define SPECIAL_WAIT 0
+#define DOGGY_OFFSET 18
+#define DOGGY_SPEED DISPLAY_TO_WORLD (8)
+#define ENERGY_DRAIN 10
+#define MAX_DOGGIES 4
+#define DOGGY_HITS 3
+#define DOGGY_MASS 4
+
+static RACE_DESC chenjesu_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_SPECIAL | SEEKING_WEAPON,
+ 28, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ CHENJESU_RACE_STRINGS,
+ CHENJESU_ICON_MASK_PMAP_ANIM,
+ CHENJESU_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ CHENJESU_BIG_MASK_PMAP_ANIM,
+ CHENJESU_MED_MASK_PMAP_ANIM,
+ CHENJESU_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SPARK_BIG_MASK_PMAP_ANIM,
+ SPARK_MED_MASK_PMAP_ANIM,
+ SPARK_SML_MASK_PMAP_ANIM,
+ },
+ {
+ DOGGY_BIG_MASK_PMAP_ANIM,
+ DOGGY_MED_MASK_PMAP_ANIM,
+ DOGGY_SML_MASK_PMAP_ANIM,
+ },
+ {
+ CHENJESU_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ CHENJESU_VICTORY_SONG,
+ CHENJESU_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ LONG_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+crystal_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.index = 1;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = 0;
+ MissileBlock.speed = FRAGMENT_SPEED;
+ MissileBlock.hit_points = FRAGMENT_HITS;
+ MissileBlock.damage = FRAGMENT_DAMAGE;
+ MissileBlock.life = FRAGMENT_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = FRAGMENT_OFFSET;
+
+ for (MissileBlock.face = 0;
+ MissileBlock.face < ANGLE_TO_FACING (FULL_CIRCLE);
+ MissileBlock.face +=
+ (ANGLE_TO_FACING (FULL_CIRCLE) / NUM_FRAGMENTS))
+ {
+ HELEMENT hFragment;
+
+ hFragment = initialize_missile (&MissileBlock);
+ if (hFragment)
+ {
+ ELEMENT *FragPtr;
+
+ LockElement (hFragment, &FragPtr);
+ SetElementStarShip (FragPtr, StarShipPtr);
+ UnlockElement (hFragment);
+ PutElement (hFragment);
+ }
+ }
+
+ ProcessSound (SetAbsSoundIndex (
+ /* CRYSTAL_FRAGMENTS */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+}
+
+static void
+crystal_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->cur_status_flags & WEAPON)
+ ++ElementPtr->life_span; /* keep it going while key pressed */
+ else
+ {
+ ElementPtr->life_span = 1;
+
+ ElementPtr->postprocess_func = crystal_postprocess;
+ }
+}
+
+static void
+animate (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+static void
+crystal_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ HELEMENT hBlastElement;
+
+ hBlastElement =
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ if (hBlastElement)
+ {
+ ELEMENT *BlastElementPtr;
+
+ LockElement (hBlastElement, &BlastElementPtr);
+ BlastElementPtr->current.location = ElementPtr1->current.location;
+
+ BlastElementPtr->life_span = NUM_SPARKLES;
+ BlastElementPtr->turn_wait = BlastElementPtr->next_turn = 0;
+ {
+ BlastElementPtr->preprocess_func = animate;
+ }
+
+ BlastElementPtr->current.image.farray = ElementPtr0->next.image.farray;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (BlastElementPtr->current.image.farray[0],
+ 2); /* skip stones */
+
+ UnlockElement (hBlastElement);
+ }
+}
+
+static void
+doggy_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ++StarShipPtr->special_counter;
+ if (ElementPtr->thrust_wait > 0) /* could be non-zero after a collision */
+ --ElementPtr->thrust_wait;
+ else
+ {
+ COUNT facing, orig_facing;
+ SIZE delta_facing;
+
+ facing = orig_facing =
+ NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)
+ ));
+ if ((delta_facing = TrackShip (ElementPtr, &facing)) < 0)
+ facing = NORMALIZE_FACING (TFB_Random ());
+ else
+ {
+ ELEMENT *ShipPtr;
+
+ LockElement (ElementPtr->hTarget, &ShipPtr);
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ ARCTAN (ShipPtr->current.location.x -
+ ElementPtr->current.location.x,
+ ShipPtr->current.location.y -
+ ElementPtr->current.location.y)
+ ));
+ delta_facing = NORMALIZE_FACING (facing -
+ GetFrameIndex (ShipPtr->current.image.frame));
+ UnlockElement (ElementPtr->hTarget);
+
+ if (delta_facing > ANGLE_TO_FACING (HALF_CIRCLE - OCTANT) &&
+ delta_facing < ANGLE_TO_FACING (HALF_CIRCLE + OCTANT))
+ {
+ if (delta_facing >= ANGLE_TO_FACING (HALF_CIRCLE))
+ facing -= ANGLE_TO_FACING (QUADRANT);
+ else
+ facing += ANGLE_TO_FACING (QUADRANT);
+ }
+
+ facing = NORMALIZE_FACING (facing);
+ }
+
+ if (facing != orig_facing)
+ SetVelocityVector (&ElementPtr->velocity,
+ DOGGY_SPEED, facing);
+ }
+}
+
+static void
+doggy_death (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ProcessSound (SetAbsSoundIndex (
+ /* DOGGY_DIES */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), ElementPtr);
+
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= NONSOLID | FINITE_LIFE;
+ ElementPtr->life_span = 6;
+ {
+ ElementPtr->preprocess_func = animate;
+ }
+ ElementPtr->death_func = NULL;
+ ElementPtr->collision_func = NULL;
+ ZeroVelocityComponents (&ElementPtr->velocity);
+
+ ElementPtr->turn_wait = ElementPtr->next_turn = 0;
+}
+
+static void
+doggy_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && !elementsOfSamePlayer (ElementPtr0, ElementPtr1))
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ ProcessSound (SetAbsSoundIndex (
+ /* DOGGY_STEALS_ENERGY */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), ElementPtr0);
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level < ENERGY_DRAIN)
+ DeltaEnergy (ElementPtr1, -StarShipPtr->RaceDescPtr->ship_info.energy_level);
+ else
+ DeltaEnergy (ElementPtr1, -ENERGY_DRAIN);
+ }
+ if (ElementPtr0->thrust_wait <= COLLISION_THRUST_WAIT)
+ ElementPtr0->thrust_wait += COLLISION_THRUST_WAIT << 1;
+}
+
+static void
+spawn_doggy (ELEMENT *ElementPtr)
+{
+ HELEMENT hDoggyElement;
+
+ if ((hDoggyElement = AllocElement ()) != 0)
+ {
+ COUNT angle;
+ ELEMENT *DoggyElementPtr;
+ STARSHIP *StarShipPtr;
+
+ ElementPtr->state_flags |= DEFY_PHYSICS;
+
+ PutElement (hDoggyElement);
+ LockElement (hDoggyElement, &DoggyElementPtr);
+ DoggyElementPtr->hit_points = DOGGY_HITS;
+ DoggyElementPtr->mass_points = DOGGY_MASS;
+ DoggyElementPtr->thrust_wait = 0;
+ DoggyElementPtr->playerNr = ElementPtr->playerNr;
+ DoggyElementPtr->state_flags = APPEARING;
+ DoggyElementPtr->life_span = NORMAL_LIFE;
+ SetPrimType (&(GLOBAL (DisplayArray))[DoggyElementPtr->PrimIndex],
+ STAMP_PRIM);
+ {
+ DoggyElementPtr->preprocess_func = doggy_preprocess;
+ DoggyElementPtr->postprocess_func = NULL;
+ DoggyElementPtr->collision_func = doggy_collision;
+ DoggyElementPtr->death_func = doggy_death;
+ }
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE;
+ DoggyElementPtr->current.location.x = ElementPtr->next.location.x
+ + COSINE (angle, DISPLAY_TO_WORLD (CHENJESU_OFFSET + DOGGY_OFFSET));
+ DoggyElementPtr->current.location.y = ElementPtr->next.location.y
+ + SINE (angle, DISPLAY_TO_WORLD (CHENJESU_OFFSET + DOGGY_OFFSET));
+ DoggyElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ DoggyElementPtr->current.image.frame = StarShipPtr->RaceDescPtr->ship_data.special[0];
+
+ SetVelocityVector (&DoggyElementPtr->velocity,
+ DOGGY_SPEED, NORMALIZE_FACING (ANGLE_TO_FACING (angle)));
+
+ SetElementStarShip (DoggyElementPtr, StarShipPtr);
+
+ ProcessSound (SetAbsSoundIndex (
+ /* RELEASE_DOGGY */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 4), DoggyElementPtr);
+
+ UnlockElement (hDoggyElement);
+ }
+}
+
+static void
+chenjesu_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if ((lpEvalDesc->which_turn <= 16
+ && MANEUVERABILITY (
+ &EnemyStarShipPtr->RaceDescPtr->cyborg_control
+ ) >= MEDIUM_SHIP)
+ || (MANEUVERABILITY (
+ &EnemyStarShipPtr->RaceDescPtr->cyborg_control
+ ) <= SLOW_SHIP
+ && WEAPON_RANGE (
+ &EnemyStarShipPtr->RaceDescPtr->cyborg_control
+ ) >= LONG_RANGE_WEAPON * 3 / 4
+ && (EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & SEEKING_WEAPON)))
+ lpEvalDesc->MoveState = PURSUE;
+ }
+
+ if (StarShipPtr->special_counter == 1
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState == ENTICE
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 8)
+ {
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ if (lpEvalDesc->ObjectPtr)
+ {
+ HELEMENT h, hNext;
+ ELEMENT *CrystalPtr;
+
+ h = (StarShipPtr->old_status_flags & WEAPON) ?
+ GetTailElement () : (HELEMENT)0;
+ for (; h; h = hNext)
+ {
+ LockElement (h, &CrystalPtr);
+ hNext = GetPredElement (CrystalPtr);
+ if (!(CrystalPtr->state_flags & NONSOLID)
+ && CrystalPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.weapon
+ && CrystalPtr->preprocess_func
+ && CrystalPtr->life_span > 0
+ && elementsOfSamePlayer (CrystalPtr, ShipPtr))
+ {
+ if (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr)
+ {
+ COUNT which_turn;
+
+ if ((which_turn = PlotIntercept (CrystalPtr,
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr,
+ CrystalPtr->life_span,
+ FRAGMENT_RANGE / 2)) == 0
+ || (which_turn == 1
+ && PlotIntercept (CrystalPtr,
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr,
+ CrystalPtr->life_span, 0) == 0))
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ else if (StarShipPtr->weapon_counter == 0)
+ {
+ StarShipPtr->ship_input_state |= WEAPON;
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ }
+
+ UnlockElement (h);
+ break;
+ }
+ hNext = 0;
+ }
+ UnlockElement (h);
+ }
+
+ if (h == 0)
+ {
+ if (StarShipPtr->old_status_flags & WEAPON)
+ {
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ if (lpEvalDesc == &ObjectsOfConcern[ENEMY_WEAPON_INDEX])
+ StarShipPtr->weapon_counter = 3;
+ }
+ else if (StarShipPtr->weapon_counter == 0
+ && ship_weapons (ShipPtr, lpEvalDesc->ObjectPtr, FRAGMENT_RANGE / 2))
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+ }
+
+ if (StarShipPtr->special_counter < MAX_DOGGIES)
+ {
+ if (lpEvalDesc->ObjectPtr
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level <= SPECIAL_ENERGY_COST
+ && !(StarShipPtr->ship_input_state & WEAPON))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+}
+
+static COUNT
+initialize_crystal (ELEMENT *ShipPtr, HELEMENT CrystalArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = CHENJESU_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = crystal_preprocess;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ CrystalArray[0] = initialize_missile (&MissileBlock);
+
+ if (CrystalArray[0])
+ {
+ ELEMENT *CrystalPtr;
+
+ LockElement (CrystalArray[0], &CrystalPtr);
+ CrystalPtr->collision_func = crystal_collision;
+ UnlockElement (CrystalArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+chenjesu_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter < MAX_DOGGIES
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ spawn_doggy (ElementPtr);
+ }
+
+ StarShipPtr->special_counter = 1;
+ /* say there is one doggy, because ship_postprocess will
+ * decrement special_counter */
+}
+
+static void
+chenjesu_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter > 1) /* only when STANDARD
+ * computer opponent
+ */
+ StarShipPtr->special_counter += MAX_DOGGIES;
+ if (StarShipPtr->cur_status_flags
+ & StarShipPtr->old_status_flags
+ & WEAPON)
+ ++StarShipPtr->weapon_counter;
+}
+
+RACE_DESC*
+init_chenjesu (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ chenjesu_desc.preprocess_func = chenjesu_preprocess;
+ chenjesu_desc.postprocess_func = chenjesu_postprocess;
+ chenjesu_desc.init_weapon_func = initialize_crystal;
+ chenjesu_desc.cyborg_control.intelligence_func = chenjesu_intelligence;
+
+ RaceDescPtr = &chenjesu_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/chenjesu/chenjesu.h b/src/uqm/ships/chenjesu/chenjesu.h
new file mode 100644
index 0000000..37ac9d6
--- /dev/null
+++ b/src/uqm/ships/chenjesu/chenjesu.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CHENJESU_H
+#define CHENJESU_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_chenjesu (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* CHENJESU_H */
+
diff --git a/src/uqm/ships/chenjesu/icode.h b/src/uqm/ships/chenjesu/icode.h
new file mode 100644
index 0000000..1f4b693
--- /dev/null
+++ b/src/uqm/ships/chenjesu/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define CHENJESU_CODE "ship.chenjesu.code"
diff --git a/src/uqm/ships/chenjesu/resinst.h b/src/uqm/ships/chenjesu/resinst.h
new file mode 100644
index 0000000..966029a
--- /dev/null
+++ b/src/uqm/ships/chenjesu/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define CHENJESU_BIG_MASK_PMAP_ANIM "ship.chenjesu.graphics.broodhome.large"
+#define CHENJESU_CAPTAIN_MASK_PMAP_ANIM "ship.chenjesu.graphics.captain"
+#define CHENJESU_ICON_MASK_PMAP_ANIM "ship.chenjesu.icons"
+#define CHENJESU_MED_MASK_PMAP_ANIM "ship.chenjesu.graphics.broodhome.medium"
+#define CHENJESU_MICON_MASK_PMAP_ANIM "ship.chenjesu.meleeicons"
+#define CHENJESU_RACE_STRINGS "ship.chenjesu.text"
+#define CHENJESU_SHIP_SOUNDS "ship.chenjesu.sounds"
+#define CHENJESU_SML_MASK_PMAP_ANIM "ship.chenjesu.graphics.broodhome.small"
+#define CHENJESU_VICTORY_SONG "ship.chenjesu.ditty"
+#define DOGGY_BIG_MASK_PMAP_ANIM "ship.chenjesu.graphics.doggy.large"
+#define DOGGY_MED_MASK_PMAP_ANIM "ship.chenjesu.graphics.doggy.medium"
+#define DOGGY_SML_MASK_PMAP_ANIM "ship.chenjesu.graphics.doggy.small"
+#define SPARK_BIG_MASK_PMAP_ANIM "ship.chenjesu.graphics.spark.large"
+#define SPARK_MED_MASK_PMAP_ANIM "ship.chenjesu.graphics.spark.medium"
+#define SPARK_SML_MASK_PMAP_ANIM "ship.chenjesu.graphics.spark.small"
diff --git a/src/uqm/ships/chmmr/Makeinfo b/src/uqm/ships/chmmr/Makeinfo
new file mode 100644
index 0000000..70b8bc6
--- /dev/null
+++ b/src/uqm/ships/chmmr/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="chmmr.c"
+uqm_HFILES="chmmr.h icode.h resinst.h"
diff --git a/src/uqm/ships/chmmr/chmmr.c b/src/uqm/ships/chmmr/chmmr.c
new file mode 100644
index 0000000..8f6abf0
--- /dev/null
+++ b/src/uqm/ships/chmmr/chmmr.c
@@ -0,0 +1,790 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "chmmr.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW MAX_CREW_SIZE
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 1
+#define MAX_THRUST 35
+#define THRUST_INCREMENT 7
+#define THRUST_WAIT 5
+#define TURN_WAIT 3
+#define SHIP_MASS 10
+
+// Laser
+#define WEAPON_ENERGY_COST 2
+#define WEAPON_WAIT 0
+#define CHMMR_OFFSET 18
+#define LASER_RANGE DISPLAY_TO_WORLD (150)
+#define NUM_CYCLES 4
+
+// Tractor Beam
+#define SPECIAL_ENERGY_COST 1
+#define SPECIAL_WAIT 0
+#define NUM_SHADOWS 5
+
+// Satellites
+#define NUM_SATELLITES 3
+#define SATELLITE_OFFSET DISPLAY_TO_WORLD (64)
+#define SATELLITE_HITPOINTS 10
+#define SATELLITE_MASS 10
+#define DEFENSE_RANGE (UWORD)64
+#define DEFENSE_WAIT 2
+
+static RACE_DESC chmmr_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | IMMEDIATE_WEAPON | SEEKING_SPECIAL | POINT_DEFENSE,
+ 30, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ CHMMR_RACE_STRINGS,
+ CHMMR_ICON_MASK_PMAP_ANIM,
+ CHMMR_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ CHMMR_BIG_MASK_PMAP_ANIM,
+ CHMMR_MED_MASK_PMAP_ANIM,
+ CHMMR_SML_MASK_PMAP_ANIM,
+ },
+ {
+ MUZZLE_BIG_MASK_PMAP_ANIM,
+ MUZZLE_MED_MASK_PMAP_ANIM,
+ MUZZLE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SATELLITE_BIG_MASK_PMAP_ANIM,
+ SATELLITE_MED_MASK_PMAP_ANIM,
+ SATELLITE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ CHMMR_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ CHMMR_VICTORY_SONG,
+ CHMMR_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+animate (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+static void
+laser_death (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ StarShipPtr->special_counter = ElementPtr->turn_wait;
+
+ if (StarShipPtr->hShip)
+ {
+ SIZE dx, dy;
+ long dist;
+ HELEMENT hIonSpots;
+ ELEMENT *ShipPtr;
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+
+ dx = ElementPtr->current.location.x
+ - ShipPtr->current.location.x;
+ dy = ElementPtr->current.location.y
+ - ShipPtr->current.location.y;
+ if (((BYTE)TFB_Random () & 0x07)
+ && (dist = (long)dx * dx + (long)dy * dy) >=
+ (long)DISPLAY_TO_WORLD (CHMMR_OFFSET + 10)
+ * DISPLAY_TO_WORLD (CHMMR_OFFSET + 10)
+ && (hIonSpots = AllocElement ()))
+ {
+ COUNT angle, magnitude;
+ ELEMENT *IonSpotsPtr;
+
+ LockElement (hIonSpots, &IonSpotsPtr);
+ IonSpotsPtr->playerNr = ElementPtr->playerNr;
+ IonSpotsPtr->state_flags = FINITE_LIFE | NONSOLID
+ | IGNORE_SIMILAR | APPEARING;
+ IonSpotsPtr->turn_wait = IonSpotsPtr->next_turn = 0;
+ IonSpotsPtr->life_span = 9;
+
+ angle = ARCTAN (dx, dy);
+ magnitude = ((COUNT)TFB_Random ()
+ % ((square_root (dist) + 1)
+ - DISPLAY_TO_WORLD (CHMMR_OFFSET + 10)))
+ + DISPLAY_TO_WORLD (CHMMR_OFFSET + 10);
+ IonSpotsPtr->current.location.x =
+ ShipPtr->current.location.x
+ + COSINE (angle, magnitude);
+ IonSpotsPtr->current.location.y =
+ ShipPtr->current.location.y
+ + SINE (angle, magnitude);
+ IonSpotsPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.weapon;
+ IonSpotsPtr->current.image.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.weapon[0],
+ ANGLE_TO_FACING (FULL_CIRCLE) << 1
+ );
+
+ IonSpotsPtr->preprocess_func = animate;
+
+ SetElementStarShip (IonSpotsPtr, StarShipPtr);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ IonSpotsPtr->PrimIndex
+ ], STAMP_PRIM);
+
+ UnlockElement (hIonSpots);
+ PutElement (hIonSpots);
+ }
+
+ UnlockElement (StarShipPtr->hShip);
+ }
+}
+
+static COUNT
+initialize_megawatt_laser (ELEMENT *ShipPtr, HELEMENT LaserArray[])
+{
+ RECT r;
+ STARSHIP *StarShipPtr;
+ LASER_BLOCK LaserBlock;
+ static const Color cycle_array[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7F),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7F),
+ };
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ LaserBlock.face = StarShipPtr->ShipFacing;
+ GetFrameRect (SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.weapon[0], LaserBlock.face
+ ), &r);
+ LaserBlock.cx = DISPLAY_ALIGN (ShipPtr->next.location.x)
+ + DISPLAY_TO_WORLD (r.corner.x);
+ LaserBlock.cy = DISPLAY_ALIGN (ShipPtr->next.location.y)
+ + DISPLAY_TO_WORLD (r.corner.y);
+ LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = 0;
+ LaserBlock.color = cycle_array[StarShipPtr->special_counter];
+ LaserArray[0] = initialize_laser (&LaserBlock);
+
+ if (LaserArray[0])
+ {
+ ELEMENT *LaserPtr;
+
+ LockElement (LaserArray[0], &LaserPtr);
+
+ LaserPtr->mass_points = 2;
+ LaserPtr->death_func = laser_death;
+ LaserPtr->turn_wait = (BYTE)((StarShipPtr->special_counter + 1)
+ % NUM_CYCLES);
+
+ UnlockElement (LaserArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+chmmr_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr
+ && !(StarShipPtr->ship_input_state & WEAPON)
+ && lpEvalDesc->which_turn > 12
+ && NORMALIZE_ANGLE (
+ GetVelocityTravelAngle (&ShipPtr->velocity)
+ - (GetVelocityTravelAngle (&lpEvalDesc->ObjectPtr->velocity)
+ + HALF_CIRCLE) + QUADRANT
+ ) > HALF_CIRCLE)
+ StarShipPtr->ship_input_state |= SPECIAL;
+}
+
+static void
+chmmr_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ if (StarShipPtr->cur_status_flags & WEAPON)
+ {
+ HELEMENT hMuzzleFlash;
+ ELEMENT *MuzzleFlashPtr;
+
+ LockElement (GetTailElement (), &MuzzleFlashPtr);
+ if (MuzzleFlashPtr != ElementPtr
+ && elementsOfSamePlayer (MuzzleFlashPtr, ElementPtr)
+ && (MuzzleFlashPtr->state_flags & APPEARING)
+ && GetPrimType (&(GLOBAL (DisplayArray))[
+ MuzzleFlashPtr->PrimIndex
+ ]) == LINE_PRIM
+ && !(StarShipPtr->special_counter & 1)
+ && (hMuzzleFlash = AllocElement ()))
+ {
+ LockElement (hMuzzleFlash, &MuzzleFlashPtr);
+ MuzzleFlashPtr->playerNr = ElementPtr->playerNr;
+ MuzzleFlashPtr->state_flags = FINITE_LIFE | NONSOLID
+ | IGNORE_SIMILAR | APPEARING;
+ MuzzleFlashPtr->life_span = 1;
+
+ MuzzleFlashPtr->current = ElementPtr->next;
+ MuzzleFlashPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MuzzleFlashPtr->current.image.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.weapon[0],
+ StarShipPtr->ShipFacing + ANGLE_TO_FACING (FULL_CIRCLE)
+ );
+
+ SetElementStarShip (MuzzleFlashPtr, StarShipPtr);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ MuzzleFlashPtr->PrimIndex
+ ], STAMP_PRIM);
+
+ UnlockElement (hMuzzleFlash);
+ PutElement (hMuzzleFlash);
+ }
+ UnlockElement (GetTailElement ());
+ }
+
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ COUNT facing;
+ ELEMENT *ShipElementPtr;
+
+ LockElement (ElementPtr->hTarget, &ShipElementPtr);
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
+ ShipElementPtr);
+
+ UnlockElement (ElementPtr->hTarget);
+
+ facing = 0;
+ if (TrackShip (ElementPtr, &facing) >= 0)
+ {
+ ELEMENT *ShipElementPtr;
+
+ LockElement (ElementPtr->hTarget, &ShipElementPtr);
+ if (!GRAVITY_MASS (ShipElementPtr->mass_points + 1))
+ {
+ SIZE i, dx, dy;
+ COUNT angle, magnitude;
+ STARSHIP *EnemyStarShipPtr;
+ static const SIZE shadow_offs[] =
+ {
+ DISPLAY_TO_WORLD (8),
+ DISPLAY_TO_WORLD (8 + 9),
+ DISPLAY_TO_WORLD (8 + 9 + 11),
+ DISPLAY_TO_WORLD (8 + 9 + 11 + 14),
+ DISPLAY_TO_WORLD (8 + 9 + 11 + 14 + 18),
+ };
+ static const Color color_tab[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x10), 0x53),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x0E), 0x54),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x0C), 0x55),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x09), 0x56),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x07), 0x57),
+ };
+ DWORD current_speed, max_speed;
+
+ // calculate tractor beam effect
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ dx = (ElementPtr->next.location.x
+ + COSINE (angle, (LASER_RANGE / 3)
+ + DISPLAY_TO_WORLD (CHMMR_OFFSET)))
+ - ShipElementPtr->next.location.x;
+ dy = (ElementPtr->next.location.y
+ + SINE (angle, (LASER_RANGE / 3)
+ + DISPLAY_TO_WORLD (CHMMR_OFFSET)))
+ - ShipElementPtr->next.location.y;
+ angle = ARCTAN (dx, dy);
+ magnitude = WORLD_TO_VELOCITY (12) /
+ ShipElementPtr->mass_points;
+ DeltaVelocityComponents (&ShipElementPtr->velocity,
+ COSINE (angle, magnitude), SINE (angle, magnitude));
+
+ GetCurrentVelocityComponents (&ShipElementPtr->velocity,
+ &dx, &dy);
+ GetElementStarShip (ShipElementPtr, &EnemyStarShipPtr);
+
+ // set the effected ship's speed flags
+ current_speed = VelocitySquared (dx, dy);
+ max_speed = VelocitySquared (WORLD_TO_VELOCITY (
+ EnemyStarShipPtr->RaceDescPtr->characteristics.max_thrust),
+ 0);
+ EnemyStarShipPtr->cur_status_flags &= ~(SHIP_AT_MAX_SPEED
+ | SHIP_BEYOND_MAX_SPEED);
+ if (current_speed > max_speed)
+ EnemyStarShipPtr->cur_status_flags |= (SHIP_AT_MAX_SPEED
+ | SHIP_BEYOND_MAX_SPEED);
+ else if (current_speed == max_speed)
+ EnemyStarShipPtr->cur_status_flags |= SHIP_AT_MAX_SPEED;
+
+ // add tractor beam graphical effects
+ for (i = 0; i < NUM_SHADOWS; ++i)
+ {
+ HELEMENT hShadow;
+
+ hShadow = AllocElement ();
+ if (hShadow)
+ {
+ ELEMENT *ShadowElementPtr;
+
+ LockElement (hShadow, &ShadowElementPtr);
+ ShadowElementPtr->playerNr = ShipElementPtr->playerNr;
+ ShadowElementPtr->state_flags = FINITE_LIFE | NONSOLID
+ | IGNORE_SIMILAR | POST_PROCESS;
+ ShadowElementPtr->life_span = 1;
+
+ ShadowElementPtr->current = ShipElementPtr->next;
+ ShadowElementPtr->current.location.x +=
+ COSINE (angle, shadow_offs[i]);
+ ShadowElementPtr->current.location.y +=
+ SINE (angle, shadow_offs[i]);
+ ShadowElementPtr->next = ShadowElementPtr->current;
+
+ SetElementStarShip (ShadowElementPtr, EnemyStarShipPtr);
+ SetVelocityComponents (&ShadowElementPtr->velocity,
+ dx, dy);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ ShadowElementPtr->PrimIndex
+ ], STAMPFILL_PRIM);
+ SetPrimColor (&(GLOBAL (DisplayArray))[
+ ShadowElementPtr->PrimIndex
+ ], color_tab[i]);
+
+ UnlockElement (hShadow);
+ InsertElement (hShadow, GetHeadElement ());
+ }
+ }
+ }
+ UnlockElement (ElementPtr->hTarget);
+ }
+ }
+
+ StarShipPtr->special_counter = 0;
+}
+
+static void
+satellite_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ ++ElementPtr->life_span;
+
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) + 1) & 7);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = (BYTE)NORMALIZE_ANGLE (
+ ElementPtr->turn_wait + 1
+ );
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->hShip)
+ {
+ SIZE dx, dy;
+ ELEMENT *ShipPtr;
+
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags |= POINT_DEFENSE;
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+
+ dx = (ShipPtr->next.location.x
+ + COSINE (ElementPtr->turn_wait, SATELLITE_OFFSET))
+ - ElementPtr->current.location.x;
+ dy = (ShipPtr->next.location.y
+ + SINE (ElementPtr->turn_wait, SATELLITE_OFFSET))
+ - ElementPtr->current.location.y;
+ dx = WRAP_DELTA_X (dx);
+ dy = WRAP_DELTA_Y (dy);
+ if ((long)dx * dx + (long)dy * dy
+ <= DISPLAY_TO_WORLD (20L) * DISPLAY_TO_WORLD (20L))
+ SetVelocityComponents (&ElementPtr->velocity,
+ WORLD_TO_VELOCITY (dx),
+ WORLD_TO_VELOCITY (dy));
+ else
+ {
+ COUNT angle;
+
+ angle = ARCTAN (dx, dy);
+ SetVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (20))),
+ SINE (angle, WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (20))));
+ }
+
+ UnlockElement (StarShipPtr->hShip);
+ }
+}
+
+static void
+spawn_point_defense (ELEMENT *ElementPtr)
+{
+ BYTE weakest;
+ UWORD best_dist;
+ STARSHIP *StarShipPtr;
+ HELEMENT hObject, hNextObject, hBestObject;
+ ELEMENT *ShipPtr;
+ ELEMENT *SattPtr;
+ ELEMENT *ObjectPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ hBestObject = 0;
+ best_dist = DEFENSE_RANGE + 1;
+ weakest = 255;
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ LockElement (ElementPtr->hTarget, &SattPtr);
+ for (hObject = GetPredElement (ElementPtr);
+ hObject; hObject = hNextObject)
+ {
+ LockElement (hObject, &ObjectPtr);
+ hNextObject = GetPredElement (ObjectPtr);
+ if (!elementsOfSamePlayer (ObjectPtr, ShipPtr)
+ && ObjectPtr->playerNr != NEUTRAL_PLAYER_NUM
+ && CollisionPossible (ObjectPtr, ShipPtr)
+ && !OBJECT_CLOAKED (ObjectPtr))
+ {
+ SIZE delta_x, delta_y;
+ UWORD dist;
+
+ delta_x = ObjectPtr->next.location.x
+ - SattPtr->next.location.x;
+ delta_y = ObjectPtr->next.location.y
+ - SattPtr->next.location.y;
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x = WORLD_TO_DISPLAY (delta_x);
+ delta_y = WORLD_TO_DISPLAY (delta_y);
+ if ((UWORD)delta_x <= DEFENSE_RANGE &&
+ (UWORD)delta_y <= DEFENSE_RANGE &&
+ (dist =
+ (UWORD)delta_x * (UWORD)delta_x
+ + (UWORD)delta_y * (UWORD)delta_y) <=
+ DEFENSE_RANGE * DEFENSE_RANGE
+ && (ObjectPtr->hit_points < weakest
+ || (ObjectPtr->hit_points == weakest
+ && dist < best_dist)))
+ {
+ hBestObject = hObject;
+ best_dist = dist;
+ weakest = ObjectPtr->hit_points;
+ }
+ }
+ UnlockElement (hObject);
+ }
+
+ if (hBestObject)
+ {
+ LASER_BLOCK LaserBlock;
+ HELEMENT hPointDefense;
+
+ LockElement (hBestObject, &ObjectPtr);
+
+ LaserBlock.cx = SattPtr->next.location.x;
+ LaserBlock.cy = SattPtr->next.location.y;
+ LaserBlock.face = 0;
+ LaserBlock.ex = ObjectPtr->next.location.x
+ - SattPtr->next.location.x;
+ LaserBlock.ey = ObjectPtr->next.location.y
+ - SattPtr->next.location.y;
+ LaserBlock.sender = SattPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = 0;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x1F), 0x4D);
+ hPointDefense = initialize_laser (&LaserBlock);
+ if (hPointDefense)
+ {
+ ELEMENT *PDPtr;
+
+ LockElement (hPointDefense, &PDPtr);
+ SetElementStarShip (PDPtr, StarShipPtr);
+ PDPtr->hTarget = 0;
+ UnlockElement (hPointDefense);
+
+ PutElement (hPointDefense);
+
+ SattPtr->thrust_wait = DEFENSE_WAIT;
+ }
+
+ UnlockElement (hBestObject);
+ }
+
+ UnlockElement (ElementPtr->hTarget);
+ UnlockElement (StarShipPtr->hShip);
+}
+
+static void
+satellite_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ if (ElementPtr->thrust_wait || ElementPtr->life_span == 0)
+ --ElementPtr->thrust_wait;
+ else
+ {
+ HELEMENT hDefense;
+
+ hDefense = AllocElement ();
+ if (hDefense)
+ {
+ ELEMENT *DefensePtr;
+
+ PutElement (hDefense);
+
+ LockElement (hDefense, &DefensePtr);
+ DefensePtr->playerNr = ElementPtr->playerNr;
+ DefensePtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
+
+ {
+ ELEMENT *SuccPtr;
+
+ LockElement (GetSuccElement (ElementPtr), &SuccPtr);
+ DefensePtr->hTarget = GetPredElement (SuccPtr);
+ UnlockElement (GetSuccElement (ElementPtr));
+
+ DefensePtr->death_func = spawn_point_defense;
+ }
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ SetElementStarShip (DefensePtr, StarShipPtr);
+
+ UnlockElement (hDefense);
+ }
+ }
+}
+
+static void
+satellite_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ (void) ElementPtr0; /* Satisfying compiler (unused parameter) */
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) ElementPtr1; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+satellite_death (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags &= ~POINT_DEFENSE;
+
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= NONSOLID | FINITE_LIFE | CHANGING;
+ ElementPtr->life_span = 4;
+ ElementPtr->turn_wait = 1;
+ ElementPtr->next_turn = 0;
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame, 8);
+
+ ElementPtr->preprocess_func = animate;
+ ElementPtr->death_func = NULL;
+ ElementPtr->postprocess_func = NULL;
+ ElementPtr->collision_func = NULL;
+}
+
+static void
+spawn_satellites (ELEMENT *ElementPtr)
+{
+ COUNT i;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->hShip)
+ {
+ LockElement (StarShipPtr->hShip, &ElementPtr);
+ for (i = 0; i < NUM_SATELLITES; ++i)
+ {
+ HELEMENT hSatellite;
+
+ hSatellite = AllocElement ();
+ if (hSatellite)
+ {
+ COUNT angle;
+ ELEMENT *SattPtr;
+
+ LockElement (hSatellite, &SattPtr);
+ SattPtr->playerNr = ElementPtr->playerNr;
+ SattPtr->state_flags = IGNORE_SIMILAR | APPEARING
+ | FINITE_LIFE;
+ SattPtr->life_span = NORMAL_LIFE + 1;
+ SattPtr->hit_points = SATELLITE_HITPOINTS;
+ SattPtr->mass_points = SATELLITE_MASS;
+
+ angle = (i * FULL_CIRCLE + (NUM_SATELLITES >> 1))
+ / NUM_SATELLITES;
+ SattPtr->turn_wait = (BYTE)angle;
+ SattPtr->current.location.x = ElementPtr->next.location.x
+ + COSINE (angle, SATELLITE_OFFSET);
+ SattPtr->current.location.y = ElementPtr->next.location.y
+ + SINE (angle, SATELLITE_OFFSET);
+ SattPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ SattPtr->current.image.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0],
+ (COUNT)TFB_Random () & 0x07
+ );
+
+ SattPtr->preprocess_func = satellite_preprocess;
+ SattPtr->postprocess_func = satellite_postprocess;
+ SattPtr->death_func = satellite_death;
+ SattPtr->collision_func = satellite_collision;
+
+ SetElementStarShip (SattPtr, StarShipPtr);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ SattPtr->PrimIndex
+ ], STAMP_PRIM);
+
+ UnlockElement (hSatellite);
+ PutElement (hSatellite);
+ }
+ }
+ UnlockElement (StarShipPtr->hShip);
+ }
+}
+
+static void
+chmmr_preprocess (ELEMENT *ElementPtr)
+{
+ HELEMENT hSatellite;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ hSatellite = AllocElement ();
+ if (hSatellite)
+ {
+ ELEMENT *SattPtr;
+ STARSHIP *StarShipPtr;
+
+ LockElement (hSatellite, &SattPtr);
+ SattPtr->playerNr = ElementPtr->playerNr;
+ SattPtr->state_flags = FINITE_LIFE | NONSOLID | IGNORE_SIMILAR
+ | APPEARING;
+ SattPtr->life_span = HYPERJUMP_LIFE + 1;
+
+ SattPtr->death_func = spawn_satellites;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ SetElementStarShip (SattPtr, StarShipPtr);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ SattPtr->PrimIndex
+ ], NO_PRIM);
+
+ UnlockElement (hSatellite);
+ PutElement (hSatellite);
+ }
+
+ StarShipPtr->RaceDescPtr->preprocess_func = 0;
+}
+
+RACE_DESC*
+init_chmmr (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ chmmr_desc.preprocess_func = chmmr_preprocess;
+ chmmr_desc.postprocess_func = chmmr_postprocess;
+ chmmr_desc.init_weapon_func = initialize_megawatt_laser;
+ chmmr_desc.cyborg_control.intelligence_func = chmmr_intelligence;
+
+ RaceDescPtr = &chmmr_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/chmmr/chmmr.h b/src/uqm/ships/chmmr/chmmr.h
new file mode 100644
index 0000000..88dd9b8
--- /dev/null
+++ b/src/uqm/ships/chmmr/chmmr.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CHMMR_H
+#define CHMMR_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_chmmr (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* CHMMR_H */
+
diff --git a/src/uqm/ships/chmmr/icode.h b/src/uqm/ships/chmmr/icode.h
new file mode 100644
index 0000000..0873a15
--- /dev/null
+++ b/src/uqm/ships/chmmr/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define CHMMR_CODE "ship.chmmr.code"
diff --git a/src/uqm/ships/chmmr/resinst.h b/src/uqm/ships/chmmr/resinst.h
new file mode 100644
index 0000000..a830273
--- /dev/null
+++ b/src/uqm/ships/chmmr/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define CHMMR_BIG_MASK_PMAP_ANIM "ship.chmmr.graphics.avatar.large"
+#define CHMMR_CAPTAIN_MASK_PMAP_ANIM "ship.chmmr.graphics.captain"
+#define CHMMR_ICON_MASK_PMAP_ANIM "ship.chmmr.icons"
+#define CHMMR_MED_MASK_PMAP_ANIM "ship.chmmr.graphics.avatar.medium"
+#define CHMMR_MICON_MASK_PMAP_ANIM "ship.chmmr.meleeicons"
+#define CHMMR_RACE_STRINGS "ship.chmmr.text"
+#define CHMMR_SHIP_SOUNDS "ship.chmmr.sounds"
+#define CHMMR_SML_MASK_PMAP_ANIM "ship.chmmr.graphics.avatar.small"
+#define CHMMR_VICTORY_SONG "ship.chmmr.ditty"
+#define MUZZLE_BIG_MASK_PMAP_ANIM "ship.chmmr.graphics.muzzle.large"
+#define MUZZLE_MED_MASK_PMAP_ANIM "ship.chmmr.graphics.muzzle.medium"
+#define MUZZLE_SML_MASK_PMAP_ANIM "ship.chmmr.graphics.muzzle.small"
+#define SATELLITE_BIG_MASK_PMAP_ANIM "ship.chmmr.graphics.satellite.large"
+#define SATELLITE_MED_MASK_PMAP_ANIM "ship.chmmr.graphics.satellite.medium"
+#define SATELLITE_SML_MASK_PMAP_ANIM "ship.chmmr.graphics.satellite.small"
diff --git a/src/uqm/ships/druuge/Makeinfo b/src/uqm/ships/druuge/Makeinfo
new file mode 100644
index 0000000..6687b56
--- /dev/null
+++ b/src/uqm/ships/druuge/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="druuge.c"
+uqm_HFILES="druuge.h icode.h resinst.h"
diff --git a/src/uqm/ships/druuge/druuge.c b/src/uqm/ships/druuge/druuge.c
new file mode 100644
index 0000000..6ba2591
--- /dev/null
+++ b/src/uqm/ships/druuge/druuge.c
@@ -0,0 +1,324 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "druuge.h"
+#include "resinst.h"
+
+// Core characteristics
+#define MAX_CREW 14
+#define MAX_ENERGY 32
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 50
+#define MAX_THRUST 20
+#define THRUST_INCREMENT 2
+#define THRUST_WAIT 1
+#define TURN_WAIT 4
+#define SHIP_MASS 5
+
+// Mass Driver
+#define WEAPON_ENERGY_COST 4
+#define WEAPON_WAIT 10
+#define DRUUGE_OFFSET 24
+#define MISSILE_OFFSET 6
+#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define MISSILE_LIFE 20
+#define MISSILE_RANGE (MISSILE_SPEED * MISSILE_LIFE)
+#define MISSILE_HITS 4
+#define MISSILE_DAMAGE 6
+#define RECOIL_VELOCITY WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (6))
+#define MAX_RECOIL_VELOCITY (RECOIL_VELOCITY * 4)
+
+// Furnace
+#define SPECIAL_ENERGY_COST 16
+#define SPECIAL_WAIT 30
+
+static RACE_DESC druuge_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 17, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ DRUUGE_RACE_STRINGS,
+ DRUUGE_ICON_MASK_PMAP_ANIM,
+ DRUUGE_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 1400 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 9500, 2792,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ DRUUGE_BIG_MASK_PMAP_ANIM,
+ DRUUGE_MED_MASK_PMAP_ANIM,
+ DRUUGE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ DRUUGE_CANNON_BIG_MASK_PMAP_ANIM,
+ DRUUGE_CANNON_MED_MASK_PMAP_ANIM,
+ DRUUGE_CANNON_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ DRUUGE_CAPT_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ DRUUGE_VICTORY_SONG,
+ DRUUGE_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_RANGE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+cannon_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && ElementPtr1->crew_level
+ && !GRAVITY_MASS (ElementPtr1->mass_points + 1))
+ {
+ COUNT angle;
+ SIZE cur_delta_x, cur_delta_y;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+
+ angle = FACING_TO_ANGLE (
+ GetFrameIndex (ElementPtr0->next.image.frame)
+ );
+ DeltaVelocityComponents (&ElementPtr1->velocity,
+ COSINE (angle, RECOIL_VELOCITY),
+ SINE (angle, RECOIL_VELOCITY));
+ GetCurrentVelocityComponents (&ElementPtr1->velocity,
+ &cur_delta_x, &cur_delta_y);
+ if ((long)cur_delta_x * (long)cur_delta_x
+ + (long)cur_delta_y * (long)cur_delta_y
+ > (long)MAX_RECOIL_VELOCITY * (long)MAX_RECOIL_VELOCITY)
+ {
+ angle = ARCTAN (cur_delta_x, cur_delta_y);
+ SetVelocityComponents (&ElementPtr1->velocity,
+ COSINE (angle, MAX_RECOIL_VELOCITY),
+ SINE (angle, MAX_RECOIL_VELOCITY));
+ }
+ }
+}
+
+static COUNT
+initialize_cannon (ELEMENT *ShipPtr, HELEMENT CannonArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = MissileBlock.face;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = DRUUGE_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ CannonArray[0] = initialize_missile (&MissileBlock);
+
+ if (CannonArray[0])
+ {
+ ELEMENT *CannonPtr;
+
+ LockElement (CannonArray[0], &CannonPtr);
+ CannonPtr->collision_func = cannon_collision;
+ UnlockElement (CannonArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+druuge_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ UWORD ship_flags = 0;
+ STARSHIP *StarShipPtr;
+ STARSHIP *EnemyStarShipPtr = NULL;
+ EVALUATE_DESC *lpEvalDesc;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED)
+ lpEvalDesc->MoveState = ENTICE;
+ else if (lpEvalDesc->ObjectPtr
+ && lpEvalDesc->which_turn <= WORLD_TO_TURN (MISSILE_RANGE * 3 / 4))
+ {
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ ship_flags = EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags;
+ EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags &=
+ ~(FIRES_FORE | FIRES_RIGHT | FIRES_AFT | FIRES_LEFT);
+
+ lpEvalDesc->MoveState = PURSUE;
+ if (ShipPtr->thrust_wait == 0)
+ ++ShipPtr->thrust_wait;
+ }
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+ if (EnemyStarShipPtr)
+ {
+ EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags = ship_flags;
+ }
+
+ if (!(StarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED)
+ && (lpEvalDesc->which_turn <= 12
+ || (
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 6
+ )))
+ {
+ StarShipPtr->ship_input_state |= WEAPON;
+ if (ShipPtr->thrust_wait < WEAPON_WAIT + 1)
+ ShipPtr->thrust_wait = WEAPON_WAIT + 1;
+ }
+
+
+ if ((StarShipPtr->ship_input_state & WEAPON)
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level < WEAPON_ENERGY_COST
+ && ShipPtr->crew_level > 1)
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+}
+
+static void
+druuge_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ /* if just fired cannon */
+ if ((StarShipPtr->cur_status_flags & WEAPON)
+ && StarShipPtr->weapon_counter ==
+ StarShipPtr->RaceDescPtr->characteristics.weapon_wait)
+ {
+ COUNT angle;
+ SIZE cur_delta_x, cur_delta_y;
+
+ StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED;
+
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE;
+ DeltaVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, RECOIL_VELOCITY),
+ SINE (angle, RECOIL_VELOCITY));
+ GetCurrentVelocityComponents (&ElementPtr->velocity,
+ &cur_delta_x, &cur_delta_y);
+ if ((long)cur_delta_x * (long)cur_delta_x
+ + (long)cur_delta_y * (long)cur_delta_y
+ > (long)MAX_RECOIL_VELOCITY * (long)MAX_RECOIL_VELOCITY)
+ {
+ angle = ARCTAN (cur_delta_x, cur_delta_y);
+ SetVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, MAX_RECOIL_VELOCITY),
+ SINE (angle, MAX_RECOIL_VELOCITY));
+ }
+ }
+}
+
+static void
+druuge_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->cur_status_flags & SPECIAL)
+ {
+ if (StarShipPtr->special_counter
+ || ElementPtr->crew_level == 1
+ || StarShipPtr->RaceDescPtr->ship_info.energy_level
+ == StarShipPtr->RaceDescPtr->ship_info.max_energy)
+ StarShipPtr->cur_status_flags &= ~SPECIAL;
+ else
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* BURN UP CREW */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+
+ DeltaCrew (ElementPtr, -1);
+ DeltaEnergy (ElementPtr, SPECIAL_ENERGY_COST);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+ }
+}
+
+RACE_DESC*
+init_druuge (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ druuge_desc.preprocess_func = druuge_preprocess;
+ druuge_desc.postprocess_func = druuge_postprocess;
+ druuge_desc.init_weapon_func = initialize_cannon;
+ druuge_desc.cyborg_control.intelligence_func = druuge_intelligence;
+
+ RaceDescPtr = &druuge_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/druuge/druuge.h b/src/uqm/ships/druuge/druuge.h
new file mode 100644
index 0000000..f332bc3
--- /dev/null
+++ b/src/uqm/ships/druuge/druuge.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DRUUGE_H
+#define DRUUGE_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_druuge (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* DRUUGE_H */
+
diff --git a/src/uqm/ships/druuge/icode.h b/src/uqm/ships/druuge/icode.h
new file mode 100644
index 0000000..8599490
--- /dev/null
+++ b/src/uqm/ships/druuge/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define DRUUGE_CODE "ship.druuge.code"
diff --git a/src/uqm/ships/druuge/resinst.h b/src/uqm/ships/druuge/resinst.h
new file mode 100644
index 0000000..2213ad9
--- /dev/null
+++ b/src/uqm/ships/druuge/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define DRUUGE_BIG_MASK_PMAP_ANIM "ship.druuge.graphics.mauler.large"
+#define DRUUGE_CANNON_BIG_MASK_PMAP_ANIM "ship.druuge.graphics.cannon.large"
+#define DRUUGE_CANNON_MED_MASK_PMAP_ANIM "ship.druuge.graphics.cannon.medium"
+#define DRUUGE_CANNON_SML_MASK_PMAP_ANIM "ship.druuge.graphics.cannon.small"
+#define DRUUGE_CAPT_MASK_PMAP_ANIM "ship.druuge.graphics.captain"
+#define DRUUGE_ICON_MASK_PMAP_ANIM "ship.druuge.icons"
+#define DRUUGE_MED_MASK_PMAP_ANIM "ship.druuge.graphics.mauler.medium"
+#define DRUUGE_MICON_MASK_PMAP_ANIM "ship.druuge.meleeicons"
+#define DRUUGE_RACE_STRINGS "ship.druuge.text"
+#define DRUUGE_SHIP_SOUNDS "ship.druuge.sounds"
+#define DRUUGE_SML_MASK_PMAP_ANIM "ship.druuge.graphics.mauler.small"
+#define DRUUGE_VICTORY_SONG "ship.druuge.ditty"
diff --git a/src/uqm/ships/human/Makeinfo b/src/uqm/ships/human/Makeinfo
new file mode 100644
index 0000000..f80f814
--- /dev/null
+++ b/src/uqm/ships/human/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="human.c"
+uqm_HFILES="human.h icode.h resinst.h"
diff --git a/src/uqm/ships/human/human.c b/src/uqm/ships/human/human.c
new file mode 100644
index 0000000..354486d
--- /dev/null
+++ b/src/uqm/ships/human/human.c
@@ -0,0 +1,360 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "human.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/globdata.h"
+
+// Core characteristics
+#define MAX_CREW 18
+#define MAX_ENERGY 18
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 8
+#define MAX_THRUST /* DISPLAY_TO_WORLD (6) */ 24
+#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 3
+#define THRUST_WAIT 4
+#define TURN_WAIT 1
+#define SHIP_MASS 6
+
+// Nuke
+#define WEAPON_ENERGY_COST 9
+#define WEAPON_WAIT 10
+#define HUMAN_OFFSET 42
+#define NUKE_OFFSET 8
+#define MIN_MISSILE_SPEED DISPLAY_TO_WORLD (10)
+#define MAX_MISSILE_SPEED DISPLAY_TO_WORLD (20)
+#define MISSILE_SPEED (MAX_THRUST >= MIN_MISSILE_SPEED ? \
+ MAX_THRUST : MIN_MISSILE_SPEED)
+#define THRUST_SCALE DISPLAY_TO_WORLD (1)
+#define MISSILE_LIFE 60
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 4
+#define TRACK_WAIT 3
+
+// Point-Defense Laser
+#define SPECIAL_ENERGY_COST 4
+#define SPECIAL_WAIT 9
+#define LASER_RANGE (UWORD)100
+
+static RACE_DESC human_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_WEAPON | POINT_DEFENSE,
+ 11, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ HUMAN_RACE_STRINGS,
+ HUMAN_ICON_MASK_PMAP_ANIM,
+ HUMAN_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 1752, 1450,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ HUMAN_BIG_MASK_PMAP_ANIM,
+ HUMAN_MED_MASK_PMAP_ANIM,
+ HUMAN_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SATURN_BIG_MASK_PMAP_ANIM,
+ SATURN_MED_MASK_PMAP_ANIM,
+ SATURN_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ HUMAN_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ HUMAN_VICTORY_SONG,
+ HUMAN_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ LONG_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+nuke_preprocess (ELEMENT *ElementPtr)
+{
+ COUNT facing;
+
+ facing = GetFrameIndex (ElementPtr->next.image.frame);
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ if (TrackShip (ElementPtr, &facing) > 0)
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->next.image.frame,
+ facing);
+ ElementPtr->state_flags |= CHANGING;
+ }
+
+ ElementPtr->turn_wait = TRACK_WAIT;
+ }
+
+ {
+ SIZE speed;
+
+ if ((speed = MISSILE_SPEED +
+ ((MISSILE_LIFE - ElementPtr->life_span) *
+ THRUST_SCALE)) > MAX_MISSILE_SPEED)
+ speed = MAX_MISSILE_SPEED;
+ SetVelocityVector (&ElementPtr->velocity,
+ speed, facing);
+ }
+}
+
+static void
+spawn_point_defense (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ HELEMENT hDefense;
+
+ hDefense = AllocElement ();
+ if (hDefense)
+ {
+ ELEMENT *DefensePtr;
+
+ LockElement (hDefense, &DefensePtr);
+ DefensePtr->playerNr = ElementPtr->playerNr;
+ DefensePtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
+ DefensePtr->death_func = spawn_point_defense;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ SetElementStarShip (DefensePtr, StarShipPtr);
+ UnlockElement (hDefense);
+
+ PutElement (hDefense);
+ }
+ }
+ else
+ {
+ BOOLEAN PaidFor;
+ HELEMENT hObject, hNextObject;
+ ELEMENT *ShipPtr;
+
+ PaidFor = FALSE;
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ for (hObject = GetTailElement (); hObject; hObject = hNextObject)
+ {
+ ELEMENT *ObjectPtr;
+
+ LockElement (hObject, &ObjectPtr);
+ hNextObject = GetPredElement (ObjectPtr);
+ if (ObjectPtr != ShipPtr && CollidingElement (ObjectPtr) &&
+ !OBJECT_CLOAKED (ObjectPtr))
+ {
+ SIZE delta_x, delta_y;
+
+ delta_x = ObjectPtr->next.location.x -
+ ShipPtr->next.location.x;
+ delta_y = ObjectPtr->next.location.y -
+ ShipPtr->next.location.y;
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x = WORLD_TO_DISPLAY (delta_x);
+ delta_y = WORLD_TO_DISPLAY (delta_y);
+ if ((UWORD)delta_x <= LASER_RANGE &&
+ (UWORD)delta_y <= LASER_RANGE &&
+ (UWORD)delta_x * (UWORD)delta_x +
+ (UWORD)delta_y * (UWORD)delta_y <=
+ LASER_RANGE * LASER_RANGE)
+ {
+ HELEMENT hPointDefense;
+ LASER_BLOCK LaserBlock;
+
+ if (!PaidFor)
+ {
+ if (!DeltaEnergy (ShipPtr, -SPECIAL_ENERGY_COST))
+ break;
+
+ ProcessSound (SetAbsSoundIndex (
+ /* POINT_DEFENSE_LASER */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ PaidFor = TRUE;
+ }
+
+ LaserBlock.cx = ShipPtr->next.location.x;
+ LaserBlock.cy = ShipPtr->next.location.y;
+ LaserBlock.face = 0;
+ LaserBlock.ex = ObjectPtr->next.location.x
+ - ShipPtr->next.location.x;
+ LaserBlock.ey = ObjectPtr->next.location.y
+ - ShipPtr->next.location.y;
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = 0;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F);
+ hPointDefense = initialize_laser (&LaserBlock);
+ if (hPointDefense)
+ {
+ ELEMENT *PDPtr;
+
+ LockElement (hPointDefense, &PDPtr);
+ SetElementStarShip (PDPtr, StarShipPtr);
+ PDPtr->hTarget = 0;
+ UnlockElement (hPointDefense);
+
+ PutElement (hPointDefense);
+ }
+ }
+ }
+ UnlockElement (hObject);
+ }
+ UnlockElement (StarShipPtr->hShip);
+ }
+}
+
+static COUNT
+initialize_nuke (ELEMENT *ShipPtr, HELEMENT NukeArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = 0;
+ MissileBlock.pixoffs = HUMAN_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = nuke_preprocess;
+ MissileBlock.blast_offs = NUKE_OFFSET;
+ NukeArray[0] = initialize_missile (&MissileBlock);
+
+ if (NukeArray[0])
+ {
+ ELEMENT *NukePtr;
+
+ LockElement (NukeArray[0], &NukePtr);
+ NukePtr->turn_wait = TRACK_WAIT;
+ UnlockElement (NukeArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+human_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter == 0
+ && ((ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr != NULL
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 2)
+ || (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr != NULL
+ && ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn <= 4)))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr = NULL;
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ if (StarShipPtr->weapon_counter == 0)
+ {
+ if (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr
+ && (!(StarShipPtr->ship_input_state & (LEFT | RIGHT /* | THRUST */))
+ || ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn <= 12))
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+}
+
+static void
+human_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0)
+ {
+ spawn_point_defense (ElementPtr);
+ }
+}
+
+RACE_DESC*
+init_human (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ human_desc.postprocess_func = human_postprocess;
+ human_desc.init_weapon_func = initialize_nuke;
+ human_desc.cyborg_control.intelligence_func = human_intelligence;
+
+ RaceDescPtr = &human_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/human/human.h b/src/uqm/ships/human/human.h
new file mode 100644
index 0000000..6f7314d
--- /dev/null
+++ b/src/uqm/ships/human/human.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef HUMAN_H
+#define HUMAN_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_human (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* HUMAN_H */
+
diff --git a/src/uqm/ships/human/icode.h b/src/uqm/ships/human/icode.h
new file mode 100644
index 0000000..acfd62e
--- /dev/null
+++ b/src/uqm/ships/human/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define HUMAN_CODE "ship.earthling.code"
diff --git a/src/uqm/ships/human/resinst.h b/src/uqm/ships/human/resinst.h
new file mode 100644
index 0000000..3d4022e
--- /dev/null
+++ b/src/uqm/ships/human/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define HUMAN_BIG_MASK_PMAP_ANIM "ship.earthling.graphics.human.large"
+#define HUMAN_CAPTAIN_MASK_PMAP_ANIM "ship.earthling.graphics.captain"
+#define HUMAN_ICON_MASK_PMAP_ANIM "ship.earthling.icons"
+#define HUMAN_MED_MASK_PMAP_ANIM "ship.earthling.graphics.human.medium"
+#define HUMAN_MICON_MASK_PMAP_ANIM "ship.earthling.meleeicons"
+#define HUMAN_RACE_STRINGS "ship.earthling.text"
+#define HUMAN_SHIP_SOUNDS "ship.earthling.sounds"
+#define HUMAN_SML_MASK_PMAP_ANIM "ship.earthling.graphics.human.small"
+#define HUMAN_VICTORY_SONG "ship.earthling.ditty"
+#define SATURN_BIG_MASK_PMAP_ANIM "ship.earthling.graphics.saturn.large"
+#define SATURN_MED_MASK_PMAP_ANIM "ship.earthling.graphics.saturn.medium"
+#define SATURN_SML_MASK_PMAP_ANIM "ship.earthling.graphics.saturn.small"
diff --git a/src/uqm/ships/ilwrath/Makeinfo b/src/uqm/ships/ilwrath/Makeinfo
new file mode 100644
index 0000000..cbc8f69
--- /dev/null
+++ b/src/uqm/ships/ilwrath/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="ilwrath.c"
+uqm_HFILES="icode.h ilwrath.h resinst.h"
diff --git a/src/uqm/ships/ilwrath/icode.h b/src/uqm/ships/ilwrath/icode.h
new file mode 100644
index 0000000..fa78adc
--- /dev/null
+++ b/src/uqm/ships/ilwrath/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ILWRATH_CODE "ship.ilwrath.code"
diff --git a/src/uqm/ships/ilwrath/ilwrath.c b/src/uqm/ships/ilwrath/ilwrath.c
new file mode 100644
index 0000000..3947081
--- /dev/null
+++ b/src/uqm/ships/ilwrath/ilwrath.c
@@ -0,0 +1,409 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "ilwrath.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/globdata.h"
+
+
+// Core characteristics
+#define MAX_CREW 22
+#define MAX_ENERGY 16
+#define ENERGY_REGENERATION 4
+#define ENERGY_WAIT 4
+#define MAX_THRUST 25
+#define THRUST_INCREMENT 5
+#define THRUST_WAIT 0
+#define TURN_WAIT 2
+#define SHIP_MASS 7
+#define LOOK_AHEAD 4
+ /* Controls how much the auto-turn will attempt to "lead"
+ * its target. */
+
+// Hellfire Spout
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 0
+#define MISSILE_LIFE 8
+#define ILWRATH_OFFSET 29
+#define MISSILE_SPEED MAX_THRUST
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+#define MISSILE_OFFSET 0
+
+// Cloaking Device
+#define SPECIAL_ENERGY_COST 3
+#define SPECIAL_WAIT 13
+
+static RACE_DESC ilwrath_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 10, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ ILWRATH_RACE_STRINGS,
+ ILWRATH_ICON_MASK_PMAP_ANIM,
+ ILWRATH_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 1410 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 48, 1700,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ ILWRATH_BIG_MASK_PMAP_ANIM,
+ ILWRATH_MED_MASK_PMAP_ANIM,
+ ILWRATH_SML_MASK_PMAP_ANIM,
+ },
+ {
+ FIRE_BIG_MASK_PMAP_ANIM,
+ FIRE_MED_MASK_PMAP_ANIM,
+ FIRE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ ILWRATH_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ ILWRATH_VICTORY_SONG,
+ ILWRATH_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+flame_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+static void
+flame_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->state_flags &= ~DISAPPEARING;
+ ElementPtr0->state_flags |= NONSOLID;
+}
+
+static void
+ilwrath_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ lpEvalDesc->MoveState = PURSUE;
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->which_turn <= 10)
+ /* don't want to dodge when you could be flaming */
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr = 0;
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (lpEvalDesc->ObjectPtr
+ && (lpEvalDesc->which_turn <= 6
+ || (lpEvalDesc->which_turn <= 10
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].which_turn <= 10)))
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (OBJECT_CLOAKED (ShipPtr))
+ {
+ StarShipPtr->ship_input_state &= ~LEFT | RIGHT;
+ StarShipPtr->ship_input_state |= THRUST;
+ }
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+ else if (StarShipPtr->special_counter == 0
+ && (LOBYTE (GLOBAL (CurrentActivity)) != IN_ENCOUNTER
+ || !GET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER)))
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (!OBJECT_CLOAKED (ShipPtr)
+ && !(StarShipPtr->ship_input_state & WEAPON))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+}
+
+static COUNT
+initialize_flame (ELEMENT *ShipPtr, HELEMENT FlameArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = ILWRATH_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = flame_preprocess;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ FlameArray[0] = initialize_missile (&MissileBlock);
+
+ if (FlameArray[0])
+ {
+ SIZE dx, dy;
+ ELEMENT *FlamePtr;
+
+ LockElement (FlameArray[0], &FlamePtr);
+ GetCurrentVelocityComponents (&ShipPtr->velocity, &dx, &dy);
+ DeltaVelocityComponents (&FlamePtr->velocity, dx, dy);
+ FlamePtr->current.location.x -= VELOCITY_TO_WORLD (dx);
+ FlamePtr->current.location.y -= VELOCITY_TO_WORLD (dy);
+
+ FlamePtr->collision_func = flame_collision;
+ FlamePtr->turn_wait = 0;
+ UnlockElement (FlameArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+ilwrath_preprocess (ELEMENT *ElementPtr)
+{
+ STATUS_FLAGS status_flags;
+ STARSHIP *StarShipPtr;
+ PRIMITIVE *lpPrim;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ status_flags = StarShipPtr->cur_status_flags;
+ lpPrim = &(GLOBAL (DisplayArray))[ElementPtr->PrimIndex];
+ if (GetPrimType (lpPrim) == STAMPFILL_PRIM)
+ {
+ Color color;
+ BOOLEAN weapon_discharge;
+
+ color = GetPrimColor (lpPrim);
+ weapon_discharge = ((status_flags & WEAPON)
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >= WEAPON_ENERGY_COST);
+ if (weapon_discharge
+ || (StarShipPtr->special_counter == 0
+ && ((status_flags & SPECIAL) ||
+ !sameColor (color, BLACK_COLOR))))
+ {
+ if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)))
+ SetPrimType (lpPrim, STAMP_PRIM);
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09));
+ else
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* CLOAKING_OFF */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), ElementPtr);
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01));
+ if (weapon_discharge)
+ {
+ COUNT facing;
+
+ facing = StarShipPtr->ShipFacing;
+ if (TrackShip (ElementPtr, &facing) >= 0)
+ {
+ ELEMENT *eptr;
+ SIZE dx0, dy0, dx1, dy1;
+ VELOCITY_DESC v;
+
+ LockElement (ElementPtr->hTarget, &eptr);
+ v = eptr->velocity;
+ GetNextVelocityComponents (&v, &dx0, &dy0, LOOK_AHEAD);
+ v = ElementPtr->velocity;
+ GetNextVelocityComponents (&v, &dx1, &dy1, LOOK_AHEAD);
+ dx0 = (eptr->current.location.x + dx0)
+ - (ElementPtr->current.location.x + dx1);
+ dy0 = (eptr->current.location.y + dy0)
+ - (ElementPtr->current.location.y + dy1);
+ UnlockElement (ElementPtr->hTarget);
+
+ StarShipPtr->ShipFacing =
+ NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (dx0, dy0))
+ );
+#ifdef NOTYET
+ if (ElementPtr->thrust_wait == 0
+ && (StarShipPtr->cur_status_flags & THRUST))
+ {
+ COUNT last_facing;
+
+ do
+ {
+ VELOCITY_DESC temp_v;
+
+ last_facing = StarShipPtr->ShipFacing;
+ inertial_thrust (ElementPtr);
+ temp_v = ElementPtr->velocity;
+ ElementPtr->velocity = v;
+
+ dx0 += dx1;
+ dy0 += dy1;
+ GetNextVelocityComponents (&temp_v,
+ &dx1, &dy1, LOOK_AHEAD);
+ dx0 -= dx1;
+ dy0 -= dy1;
+ StarShipPtr->ShipFacing =
+ NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (dx0, dy0))
+ );
+ } while (StarShipPtr->ShipFacing != last_facing);
+ }
+#endif /* NOTYET */
+ if (ElementPtr->turn_wait == 0)
+ ++ElementPtr->turn_wait;
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->next.image.frame,
+ StarShipPtr->ShipFacing);
+ }
+ ElementPtr->hTarget = 0;
+ }
+ }
+
+ ElementPtr->state_flags |= CHANGING;
+ status_flags &= ~SPECIAL;
+ StarShipPtr->special_counter = 0;
+ }
+ else if (!sameColor (color, BLACK_COLOR))
+ {
+ if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)))
+ {
+ SetPrimColor (lpPrim, BLACK_COLOR);
+ Untarget (ElementPtr);
+ }
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03));
+ else
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B));
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+ }
+
+ if ((status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F));
+ SetPrimType (lpPrim, STAMPFILL_PRIM);
+
+ ProcessSound (SetAbsSoundIndex (
+ /* CLOAKING_ON */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
+ ElementPtr);
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+RACE_DESC*
+init_ilwrath (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ ilwrath_desc.preprocess_func = ilwrath_preprocess;
+ ilwrath_desc.init_weapon_func = initialize_flame;
+ ilwrath_desc.cyborg_control.intelligence_func = ilwrath_intelligence;
+
+ RaceDescPtr = &ilwrath_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/ilwrath/ilwrath.h b/src/uqm/ships/ilwrath/ilwrath.h
new file mode 100644
index 0000000..a442b9d
--- /dev/null
+++ b/src/uqm/ships/ilwrath/ilwrath.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ILWRATH_H
+#define ILWRATH_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_ilwrath (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ILWRATH_H */
+
diff --git a/src/uqm/ships/ilwrath/resinst.h b/src/uqm/ships/ilwrath/resinst.h
new file mode 100644
index 0000000..46cb53a
--- /dev/null
+++ b/src/uqm/ships/ilwrath/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define FIRE_BIG_MASK_PMAP_ANIM "ship.ilwrath.graphics.fire.large"
+#define FIRE_MED_MASK_PMAP_ANIM "ship.ilwrath.graphics.fire.medium"
+#define FIRE_SML_MASK_PMAP_ANIM "ship.ilwrath.graphics.fire.small"
+#define ILWRATH_BIG_MASK_PMAP_ANIM "ship.ilwrath.graphics.avenger.large"
+#define ILWRATH_CAPTAIN_MASK_PMAP_ANIM "ship.ilwrath.graphics.captain"
+#define ILWRATH_ICON_MASK_PMAP_ANIM "ship.ilwrath.icons"
+#define ILWRATH_MED_MASK_PMAP_ANIM "ship.ilwrath.graphics.avenger.medium"
+#define ILWRATH_MICON_MASK_PMAP_ANIM "ship.ilwrath.meleeicons"
+#define ILWRATH_RACE_STRINGS "ship.ilwrath.text"
+#define ILWRATH_SHIP_SOUNDS "ship.ilwrath.sounds"
+#define ILWRATH_SML_MASK_PMAP_ANIM "ship.ilwrath.graphics.avenger.small"
+#define ILWRATH_VICTORY_SONG "ship.ilwrath.ditty"
diff --git a/src/uqm/ships/lastbat/Makeinfo b/src/uqm/ships/lastbat/Makeinfo
new file mode 100644
index 0000000..589d8d0
--- /dev/null
+++ b/src/uqm/ships/lastbat/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="lastbat.c"
+uqm_HFILES="icode.h lastbat.h resinst.h"
diff --git a/src/uqm/ships/lastbat/icode.h b/src/uqm/ships/lastbat/icode.h
new file mode 100644
index 0000000..087c891
--- /dev/null
+++ b/src/uqm/ships/lastbat/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SAMATRA_CODE "ship.samatra.code"
diff --git a/src/uqm/ships/lastbat/lastbat.c b/src/uqm/ships/lastbat/lastbat.c
new file mode 100644
index 0000000..9d44742
--- /dev/null
+++ b/src/uqm/ships/lastbat/lastbat.c
@@ -0,0 +1,926 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "lastbat.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/globdata.h"
+#include "uqm/battle.h"
+ // For BATTLE_FRAME_RATE
+#include "libs/mathlib.h"
+#include "libs/timelib.h"
+
+#define num_generators characteristics.max_thrust
+
+// Core characteristics
+#define MAX_CREW 1
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 6
+#define MAX_THRUST 0
+#define THRUST_INCREMENT 0
+#define TURN_WAIT 0
+#define THRUST_WAIT 0
+#define SHIP_MASS (MAX_SHIP_MASS * 10)
+#define TURRET_WAIT 0 /* Controls animation of the Sa-Matra's central
+ * 'furnace', a new frame is displayed once every
+ * TURRET_WAIT frames. */
+
+// Yellow comet
+#define WEAPON_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 10)
+#define COMET_DAMAGE 2
+#define COMET_OFFSET 0
+#define COMET_HITS 12
+#define COMET_SPEED DISPLAY_TO_WORLD (12)
+#define COMET_LIFE 2
+#define COMET_TURN_WAIT 3
+#define MAX_COMETS 3
+#define WEAPON_ENERGY_COST 2
+ /* Used for samatra_desc.weapon_energy_cost, but the value isn't
+ * actually used. */
+
+// Green sentinel
+#define SPECIAL_WAIT ((ONE_SECOND / BATTLE_FRAME_RATE) * 3)
+#define SENTINEL_SPEED DISPLAY_TO_WORLD (8)
+#define SENTINEL_LIFE 2
+#define SENTINEL_OFFSET 0
+#define SENTINEL_HITS 10
+#define SENTINEL_DAMAGE 1
+#define TRACK_WAIT 1
+#define ANIMATION_WAIT 1
+#define RECOIL_VELOCITY WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (10))
+#define MAX_RECOIL_VELOCITY (RECOIL_VELOCITY * 4)
+#define MAX_SENTINELS 4
+#define SPECIAL_ENERGY_COST 3
+ /* Used for samatra_desc.special_energy_cost, but the value isn't
+ * actually used. */
+
+// Blue force field
+#define GATE_DAMAGE 1
+#define GATE_HITS 100
+
+// Red generators
+#define GENERATOR_HITS 15
+#define MAX_GENERATORS 8
+
+static RACE_DESC samatra_desc =
+{
+ { /* SHIP_INFO */
+ /* FIRES_FORE | */ IMMEDIATE_WEAPON | CREW_IMMUNE,
+ 16, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SAMATRA_BIG_MASK_ANIM,
+ SAMATRA_MED_MASK_PMAP_ANIM,
+ SAMATRA_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SENTINEL_BIG_MASK_ANIM,
+ SENTINEL_MED_MASK_PMAP_ANIM,
+ SENTINEL_SML_MASK_PMAP_ANIM,
+ },
+ {
+ GENERATOR_BIG_MASK_ANIM,
+ GENERATOR_MED_MASK_PMAP_ANIM,
+ GENERATOR_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SAMATRA_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ NULL_RESOURCE,
+ SAMATRA_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ 0,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static HELEMENT spawn_comet (ELEMENT *ElementPtr);
+
+static void
+comet_preprocess (ELEMENT *ElementPtr)
+{
+ COUNT frame_index;
+
+ frame_index = GetFrameIndex (ElementPtr->current.image.frame) + 1;
+ if (frame_index < 29)
+ {
+ if (frame_index == 25)
+ {
+ SIZE cur_delta_x, cur_delta_y;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ++StarShipPtr->RaceDescPtr->characteristics.weapon_wait;
+ spawn_comet (ElementPtr);
+ ElementPtr->state_flags |= NONSOLID;
+
+ GetCurrentVelocityComponents (&ElementPtr->velocity,
+ &cur_delta_x, &cur_delta_y);
+ SetVelocityComponents (&ElementPtr->velocity,
+ cur_delta_x / 2, cur_delta_y / 2);
+ }
+ ++ElementPtr->life_span;
+ }
+
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (
+ ElementPtr->current.image.frame, frame_index
+ );
+ ElementPtr->state_flags |= CHANGING;
+}
+
+static void
+comet_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->playerNr == RPG_PLAYER_NUM)
+ {
+ BYTE old_hits;
+ COUNT old_life;
+ HELEMENT hBlastElement;
+
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ ElementPtr0->mass_points = COMET_DAMAGE;
+ else
+ ElementPtr0->mass_points = 50;
+
+ old_hits = ElementPtr0->hit_points;
+ old_life = ElementPtr0->life_span;
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ ElementPtr0->hit_points = old_hits;
+ ElementPtr0->life_span = old_life;
+ ElementPtr0->state_flags &= ~(DISAPPEARING | NONSOLID | COLLISION);
+
+ if (hBlastElement)
+ {
+ RemoveElement (hBlastElement);
+ FreeElement (hBlastElement);
+ }
+ }
+
+ if (ElementPtr0->state_flags & DISAPPEARING)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ --StarShipPtr->RaceDescPtr->characteristics.weapon_wait;
+ }
+ }
+}
+
+static HELEMENT
+spawn_comet (ELEMENT *ElementPtr)
+{
+ MISSILE_BLOCK MissileBlock;
+ HELEMENT hComet;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = 0;
+ MissileBlock.index = 24;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = 0;
+ MissileBlock.speed = 0;
+ MissileBlock.hit_points = COMET_HITS;
+ MissileBlock.damage = COMET_DAMAGE;
+ MissileBlock.life = COMET_LIFE;
+ MissileBlock.preprocess_func = comet_preprocess;
+ MissileBlock.blast_offs = COMET_OFFSET;
+ hComet = initialize_missile (&MissileBlock);
+
+ if (hComet)
+ {
+ ELEMENT *CometPtr;
+
+ PutElement (hComet);
+
+ LockElement (hComet, &CometPtr);
+ CometPtr->collision_func = comet_collision;
+ SetElementStarShip (CometPtr, StarShipPtr);
+ {
+ COUNT facing;
+
+ CometPtr->turn_wait = ElementPtr->turn_wait;
+ CometPtr->hTarget = ElementPtr->hTarget;
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ CometPtr->turn_wait = 0;
+ facing = (COUNT)TFB_Random ();
+ SetVelocityVector (&CometPtr->velocity,
+ COMET_SPEED, facing);
+ }
+ else
+ {
+ CometPtr->velocity = ElementPtr->velocity;
+ CometPtr->hit_points = ElementPtr->hit_points;
+ facing = ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&CometPtr->velocity)
+ );
+ }
+
+ if (CometPtr->turn_wait)
+ --CometPtr->turn_wait;
+ else
+ {
+ facing = NORMALIZE_FACING (facing);
+ if (TrackShip (CometPtr, &facing) > 0)
+ SetVelocityVector (&CometPtr->velocity,
+ COMET_SPEED, facing);
+ CometPtr->turn_wait = COMET_TURN_WAIT;
+ }
+ }
+ UnlockElement (hComet);
+ }
+
+ return (hComet);
+}
+
+static void
+turret_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) % 10) + 1);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = TURRET_WAIT;
+ }
+}
+
+static void
+gate_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->playerNr == RPG_PLAYER_NUM)
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->num_generators == 0)
+ {
+ if (!(ElementPtr1->state_flags & FINITE_LIFE))
+ ElementPtr0->state_flags |= COLLISION;
+
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && GetPrimType (
+ &GLOBAL (DisplayArray[ElementPtr0->PrimIndex])
+ ) == STAMPFILL_PRIM
+ && GET_GAME_STATE (BOMB_CARRIER))
+ {
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ }
+ }
+ else
+ {
+ HELEMENT hBlastElement;
+
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ ElementPtr0->mass_points = GATE_DAMAGE;
+ else
+ ElementPtr0->mass_points = 50;
+
+ ElementPtr0->hit_points = GATE_HITS;
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->state_flags &= ~(DISAPPEARING | NONSOLID | COLLISION);
+ ElementPtr0->life_span = NORMAL_LIFE;
+
+ if (hBlastElement)
+ {
+ RemoveElement (hBlastElement);
+ FreeElement (hBlastElement);
+ }
+ }
+ }
+}
+
+static void
+gate_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->num_generators == 0)
+ {
+ ElementPtr->mass_points = SHIP_MASS;
+ ElementPtr->state_flags &= ~FINITE_LIFE;
+ ElementPtr->life_span = NORMAL_LIFE + 1;
+ ElementPtr->preprocess_func = 0;
+ SetPrimColor (
+ &GLOBAL (DisplayArray[ElementPtr->PrimIndex]),
+ BLACK_COLOR
+ );
+ SetPrimType (
+ &GLOBAL (DisplayArray[ElementPtr->PrimIndex]),
+ STAMPFILL_PRIM
+ );
+ }
+ else
+ {
+ ++ElementPtr->life_span;
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ if (GetFrameIndex (ElementPtr->next.image.frame) == 0)
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (
+ ElementPtr->next.image.frame, 11
+ );
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static void
+generator_death (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & FINITE_LIFE))
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ --StarShipPtr->RaceDescPtr->num_generators;
+ ElementPtr->state_flags |= FINITE_LIFE | NONSOLID;
+ ElementPtr->preprocess_func = 0;
+ ElementPtr->turn_wait = 12;
+ ElementPtr->thrust_wait = 0;
+
+ ElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame, 10 - 1);
+ }
+
+ if (ElementPtr->thrust_wait)
+ {
+ --ElementPtr->thrust_wait;
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= CHANGING;
+ ++ElementPtr->life_span;
+ }
+ else if (ElementPtr->turn_wait--)
+ {
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= CHANGING;
+ ++ElementPtr->life_span;
+
+ ElementPtr->next.image.frame = IncFrameIndex (
+ ElementPtr->current.image.frame
+ );
+
+ ElementPtr->thrust_wait = 1;
+ }
+}
+
+static void
+generator_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else if ((ElementPtr->turn_wait =
+ (BYTE)((GENERATOR_HITS
+ - ElementPtr->hit_points) / 5)) < 3)
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) + 1) % 10);
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static void
+generator_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (!(ElementPtr1->state_flags & FINITE_LIFE))
+ {
+ ElementPtr0->state_flags |= COLLISION;
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+sentinel_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ++StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ ++ElementPtr->life_span;
+
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) + 1) % 6);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->thrust_wait = ANIMATION_WAIT;
+ }
+
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT facing;
+ HELEMENT hTarget;
+
+ if (!(ElementPtr->state_flags & NONSOLID))
+ facing = ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)
+ );
+ else
+ {
+ ElementPtr->state_flags &= ~NONSOLID;
+ facing = (COUNT)TFB_Random ();
+ SetVelocityVector (&ElementPtr->velocity,
+ SENTINEL_SPEED, facing);
+ }
+ facing = NORMALIZE_FACING (facing);
+ if (ElementPtr->hTarget == 0)
+ {
+ COUNT f;
+
+ f = facing;
+ TrackShip (ElementPtr, &f);
+ }
+
+ if (ElementPtr->hTarget == 0)
+ hTarget = StarShipPtr->hShip;
+ else if (StarShipPtr->hShip == 0)
+ hTarget = ElementPtr->hTarget;
+ else
+ {
+ SIZE delta_x0, delta_y0, delta_x1, delta_y1;
+ ELEMENT *ShipPtr;
+ ELEMENT *EnemyShipPtr;
+
+ LockElement (ElementPtr->hTarget, &EnemyShipPtr);
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ delta_x0 = ShipPtr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_y0 = ShipPtr->current.location.y
+ - ElementPtr->current.location.y;
+
+ delta_x1 = ShipPtr->current.location.x
+ - EnemyShipPtr->current.location.x;
+ delta_y1 = ShipPtr->current.location.y
+ - EnemyShipPtr->current.location.y;
+ UnlockElement (StarShipPtr->hShip);
+
+ if ((long)delta_x0 * delta_x0
+ + (long)delta_y0 * delta_y0 >
+ (long)delta_x1 * delta_x1
+ + (long)delta_y1 * delta_y1)
+ hTarget = StarShipPtr->hShip;
+ else
+ hTarget = ElementPtr->hTarget;
+
+ UnlockElement (ElementPtr->hTarget);
+ }
+
+ if (hTarget)
+ {
+ COUNT num_frames;
+ SIZE delta_x, delta_y;
+ ELEMENT *TargetPtr;
+ VELOCITY_DESC TargetVelocity;
+
+ LockElement (hTarget, &TargetPtr);
+
+ delta_x = TargetPtr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = TargetPtr->current.location.y
+ - ElementPtr->current.location.y;
+ delta_y = WRAP_DELTA_Y (delta_y);
+
+ if ((num_frames = WORLD_TO_TURN (
+ square_root ((long)delta_x * delta_x
+ + (long)delta_y * delta_y)
+ )) == 0)
+ num_frames = 1;
+
+ TargetVelocity = TargetPtr->velocity;
+ GetNextVelocityComponents (&TargetVelocity,
+ &delta_x, &delta_y, num_frames);
+
+ delta_x = (TargetPtr->current.location.x + delta_x)
+ - ElementPtr->current.location.x;
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = (TargetPtr->current.location.y + delta_y)
+ - ElementPtr->current.location.y;
+ delta_y = WRAP_DELTA_Y (delta_y);
+
+ UnlockElement (hTarget);
+
+ delta_x = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)) - facing
+ );
+
+ if (delta_x > 0)
+ {
+ if (delta_x <= ANGLE_TO_FACING (HALF_CIRCLE))
+ ++facing;
+ else
+ --facing;
+ }
+
+ SetVelocityVector (&ElementPtr->velocity,
+ SENTINEL_SPEED, facing);
+ }
+
+ ElementPtr->turn_wait = TRACK_WAIT;
+ }
+}
+
+static void
+sentinel_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ COUNT angle;
+ STARSHIP *StarShipPtr;
+
+ if (ElementPtr1->playerNr == NPC_PLAYER_NUM)
+ {
+ if (ElementPtr0->preprocess_func == ElementPtr1->preprocess_func
+ && !(ElementPtr0->state_flags & DEFY_PHYSICS)
+ && (pPt0->x != ElementPtr0->IntersectControl.IntersectStamp.origin.x
+ || pPt0->y != ElementPtr0->IntersectControl.IntersectStamp.origin.y))
+ {
+ angle = ARCTAN (pPt0->x - pPt1->x, pPt0->y - pPt1->y);
+
+ SetVelocityComponents (&ElementPtr0->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (SENTINEL_SPEED)),
+ SINE (angle, WORLD_TO_VELOCITY (SENTINEL_SPEED)));
+ ElementPtr0->turn_wait = TRACK_WAIT;
+ ElementPtr0->state_flags |= COLLISION | DEFY_PHYSICS;
+ }
+ }
+ else
+ {
+ BYTE old_hits;
+ COUNT old_life;
+ HELEMENT hBlastElement;
+
+ old_hits = ElementPtr0->hit_points;
+ old_life = ElementPtr0->life_span;
+ ElementPtr0->blast_offset = 0;
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->thrust_wait = 0;
+
+ if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && ElementPtr1->crew_level
+ && !GRAVITY_MASS (ElementPtr1->mass_points + 1))
+ {
+ SIZE cur_delta_x, cur_delta_y;
+
+ ElementPtr0->life_span = old_life;
+ ElementPtr0->hit_points = old_hits;
+ ElementPtr0->state_flags &= ~DISAPPEARING;
+ ElementPtr0->state_flags |= DEFY_PHYSICS;
+ ElementPtr0->turn_wait = (ONE_SECOND / BATTLE_FRAME_RATE) >> 1;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ if (ElementPtr1->turn_wait < COLLISION_TURN_WAIT)
+ ElementPtr1->turn_wait += COLLISION_TURN_WAIT;
+ if (ElementPtr1->thrust_wait < COLLISION_THRUST_WAIT)
+ ElementPtr1->thrust_wait += COLLISION_THRUST_WAIT;
+
+ angle = GetVelocityTravelAngle (&ElementPtr0->velocity);
+ DeltaVelocityComponents (&ElementPtr1->velocity,
+ COSINE (angle, RECOIL_VELOCITY),
+ SINE (angle, RECOIL_VELOCITY));
+ GetCurrentVelocityComponents (&ElementPtr1->velocity,
+ &cur_delta_x, &cur_delta_y);
+ if ((long)cur_delta_x * (long)cur_delta_x
+ + (long)cur_delta_y * (long)cur_delta_y
+ > (long)MAX_RECOIL_VELOCITY * (long)MAX_RECOIL_VELOCITY)
+ {
+ angle = ARCTAN (cur_delta_x, cur_delta_y);
+ SetVelocityComponents (&ElementPtr1->velocity,
+ COSINE (angle, MAX_RECOIL_VELOCITY),
+ SINE (angle, MAX_RECOIL_VELOCITY));
+ }
+
+ ZeroVelocityComponents (&ElementPtr0->velocity);
+ }
+
+ if (ElementPtr0->state_flags & DISAPPEARING)
+ {
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ --StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ if (hBlastElement)
+ {
+ ELEMENT *BlastElementPtr;
+
+ LockElement (hBlastElement, &BlastElementPtr);
+ BlastElementPtr->life_span = 6;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (
+ BlastElementPtr->current.image.farray[0], 6
+ );
+ UnlockElement (hBlastElement);
+ }
+ }
+ }
+}
+
+static void
+samatra_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+}
+
+static void
+samatra_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->num_generators)
+ {
+ if (StarShipPtr->weapon_counter == 0
+ && StarShipPtr->RaceDescPtr->characteristics.weapon_wait < MAX_COMETS
+ && spawn_comet (ElementPtr))
+ {
+ StarShipPtr->weapon_counter = WEAPON_WAIT;
+ }
+
+ if (StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->characteristics.special_wait < MAX_SENTINELS)
+ {
+ MISSILE_BLOCK MissileBlock;
+ HELEMENT hSentinel;
+
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = 0;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = 0;
+ MissileBlock.pixoffs = 0;
+ MissileBlock.speed = SENTINEL_SPEED;
+ MissileBlock.hit_points = SENTINEL_HITS;
+ MissileBlock.damage = SENTINEL_DAMAGE;
+ MissileBlock.life = SENTINEL_LIFE;
+ MissileBlock.preprocess_func = sentinel_preprocess;
+ MissileBlock.blast_offs = SENTINEL_OFFSET;
+ hSentinel = initialize_missile (&MissileBlock);
+
+ if (hSentinel)
+ {
+ ELEMENT *SentinelPtr;
+
+ LockElement (hSentinel, &SentinelPtr);
+ SentinelPtr->collision_func = sentinel_collision;
+ SentinelPtr->turn_wait = TRACK_WAIT + 2;
+ SetElementStarShip (SentinelPtr, StarShipPtr);
+ UnlockElement (hSentinel);
+
+ StarShipPtr->special_counter = SPECIAL_WAIT;
+
+ PutElement (hSentinel);
+ }
+ }
+ }
+}
+
+static void
+samatra_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ StarShipPtr->RaceDescPtr->characteristics.weapon_wait = 0;
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
+ if (!(ElementPtr->state_flags & APPEARING))
+ {
+ ++ElementPtr->turn_wait;
+ ++ElementPtr->thrust_wait;
+ }
+ else
+ {
+ POINT offs[] =
+ {
+ {-127-9, -53+18},
+ { -38-9, -88+18},
+ { 44-9, -85+18},
+ { 127-9, -60+18},
+ { 124-9, 28+18},
+ { 73-9, 61+18},
+ { -87-9, 58+18},
+ {-136-9, 29+18},
+ };
+
+ for (StarShipPtr->RaceDescPtr->num_generators = 0;
+ StarShipPtr->RaceDescPtr->num_generators < MAX_GENERATORS;
+ ++StarShipPtr->RaceDescPtr->num_generators)
+ {
+ HELEMENT hGenerator;
+
+ hGenerator = AllocElement ();
+ if (hGenerator)
+ {
+ ELEMENT *GeneratorPtr;
+
+ LockElement (hGenerator, &GeneratorPtr);
+ GeneratorPtr->hit_points = GENERATOR_HITS;
+ GeneratorPtr->mass_points = MAX_SHIP_MASS * 10;
+ GeneratorPtr->life_span = NORMAL_LIFE;
+ GeneratorPtr->playerNr = ElementPtr->playerNr;
+ GeneratorPtr->state_flags = APPEARING | IGNORE_SIMILAR;
+ SetPrimType (
+ &GLOBAL (DisplayArray[GeneratorPtr->PrimIndex]),
+ STAMP_PRIM
+ );
+ GeneratorPtr->current.location.x =
+ ((LOG_SPACE_WIDTH >> 1)
+ + DISPLAY_TO_WORLD (offs[StarShipPtr->RaceDescPtr->num_generators].x))
+ & ~((SCALED_ONE << MAX_VIS_REDUCTION) - 1);
+ GeneratorPtr->current.location.y =
+ ((LOG_SPACE_HEIGHT >> 1)
+ + DISPLAY_TO_WORLD (offs[StarShipPtr->RaceDescPtr->num_generators].y))
+ & ~((SCALED_ONE << MAX_VIS_REDUCTION) - 1);
+ GeneratorPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ GeneratorPtr->current.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0],
+ (BYTE)TFB_Random () % 10
+ );
+
+ GeneratorPtr->preprocess_func = generator_preprocess;
+ GeneratorPtr->collision_func = generator_collision;
+ GeneratorPtr->death_func = generator_death;
+
+ SetElementStarShip (GeneratorPtr, StarShipPtr);
+ UnlockElement (hGenerator);
+
+ InsertElement (hGenerator, GetHeadElement ());
+ }
+ }
+
+ {
+ HELEMENT hTurret;
+
+ hTurret = AllocElement ();
+ if (hTurret)
+ {
+ ELEMENT *TurretPtr;
+
+ LockElement (hTurret, &TurretPtr);
+ TurretPtr->hit_points = 1;
+ TurretPtr->life_span = NORMAL_LIFE;
+ TurretPtr->playerNr = ElementPtr->playerNr;
+ TurretPtr->state_flags = APPEARING | IGNORE_SIMILAR | NONSOLID;
+ SetPrimType (
+ &GLOBAL (DisplayArray[TurretPtr->PrimIndex]),
+ STAMP_PRIM
+ );
+ TurretPtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ TurretPtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ TurretPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ TurretPtr->current.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship[0], 1
+ );
+
+ TurretPtr->preprocess_func = turret_preprocess;
+
+ SetElementStarShip (TurretPtr, StarShipPtr);
+ UnlockElement (hTurret);
+
+ InsertElement (hTurret, GetSuccElement (ElementPtr));
+ }
+ }
+
+ {
+ HELEMENT hGate;
+
+ hGate = AllocElement ();
+ if (hGate)
+ {
+ ELEMENT *GatePtr;
+
+ LockElement (hGate, &GatePtr);
+ GatePtr->hit_points = GATE_HITS;
+ GatePtr->mass_points = GATE_DAMAGE;
+ GatePtr->life_span = 2;
+ GatePtr->playerNr = ElementPtr->playerNr;
+ GatePtr->state_flags = APPEARING | FINITE_LIFE
+ | IGNORE_SIMILAR;
+ SetPrimType (
+ &GLOBAL (DisplayArray[GatePtr->PrimIndex]),
+ STAMP_PRIM
+ );
+ GatePtr->current.location.x = LOG_SPACE_WIDTH >> 1;
+ GatePtr->current.location.y = LOG_SPACE_HEIGHT >> 1;
+ GatePtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ GatePtr->current.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship[0], 11
+ );
+
+ GatePtr->preprocess_func = gate_preprocess;
+ GatePtr->collision_func = gate_collision;
+
+ SetElementStarShip (GatePtr, StarShipPtr);
+ UnlockElement (hGate);
+
+ InsertElement (hGate, GetSuccElement (ElementPtr));
+ }
+ }
+
+ StarShipPtr->weapon_counter = WEAPON_WAIT >> 1;
+ StarShipPtr->special_counter = SPECIAL_WAIT >> 1;
+ }
+}
+
+RACE_DESC*
+init_samatra (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ samatra_desc.preprocess_func = samatra_preprocess;
+ samatra_desc.postprocess_func = samatra_postprocess;
+ samatra_desc.cyborg_control.intelligence_func = samatra_intelligence;
+
+ RaceDescPtr = &samatra_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/lastbat/lastbat.h b/src/uqm/ships/lastbat/lastbat.h
new file mode 100644
index 0000000..ccda7f1
--- /dev/null
+++ b/src/uqm/ships/lastbat/lastbat.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef LASTBAT_H
+#define LASTBAT_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_samatra (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* LASTBAT_H */
+
diff --git a/src/uqm/ships/lastbat/resinst.h b/src/uqm/ships/lastbat/resinst.h
new file mode 100644
index 0000000..779c00a
--- /dev/null
+++ b/src/uqm/ships/lastbat/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define GENERATOR_BIG_MASK_ANIM "ship.samatra.graphics.generator.large"
+#define GENERATOR_MED_MASK_PMAP_ANIM "ship.samatra.graphics.generator.medium"
+#define GENERATOR_SML_MASK_PMAP_ANIM "ship.samatra.graphics.generator.small"
+#define SAMATRA_BIG_MASK_ANIM "ship.samatra.graphics.samatra.large"
+#define SAMATRA_CAPTAIN_MASK_PMAP_ANIM "ship.samatra.graphics.captain"
+#define SAMATRA_MED_MASK_PMAP_ANIM "ship.samatra.graphics.samatra.medium"
+#define SAMATRA_SHIP_SOUNDS "ship.samatra.sounds"
+#define SAMATRA_SML_MASK_PMAP_ANIM "ship.samatra.graphics.samatra.small"
+#define SAMATRA_VICTORY_SONG "ship.samatra.ditty"
+#define SENTINEL_BIG_MASK_ANIM "ship.samatra.graphics.sentinel.large"
+#define SENTINEL_MED_MASK_PMAP_ANIM "ship.samatra.graphics.sentinel.medium"
+#define SENTINEL_SML_MASK_PMAP_ANIM "ship.samatra.graphics.sentinel.small"
diff --git a/src/uqm/ships/melnorme/Makeinfo b/src/uqm/ships/melnorme/Makeinfo
new file mode 100644
index 0000000..f5bb991
--- /dev/null
+++ b/src/uqm/ships/melnorme/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="melnorme.c"
+uqm_HFILES="icode.h melnorme.h resinst.h"
diff --git a/src/uqm/ships/melnorme/icode.h b/src/uqm/ships/melnorme/icode.h
new file mode 100644
index 0000000..d9dd355
--- /dev/null
+++ b/src/uqm/ships/melnorme/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define MELNORME_CODE "ship.melnorme.code"
diff --git a/src/uqm/ships/melnorme/melnorme.c b/src/uqm/ships/melnorme/melnorme.c
new file mode 100644
index 0000000..8e5ab2b
--- /dev/null
+++ b/src/uqm/ships/melnorme/melnorme.c
@@ -0,0 +1,658 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "melnorme.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST 36
+#define THRUST_INCREMENT 6
+#define THRUST_WAIT 4
+#define TURN_WAIT 4
+#define SHIP_MASS 7
+
+// Blaster Pulse
+#define WEAPON_ENERGY_COST 5
+#define WEAPON_WAIT 1
+#define MELNORME_OFFSET 24
+#define LEVEL_COUNTER 72
+#define MAX_PUMP 4
+#define PUMPUP_SPEED DISPLAY_TO_WORLD (45)
+#define PUMPUP_LIFE 10
+#define PUMPUP_DAMAGE 2
+#define MIN_PUMPITUDE_ANIMS 3
+#define NUM_PUMP_ANIMS 5
+#define REVERSE_DIR (BYTE)(1 << 7)
+
+// Confusion Pulse
+#define SPECIAL_ENERGY_COST 20
+#define SPECIAL_WAIT 20
+#define CMISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define CMISSILE_LIFE 20
+#define CMISSILE_HITS 200
+#define CMISSILE_DAMAGE 0
+#define CMISSILE_OFFSET 4
+
+static RACE_DESC melnorme_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 18, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ MELNORME_RACE_STRINGS,
+ MELNORME_ICON_MASK_PMAP_ANIM,
+ MELNORME_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ INFINITE_RADIUS, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ MAX_X_UNIVERSE >> 1, MAX_Y_UNIVERSE >> 1,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ MELNORME_BIG_MASK_PMAP_ANIM,
+ MELNORME_MED_MASK_PMAP_ANIM,
+ MELNORME_SML_MASK_PMAP_ANIM,
+ },
+ {
+ PUMPUP_BIG_MASK_PMAP_ANIM,
+ PUMPUP_MED_MASK_PMAP_ANIM,
+ PUMPUP_SML_MASK_PMAP_ANIM,
+ },
+ {
+ CONFUSE_BIG_MASK_PMAP_ANIM,
+ CONFUSE_MED_MASK_PMAP_ANIM,
+ CONFUSE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ MELNORME_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ MELNORME_VICTORY_SONG,
+ MELNORME_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ PUMPUP_SPEED * PUMPUP_LIFE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+pump_up_preprocess (ELEMENT *ElementPtr)
+{
+ if (--ElementPtr->thrust_wait & 1)
+ {
+ COUNT frame_index;
+
+ frame_index = GetFrameIndex (ElementPtr->current.image.frame);
+ if (((ElementPtr->turn_wait & REVERSE_DIR)
+ && (frame_index % NUM_PUMP_ANIMS) != 0)
+ || (!(ElementPtr->turn_wait & REVERSE_DIR)
+ && ((frame_index + 1) % NUM_PUMP_ANIMS) == 0))
+ {
+ --frame_index;
+ ElementPtr->turn_wait |= REVERSE_DIR;
+ }
+ else
+ {
+ ++frame_index;
+ ElementPtr->turn_wait &= ~REVERSE_DIR;
+ }
+
+ ElementPtr->next.image.frame = SetAbsFrameIndex (
+ ElementPtr->current.image.frame, frame_index);
+
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static COUNT initialize_pump_up (ELEMENT *ShipPtr, HELEMENT PumpUpArray[]);
+
+static void
+pump_up_postprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ }
+ else
+ {
+ HELEMENT hPumpUp;
+ ELEMENT *EPtr;
+ ELEMENT *ShipPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ initialize_pump_up (ShipPtr, &hPumpUp);
+ DeltaEnergy (ShipPtr, 0);
+ UnlockElement (StarShipPtr->hShip);
+
+ LockElement (hPumpUp, &EPtr);
+
+ EPtr->current.image.frame = ElementPtr->current.image.frame;
+ EPtr->turn_wait = ElementPtr->turn_wait;
+ EPtr->thrust_wait = ElementPtr->thrust_wait;
+ if (--EPtr->thrust_wait == 0)
+ {
+ if ((EPtr->turn_wait & ~REVERSE_DIR) < MAX_PUMP - 1)
+ {
+ ++EPtr->turn_wait;
+ EPtr->current.image.frame = SetRelFrameIndex (
+ EPtr->current.image.frame, NUM_PUMP_ANIMS);
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2),
+ EPtr);
+ }
+ EPtr->thrust_wait = LEVEL_COUNTER;
+ }
+
+ EPtr->mass_points = EPtr->hit_points =
+ (PUMPUP_DAMAGE << (ElementPtr->turn_wait & ~REVERSE_DIR));
+ SetElementStarShip (EPtr, StarShipPtr);
+
+ if (EPtr->thrust_wait & 1)
+ {
+ COUNT frame_index;
+
+ frame_index = GetFrameIndex (EPtr->current.image.frame);
+ if (((EPtr->turn_wait & REVERSE_DIR)
+ && (frame_index % NUM_PUMP_ANIMS) != 0)
+ || (!(EPtr->turn_wait & REVERSE_DIR)
+ && ((frame_index + 1) % NUM_PUMP_ANIMS) == 0))
+ {
+ --frame_index;
+ EPtr->turn_wait |= REVERSE_DIR;
+ }
+ else
+ {
+ ++frame_index;
+ EPtr->turn_wait &= ~REVERSE_DIR;
+ }
+
+ EPtr->current.image.frame = SetAbsFrameIndex (
+ EPtr->current.image.frame, frame_index);
+ }
+
+ if (StarShipPtr->cur_status_flags & StarShipPtr->old_status_flags
+ & WEAPON)
+ {
+ StarShipPtr->weapon_counter = WEAPON_WAIT;
+ }
+ else
+ {
+ COUNT angle;
+
+ EPtr->life_span = PUMPUP_LIFE;
+ EPtr->preprocess_func = pump_up_preprocess;
+ EPtr->postprocess_func = 0;
+
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ SetVelocityComponents (&EPtr->velocity,
+ COSINE (angle, WORLD_TO_VELOCITY (PUMPUP_SPEED)),
+ SINE (angle, WORLD_TO_VELOCITY (PUMPUP_SPEED)));
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), EPtr);
+ }
+
+ UnlockElement (hPumpUp);
+ PutElement (hPumpUp);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ NO_PRIM);
+ ElementPtr->state_flags |= NONSOLID;
+ }
+}
+
+static void
+animate (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+static void
+pump_up_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ RECT r;
+ BYTE old_thrust_wait;
+ HELEMENT hBlastElement;
+
+ GetFrameRect (ElementPtr0->next.image.frame, &r);
+
+ old_thrust_wait = ElementPtr0->thrust_wait;
+ ElementPtr0->blast_offset = r.extent.width >> 1;
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->thrust_wait = old_thrust_wait;
+
+ if (hBlastElement)
+ {
+ ELEMENT *BlastElementPtr;
+
+ LockElement (hBlastElement, &BlastElementPtr);
+
+ BlastElementPtr->life_span =
+ MIN_PUMPITUDE_ANIMS
+ + (ElementPtr0->turn_wait & ~REVERSE_DIR);
+ BlastElementPtr->turn_wait = BlastElementPtr->next_turn = 0;
+ {
+ BlastElementPtr->preprocess_func = animate;
+ }
+
+ BlastElementPtr->current.image.farray = ElementPtr0->next.image.farray;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (BlastElementPtr->current.image.farray[0],
+ MAX_PUMP * NUM_PUMP_ANIMS);
+
+ UnlockElement (hBlastElement);
+ }
+}
+
+static COUNT
+initialize_pump_up (ELEMENT *ShipPtr, HELEMENT PumpUpArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = MELNORME_OFFSET;
+ MissileBlock.speed = DISPLAY_TO_WORLD (MELNORME_OFFSET);
+ MissileBlock.hit_points = PUMPUP_DAMAGE;
+ MissileBlock.damage = PUMPUP_DAMAGE;
+ MissileBlock.life = 2;
+ MissileBlock.preprocess_func = 0;
+ MissileBlock.blast_offs = 0;
+ PumpUpArray[0] = initialize_missile (&MissileBlock);
+
+ if (PumpUpArray[0])
+ {
+ ELEMENT *PumpUpPtr;
+
+ LockElement (PumpUpArray[0], &PumpUpPtr);
+ PumpUpPtr->postprocess_func = pump_up_postprocess;
+ PumpUpPtr->collision_func = pump_up_collision;
+ PumpUpPtr->thrust_wait = LEVEL_COUNTER;
+ UnlockElement (PumpUpArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+confuse_preprocess (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & NONSOLID))
+ {
+ ElementPtr->next.image.frame = SetAbsFrameIndex (
+ ElementPtr->current.image.frame,
+ (GetFrameIndex (ElementPtr->current.image.frame) + 1) & 7);
+ ElementPtr->state_flags |= CHANGING;
+ }
+ else if (ElementPtr->hTarget == 0)
+ {
+ ElementPtr->life_span = 0;
+ ElementPtr->state_flags |= DISAPPEARING;
+ }
+ else
+ {
+ ELEMENT *eptr;
+
+ LockElement (ElementPtr->hTarget, &eptr);
+
+ ElementPtr->next.location = eptr->next.location;
+
+ if (ElementPtr->turn_wait)
+ {
+ HELEMENT hEffect;
+ STARSHIP *StarShipPtr;
+
+ if (GetFrameIndex (ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame)) == 0)
+ ElementPtr->next.image.frame =
+ SetRelFrameIndex (ElementPtr->next.image.frame, -8);
+
+ GetElementStarShip (eptr, &StarShipPtr);
+ StarShipPtr->ship_input_state =
+ (StarShipPtr->ship_input_state
+ & ~(LEFT | RIGHT | SPECIAL))
+ | ElementPtr->turn_wait;
+
+ hEffect = AllocElement ();
+ if (hEffect)
+ {
+ LockElement (hEffect, &eptr);
+ eptr->playerNr = ElementPtr->playerNr;
+ eptr->state_flags = FINITE_LIFE | NONSOLID | CHANGING;
+ eptr->life_span = 1;
+ eptr->current = eptr->next = ElementPtr->next;
+ eptr->preprocess_func = confuse_preprocess;
+ SetPrimType (&(GLOBAL (DisplayArray))[eptr->PrimIndex],
+ STAMP_PRIM);
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ SetElementStarShip (eptr, StarShipPtr);
+ eptr->hTarget = ElementPtr->hTarget;
+
+ UnlockElement (hEffect);
+ PutElement (hEffect);
+ }
+ }
+
+ UnlockElement (ElementPtr->hTarget);
+ }
+}
+
+static void
+confusion_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ HELEMENT hConfusionElement, hNextElement;
+ ELEMENT *ConfusionPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ for (hConfusionElement = GetHeadElement ();
+ hConfusionElement; hConfusionElement = hNextElement)
+ {
+ LockElement (hConfusionElement, &ConfusionPtr);
+ if (elementsOfSamePlayer (ConfusionPtr, ElementPtr0)
+ && ConfusionPtr->current.image.farray ==
+ StarShipPtr->RaceDescPtr->ship_data.special
+ && (ConfusionPtr->state_flags & NONSOLID))
+ {
+ UnlockElement (hConfusionElement);
+ break;
+ }
+ hNextElement = GetSuccElement (ConfusionPtr);
+ UnlockElement (hConfusionElement);
+ }
+
+ if (hConfusionElement || (hConfusionElement = AllocElement ()))
+ {
+ LockElement (hConfusionElement, &ConfusionPtr);
+
+ if (ConfusionPtr->state_flags == 0) /* not allocated before */
+ {
+ InsertElement (hConfusionElement, GetHeadElement ());
+
+ ConfusionPtr->current = ElementPtr0->next;
+ ConfusionPtr->current.image.frame = SetAbsFrameIndex (
+ ConfusionPtr->current.image.frame, 8
+ );
+ ConfusionPtr->next = ConfusionPtr->current;
+ ConfusionPtr->playerNr = ElementPtr0->playerNr;
+ ConfusionPtr->state_flags = FINITE_LIFE | NONSOLID | CHANGING;
+ ConfusionPtr->preprocess_func = confuse_preprocess;
+ SetPrimType (
+ &(GLOBAL (DisplayArray))[ConfusionPtr->PrimIndex],
+ NO_PRIM
+ );
+
+ SetElementStarShip (ConfusionPtr, StarShipPtr);
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ ConfusionPtr->hTarget = StarShipPtr->hShip;
+ }
+
+ ConfusionPtr->life_span = 400;
+ ConfusionPtr->turn_wait =
+ (BYTE)(1 << ((BYTE)TFB_Random () & 1)); /* LEFT or RIGHT */
+
+ UnlockElement (hConfusionElement);
+ }
+
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ ElementPtr0->state_flags |= DISAPPEARING | COLLISION | NONSOLID;
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static COUNT
+initialize_confusion (ELEMENT *ShipPtr, HELEMENT ConfusionArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK ConfusionBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ ConfusionBlock.cx = ShipPtr->next.location.x;
+ ConfusionBlock.cy = ShipPtr->next.location.y;
+ ConfusionBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ ConfusionBlock.index = 0;
+ ConfusionBlock.face = StarShipPtr->ShipFacing;
+ ConfusionBlock.sender = ShipPtr->playerNr;
+ ConfusionBlock.flags = IGNORE_SIMILAR;
+ ConfusionBlock.pixoffs = MELNORME_OFFSET;
+ ConfusionBlock.speed = CMISSILE_SPEED;
+ ConfusionBlock.hit_points = CMISSILE_HITS;
+ ConfusionBlock.damage = CMISSILE_DAMAGE;
+ ConfusionBlock.life = CMISSILE_LIFE;
+ ConfusionBlock.preprocess_func = confuse_preprocess;
+ ConfusionBlock.blast_offs = CMISSILE_OFFSET;
+ ConfusionArray[0] = initialize_missile (&ConfusionBlock);
+
+ if (ConfusionArray[0])
+ {
+ ELEMENT *CMissilePtr;
+
+ LockElement (ConfusionArray[0], &CMissilePtr);
+ CMissilePtr->collision_func = confusion_collision;
+ SetElementStarShip (CMissilePtr, StarShipPtr);
+ UnlockElement (ConfusionArray[0]);
+ }
+ return (1);
+}
+
+static COUNT
+initialize_test_pump_up (ELEMENT *ShipPtr, HELEMENT PumpUpArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+ //ELEMENT *PumpUpPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = MELNORME_OFFSET;
+ MissileBlock.speed = PUMPUP_SPEED;
+ MissileBlock.hit_points = PUMPUP_DAMAGE;
+ MissileBlock.damage = PUMPUP_DAMAGE;
+ MissileBlock.life = PUMPUP_LIFE;
+ MissileBlock.preprocess_func = 0;
+ MissileBlock.blast_offs = 0;
+ PumpUpArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void
+melnorme_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ BYTE old_count;
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ StarShipPtr->RaceDescPtr->init_weapon_func = initialize_test_pump_up;
+ old_count = StarShipPtr->weapon_counter;
+
+ if (StarShipPtr->weapon_counter == WEAPON_WAIT)
+ StarShipPtr->weapon_counter = 0;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level < SPECIAL_ENERGY_COST
+ + WEAPON_ENERGY_COST
+ && !(StarShipPtr->old_status_flags & WEAPON))
+ lpEvalDesc->MoveState = ENTICE;
+ else
+ {
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (!(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & IMMEDIATE_WEAPON))
+ lpEvalDesc->MoveState = PURSUE;
+ }
+ }
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ if (StarShipPtr->weapon_counter == 0
+ && (old_count != 0
+ || ((StarShipPtr->special_counter
+ || StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST
+ + WEAPON_ENERGY_COST)
+ && !(StarShipPtr->ship_input_state & WEAPON))))
+ StarShipPtr->ship_input_state ^= WEAPON;
+
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST)
+ {
+ BYTE old_input_state;
+
+ old_input_state = StarShipPtr->ship_input_state;
+
+ StarShipPtr->RaceDescPtr->init_weapon_func = initialize_confusion;
+
+ ++ShipPtr->turn_wait;
+ ++ShipPtr->thrust_wait;
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ENEMY_SHIP_INDEX + 1);
+ --ShipPtr->thrust_wait;
+ --ShipPtr->turn_wait;
+
+ if (StarShipPtr->ship_input_state & WEAPON)
+ {
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+
+ StarShipPtr->ship_input_state = (unsigned char)(old_input_state
+ | (StarShipPtr->ship_input_state & SPECIAL));
+ }
+
+ StarShipPtr->weapon_counter = old_count;
+
+ StarShipPtr->RaceDescPtr->init_weapon_func = initialize_pump_up;
+}
+
+static void
+melnorme_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ HELEMENT Confusion;
+
+ initialize_confusion (ElementPtr, &Confusion);
+ if (Confusion)
+ {
+ ELEMENT *CMissilePtr;
+ LockElement (Confusion, &CMissilePtr);
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), CMissilePtr);
+
+ UnlockElement (Confusion);
+ PutElement (Confusion);
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+ }
+}
+
+RACE_DESC*
+init_melnorme (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ melnorme_desc.postprocess_func = melnorme_postprocess;
+ melnorme_desc.init_weapon_func = initialize_pump_up;
+ melnorme_desc.cyborg_control.intelligence_func = melnorme_intelligence;
+
+ RaceDescPtr = &melnorme_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/melnorme/melnorme.h b/src/uqm/ships/melnorme/melnorme.h
new file mode 100644
index 0000000..287ec76
--- /dev/null
+++ b/src/uqm/ships/melnorme/melnorme.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MELNORME_H
+#define MELNORME_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_melnorme (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* MELNORME_H */
+
diff --git a/src/uqm/ships/melnorme/resinst.h b/src/uqm/ships/melnorme/resinst.h
new file mode 100644
index 0000000..01b93df
--- /dev/null
+++ b/src/uqm/ships/melnorme/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define CONFUSE_BIG_MASK_PMAP_ANIM "ship.melnorme.graphics.confuse.large"
+#define CONFUSE_MED_MASK_PMAP_ANIM "ship.melnorme.graphics.confuse.medium"
+#define CONFUSE_SML_MASK_PMAP_ANIM "ship.melnorme.graphics.confuse.small"
+#define MELNORME_BIG_MASK_PMAP_ANIM "ship.melnorme.graphics.trader.large"
+#define MELNORME_CAPTAIN_MASK_PMAP_ANIM "ship.melnorme.graphics.captain"
+#define MELNORME_ICON_MASK_PMAP_ANIM "ship.melnorme.icons"
+#define MELNORME_MED_MASK_PMAP_ANIM "ship.melnorme.graphics.trader.medium"
+#define MELNORME_MICON_MASK_PMAP_ANIM "ship.melnorme.meleeicons"
+#define MELNORME_RACE_STRINGS "ship.melnorme.text"
+#define MELNORME_SHIP_SOUNDS "ship.melnorme.sounds"
+#define MELNORME_SML_MASK_PMAP_ANIM "ship.melnorme.graphics.trader.small"
+#define MELNORME_VICTORY_SONG "ship.melnorme.ditty"
+#define PUMPUP_BIG_MASK_PMAP_ANIM "ship.melnorme.graphics.pumpup.large"
+#define PUMPUP_MED_MASK_PMAP_ANIM "ship.melnorme.graphics.pumpup.medium"
+#define PUMPUP_SML_MASK_PMAP_ANIM "ship.melnorme.graphics.pumpup.small"
diff --git a/src/uqm/ships/mmrnmhrm/Makeinfo b/src/uqm/ships/mmrnmhrm/Makeinfo
new file mode 100644
index 0000000..0c86637
--- /dev/null
+++ b/src/uqm/ships/mmrnmhrm/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="mmrnmhrm.c"
+uqm_HFILES="icode.h mmrnmhrm.h resinst.h"
diff --git a/src/uqm/ships/mmrnmhrm/icode.h b/src/uqm/ships/mmrnmhrm/icode.h
new file mode 100644
index 0000000..ba3f593
--- /dev/null
+++ b/src/uqm/ships/mmrnmhrm/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define MMRNMHRM_CODE "ship.mmrnmhrm.code"
diff --git a/src/uqm/ships/mmrnmhrm/mmrnmhrm.c b/src/uqm/ships/mmrnmhrm/mmrnmhrm.c
new file mode 100644
index 0000000..e8f8348
--- /dev/null
+++ b/src/uqm/ships/mmrnmhrm/mmrnmhrm.c
@@ -0,0 +1,527 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "mmrnmhrm.h"
+#include "resinst.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 10
+#define SHIP_MASS 3
+
+// X-Wing characteristics
+#define ENERGY_REGENERATION 2
+#define ENERGY_WAIT 6
+#define MAX_THRUST 20
+#define THRUST_INCREMENT 5
+#define THRUST_WAIT 1
+#define TURN_WAIT 2
+
+// Y-Wing characteristics
+#define YWING_ENERGY_REGENERATION 1
+#define YWING_SPECIAL_ENERGY_COST MAX_ENERGY
+#define YWING_ENERGY_WAIT 6
+#define YWING_MAX_THRUST 50
+#define YWING_THRUST_INCREMENT 10
+#define YWING_THRUST_WAIT 0
+#define YWING_TURN_WAIT 14
+
+// X-Wing Lasers
+#define MMRNMHRM_OFFSET 16
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 0
+#define CENTER_OFFS DISPLAY_TO_WORLD (4)
+#define WING_OFFS DISPLAY_TO_WORLD (10)
+#define LASER_RANGE DISPLAY_TO_WORLD (125 + MMRNMHRM_OFFSET)
+
+// Y-Wing Missiles
+#define YWING_WEAPON_ENERGY_COST 1
+#define YWING_WEAPON_WAIT 20
+#define LAUNCH_OFFS DISPLAY_TO_WORLD (4)
+#define MISSILE_OFFSET 0
+#define MISSILE_SPEED DISPLAY_TO_WORLD (20)
+#define MISSILE_LIFE 40
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+#define TRACK_WAIT 5
+
+// Transform
+#define SPECIAL_ENERGY_COST MAX_ENERGY
+#define SPECIAL_WAIT 0
+#define YWING_SPECIAL_WAIT 0
+
+static RACE_DESC mmrnmhrm_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | IMMEDIATE_WEAPON,
+ 19, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ MMRNMHRM_RACE_STRINGS,
+ MMRNMHRM_ICON_MASK_PMAP_ANIM,
+ MMRNMHRM_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ MMRNMHRM_BIG_MASK_PMAP_ANIM,
+ MMRNMHRM_MED_MASK_PMAP_ANIM,
+ MMRNMHRM_SML_MASK_PMAP_ANIM,
+ },
+ {
+ TORP_BIG_MASK_PMAP_ANIM,
+ TORP_MED_MASK_PMAP_ANIM,
+ TORP_SML_MASK_PMAP_ANIM,
+ },
+ {
+ YWING_BIG_MASK_PMAP_ANIM,
+ YWING_MED_MASK_PMAP_ANIM,
+ YWING_SML_MASK_PMAP_ANIM,
+ },
+ {
+ MMRNMHRM_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ MMRNMHRM_VICTORY_SONG,
+ MMRNMHRM_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+// Private per-instance ship data
+typedef CHARACTERISTIC_STUFF MMRNMHRM_DATA;
+
+// Local typedef
+typedef MMRNMHRM_DATA CustomShipData_t;
+
+// Retrieve race-specific ship data from a race desc
+static CustomShipData_t *
+GetCustomShipData (RACE_DESC *pRaceDesc)
+{
+ return pRaceDesc->data;
+}
+
+// Set the race-specific data in a race desc
+// (Re)Allocates its own storage for the data.
+static void
+SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data)
+{
+ if (pRaceDesc->data == data)
+ return; // no-op
+
+ if (pRaceDesc->data) // Out with the old
+ {
+ HFree (pRaceDesc->data);
+ pRaceDesc->data = NULL;
+ }
+
+ if (data) // In with the new
+ {
+ CustomShipData_t* newData = HMalloc (sizeof (*data));
+ *newData = *data;
+ pRaceDesc->data = newData;
+ }
+}
+
+static void
+missile_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT facing;
+
+ facing = GetFrameIndex (ElementPtr->next.image.frame);
+ if (TrackShip (ElementPtr, &facing) > 0)
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->next.image.frame,
+ facing);
+ ElementPtr->state_flags |= CHANGING;
+
+ SetVelocityVector (&ElementPtr->velocity,
+ MISSILE_SPEED, facing);
+ }
+
+ ElementPtr->turn_wait = TRACK_WAIT;
+ }
+}
+
+static void
+mmrnmhrm_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ BOOLEAN CanTransform;
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+ STARSHIP *EnemyStarShipPtr = NULL;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ CanTransform = (BOOLEAN)(StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
+ StarShipPtr->RaceDescPtr->characteristics.special_energy_cost);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (CanTransform
+ && lpEvalDesc->ObjectPtr
+ && !(StarShipPtr->ship_input_state & WEAPON))
+ {
+ SIZE delta_x, delta_y;
+ COUNT travel_angle, direction_angle;
+
+ GetCurrentVelocityComponents (&lpEvalDesc->ObjectPtr->velocity,
+ &delta_x, &delta_y);
+ if (delta_x == 0 && delta_y == 0)
+ direction_angle = travel_angle = 0;
+ else
+ {
+ delta_x = lpEvalDesc->ObjectPtr->current.location.x
+ - ShipPtr->current.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->current.location.y
+ - ShipPtr->current.location.y;
+ direction_angle = ARCTAN (-delta_x, -delta_y);
+ travel_angle = GetVelocityTravelAngle (
+ &lpEvalDesc->ObjectPtr->velocity
+ );
+ }
+
+ if (ShipPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
+ {
+ if (lpEvalDesc->which_turn > 8)
+ {
+ if (MANEUVERABILITY (&EnemyStarShipPtr->RaceDescPtr->cyborg_control) <= SLOW_SHIP
+ || NORMALIZE_ANGLE (
+ direction_angle - travel_angle + QUADRANT
+ ) > HALF_CIRCLE)
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ }
+ else
+ {
+ SIZE ship_delta_x, ship_delta_y;
+
+ GetCurrentVelocityComponents (&ShipPtr->velocity,
+ &ship_delta_x, &ship_delta_y);
+ delta_x -= ship_delta_x;
+ delta_y -= ship_delta_y;
+ travel_angle = ARCTAN (delta_x, delta_y);
+ if (lpEvalDesc->which_turn < 16)
+ {
+ if (lpEvalDesc->which_turn <= 8
+ || NORMALIZE_ANGLE (
+ direction_angle - travel_angle + OCTANT
+ ) <= QUADRANT)
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ else if (lpEvalDesc->which_turn > 32
+ && NORMALIZE_ANGLE (
+ direction_angle - travel_angle + QUADRANT
+ ) > HALF_CIRCLE)
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ }
+
+ if (ShipPtr->current.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
+ {
+ if (!(StarShipPtr->ship_input_state & SPECIAL)
+ && lpEvalDesc->ObjectPtr)
+ StarShipPtr->ship_input_state |= WEAPON;
+ else
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ }
+}
+
+static void
+twin_laser_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (!(ElementPtr1->state_flags & PLAYER_SHIP)
+ || !elementsOfSamePlayer (ElementPtr0, ElementPtr1))
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+}
+
+static COUNT
+initialize_dual_weapons (ELEMENT *ShipPtr, HELEMENT WeaponArray[])
+{
+ COORD cx, cy;
+ COUNT facing, angle;
+ SIZE offs_x, offs_y;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ facing = StarShipPtr->ShipFacing;
+ angle = FACING_TO_ANGLE (facing);
+ cx = ShipPtr->next.location.x + COSINE (angle, CENTER_OFFS);
+ cy = ShipPtr->next.location.y + SINE (angle, CENTER_OFFS);
+
+ if (ShipPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
+ {
+ COORD ex, ey;
+ LASER_BLOCK LaserBlock;
+ ELEMENT *LaserPtr;
+
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = 0;
+ LaserBlock.pixoffs = 0;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C);
+ LaserBlock.face = facing;
+
+ ex = cx + COSINE (angle, LASER_RANGE);
+ ey = cy + SINE (angle, LASER_RANGE);
+ offs_x = -SINE (angle, WING_OFFS);
+ offs_y = COSINE (angle, WING_OFFS);
+
+ LaserBlock.cx = cx + offs_x;
+ LaserBlock.cy = cy + offs_y;
+ LaserBlock.ex = ex - LaserBlock.cx;
+ LaserBlock.ey = ey - LaserBlock.cy;
+ if ((WeaponArray[0] = initialize_laser (&LaserBlock)))
+ {
+ LockElement (WeaponArray[0], &LaserPtr);
+ LaserPtr->collision_func = twin_laser_collision;
+ UnlockElement (WeaponArray[0]);
+ }
+
+ LaserBlock.cx = cx - offs_x;
+ LaserBlock.cy = cy - offs_y;
+ LaserBlock.ex = ex - LaserBlock.cx;
+ LaserBlock.ey = ey - LaserBlock.cy;
+ if ((WeaponArray[1] = initialize_laser (&LaserBlock)))
+ {
+ LockElement (WeaponArray[1], &LaserPtr);
+ LaserPtr->collision_func = twin_laser_collision;
+ UnlockElement (WeaponArray[1]);
+ }
+ }
+ else
+ {
+ MISSILE_BLOCK TorpBlock;
+ ELEMENT *TorpPtr;
+
+ TorpBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ TorpBlock.sender = ShipPtr->playerNr;
+ TorpBlock.flags = IGNORE_SIMILAR;
+ TorpBlock.pixoffs = 0;
+ TorpBlock.speed = MISSILE_SPEED;
+ TorpBlock.hit_points = MISSILE_HITS;
+ TorpBlock.damage = MISSILE_DAMAGE;
+ TorpBlock.life = MISSILE_LIFE;
+ TorpBlock.preprocess_func = missile_preprocess;
+ TorpBlock.blast_offs = MISSILE_OFFSET;
+
+ TorpBlock.face = TorpBlock.index = NORMALIZE_FACING (facing - 1);
+ offs_x = -SINE (FACING_TO_ANGLE (TorpBlock.face), LAUNCH_OFFS);
+ offs_y = COSINE (FACING_TO_ANGLE (TorpBlock.face), LAUNCH_OFFS);
+
+ TorpBlock.cx = cx + offs_x;
+ TorpBlock.cy = cy + offs_y;
+ if ((WeaponArray[0] = initialize_missile (&TorpBlock)))
+ {
+ LockElement (WeaponArray[0], &TorpPtr);
+ TorpPtr->turn_wait = TRACK_WAIT;
+ UnlockElement (WeaponArray[0]);
+ }
+
+ TorpBlock.face = TorpBlock.index = NORMALIZE_FACING (facing + 1);
+
+ TorpBlock.cx = cx - offs_x;
+ TorpBlock.cy = cy - offs_y;
+ if ((WeaponArray[1] = initialize_missile (&TorpBlock)))
+ {
+ LockElement (WeaponArray[1], &TorpPtr);
+ TorpPtr->turn_wait = TRACK_WAIT;
+ UnlockElement (WeaponArray[1]);
+ }
+ }
+
+ return (2);
+}
+
+static void
+mmrnmhrm_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ /* take care of transform effect */
+ if (ElementPtr->next.image.farray != ElementPtr->current.image.farray)
+ {
+ MMRNMHRM_DATA tempShipData;
+ MMRNMHRM_DATA *otherwing_desc;
+
+ ProcessSound (SetAbsSoundIndex (
+ /* TRANSFORM */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+
+ StarShipPtr->weapon_counter = 0;
+
+ /* Swap characteristics descriptors around */
+ otherwing_desc = GetCustomShipData (StarShipPtr->RaceDescPtr);
+ if (!otherwing_desc)
+ return; // No ship data (?!)
+
+ tempShipData = *otherwing_desc;
+ SetCustomShipData (StarShipPtr->RaceDescPtr, &StarShipPtr->RaceDescPtr->characteristics);
+ StarShipPtr->RaceDescPtr->characteristics = tempShipData;
+ StarShipPtr->RaceDescPtr->cyborg_control.ManeuverabilityIndex = 0;
+
+ if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.special)
+ {
+ StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = LONG_RANGE_WEAPON - 1;
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags &= ~IMMEDIATE_WEAPON;
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags |= SEEKING_WEAPON;
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds =
+ SetAbsSoundIndex (StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2);
+
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+ }
+ else
+ {
+ StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = CLOSE_RANGE_WEAPON;
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags &= ~SEEKING_WEAPON;
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags |= IMMEDIATE_WEAPON;
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds =
+ SetAbsSoundIndex (StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 0);
+
+ if (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
+ StarShipPtr->cur_status_flags |=
+ SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED;
+ }
+ }
+}
+
+static void
+mmrnmhrm_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ if (!(ElementPtr->state_flags & APPEARING))
+ {
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0)
+ {
+ /* Either we transform or text will flash */
+ if (DeltaEnergy (ElementPtr,
+ -StarShipPtr->RaceDescPtr->characteristics.special_energy_cost))
+ {
+ if (ElementPtr->next.image.farray == StarShipPtr->RaceDescPtr->ship_data.ship)
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ else
+ ElementPtr->next.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.ship;
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (ElementPtr->next.image.farray[0],
+ ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+ }
+ }
+}
+
+static void
+uninit_mmrnmhrm (RACE_DESC *pRaceDesc)
+{
+ SetCustomShipData (pRaceDesc, NULL);
+}
+
+RACE_DESC*
+init_mmrnmhrm (void)
+{
+ RACE_DESC *RaceDescPtr;
+ // The caller of this func will copy the struct
+ static RACE_DESC new_mmrnmhrm_desc;
+ MMRNMHRM_DATA otherwing_desc;
+
+ mmrnmhrm_desc.uninit_func = uninit_mmrnmhrm;
+ mmrnmhrm_desc.preprocess_func = mmrnmhrm_preprocess;
+ mmrnmhrm_desc.postprocess_func = mmrnmhrm_postprocess;
+ mmrnmhrm_desc.init_weapon_func = initialize_dual_weapons;
+ mmrnmhrm_desc.cyborg_control.intelligence_func = mmrnmhrm_intelligence;
+
+ new_mmrnmhrm_desc = mmrnmhrm_desc;
+
+ otherwing_desc.max_thrust = YWING_MAX_THRUST;
+ otherwing_desc.thrust_increment = YWING_THRUST_INCREMENT;
+ otherwing_desc.energy_regeneration = YWING_ENERGY_REGENERATION;
+ otherwing_desc.weapon_energy_cost = YWING_WEAPON_ENERGY_COST;
+ otherwing_desc.special_energy_cost = YWING_SPECIAL_ENERGY_COST;
+ otherwing_desc.energy_wait = YWING_ENERGY_WAIT;
+ otherwing_desc.turn_wait = YWING_TURN_WAIT;
+ otherwing_desc.thrust_wait = YWING_THRUST_WAIT;
+ otherwing_desc.weapon_wait = YWING_WEAPON_WAIT;
+ otherwing_desc.special_wait = YWING_SPECIAL_WAIT;
+ otherwing_desc.ship_mass = SHIP_MASS;
+
+ SetCustomShipData (&new_mmrnmhrm_desc, &otherwing_desc);
+
+ RaceDescPtr = &new_mmrnmhrm_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/mmrnmhrm/mmrnmhrm.h b/src/uqm/ships/mmrnmhrm/mmrnmhrm.h
new file mode 100644
index 0000000..c2c8512
--- /dev/null
+++ b/src/uqm/ships/mmrnmhrm/mmrnmhrm.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MMRNMHRM_H
+#define MMRNMHRM_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_mmrnmhrm (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* MMRNMHRM_H */
+
diff --git a/src/uqm/ships/mmrnmhrm/resinst.h b/src/uqm/ships/mmrnmhrm/resinst.h
new file mode 100644
index 0000000..f44285f
--- /dev/null
+++ b/src/uqm/ships/mmrnmhrm/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define MMRNMHRM_BIG_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.xform.large"
+#define MMRNMHRM_CAPTAIN_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.captain"
+#define MMRNMHRM_ICON_MASK_PMAP_ANIM "ship.mmrnmhrm.icons"
+#define MMRNMHRM_MED_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.xform.medium"
+#define MMRNMHRM_MICON_MASK_PMAP_ANIM "ship.mmrnmhrm.meleeicons"
+#define MMRNMHRM_RACE_STRINGS "ship.mmrnmhrm.text"
+#define MMRNMHRM_SHIP_SOUNDS "ship.mmrnmhrm.sounds"
+#define MMRNMHRM_SML_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.xform.small"
+#define MMRNMHRM_VICTORY_SONG "ship.mmrnmhrm.ditty"
+#define TORP_BIG_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.torpedo.large"
+#define TORP_MED_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.torpedo.medium"
+#define TORP_SML_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.torpedo.small"
+#define YWING_BIG_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.ywing.large"
+#define YWING_MED_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.ywing.medium"
+#define YWING_SML_MASK_PMAP_ANIM "ship.mmrnmhrm.graphics.ywing.small"
diff --git a/src/uqm/ships/mycon/Makeinfo b/src/uqm/ships/mycon/Makeinfo
new file mode 100644
index 0000000..0ba8988
--- /dev/null
+++ b/src/uqm/ships/mycon/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="mycon.c"
+uqm_HFILES="icode.h mycon.h resinst.h"
diff --git a/src/uqm/ships/mycon/icode.h b/src/uqm/ships/mycon/icode.h
new file mode 100644
index 0000000..b3caa58
--- /dev/null
+++ b/src/uqm/ships/mycon/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define MYCON_CODE "ship.mycon.code"
diff --git a/src/uqm/ships/mycon/mycon.c b/src/uqm/ships/mycon/mycon.c
new file mode 100644
index 0000000..8c99fbe
--- /dev/null
+++ b/src/uqm/ships/mycon/mycon.c
@@ -0,0 +1,376 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "mycon.h"
+#include "resinst.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 40
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST /* DISPLAY_TO_WORLD (7) */ 27
+#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 9
+#define THRUST_WAIT 6
+#define TURN_WAIT 6
+#define SHIP_MASS 7
+
+// Plasmoid
+#define WEAPON_ENERGY_COST 20
+#define WEAPON_WAIT 5
+#define MYCON_OFFSET 24
+#define MISSILE_OFFSET 0
+#define NUM_PLASMAS 11
+#define NUM_GLOBALLS 8
+#define PLASMA_DURATION 13
+#define MISSILE_LIFE (NUM_PLASMAS * PLASMA_DURATION)
+#define MISSILE_SPEED DISPLAY_TO_WORLD (8)
+#define MISSILE_DAMAGE 10
+#define TRACK_WAIT 1
+
+// Regenerate
+#define SPECIAL_ENERGY_COST MAX_ENERGY
+#define SPECIAL_WAIT 0
+#define REGENERATION_AMOUNT 4
+
+static RACE_DESC mycon_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_WEAPON,
+ 21, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ MYCON_RACE_STRINGS,
+ MYCON_ICON_MASK_PMAP_ANIM,
+ MYCON_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 1070 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 6392, 2200,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ MYCON_BIG_MASK_PMAP_ANIM,
+ MYCON_MED_MASK_PMAP_ANIM,
+ MYCON_SML_MASK_PMAP_ANIM,
+ },
+ {
+ PLASMA_BIG_MASK_PMAP_ANIM,
+ PLASMA_MED_MASK_PMAP_ANIM,
+ PLASMA_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ MYCON_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ MYCON_VICTORY_SONG,
+ MYCON_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ DISPLAY_TO_WORLD (800),
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+plasma_preprocess (ELEMENT *ElementPtr)
+{
+ COUNT plasma_index;
+
+ if (ElementPtr->mass_points > ElementPtr->hit_points)
+ ElementPtr->life_span = ElementPtr->hit_points * PLASMA_DURATION;
+ else
+ ElementPtr->hit_points = (BYTE)((ElementPtr->life_span *
+ MISSILE_DAMAGE + (MISSILE_LIFE - 1)) / MISSILE_LIFE);
+ ElementPtr->mass_points = ElementPtr->hit_points;
+ plasma_index = NUM_PLASMAS - ((ElementPtr->life_span +
+ (PLASMA_DURATION - 1)) / PLASMA_DURATION);
+ if (plasma_index != GetFrameIndex (ElementPtr->next.image.frame))
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->next.image.frame,
+ plasma_index);
+ ElementPtr->state_flags |= CHANGING;
+ }
+
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT facing;
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)
+ ));
+ if (TrackShip (ElementPtr, &facing) > 0)
+ SetVelocityVector (&ElementPtr->velocity,
+ MISSILE_SPEED, facing);
+
+ ElementPtr->turn_wait = TRACK_WAIT;
+ }
+}
+
+static void
+plasma_blast_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->life_span >= ElementPtr->thrust_wait)
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->next.image.frame);
+ else
+ ElementPtr->next.image.frame =
+ DecFrameIndex (ElementPtr->next.image.frame);
+ if (ElementPtr->hTarget)
+ {
+ ELEMENT *ShipPtr;
+
+ LockElement (ElementPtr->hTarget, &ShipPtr);
+ ElementPtr->next.location = ShipPtr->next.location;
+ UnlockElement (ElementPtr->hTarget);
+ }
+
+ ElementPtr->state_flags |= CHANGING;
+}
+
+static void
+plasma_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ SIZE old_mass;
+ HELEMENT hBlastElement;
+
+ old_mass = (SIZE)ElementPtr0->mass_points;
+ if ((ElementPtr0->pParent != ElementPtr1->pParent
+ || (ElementPtr1->state_flags & PLAYER_SHIP))
+ && (hBlastElement =
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1)))
+ {
+ SIZE num_animations;
+ ELEMENT *BlastElementPtr;
+
+ LockElement (hBlastElement, &BlastElementPtr);
+ BlastElementPtr->pParent = ElementPtr0->pParent;
+ if (!(ElementPtr1->state_flags & PLAYER_SHIP))
+ BlastElementPtr->hTarget = 0;
+ else
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ BlastElementPtr->hTarget = StarShipPtr->hShip;
+ }
+
+ BlastElementPtr->current.location = ElementPtr1->current.location;
+
+ if ((num_animations =
+ (old_mass * NUM_GLOBALLS +
+ (MISSILE_DAMAGE - 1)) / MISSILE_DAMAGE) == 0)
+ num_animations = 1;
+
+ BlastElementPtr->thrust_wait = (BYTE)num_animations;
+ BlastElementPtr->life_span = (num_animations << 1) - 1;
+ {
+ BlastElementPtr->preprocess_func = plasma_blast_preprocess;
+ }
+ BlastElementPtr->current.image.farray = ElementPtr0->next.image.farray;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (BlastElementPtr->current.image.farray[0],
+ NUM_PLASMAS);
+
+ UnlockElement (hBlastElement);
+ }
+}
+
+static void
+mycon_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE)
+ {
+ if ((lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE)
+ && !(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
+ lpEvalDesc->MoveState = AVOID;
+ else
+ lpEvalDesc->MoveState = PURSUE;
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState == PURSUE)
+ StarShipPtr->ship_input_state &= ~THRUST; /* don't pursue seekers */
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (StarShipPtr->weapon_counter == 0
+ && lpEvalDesc->ObjectPtr
+ && (lpEvalDesc->which_turn <= 16
+ || ShipPtr->crew_level == StarShipPtr->RaceDescPtr->ship_info.max_crew))
+ {
+ COUNT travel_facing, direction_facing;
+ SIZE delta_x, delta_y;
+
+ travel_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (GetVelocityTravelAngle (&ShipPtr->velocity)
+ + HALF_CIRCLE)
+ );
+ delta_x = lpEvalDesc->ObjectPtr->current.location.x
+ - ShipPtr->current.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->current.location.y
+ - ShipPtr->current.location.y;
+ direction_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+
+ if (NORMALIZE_FACING (direction_facing
+ - StarShipPtr->ShipFacing
+ + ANGLE_TO_FACING (QUADRANT))
+ <= ANGLE_TO_FACING (HALF_CIRCLE)
+ && (!(StarShipPtr->cur_status_flags &
+ (SHIP_BEYOND_MAX_SPEED | SHIP_IN_GRAVITY_WELL))
+ || NORMALIZE_FACING (direction_facing
+ - travel_facing + ANGLE_TO_FACING (OCTANT))
+ <= ANGLE_TO_FACING (QUADRANT)))
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+
+ if (StarShipPtr->special_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = DISPLAY_TO_WORLD (800);
+ if (ShipPtr->crew_level < StarShipPtr->RaceDescPtr->ship_info.max_crew)
+ {
+ StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = MISSILE_SPEED * MISSILE_LIFE;
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST
+ && !(StarShipPtr->ship_input_state & WEAPON))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ }
+}
+
+static COUNT
+initialize_plasma (ELEMENT *ShipPtr, HELEMENT PlasmaArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = 0;
+ MissileBlock.pixoffs = MYCON_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_DAMAGE;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = plasma_preprocess;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ PlasmaArray[0] = initialize_missile (&MissileBlock);
+
+ if (PlasmaArray[0])
+ {
+ ELEMENT *PlasmaPtr;
+
+ LockElement (PlasmaArray[0], &PlasmaPtr);
+ PlasmaPtr->collision_func = plasma_collision;
+ PlasmaPtr->turn_wait = TRACK_WAIT + 2;
+ UnlockElement (PlasmaArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+mycon_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && ElementPtr->crew_level != StarShipPtr->RaceDescPtr->ship_info.max_crew
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ SIZE add_crew;
+
+ ProcessSound (SetAbsSoundIndex (
+ /* GROW_NEW_CREW */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ if ((add_crew = REGENERATION_AMOUNT) >
+ StarShipPtr->RaceDescPtr->ship_info.max_crew - ElementPtr->crew_level)
+ add_crew = StarShipPtr->RaceDescPtr->ship_info.max_crew - ElementPtr->crew_level;
+ DeltaCrew (ElementPtr, add_crew);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+RACE_DESC*
+init_mycon (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ mycon_desc.postprocess_func = mycon_postprocess;
+ mycon_desc.init_weapon_func = initialize_plasma;
+ mycon_desc.cyborg_control.intelligence_func = mycon_intelligence;
+
+ RaceDescPtr = &mycon_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/mycon/mycon.h b/src/uqm/ships/mycon/mycon.h
new file mode 100644
index 0000000..8051b7c
--- /dev/null
+++ b/src/uqm/ships/mycon/mycon.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MYCON_H
+#define MYCON_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_mycon (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* MYCON_H */
+
diff --git a/src/uqm/ships/mycon/resinst.h b/src/uqm/ships/mycon/resinst.h
new file mode 100644
index 0000000..38908a2
--- /dev/null
+++ b/src/uqm/ships/mycon/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define MYCON_BIG_MASK_PMAP_ANIM "ship.mycon.graphics.podship.large"
+#define MYCON_CAPTAIN_MASK_PMAP_ANIM "ship.mycon.graphics.captain"
+#define MYCON_ICON_MASK_PMAP_ANIM "ship.mycon.icons"
+#define MYCON_MED_MASK_PMAP_ANIM "ship.mycon.graphics.podship.medium"
+#define MYCON_MICON_MASK_PMAP_ANIM "ship.mycon.meleeicons"
+#define MYCON_RACE_STRINGS "ship.mycon.text"
+#define MYCON_SHIP_SOUNDS "ship.mycon.sounds"
+#define MYCON_SML_MASK_PMAP_ANIM "ship.mycon.graphics.podship.small"
+#define MYCON_VICTORY_SONG "ship.mycon.ditty"
+#define PLASMA_BIG_MASK_PMAP_ANIM "ship.mycon.graphics.plasma.large"
+#define PLASMA_MED_MASK_PMAP_ANIM "ship.mycon.graphics.plasma.medium"
+#define PLASMA_SML_MASK_PMAP_ANIM "ship.mycon.graphics.plasma.small"
diff --git a/src/uqm/ships/orz/Makeinfo b/src/uqm/ships/orz/Makeinfo
new file mode 100644
index 0000000..0c4961d
--- /dev/null
+++ b/src/uqm/ships/orz/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="orz.c"
+uqm_HFILES="icode.h orz.h resinst.h"
diff --git a/src/uqm/ships/orz/icode.h b/src/uqm/ships/orz/icode.h
new file mode 100644
index 0000000..bb45c4e
--- /dev/null
+++ b/src/uqm/ships/orz/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ORZ_CODE "ship.orz.code"
diff --git a/src/uqm/ships/orz/orz.c b/src/uqm/ships/orz/orz.c
new file mode 100644
index 0000000..1a95fec
--- /dev/null
+++ b/src/uqm/ships/orz/orz.c
@@ -0,0 +1,1083 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "orz.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 16
+#define MAX_ENERGY 20
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 6
+#define MAX_THRUST 35
+#define THRUST_INCREMENT 5
+#define THRUST_WAIT 0
+#define TURN_WAIT 1
+#define SHIP_MASS 4
+
+// Howitzer
+#define WEAPON_ENERGY_COST (MAX_ENERGY / 3)
+#define WEAPON_WAIT 4
+#define ORZ_OFFSET 9
+#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define MISSILE_LIFE 12
+#define MISSILE_HITS 2
+#define MISSILE_DAMAGE 3
+#define MISSILE_OFFSET 1
+
+// Marine
+#define SPECIAL_ENERGY_COST 0
+#define SPECIAL_WAIT 12
+#define MARINE_MAX_THRUST 32
+#define MARINE_THRUST_INCREMENT 8
+#define MARINE_HIT_POINTS 3
+#define MARINE_MASS_POINTS 1
+#define MAX_MARINES 8
+#define MARINE_WAIT 12
+#define ION_LIFE 1
+#define START_ION_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)
+
+// Rotating Turret
+#define TURRET_OFFSET 14
+#define TURRET_WAIT 3
+
+static RACE_DESC orz_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_SPECIAL,
+ 23, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ ORZ_RACE_STRINGS,
+ ORZ_ICON_MASK_PMAP_ANIM,
+ ORZ_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 333 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 3608, 2637,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ ORZ_BIG_MASK_PMAP_ANIM,
+ ORZ_MED_MASK_PMAP_ANIM,
+ ORZ_SML_MASK_PMAP_ANIM,
+ },
+ {
+ HOWITZER_BIG_MASK_PMAP_ANIM,
+ HOWITZER_MED_MASK_PMAP_ANIM,
+ HOWITZER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ TURRET_BIG_MASK_PMAP_ANIM,
+ TURRET_MED_MASK_PMAP_ANIM,
+ TURRET_SML_MASK_PMAP_ANIM,
+ },
+ {
+ ORZ_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ ORZ_VICTORY_SONG,
+ ORZ_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_SPEED * MISSILE_LIFE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+howitzer_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (!elementsOfSamePlayer (ElementPtr0, ElementPtr1))
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+}
+
+static COUNT
+initialize_turret_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[])
+{
+ ELEMENT *TurretPtr;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+
+ LockElement (GetSuccElement (ShipPtr), &TurretPtr);
+ if (TurretPtr->turn_wait == 0
+ && (StarShipPtr->cur_status_flags & SPECIAL)
+ && (StarShipPtr->cur_status_flags & (LEFT | RIGHT)))
+ {
+ if (StarShipPtr->cur_status_flags & RIGHT)
+ ++TurretPtr->thrust_wait;
+ else
+ --TurretPtr->thrust_wait;
+
+ TurretPtr->turn_wait = TURRET_WAIT + 1;
+ }
+ MissileBlock.face = MissileBlock.index =
+ NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + TurretPtr->thrust_wait);
+ UnlockElement (GetSuccElement (ShipPtr));
+
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = TURRET_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ MissileArray[0] = initialize_missile (&MissileBlock);
+
+ if (MissileArray[0])
+ {
+ ELEMENT *HowitzerPtr;
+
+ LockElement (MissileArray[0], &HowitzerPtr);
+ HowitzerPtr->collision_func = howitzer_collision;
+ UnlockElement (MissileArray[0]);
+ }
+
+ return (1);
+}
+
+static BYTE
+count_marines (STARSHIP *StarShipPtr, BOOLEAN FindSpot)
+{
+ BYTE num_marines, id_use[MAX_MARINES];
+ HELEMENT hElement, hNextElement;
+
+ num_marines = MAX_MARINES;
+ while (num_marines--)
+ id_use[num_marines] = 0;
+
+ num_marines = 0;
+ for (hElement = GetTailElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetPredElement (ElementPtr);
+ if (ElementPtr->current.image.farray ==
+ StarShipPtr->RaceDescPtr->ship_data.special
+ && ElementPtr->life_span
+ && !(ElementPtr->state_flags & (FINITE_LIFE | DISAPPEARING)))
+ {
+ if (ElementPtr->state_flags & NONSOLID)
+ {
+ id_use[ElementPtr->turn_wait] = 1;
+ }
+
+ if (++num_marines == MAX_MARINES)
+ {
+ UnlockElement (hElement);
+ hNextElement = 0;
+ }
+ }
+ UnlockElement (hElement);
+ }
+
+ if (FindSpot)
+ {
+ num_marines = 0;
+ while (id_use[num_marines])
+ ++num_marines;
+ }
+
+ return (num_marines);
+}
+
+static void
+orz_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ ELEMENT *TurretPtr;
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ LockElement (GetSuccElement (ShipPtr), &TurretPtr);
+
+ ++TurretPtr->turn_wait;
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+ --TurretPtr->turn_wait;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr == 0)
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ else if (StarShipPtr->special_counter != 1)
+ {
+ STARSHIP *EnemyStarShipPtr;
+
+ if (ShipPtr->turn_wait == 0
+ && lpEvalDesc->MoveState == ENTICE
+ && lpEvalDesc->which_turn < 24
+ && (StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))
+ && !(StarShipPtr->ship_input_state & THRUST)
+ && NORMALIZE_ANGLE (
+ GetVelocityTravelAngle (&ShipPtr->velocity)
+ - ARCTAN (
+ lpEvalDesc->ObjectPtr->next.location.x
+ - ShipPtr->next.location.x,
+ lpEvalDesc->ObjectPtr->next.location.y
+ - ShipPtr->next.location.y
+ ) + (QUADRANT - (OCTANT >> 1))) >=
+ ((QUADRANT - (OCTANT >> 1)) << 1))
+ StarShipPtr->ship_input_state &= ~(LEFT | RIGHT);
+
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (ShipPtr->turn_wait == 0
+ && !(StarShipPtr->ship_input_state & (LEFT | RIGHT | WEAPON))
+ && TurretPtr->turn_wait == 0)
+ {
+ SIZE delta_facing;
+ COUNT facing;//, orig_facing;
+
+ facing = NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + TurretPtr->thrust_wait);
+ if ((delta_facing = TrackShip (TurretPtr, &facing)) > 0)
+ {
+ StarShipPtr->ship_input_state |= SPECIAL;
+ if (delta_facing == ANGLE_TO_FACING (HALF_CIRCLE))
+ delta_facing += (((BYTE)TFB_Random () & 1) << 1) - 1;
+
+ if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
+ StarShipPtr->ship_input_state |= RIGHT;
+ else
+ StarShipPtr->ship_input_state |= LEFT;
+ }
+ }
+
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (StarShipPtr->special_counter == 0
+ && !(StarShipPtr->ship_input_state & WEAPON)
+ && StarShipPtr->RaceDescPtr->ship_info.crew_level >
+ (BYTE)(StarShipPtr->RaceDescPtr->ship_info.max_crew >> 2)
+ && !(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & POINT_DEFENSE)
+ && (MANEUVERABILITY (
+ &EnemyStarShipPtr->RaceDescPtr->cyborg_control
+ ) < SLOW_SHIP
+ || lpEvalDesc->which_turn <= 12
+ || count_marines (StarShipPtr, FALSE) < 2))
+ {
+ StarShipPtr->ship_input_state |= WEAPON | SPECIAL;
+ }
+ }
+
+ UnlockElement (GetSuccElement (ShipPtr));
+}
+
+static void
+ion_preprocess (ELEMENT *ElementPtr)
+{
+ /* Originally, this table also contained the now commented out
+ * entries. It then used some if statements to skip over these.
+ * The current behaviour is the same as the old behavior.
+ */
+ static const Color colorTable[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7a),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7b),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7c),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7d),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7e),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7f),
+
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x2a),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2b),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2c),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2d),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2e),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x0B, 0x00, 0x00), 0x2f),
+ };
+ const size_t colorTabCount = sizeof colorTable / sizeof colorTable[0];
+
+ ElementPtr->colorCycleIndex++;
+ if (ElementPtr->colorCycleIndex != colorTabCount)
+ {
+ ElementPtr->life_span = ElementPtr->thrust_wait;
+
+ SetPrimColor (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ colorTable[ElementPtr->colorCycleIndex]);
+
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static void marine_preprocess (ELEMENT *ElementPtr);
+
+void
+intruder_preprocess (ELEMENT *ElementPtr)
+{
+ HELEMENT hElement, hNextElement;
+ ELEMENT *ShipPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ if (ShipPtr->crew_level == 0
+ && ShipPtr->life_span == 1
+ && (ShipPtr->state_flags & (FINITE_LIFE | NONSOLID)) ==
+ (FINITE_LIFE | NONSOLID))
+ {
+ ElementPtr->life_span = 0;
+ ElementPtr->state_flags |= DISAPPEARING;
+ }
+ UnlockElement (StarShipPtr->hShip);
+
+ if (ElementPtr->thrust_wait)
+ --ElementPtr->thrust_wait;
+
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ LockElement (hElement, &ShipPtr);
+ if ((ShipPtr->state_flags & PLAYER_SHIP)
+ && !elementsOfSamePlayer (ShipPtr, ElementPtr))
+ {
+ STAMP s;
+
+ if (ElementPtr->thrust_wait == MARINE_WAIT)
+ {
+ --ElementPtr->thrust_wait;
+
+ s.origin.x = 16 + (ElementPtr->turn_wait & 3) * 9;
+ s.origin.y = 14 + (ElementPtr->turn_wait >> 2) * 11;
+ s.frame = SetAbsFrameIndex (ElementPtr->next.image.farray[0],
+ GetFrameCount (ElementPtr->next.image.farray[0]) - 2);
+ ModifySilhouette (ShipPtr, &s, 0);
+ }
+
+ ElementPtr->next.location = ShipPtr->next.location;
+
+ if (ShipPtr->crew_level == 0
+ || ElementPtr->life_span == 0)
+ {
+ UnlockElement (hElement);
+ hElement = 0;
+LeftShip:
+ s.origin.x = 16 + (ElementPtr->turn_wait & 3) * 9;
+ s.origin.y = 14 + (ElementPtr->turn_wait >> 2) * 11;
+ s.frame = ElementPtr->next.image.frame;
+ ModifySilhouette (ShipPtr, &s, MODIFY_SWAP);
+ }
+ else if (ElementPtr->thrust_wait == 0)
+ {
+ BYTE randval;
+
+ ElementPtr->thrust_wait = MARINE_WAIT;
+
+ randval = (BYTE)TFB_Random ();
+ if (randval < (0x0100 / 16))
+ {
+ ElementPtr->life_span = 0;
+ ElementPtr->state_flags |= DISAPPEARING;
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 4), ElementPtr);
+ goto LeftShip;
+ }
+ else if (randval < (0x0100 / 2 + 0x0100 / 16))
+ {
+ if (!DeltaCrew (ShipPtr, -1))
+ ShipPtr->life_span = 0;
+
+ ++ElementPtr->thrust_wait;
+ s.origin.x = 16 + (ElementPtr->turn_wait & 3) * 9;
+ s.origin.y = 14 + (ElementPtr->turn_wait >> 2) * 11;
+ s.frame = SetAbsFrameIndex (ElementPtr->next.image.farray[0],
+ GetFrameCount (ElementPtr->next.image.farray[0]) - 1);
+ ModifySilhouette (ShipPtr, &s, 0);
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), ElementPtr);
+ }
+ }
+
+ UnlockElement (hElement);
+ break;
+ }
+ hNextElement = GetSuccElement (ShipPtr);
+ UnlockElement (hElement);
+ }
+
+ if (hElement == 0 && ElementPtr->life_span)
+ {
+ ElementPtr->state_flags &= ~NONSOLID;
+ ElementPtr->state_flags |= CHANGING | CREW_OBJECT;
+ SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ STAMP_PRIM);
+
+ ElementPtr->current.image.frame =
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0], 21);
+ ElementPtr->thrust_wait = 0;
+ ElementPtr->turn_wait =
+ MAKE_BYTE (0, NORMALIZE_FACING ((BYTE)TFB_Random ()));
+ ElementPtr->preprocess_func = marine_preprocess;
+ }
+}
+
+// XXX: merge this with spawn_ion_trail from tactrans.c?
+static void
+spawn_marine_ion_trail (ELEMENT *ElementPtr, STARSHIP *StarShipPtr,
+ COUNT facing)
+{
+ HELEMENT hIonElement;
+
+ hIonElement = AllocElement ();
+ if (hIonElement)
+ {
+ COUNT angle;
+ ELEMENT *IonElementPtr;
+
+ angle = FACING_TO_ANGLE (facing) + HALF_CIRCLE;
+
+ InsertElement (hIonElement, GetHeadElement ());
+ LockElement (hIonElement, &IonElementPtr);
+ IonElementPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ IonElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ IonElementPtr->thrust_wait = ION_LIFE;
+ IonElementPtr->life_span = IonElementPtr->thrust_wait;
+ // When the element "dies", in the death_func
+ // 'cycle_ion_trail', it is given new life a number of
+ // times, by setting life_span to thrust_wait.
+ SetPrimType (&(GLOBAL (DisplayArray))[IonElementPtr->PrimIndex],
+ POINT_PRIM);
+ SetPrimColor (&(GLOBAL (DisplayArray))[IonElementPtr->PrimIndex],
+ START_ION_COLOR);
+ IonElementPtr->colorCycleIndex = 0;
+ IonElementPtr->current.location = ElementPtr->current.location;
+ IonElementPtr->current.location.x +=
+ (COORD)COSINE (angle, DISPLAY_TO_WORLD (2));
+ IonElementPtr->current.location.y +=
+ (COORD)SINE (angle, DISPLAY_TO_WORLD (2));
+ IonElementPtr->death_func = ion_preprocess;
+
+ SetElementStarShip (IonElementPtr, StarShipPtr);
+
+ {
+ /* normally done during preprocess, but because
+ * object is being inserted at head rather than
+ * appended after tail it may never get preprocessed.
+ */
+ IonElementPtr->next = IonElementPtr->current;
+ --IonElementPtr->life_span;
+ IonElementPtr->state_flags |= PRE_PROCESS;
+ }
+
+ UnlockElement (hIonElement);
+ }
+}
+
+static void
+marine_preprocess (ELEMENT *ElementPtr)
+{
+ ELEMENT *ShipPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ if (ShipPtr->crew_level == 0
+ && ShipPtr->life_span == 1
+ && (ShipPtr->state_flags & (FINITE_LIFE | NONSOLID)) ==
+ (FINITE_LIFE | NONSOLID))
+ {
+ ElementPtr->life_span = 0;
+ ElementPtr->state_flags |= DISAPPEARING | NONSOLID;
+ ElementPtr->turn_wait = 1;
+ }
+ UnlockElement (StarShipPtr->hShip);
+
+ if (LONIBBLE (ElementPtr->turn_wait))
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT facing, pfacing = 0;
+ SIZE delta_x, delta_y, delta_facing;
+ HELEMENT hObject, hNextObject, hTarget;
+ ELEMENT *ObjectPtr;
+
+ // XXX: thrust_wait is abused to store marine speed and
+ // gravity well flags
+ ElementPtr->thrust_wait &= ~(SHIP_IN_GRAVITY_WELL >> 6);
+
+ hTarget = 0;
+ for (hObject = GetHeadElement ();
+ hObject; hObject = hNextObject)
+ {
+ LockElement (hObject, &ObjectPtr);
+ hNextObject = GetSuccElement (ObjectPtr);
+ if (GRAVITY_MASS (ObjectPtr->mass_points))
+ {
+ delta_x = ObjectPtr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_x = WRAP_DELTA_X (delta_x);
+
+ delta_y = ObjectPtr->current.location.y
+ - ElementPtr->current.location.y;
+ delta_y = WRAP_DELTA_Y (delta_y);
+ if ((long)delta_x * delta_x + (long)delta_y * delta_y <=
+ (long)(DISPLAY_TO_WORLD (GRAVITY_THRESHOLD)
+ * DISPLAY_TO_WORLD (GRAVITY_THRESHOLD)))
+ {
+ pfacing = ANGLE_TO_FACING (ARCTAN (delta_x, delta_y));
+ delta_facing = NORMALIZE_FACING (
+ pfacing - ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity))
+ + ANGLE_TO_FACING (OCTANT));
+ if (delta_facing <= ANGLE_TO_FACING (QUADRANT))
+ {
+ hTarget = hObject;
+ hNextObject = 0;
+ }
+
+ ElementPtr->thrust_wait |= (SHIP_IN_GRAVITY_WELL >> 6);
+ }
+ }
+ else if ((ObjectPtr->state_flags & PLAYER_SHIP)
+ && ObjectPtr->crew_level
+ && !OBJECT_CLOAKED (ObjectPtr))
+ {
+ if (!elementsOfSamePlayer (ObjectPtr, ElementPtr))
+ {
+ if (ElementPtr->state_flags & IGNORE_SIMILAR)
+ hTarget = hObject;
+ }
+ else if (hTarget == 0)
+ hTarget = hObject;
+ }
+ UnlockElement (hObject);
+ }
+
+ facing = HINIBBLE (ElementPtr->turn_wait);
+ if (hTarget == 0)
+ delta_facing = -1;
+ else
+ {
+ LockElement (hTarget, &ObjectPtr);
+ delta_x = ObjectPtr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = ObjectPtr->current.location.y
+ - ElementPtr->current.location.y;
+ delta_y = WRAP_DELTA_Y (delta_y);
+ if (GRAVITY_MASS (ObjectPtr->mass_points))
+ {
+ delta_facing = NORMALIZE_FACING (pfacing - facing
+ + ANGLE_TO_FACING (OCTANT));
+
+ if (delta_facing > ANGLE_TO_FACING (QUADRANT))
+ delta_facing = 0;
+ else
+ {
+ if (delta_facing == ANGLE_TO_FACING (OCTANT))
+ facing += (((SIZE)TFB_Random () & 1) << 1) - 1;
+ else if (delta_facing < ANGLE_TO_FACING (OCTANT))
+ ++facing;
+ else
+ --facing;
+ }
+ }
+ else
+ {
+ COUNT num_frames;
+ VELOCITY_DESC ShipVelocity;
+
+ if (elementsOfSamePlayer (ObjectPtr, ElementPtr)
+ && (ElementPtr->state_flags & IGNORE_SIMILAR))
+ {
+ ElementPtr->next.image.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0],
+ 21);
+ ElementPtr->state_flags &= ~IGNORE_SIMILAR;
+ ElementPtr->state_flags |= CHANGING;
+ }
+
+ num_frames = WORLD_TO_TURN (
+ square_root ((long)delta_x * delta_x
+ + (long)delta_y * delta_y));
+ if (num_frames == 0)
+ num_frames = 1;
+
+ ShipVelocity = ObjectPtr->velocity;
+ GetNextVelocityComponents (&ShipVelocity,
+ &delta_x, &delta_y, num_frames);
+
+ delta_x = (ObjectPtr->current.location.x + delta_x)
+ - ElementPtr->current.location.x;
+ delta_y = (ObjectPtr->current.location.y + delta_y)
+ - ElementPtr->current.location.y;
+
+ delta_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)) - facing);
+
+ if (delta_facing > 0)
+ {
+ if (delta_facing == ANGLE_TO_FACING (HALF_CIRCLE))
+ facing += (((BYTE)TFB_Random () & 1) << 1) - 1;
+ else if (delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
+ ++facing;
+ else
+ --facing;
+ }
+ }
+ UnlockElement (hTarget);
+ }
+
+ ElementPtr->turn_wait = MAKE_BYTE (0, NORMALIZE_FACING (facing));
+ if (delta_facing == 0
+ || ((ElementPtr->thrust_wait & (SHIP_BEYOND_MAX_SPEED >> 6))
+ && !(ElementPtr->thrust_wait & (SHIP_IN_GRAVITY_WELL >> 6))))
+ {
+ STATUS_FLAGS thrust_status;
+ COUNT OldFacing;
+ STATUS_FLAGS OldStatus;
+ COUNT OldIncrement, OldThrust;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ // XXX: Hack: abusing the primary STARSHIP struct in order
+ // to call inertial_thrust() for a marine
+ OldFacing = StarShipPtr->ShipFacing;
+ OldStatus = StarShipPtr->cur_status_flags;
+ OldIncrement = StarShipPtr->RaceDescPtr->characteristics.
+ thrust_increment;
+ OldThrust = StarShipPtr->RaceDescPtr->characteristics.max_thrust;
+
+ StarShipPtr->ShipFacing = facing;
+ // XXX: thrust_wait is abused to store marine speed and
+ // gravity well flags
+ StarShipPtr->cur_status_flags = ElementPtr->thrust_wait << 6;
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment =
+ MARINE_THRUST_INCREMENT;
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust =
+ MARINE_MAX_THRUST;
+
+ thrust_status = inertial_thrust (ElementPtr);
+
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust = OldThrust;
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment =
+ OldIncrement;
+ StarShipPtr->cur_status_flags = OldStatus;
+ StarShipPtr->ShipFacing = OldFacing;
+
+ if ((ElementPtr->thrust_wait & (SHIP_IN_GRAVITY_WELL >> 6))
+ || delta_facing
+ || !(thrust_status
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED)))
+ {
+ spawn_marine_ion_trail (ElementPtr, StarShipPtr, facing);
+ }
+
+ // XXX: thrust_wait is abused to store marine speed and
+ // gravity well flags
+ ElementPtr->thrust_wait = (BYTE)(thrust_status >> 6);
+ }
+ }
+}
+
+void
+marine_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr0->life_span
+ && !(ElementPtr0->state_flags & (NONSOLID | COLLISION))
+ && !(ElementPtr1->state_flags & FINITE_LIFE))
+ {
+ if (!elementsOfSamePlayer (ElementPtr0, ElementPtr1))
+ {
+ ElementPtr0->turn_wait =
+ MAKE_BYTE (5, HINIBBLE (ElementPtr0->turn_wait));
+ ElementPtr0->thrust_wait &=
+ ~((SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED) >> 6);
+ ElementPtr0->state_flags |= COLLISION;
+ }
+
+ if (GRAVITY_MASS (ElementPtr1->mass_points))
+ {
+ ElementPtr0->state_flags |= NONSOLID | FINITE_LIFE;
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ }
+ else if ((ElementPtr1->state_flags & PLAYER_SHIP)
+ && ((ElementPtr1->state_flags & FINITE_LIFE)
+ || ElementPtr1->life_span == NORMAL_LIFE))
+ {
+ ElementPtr1->state_flags &= ~COLLISION;
+
+ if (!(ElementPtr0->state_flags & COLLISION))
+ {
+ DeltaCrew (ElementPtr1, 1);
+
+ ElementPtr0->state_flags |=
+ DISAPPEARING | NONSOLID | FINITE_LIFE;
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ }
+ else if ((ElementPtr0->state_flags & IGNORE_SIMILAR)
+ && ElementPtr1->crew_level
+#ifdef NEVER
+ && (BYTE)TFB_Random () <= (0x0100 / 3)
+#endif /* NEVER */
+ )
+ {
+ STAMP s;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ if (!DeltaCrew (ElementPtr1, -1))
+ ElementPtr1->life_span = 0;
+ else
+ {
+ ElementPtr0->turn_wait = count_marines (StarShipPtr, TRUE);
+ ElementPtr0->thrust_wait = MARINE_WAIT;
+ ElementPtr0->next.image.frame = SetAbsFrameIndex (
+ ElementPtr0->next.image.farray[0],
+ 22 + ElementPtr0->turn_wait
+ );
+ ElementPtr0->state_flags |= NONSOLID;
+ ElementPtr0->state_flags &= ~CREW_OBJECT;
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ ElementPtr0->PrimIndex
+ ], NO_PRIM);
+ ElementPtr0->preprocess_func = intruder_preprocess;
+
+ s.origin.x = 16 + (ElementPtr0->turn_wait & 3) * 9;
+ s.origin.y = 14 + (ElementPtr0->turn_wait >> 2) * 11;
+ s.frame = ElementPtr0->next.image.frame;
+ ModifySilhouette (ElementPtr1, &s, 0);
+ }
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2),
+ ElementPtr1);
+ }
+
+ ElementPtr0->state_flags &= ~COLLISION;
+ }
+ }
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+animate (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+static void
+turret_postprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->life_span == 0)
+ {
+ STARSHIP *StarShipPtr;
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ ElementPtr->PrimIndex], NO_PRIM);
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->hShip)
+ {
+ COUNT facing;
+ HELEMENT hTurret, hSpaceMarine;
+ ELEMENT *ShipPtr;
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ hTurret = AllocElement ();
+ if (hTurret)
+ {
+ ELEMENT *TurretPtr;
+
+ LockElement (hTurret, &TurretPtr);
+ TurretPtr->playerNr = ElementPtr->playerNr;
+ TurretPtr->state_flags = FINITE_LIFE | NONSOLID
+ | IGNORE_SIMILAR | CHANGING | PRE_PROCESS
+ | POST_PROCESS;
+ TurretPtr->life_span = 1;
+ TurretPtr->current.image = ElementPtr->current.image;
+ TurretPtr->current.location = ShipPtr->next.location;
+ TurretPtr->turn_wait = ElementPtr->turn_wait;
+ TurretPtr->thrust_wait = ElementPtr->thrust_wait;
+
+ if (TurretPtr->turn_wait)
+ --TurretPtr->turn_wait;
+ else if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && (StarShipPtr->cur_status_flags & (LEFT | RIGHT)))
+ {
+ if (StarShipPtr->cur_status_flags & RIGHT)
+ ++TurretPtr->thrust_wait;
+ else
+ --TurretPtr->thrust_wait;
+
+ TurretPtr->turn_wait = TURRET_WAIT;
+ }
+ facing = NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + TurretPtr->thrust_wait);
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags &=
+ ~(FIRES_FORE | FIRES_RIGHT | FIRES_AFT | FIRES_LEFT);
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags |= FIRES_FORE
+ << (NORMALIZE_FACING (facing + ANGLE_TO_FACING (OCTANT))
+ / ANGLE_TO_FACING (QUADRANT));
+ TurretPtr->current.image.frame = SetAbsFrameIndex (
+ TurretPtr->current.image.frame, facing);
+ facing = FACING_TO_ANGLE (facing);
+ if (StarShipPtr->cur_status_flags & WEAPON)
+ {
+ HELEMENT hTurretEffect;
+ ELEMENT *TurretEffectPtr;
+
+ LockElement (GetTailElement (), &TurretEffectPtr);
+ if (TurretEffectPtr != ElementPtr
+ && elementsOfSamePlayer (TurretEffectPtr, ElementPtr)
+ && (TurretEffectPtr->state_flags & APPEARING)
+ && GetPrimType (&(GLOBAL (DisplayArray))[
+ TurretEffectPtr->PrimIndex
+ ]) == STAMP_PRIM
+ && (hTurretEffect = AllocElement ()))
+ {
+ TurretPtr->current.location.x -=
+ COSINE (facing, DISPLAY_TO_WORLD (2));
+ TurretPtr->current.location.y -=
+ SINE (facing, DISPLAY_TO_WORLD (2));
+
+ LockElement (hTurretEffect, &TurretEffectPtr);
+ TurretEffectPtr->playerNr = ElementPtr->playerNr;
+ TurretEffectPtr->state_flags = FINITE_LIFE
+ | NONSOLID | IGNORE_SIMILAR | APPEARING;
+ TurretEffectPtr->life_span = 4;
+
+ TurretEffectPtr->current.location.x =
+ TurretPtr->current.location.x
+ + COSINE (facing,
+ DISPLAY_TO_WORLD (TURRET_OFFSET));
+ TurretEffectPtr->current.location.y =
+ TurretPtr->current.location.y
+ + SINE (facing,
+ DISPLAY_TO_WORLD (TURRET_OFFSET));
+ TurretEffectPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ TurretEffectPtr->current.image.frame =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0],
+ ANGLE_TO_FACING (FULL_CIRCLE));
+
+ TurretEffectPtr->preprocess_func = animate;
+
+ SetElementStarShip (TurretEffectPtr, StarShipPtr);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ TurretEffectPtr->PrimIndex], STAMP_PRIM);
+
+ UnlockElement (hTurretEffect);
+ PutElement (hTurretEffect);
+ }
+ UnlockElement (GetTailElement ());
+ }
+ TurretPtr->next = TurretPtr->current;
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ TurretPtr->PrimIndex],
+ GetPrimType (&(GLOBAL (DisplayArray))[
+ ShipPtr->PrimIndex]));
+ SetPrimColor (&(GLOBAL (DisplayArray))[
+ TurretPtr->PrimIndex],
+ GetPrimColor (&(GLOBAL (DisplayArray))[
+ ShipPtr->PrimIndex]));
+
+ TurretPtr->postprocess_func = ElementPtr->postprocess_func;
+
+ SetElementStarShip (TurretPtr, StarShipPtr);
+
+ UnlockElement (hTurret);
+ InsertElement (hTurret, GetSuccElement (ElementPtr));
+ }
+
+ if (StarShipPtr->special_counter == 0
+ && (StarShipPtr->cur_status_flags & SPECIAL)
+ && (StarShipPtr->cur_status_flags & WEAPON)
+ && ShipPtr->crew_level > 1
+ && count_marines (StarShipPtr, FALSE) < MAX_MARINES
+ && TrackShip (ShipPtr, &facing) >= 0
+ && (hSpaceMarine = AllocElement ()))
+ {
+ ELEMENT *SpaceMarinePtr;
+
+ LockElement (hSpaceMarine, &SpaceMarinePtr);
+ SpaceMarinePtr->playerNr = ElementPtr->playerNr;
+ SpaceMarinePtr->state_flags = IGNORE_SIMILAR | APPEARING
+ | CREW_OBJECT;
+ SpaceMarinePtr->life_span = NORMAL_LIFE;
+ SpaceMarinePtr->hit_points = MARINE_HIT_POINTS;
+ SpaceMarinePtr->mass_points = MARINE_MASS_POINTS;
+
+ facing = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ SpaceMarinePtr->current.location.x =
+ ShipPtr->current.location.x
+ - COSINE (facing, DISPLAY_TO_WORLD (TURRET_OFFSET));
+ SpaceMarinePtr->current.location.y =
+ ShipPtr->current.location.y
+ - SINE (facing, DISPLAY_TO_WORLD (TURRET_OFFSET));
+ SpaceMarinePtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ SpaceMarinePtr->current.image.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0], 20);
+
+ SpaceMarinePtr->turn_wait =
+ MAKE_BYTE (0, NORMALIZE_FACING (
+ ANGLE_TO_FACING (facing + HALF_CIRCLE)));
+ SpaceMarinePtr->preprocess_func = marine_preprocess;
+ SpaceMarinePtr->collision_func = marine_collision;
+
+ SetElementStarShip (SpaceMarinePtr, StarShipPtr);
+
+ SetPrimType (&(GLOBAL (DisplayArray))[
+ SpaceMarinePtr->PrimIndex], STAMP_PRIM);
+
+ UnlockElement (hSpaceMarine);
+ PutElement (hSpaceMarine);
+
+ DeltaCrew (ShipPtr, -1);
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
+ SpaceMarinePtr);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+
+ UnlockElement (StarShipPtr->hShip);
+ }
+ }
+}
+
+static void
+orz_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (!(ElementPtr->state_flags & APPEARING))
+ {
+ if (((StarShipPtr->cur_status_flags
+ | StarShipPtr->old_status_flags) & SPECIAL)
+ && (StarShipPtr->cur_status_flags & (LEFT | RIGHT))
+ && ElementPtr->turn_wait == 0)
+ {
+ ++ElementPtr->turn_wait;
+ }
+
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && (StarShipPtr->cur_status_flags & WEAPON)
+ && StarShipPtr->weapon_counter == 0)
+ {
+ ++StarShipPtr->weapon_counter;
+ }
+ }
+ else
+ {
+ HELEMENT hTurret;
+
+ hTurret = AllocElement ();
+ if (hTurret)
+ {
+ ELEMENT *TurretPtr;
+
+ LockElement (hTurret, &TurretPtr);
+ TurretPtr->playerNr = ElementPtr->playerNr;
+ TurretPtr->state_flags = FINITE_LIFE | NONSOLID | IGNORE_SIMILAR;
+ TurretPtr->life_span = 1;
+ TurretPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ TurretPtr->current.image.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0],
+ StarShipPtr->ShipFacing);
+
+ TurretPtr->postprocess_func = turret_postprocess;
+
+ SetElementStarShip (TurretPtr, StarShipPtr);
+
+ UnlockElement (hTurret);
+ InsertElement (hTurret, GetSuccElement (ElementPtr));
+ }
+ }
+}
+
+RACE_DESC*
+init_orz (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ orz_desc.preprocess_func = orz_preprocess;
+ orz_desc.init_weapon_func = initialize_turret_missile;
+ orz_desc.cyborg_control.intelligence_func = orz_intelligence;
+
+ RaceDescPtr = &orz_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/orz/orz.h b/src/uqm/ships/orz/orz.h
new file mode 100644
index 0000000..a62caac
--- /dev/null
+++ b/src/uqm/ships/orz/orz.h
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ORZ_H
+#define ORZ_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_orz (void);
+
+void intruder_preprocess (ELEMENT *ElementPtr);
+void marine_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ORZ_H */
+
diff --git a/src/uqm/ships/orz/resinst.h b/src/uqm/ships/orz/resinst.h
new file mode 100644
index 0000000..4af5264
--- /dev/null
+++ b/src/uqm/ships/orz/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define HOWITZER_BIG_MASK_PMAP_ANIM "ship.orz.graphics.howitzer.large"
+#define HOWITZER_MED_MASK_PMAP_ANIM "ship.orz.graphics.howitzer.medium"
+#define HOWITZER_SML_MASK_PMAP_ANIM "ship.orz.graphics.howitzer.small"
+#define ORZ_BIG_MASK_PMAP_ANIM "ship.orz.graphics.nemesis.large"
+#define ORZ_CAPTAIN_MASK_PMAP_ANIM "ship.orz.graphics.captain"
+#define ORZ_ICON_MASK_PMAP_ANIM "ship.orz.icons"
+#define ORZ_MED_MASK_PMAP_ANIM "ship.orz.graphics.nemesis.medium"
+#define ORZ_MICON_MASK_PMAP_ANIM "ship.orz.meleeicons"
+#define ORZ_RACE_STRINGS "ship.orz.text"
+#define ORZ_SHIP_SOUNDS "ship.orz.sounds"
+#define ORZ_SML_MASK_PMAP_ANIM "ship.orz.graphics.nemesis.small"
+#define ORZ_VICTORY_SONG "ship.orz.ditty"
+#define TURRET_BIG_MASK_PMAP_ANIM "ship.orz.graphics.turret.large"
+#define TURRET_MED_MASK_PMAP_ANIM "ship.orz.graphics.turret.medium"
+#define TURRET_SML_MASK_PMAP_ANIM "ship.orz.graphics.turret.small"
diff --git a/src/uqm/ships/pkunk/Makeinfo b/src/uqm/ships/pkunk/Makeinfo
new file mode 100644
index 0000000..54d92b0
--- /dev/null
+++ b/src/uqm/ships/pkunk/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="pkunk.c"
+uqm_HFILES="icode.h pkunk.h resinst.h"
diff --git a/src/uqm/ships/pkunk/icode.h b/src/uqm/ships/pkunk/icode.h
new file mode 100644
index 0000000..4d0d4e5
--- /dev/null
+++ b/src/uqm/ships/pkunk/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define PKUNK_CODE "ship.pkunk.code"
diff --git a/src/uqm/ships/pkunk/pkunk.c b/src/uqm/ships/pkunk/pkunk.c
new file mode 100644
index 0000000..76f8413
--- /dev/null
+++ b/src/uqm/ships/pkunk/pkunk.c
@@ -0,0 +1,640 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "pkunk.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "uqm/tactrans.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 8
+#define MAX_ENERGY 12
+#define ENERGY_REGENERATION 0
+#define ENERGY_WAIT 0
+#define MAX_THRUST 64
+#define THRUST_INCREMENT 16
+#define THRUST_WAIT 0
+#define TURN_WAIT 0
+#define SHIP_MASS 1
+
+// Triple Miniguns
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 0
+#define PKUNK_OFFSET 15
+#define MISSILE_OFFSET 1
+#define MISSILE_SPEED DISPLAY_TO_WORLD (24)
+#define MISSILE_LIFE 5
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+
+// Taunt
+#define SPECIAL_ENERGY_COST 2
+#define SPECIAL_WAIT 16
+
+// Respawn
+#define PHOENIX_LIFE 12
+#define START_PHOENIX_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)
+#define TRANSITION_LIFE 1
+#define TRANSITION_SPEED DISPLAY_TO_WORLD (20)
+
+static RACE_DESC pkunk_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | FIRES_LEFT | FIRES_RIGHT,
+ 20, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ PKUNK_RACE_STRINGS,
+ PKUNK_ICON_MASK_PMAP_ANIM,
+ PKUNK_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 666 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 502, 401,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ 0, /* SPECIAL_WAIT */
+ SHIP_MASS,
+ },
+ {
+ {
+ PKUNK_BIG_MASK_PMAP_ANIM,
+ PKUNK_MED_MASK_PMAP_ANIM,
+ PKUNK_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BUG_BIG_MASK_PMAP_ANIM,
+ BUG_MED_MASK_PMAP_ANIM,
+ BUG_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ PKUNK_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ PKUNK_VICTORY_SONG,
+ PKUNK_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON + 1,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+// Private per-instance ship data
+typedef struct
+{
+ HELEMENT hPhoenix;
+ ElementProcessFunc *saved_preprocess_func;
+ ElementProcessFunc *saved_postprocess_func;
+ ElementProcessFunc *saved_death_func;
+
+} PKUNK_DATA;
+
+// Local typedef
+typedef PKUNK_DATA CustomShipData_t;
+
+// Retrieve race-specific ship data from a race desc
+static CustomShipData_t *
+GetCustomShipData (RACE_DESC *pRaceDesc)
+{
+ return pRaceDesc->data;
+}
+
+// Set the race-specific data in a race desc
+// (Re)Allocates its own storage for the data.
+static void
+SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data)
+{
+ if (pRaceDesc->data == data)
+ return; // no-op
+
+ if (pRaceDesc->data) // Out with the old
+ {
+ HFree (pRaceDesc->data);
+ pRaceDesc->data = NULL;
+ }
+
+ if (data) // In with the new
+ {
+ CustomShipData_t* newData = HMalloc (sizeof (*data));
+ *newData = *data;
+ pRaceDesc->data = newData;
+ }
+}
+
+static void
+animate (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->turn_wait = ElementPtr->next_turn;
+ }
+}
+
+static COUNT
+initialize_bug_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[])
+{
+ COUNT i;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = PKUNK_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+
+ for (i = 0; i < 3; ++i)
+ {
+ MissileBlock.face =
+ StarShipPtr->ShipFacing
+ + (ANGLE_TO_FACING (QUADRANT) * i);
+ if (i == 2)
+ MissileBlock.face += ANGLE_TO_FACING (QUADRANT);
+ MissileBlock.face = NORMALIZE_FACING (MissileBlock.face);
+
+ if ((MissileArray[i] = initialize_missile (&MissileBlock)))
+ {
+ SIZE dx, dy;
+ ELEMENT *MissilePtr;
+
+ LockElement (MissileArray[i], &MissilePtr);
+ GetCurrentVelocityComponents (&ShipPtr->velocity, &dx, &dy);
+ DeltaVelocityComponents (&MissilePtr->velocity, dx, dy);
+ MissilePtr->current.location.x -= VELOCITY_TO_WORLD (dx);
+ MissilePtr->current.location.y -= VELOCITY_TO_WORLD (dy);
+
+ MissilePtr->preprocess_func = animate;
+ UnlockElement (MissileArray[i]);
+ }
+ }
+
+ return (3);
+}
+
+static void
+pkunk_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+ PKUNK_DATA *PkunkData;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ PkunkData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+ if (PkunkData->hPhoenix && (StarShipPtr->control & STANDARD_RATING))
+ {
+ RemoveElement (PkunkData->hPhoenix);
+ FreeElement (PkunkData->hPhoenix);
+ PkunkData->hPhoenix = 0;
+ }
+
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level <
+ StarShipPtr->RaceDescPtr->ship_info.max_energy
+ && (StarShipPtr->special_counter == 0
+ || (BYTE)TFB_Random () < 20))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+}
+
+static void pkunk_preprocess (ELEMENT *ElementPtr);
+
+static void
+new_pkunk (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ PKUNK_DATA *PkunkData;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ PkunkData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+
+ ElementPtr->state_flags = APPEARING | PLAYER_SHIP | IGNORE_SIMILAR;
+ ElementPtr->mass_points = SHIP_MASS;
+ // Restore the element processing callbacks after the explosion.
+ // The callbacks were changed for the explosion sequence
+ ElementPtr->preprocess_func = PkunkData->saved_preprocess_func;
+ ElementPtr->postprocess_func = PkunkData->saved_postprocess_func;
+ ElementPtr->death_func = PkunkData->saved_death_func;
+ // preprocess_func() is called during the phoenix transition and
+ // then cleared, so we need to restore it
+ StarShipPtr->RaceDescPtr->preprocess_func = pkunk_preprocess;
+ StarShipPtr->RaceDescPtr->ship_info.crew_level = MAX_CREW;
+ StarShipPtr->RaceDescPtr->ship_info.energy_level = MAX_ENERGY;
+ /* fix vux impairment */
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust = MAX_THRUST;
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment = THRUST_INCREMENT;
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait = TURN_WAIT;
+ StarShipPtr->RaceDescPtr->characteristics.thrust_wait = THRUST_WAIT;
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
+
+ StarShipPtr->ship_input_state = 0;
+ // Pkunk wins in a simultaneous destruction if it reincarnates
+ StarShipPtr->cur_status_flags &= PLAY_VICTORY_DITTY;
+ StarShipPtr->old_status_flags = 0;
+ StarShipPtr->energy_counter = 0;
+ StarShipPtr->weapon_counter = 0;
+ StarShipPtr->special_counter = 0;
+ ElementPtr->crew_level = 0;
+ ElementPtr->turn_wait = 0;
+ ElementPtr->thrust_wait = 0;
+ ElementPtr->life_span = NORMAL_LIFE;
+
+ StarShipPtr->ShipFacing = NORMALIZE_FACING (TFB_Random ());
+ ElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.ship;
+ ElementPtr->current.image.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship[0],
+ StarShipPtr->ShipFacing);
+ SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex], STAMP_PRIM);
+
+ do
+ {
+ ElementPtr->current.location.x =
+ WRAP_X (DISPLAY_ALIGN_X (TFB_Random ()));
+ ElementPtr->current.location.y =
+ WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ()));
+ } while (CalculateGravity (ElementPtr)
+ || TimeSpaceMatterConflict (ElementPtr));
+
+ // XXX: Hack: Set hTarget!=0 so that ship_preprocess() does not
+ // call ship_transition() for us.
+ ElementPtr->hTarget = StarShipPtr->hShip;
+}
+
+// This function is called when the ship dies but reincarnates.
+// The generic ship_death() function is not called for the ship in this case.
+static void
+pkunk_reincarnation_death (ELEMENT *ShipPtr)
+{
+ // Simulate ship death
+ StopAllBattleMusic ();
+ StartShipExplosion (ShipPtr, true);
+ // Once the explosion ends, we will get a brand new ship
+ ShipPtr->death_func = new_pkunk;
+}
+
+static void
+intercept_pkunk_death (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ PKUNK_DATA *PkunkData;
+ ELEMENT *ShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ PkunkData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+
+ if (StarShipPtr->RaceDescPtr->ship_info.crew_level != 0)
+ { // Ship not dead yet.
+ // Keep the Phoenix element alive.
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->life_span = 1;
+ return;
+ }
+
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ // GRAVITY_MASS() indicates a warp-out here. If Pkunk dies while warping
+ // out, there is no reincarnation.
+ if (!GRAVITY_MASS (ShipPtr->mass_points + 1))
+ {
+ // XXX: Hack: Set mass_points to indicate a reincarnation to
+ // FindAliveStarShip()
+ ShipPtr->mass_points = MAX_SHIP_MASS + 1;
+ // Save the various element processing callbacks before the
+ // explosion happens, because we were not the ones who set
+ // these callbacks and they are about to be changed.
+ PkunkData->saved_preprocess_func = ShipPtr->preprocess_func;
+ PkunkData->saved_postprocess_func = ShipPtr->postprocess_func;
+ PkunkData->saved_death_func = ShipPtr->death_func;
+
+ ShipPtr->death_func = pkunk_reincarnation_death;
+ }
+ UnlockElement (StarShipPtr->hShip);
+}
+
+static void
+spawn_phoenix_trail (ELEMENT *ElementPtr)
+{
+ static const Color colorTable[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7a),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7b),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7c),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7d),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7e),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7f),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x2a),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2b),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2c),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2d),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2e),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0B, 0x00, 0x00), 0x2f),
+ };
+ const size_t colorTableCount = sizeof colorTable / sizeof colorTable[0];
+
+ ElementPtr->colorCycleIndex++;
+ if (ElementPtr->colorCycleIndex != colorTableCount)
+ {
+ ElementPtr->life_span = TRANSITION_LIFE;
+
+ SetPrimColor (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ colorTable[ElementPtr->colorCycleIndex]);
+
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= CHANGING;
+ } // else, the element disappears.
+}
+
+static void
+phoenix_transition (ELEMENT *ElementPtr)
+{
+ HELEMENT hShipImage;
+ ELEMENT *ShipImagePtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LockElement (StarShipPtr->hShip, &ShipImagePtr);
+
+ if (!(ShipImagePtr->state_flags & NONSOLID))
+ {
+ ElementPtr->preprocess_func = NULL;
+ }
+ else if ((hShipImage = AllocElement ()))
+ {
+ COUNT angle;
+
+ PutElement (hShipImage);
+
+ LockElement (hShipImage, &ShipImagePtr);
+ ShipImagePtr->playerNr = NEUTRAL_PLAYER_NUM;
+ ShipImagePtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ ShipImagePtr->life_span = TRANSITION_LIFE;
+ SetPrimType (&(GLOBAL (DisplayArray))[ShipImagePtr->PrimIndex],
+ STAMPFILL_PRIM);
+ SetPrimColor (
+ &(GLOBAL (DisplayArray))[ShipImagePtr->PrimIndex],
+ START_PHOENIX_COLOR);
+ ShipImagePtr->colorCycleIndex = 0;
+ ShipImagePtr->current.image = ElementPtr->current.image;
+ ShipImagePtr->current.location = ElementPtr->current.location;
+ if (!(ElementPtr->state_flags & PLAYER_SHIP))
+ {
+ angle = ElementPtr->mass_points;
+
+ ShipImagePtr->current.location.x +=
+ COSINE (angle, TRANSITION_SPEED);
+ ShipImagePtr->current.location.y +=
+ SINE (angle, TRANSITION_SPEED);
+ ElementPtr->preprocess_func = NULL;
+ }
+ else
+ {
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+
+ ShipImagePtr->current.location.x -=
+ COSINE (angle, TRANSITION_SPEED)
+ * (ElementPtr->life_span - 1);
+ ShipImagePtr->current.location.y -=
+ SINE (angle, TRANSITION_SPEED)
+ * (ElementPtr->life_span - 1);
+
+ ShipImagePtr->current.location.x =
+ WRAP_X (ShipImagePtr->current.location.x);
+ ShipImagePtr->current.location.y =
+ WRAP_Y (ShipImagePtr->current.location.y);
+ }
+
+ ShipImagePtr->mass_points = (BYTE)angle;
+ ShipImagePtr->preprocess_func = phoenix_transition;
+ ShipImagePtr->death_func = spawn_phoenix_trail;
+ SetElementStarShip (ShipImagePtr, StarShipPtr);
+
+ UnlockElement (hShipImage);
+ }
+
+ UnlockElement (StarShipPtr->hShip);
+}
+
+static void
+pkunk_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ PKUNK_DATA *PkunkData;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ PkunkData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ HELEMENT hPhoenix = 0;
+
+ if (TFB_Random () & 1)
+ hPhoenix = AllocElement ();
+
+ // The hPhoenix element is created and placed at the head of the
+ // queue so that it is preprocessed before any of the ships' elements
+ // are, and so before death_func() is called for the dead Pkunk.
+ // hPhoenix detects when the Pkunk ship dies and tweaks the ship,
+ // starting the death + reincarnation sequence.
+ if (hPhoenix)
+ {
+ ELEMENT *PhoenixPtr;
+
+ LockElement (hPhoenix, &PhoenixPtr);
+ PhoenixPtr->playerNr = ElementPtr->playerNr;
+ PhoenixPtr->state_flags = FINITE_LIFE | NONSOLID | IGNORE_SIMILAR;
+ PhoenixPtr->life_span = 1;
+
+ PhoenixPtr->death_func = intercept_pkunk_death;
+
+ SetElementStarShip (PhoenixPtr, StarShipPtr);
+
+ UnlockElement (hPhoenix);
+ InsertElement (hPhoenix, GetHeadElement ());
+ }
+ PkunkData->hPhoenix = hPhoenix;
+
+ // XXX: Hack: new_pkunk() sets hTarget!=0 which indicates a
+ // reincarnation to us.
+ if (ElementPtr->hTarget == 0)
+ {
+ // A brand new ship is preprocessed only once
+ StarShipPtr->RaceDescPtr->preprocess_func = 0;
+ }
+ else
+ { // Start the reincarnation sequence
+ COUNT angle, facing;
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1
+ ), ElementPtr);
+
+ ElementPtr->life_span = PHOENIX_LIFE;
+ SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ NO_PRIM);
+ ElementPtr->state_flags |= NONSOLID | FINITE_LIFE | CHANGING;
+
+ facing = StarShipPtr->ShipFacing;
+ for (angle = OCTANT; angle < FULL_CIRCLE; angle += QUADRANT)
+ {
+ StarShipPtr->ShipFacing = NORMALIZE_FACING (
+ facing + ANGLE_TO_FACING (angle)
+ );
+ phoenix_transition (ElementPtr);
+ }
+ StarShipPtr->ShipFacing = facing;
+ }
+ }
+
+ if (StarShipPtr->RaceDescPtr->preprocess_func)
+ {
+ StarShipPtr->cur_status_flags &=
+ ~(LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+
+ if (ElementPtr->life_span == NORMAL_LIFE)
+ {
+ ElementPtr->current.image.frame =
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (
+ ElementPtr->current.image.farray[0],
+ ElementPtr->current.image.frame);
+ SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ STAMP_PRIM);
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectFrame (ElementPtr);
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ ElementPtr->state_flags &= ~(NONSOLID | FINITE_LIFE);
+ ElementPtr->state_flags |= CHANGING;
+
+ StarShipPtr->RaceDescPtr->preprocess_func = 0;
+ }
+ }
+}
+
+static COUNT LastSound = 0;
+
+static void
+pkunk_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->characteristics.special_wait)
+ --StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ else if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level <
+ StarShipPtr->RaceDescPtr->ship_info.max_energy)
+ {
+ COUNT CurSound;
+
+ do
+ {
+ CurSound =
+ 2 + ((COUNT)TFB_Random ()
+ % (GetSoundCount (StarShipPtr->RaceDescPtr->ship_data.ship_sounds) - 2));
+ } while (CurSound == LastSound);
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, CurSound
+ ), ElementPtr);
+ LastSound = CurSound;
+
+ DeltaEnergy (ElementPtr, SPECIAL_ENERGY_COST);
+
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = SPECIAL_WAIT;
+ }
+}
+
+static void
+uninit_pkunk (RACE_DESC *pRaceDesc)
+{
+ SetCustomShipData (pRaceDesc, NULL);
+}
+
+RACE_DESC*
+init_pkunk (void)
+{
+ RACE_DESC *RaceDescPtr;
+ // The caller of this func will copy the struct
+ static RACE_DESC new_pkunk_desc;
+ PKUNK_DATA empty_data;
+ memset (&empty_data, 0, sizeof (empty_data));
+
+ pkunk_desc.uninit_func = uninit_pkunk;
+ pkunk_desc.preprocess_func = pkunk_preprocess;
+ pkunk_desc.postprocess_func = pkunk_postprocess;
+ pkunk_desc.init_weapon_func = initialize_bug_missile;
+ pkunk_desc.cyborg_control.intelligence_func = pkunk_intelligence;
+
+ /* copy initial ship settings to the new descriptor */
+ new_pkunk_desc = pkunk_desc;
+ SetCustomShipData (&new_pkunk_desc, &empty_data);
+
+ RaceDescPtr = &new_pkunk_desc;
+
+ LastSound = 0;
+ // We need to reinitialise it at least each battle, to ensure
+ // that NetPlay is synchronised if one player played another
+ // game before playing against a networked opponent.
+
+ return (RaceDescPtr);
+}
diff --git a/src/uqm/ships/pkunk/pkunk.h b/src/uqm/ships/pkunk/pkunk.h
new file mode 100644
index 0000000..91681b8
--- /dev/null
+++ b/src/uqm/ships/pkunk/pkunk.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PKUNK_H
+#define PKUNK_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_pkunk (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PKUNK_H */
+
diff --git a/src/uqm/ships/pkunk/resinst.h b/src/uqm/ships/pkunk/resinst.h
new file mode 100644
index 0000000..9c1168e
--- /dev/null
+++ b/src/uqm/ships/pkunk/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define BUG_BIG_MASK_PMAP_ANIM "ship.pkunk.graphics.bug.large"
+#define BUG_MED_MASK_PMAP_ANIM "ship.pkunk.graphics.bug.medium"
+#define BUG_SML_MASK_PMAP_ANIM "ship.pkunk.graphics.bug.small"
+#define PKUNK_BIG_MASK_PMAP_ANIM "ship.pkunk.graphics.fury.large"
+#define PKUNK_CAPTAIN_MASK_PMAP_ANIM "ship.pkunk.graphics.captain"
+#define PKUNK_ICON_MASK_PMAP_ANIM "ship.pkunk.icons"
+#define PKUNK_MED_MASK_PMAP_ANIM "ship.pkunk.graphics.fury.medium"
+#define PKUNK_MICON_MASK_PMAP_ANIM "ship.pkunk.meleeicons"
+#define PKUNK_RACE_STRINGS "ship.pkunk.text"
+#define PKUNK_SHIP_SOUNDS "ship.pkunk.sounds"
+#define PKUNK_SML_MASK_PMAP_ANIM "ship.pkunk.graphics.fury.small"
+#define PKUNK_VICTORY_SONG "ship.pkunk.ditty"
diff --git a/src/uqm/ships/probe/Makeinfo b/src/uqm/ships/probe/Makeinfo
new file mode 100644
index 0000000..27e5093
--- /dev/null
+++ b/src/uqm/ships/probe/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="probe.c"
+uqm_HFILES="icode.h probe.h resinst.h"
diff --git a/src/uqm/ships/probe/icode.h b/src/uqm/ships/probe/icode.h
new file mode 100644
index 0000000..badab2a
--- /dev/null
+++ b/src/uqm/ships/probe/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define URQUAN_DRONE_CODE "ship.drone.code"
diff --git a/src/uqm/ships/probe/probe.c b/src/uqm/ships/probe/probe.c
new file mode 100644
index 0000000..32d9149
--- /dev/null
+++ b/src/uqm/ships/probe/probe.c
@@ -0,0 +1,118 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "probe.h"
+#include "resinst.h"
+
+#define MAX_CREW 1
+#define MAX_ENERGY 1
+#define ENERGY_REGENERATION 0
+#define WEAPON_ENERGY_COST 0
+#define SPECIAL_ENERGY_COST 0
+#define ENERGY_WAIT 0
+#define MAX_THRUST 0
+#define THRUST_INCREMENT 0
+#define TURN_WAIT 0
+#define THRUST_WAIT 0
+#define WEAPON_WAIT 0
+#define SPECIAL_WAIT 0
+
+#define SHIP_MASS 0
+
+static RACE_DESC probe_desc =
+{
+ { /* SHIP_INFO */
+ 0,
+ 0, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ 0,
+ 0,
+ URQUAN_DRONE_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ NULL_RESOURCE,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ 0,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+RACE_DESC*
+init_probe (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ RaceDescPtr = &probe_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/probe/probe.h b/src/uqm/ships/probe/probe.h
new file mode 100644
index 0000000..c588970
--- /dev/null
+++ b/src/uqm/ships/probe/probe.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PROBE_H
+#define PROBE_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_probe (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PROBE_H */
+
diff --git a/src/uqm/ships/probe/resinst.h b/src/uqm/ships/probe/resinst.h
new file mode 100644
index 0000000..5365c6d
--- /dev/null
+++ b/src/uqm/ships/probe/resinst.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define URQUAN_DRONE_MICON_MASK_PMAP_ANIM "ship.drone.meleeicons"
diff --git a/src/uqm/ships/ship.h b/src/uqm/ships/ship.h
new file mode 100644
index 0000000..6a9c6d2
--- /dev/null
+++ b/src/uqm/ships/ship.h
@@ -0,0 +1,37 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file contains definitions that are common to all ship files.
+ */
+
+#ifndef UQM_SHIPS_SHIP_H_
+#define UQM_SHIPS_SHIP_H_
+
+#include "uqm/collide.h"
+
+// XXX: Do we really need this one?
+//#include "reslib.h"
+#include "uqm/intel.h"
+#include "uqm/races.h"
+#include "uqm/status.h"
+#include "uqm/sounds.h"
+#include "uqm/weapon.h"
+#include "uqm/ship.h"
+
+
+#endif /* UQM_SHIPS_SHIP_H_ */
+
diff --git a/src/uqm/ships/shofixti/Makeinfo b/src/uqm/ships/shofixti/Makeinfo
new file mode 100644
index 0000000..52d9121
--- /dev/null
+++ b/src/uqm/ships/shofixti/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="shofixti.c"
+uqm_HFILES="icode.h resinst.h shofixti.h"
diff --git a/src/uqm/ships/shofixti/icode.h b/src/uqm/ships/shofixti/icode.h
new file mode 100644
index 0000000..4afe8d3
--- /dev/null
+++ b/src/uqm/ships/shofixti/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SHOFIXTI_CODE "ship.shofixti.code"
diff --git a/src/uqm/ships/shofixti/resinst.h b/src/uqm/ships/shofixti/resinst.h
new file mode 100644
index 0000000..bd95cb2
--- /dev/null
+++ b/src/uqm/ships/shofixti/resinst.h
@@ -0,0 +1,23 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define DART_BIG_MASK_PMAP_ANIM "ship.shofixti.graphics.missile.large"
+#define DART_MED_MASK_PMAP_ANIM "ship.shofixti.graphics.missile.medium"
+#define DART_SML_MASK_PMAP_ANIM "ship.shofixti.graphics.missile.small"
+#define DESTRUCT_BIG_MASK_ANIM "ship.shofixti.graphics.destruct.large"
+#define DESTRUCT_MED_MASK_ANIM "ship.shofixti.graphics.destruct.medium"
+#define DESTRUCT_SML_MASK_ANIM "ship.shofixti.graphics.destruct.small"
+#define OLDSHOF_BIG_MASK_PMAP_ANIM "ship.shofixti.graphics.oldscout.large"
+#define OLDSHOF_CAPTAIN_MASK_PMAP_ANIM "ship.shofixti.graphics.oldcaptain"
+#define OLDSHOF_MED_MASK_PMAP_ANIM "ship.shofixti.graphics.oldscout.medium"
+#define OLDSHOF_SML_MASK_PMAP_ANIM "ship.shofixti.graphics.oldscout.small"
+#define SHOFIXTI_BIG_MASK_PMAP_ANIM "ship.shofixti.graphics.scout.large"
+#define SHOFIXTI_CAPTAIN_MASK_PMAP_ANIM "ship.shofixti.graphics.captain"
+#define SHOFIXTI_ICON_MASK_PMAP_ANIM "ship.shofixti.icons"
+#define SHOFIXTI_MED_MASK_PMAP_ANIM "ship.shofixti.graphics.scout.medium"
+#define SHOFIXTI_MICON_MASK_PMAP_ANIM "ship.shofixti.meleeicons"
+#define SHOFIXTI_RACE_STRINGS "ship.shofixti.text"
+#define SHOFIXTI_SHIP_SOUNDS "ship.shofixti.sounds"
+#define SHOFIXTI_SML_MASK_PMAP_ANIM "ship.shofixti.graphics.scout.small"
+#define SHOFIXTI_VICTORY_SONG "ship.shofixti.ditty"
diff --git a/src/uqm/ships/shofixti/shofixti.c b/src/uqm/ships/shofixti/shofixti.c
new file mode 100644
index 0000000..03de57e
--- /dev/null
+++ b/src/uqm/ships/shofixti/shofixti.c
@@ -0,0 +1,521 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "shofixti.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "uqm/tactrans.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 6
+#define MAX_ENERGY 4
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 9
+#define MAX_THRUST 35
+#define THRUST_INCREMENT 5
+#define TURN_WAIT 1
+#define THRUST_WAIT 0
+#define WEAPON_WAIT 3
+#define SPECIAL_WAIT 0
+#define SHIP_MASS 1
+
+// Dart Gun
+#define WEAPON_ENERGY_COST 1
+#define SHOFIXTI_OFFSET 15
+#define MISSILE_OFFSET 1
+#define MISSILE_SPEED DISPLAY_TO_WORLD (24)
+#define MISSILE_LIFE 10
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+
+// Glory Device
+#define SPECIAL_ENERGY_COST 0
+#define DESTRUCT_RANGE 180
+#define MAX_DESTRUCTION (DESTRUCT_RANGE / 10)
+
+// Full game: Tanaka/Katana's damaged ships
+#define NUM_LIMPETS 3
+
+static RACE_DESC shofixti_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 5, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ SHOFIXTI_RACE_STRINGS,
+ SHOFIXTI_ICON_MASK_PMAP_ANIM,
+ SHOFIXTI_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SHOFIXTI_BIG_MASK_PMAP_ANIM,
+ SHOFIXTI_MED_MASK_PMAP_ANIM,
+ SHOFIXTI_SML_MASK_PMAP_ANIM,
+ },
+ {
+ DART_BIG_MASK_PMAP_ANIM,
+ DART_MED_MASK_PMAP_ANIM,
+ DART_SML_MASK_PMAP_ANIM,
+ },
+ {
+ DESTRUCT_BIG_MASK_ANIM,
+ DESTRUCT_MED_MASK_ANIM,
+ DESTRUCT_SML_MASK_ANIM,
+ },
+ {
+ SHOFIXTI_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ SHOFIXTI_VICTORY_SONG,
+ SHOFIXTI_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_SPEED * MISSILE_LIFE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT
+initialize_standard_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[])
+{
+
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = SHOFIXTI_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ MissileArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void
+destruct_preprocess (ELEMENT *ElementPtr)
+{
+#define DESTRUCT_SWITCH ((NUM_EXPLOSION_FRAMES * 3) - 3)
+ PRIMITIVE *lpPrim;
+
+ // ship_death() set the ship element's life_span to
+ // (NUM_EXPLOSION_FRAMES * 3)
+ lpPrim = &(GLOBAL (DisplayArray))[ElementPtr->PrimIndex];
+ ElementPtr->state_flags |= CHANGING;
+ if (ElementPtr->life_span > DESTRUCT_SWITCH)
+ {
+ // First, stamp-fill the ship's own element with changing colors
+ // for 3 frames. No explosion element yet.
+ SetPrimType (lpPrim, STAMPFILL_PRIM);
+ if (ElementPtr->life_span == DESTRUCT_SWITCH + 2)
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E));
+ else
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F));
+ }
+ else if (ElementPtr->life_span < DESTRUCT_SWITCH)
+ {
+ // Stamp-fill the explosion element with cycling colors for the
+ // remainder of the glory explosion frames.
+ Color color = GetPrimColor (lpPrim);
+
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x0A, 0x00), 0x06));
+ else if (sameColor (color,
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x0A, 0x00), 0x06)))
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x04));
+ }
+ else
+ {
+ HELEMENT hDestruct;
+
+ SetPrimType (lpPrim, NO_PRIM);
+ // The ship's own element will not be drawn anymore but will remain
+ // alive all through the glory explosion.
+ ElementPtr->preprocess_func = NULL;
+
+ // Spawn a separate glory explosion element.
+ // XXX: Why? Why not keep using the ship's element?
+ // Is it because of conflicting state_flags, hit_points or
+ // mass_points?
+ hDestruct = AllocElement ();
+ if (hDestruct)
+ {
+ ELEMENT *DestructPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ PutElement (hDestruct);
+ LockElement (hDestruct, &DestructPtr);
+ SetElementStarShip (DestructPtr, StarShipPtr);
+ DestructPtr->hit_points = 0;
+ DestructPtr->mass_points = 0;
+ DestructPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ DestructPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ SetPrimType (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex],
+ STAMPFILL_PRIM);
+ SetPrimColor (&(GLOBAL (DisplayArray))[DestructPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F));
+ DestructPtr->current.image.farray =
+ StarShipPtr->RaceDescPtr->ship_data.special;
+ DestructPtr->current.image.frame =
+ StarShipPtr->RaceDescPtr->ship_data.special[0];
+ DestructPtr->life_span = GetFrameCount (
+ DestructPtr->current.image.frame);
+ DestructPtr->current.location = ElementPtr->current.location;
+ DestructPtr->preprocess_func = destruct_preprocess;
+ DestructPtr->postprocess_func = NULL;
+ DestructPtr->death_func = NULL;
+ ZeroVelocityComponents (&DestructPtr->velocity);
+ UnlockElement (hDestruct);
+ }
+ }
+}
+
+/* In order to detect any Orz Marines that have boarded the ship
+ when it self-destructs, we'll need to see some Orz functions */
+#include "../orz/orz.h"
+#define ORZ_MARINE(ptr) (ptr->preprocess_func == intruder_preprocess && \
+ ptr->collision_func == marine_collision)
+
+static void
+self_destruct_kill_objects (ELEMENT *ElementPtr)
+{
+ // This is called during PostProcessQueue(), close to or at the end,
+ // for the temporary destruct element to apply the effects of glory
+ // explosion. The effects are not seen until the next frame.
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement)
+ {
+ ELEMENT *ObjPtr;
+ SIZE delta_x, delta_y;
+ DWORD dist;
+
+ LockElement (hElement, &ObjPtr);
+ hNextElement = GetSuccElement (ObjPtr);
+
+ if (!CollidingElement (ObjPtr) && !ORZ_MARINE (ObjPtr))
+ {
+ UnlockElement (hElement);
+ continue;
+ }
+
+ delta_x = ObjPtr->next.location.x - ElementPtr->next.location.x;
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ delta_y = ObjPtr->next.location.y - ElementPtr->next.location.y;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x = WORLD_TO_DISPLAY (delta_x);
+ delta_y = WORLD_TO_DISPLAY (delta_y);
+ dist = delta_x * delta_x + delta_y * delta_y;
+ if (delta_x <= DESTRUCT_RANGE && delta_y <= DESTRUCT_RANGE
+ && dist <= DESTRUCT_RANGE * DESTRUCT_RANGE)
+ {
+ int destruction = 1 + MAX_DESTRUCTION *
+ (DESTRUCT_RANGE - square_root (dist)) / DESTRUCT_RANGE;
+
+ // XXX: Why not simply call do_damage()?
+ if (ObjPtr->state_flags & PLAYER_SHIP)
+ {
+ if (!DeltaCrew (ObjPtr, -destruction))
+ ObjPtr->life_span = 0;
+ }
+ else if (!GRAVITY_MASS (ObjPtr->mass_points))
+ {
+ if (destruction < ObjPtr->hit_points)
+ ObjPtr->hit_points -= destruction;
+ else
+ {
+ ObjPtr->hit_points = 0;
+ ObjPtr->life_span = 0;
+ }
+ }
+ }
+
+ UnlockElement (hElement);
+ }
+}
+
+// This function is called when the ship dies via Glory Device.
+// The generic ship_death() function is not called for the ship in this case.
+static void
+shofixti_destruct_death (ELEMENT *ShipPtr)
+{
+ STARSHIP *StarShip;
+ STARSHIP *winner;
+
+ GetElementStarShip (ShipPtr, &StarShip);
+
+ StopAllBattleMusic ();
+
+ StartShipExplosion (ShipPtr, false);
+ // We process the explosion ourselves because it is different
+ ShipPtr->preprocess_func = destruct_preprocess;
+
+ PlaySound (SetAbsSoundIndex (StarShip->RaceDescPtr->ship_data.ship_sounds,
+ 1), CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1);
+
+ winner = GetWinnerStarShip ();
+ if (winner == NULL)
+ { // No winner determined yet
+ winner = FindAliveStarShip (ShipPtr);
+ if (winner == NULL)
+ { // No ships left alive after the Glory Device thus Shofixti wins
+ winner = StarShip;
+ }
+ SetWinnerStarShip (winner);
+ }
+ else if (winner == StarShip)
+ { // This ship is the winner
+ // It may have self-destructed before the ditty started playing,
+ // and in that case, there should be no ditty
+ StarShip->cur_status_flags &= ~PLAY_VICTORY_DITTY;
+ }
+ RecordShipDeath (ShipPtr);
+}
+
+static void
+self_destruct (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ HELEMENT hDestruct;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ // Spawn a temporary element, which dies in this same frame, in order
+ // to defer the effects of the glory explosion.
+ // It will be the last element (or one of the last) for which the
+ // death_func() will be called from PostProcessQueue() in this frame.
+ // XXX: Why at the end? Why not just do it now?
+ hDestruct = AllocElement ();
+ if (hDestruct)
+ {
+ ELEMENT *DestructPtr;
+
+ LockElement (hDestruct, &DestructPtr);
+ DestructPtr->playerNr = ElementPtr->playerNr;
+ DestructPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
+ DestructPtr->next.location = ElementPtr->next.location;
+ DestructPtr->life_span = 0;
+ DestructPtr->pParent = ElementPtr->pParent;
+ DestructPtr->hTarget = 0;
+
+ DestructPtr->death_func = self_destruct_kill_objects;
+
+ UnlockElement (hDestruct);
+
+ PutElement (hDestruct);
+ }
+
+ // Must kill off the remaining crew ourselves
+ DeltaCrew (ElementPtr, -(int)ElementPtr->crew_level);
+
+ ElementPtr->state_flags |= NONSOLID;
+ ElementPtr->life_span = 0;
+ // The ship is now dead. It's death_func, i.e. shofixti_destruct_death(),
+ // will be called the next frame.
+ ElementPtr->death_func = shofixti_destruct_death;
+}
+
+static void
+shofixti_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter != 0)
+ return;
+
+ if (StarShipPtr->ship_input_state & SPECIAL)
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ else
+ {
+ EVALUATE_DESC *lpWeaponEvalDesc;
+ EVALUATE_DESC *lpShipEvalDesc;
+
+ lpWeaponEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ lpShipEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (StarShipPtr->RaceDescPtr->ship_data.special[0]
+ && (GetFrameCount (StarShipPtr->RaceDescPtr->ship_data.
+ captain_control.special)
+ - GetFrameIndex (StarShipPtr->RaceDescPtr->ship_data.
+ captain_control.special) > 5
+ || (lpShipEvalDesc->ObjectPtr != NULL
+ && lpShipEvalDesc->which_turn <= 4)
+ || (lpWeaponEvalDesc->ObjectPtr != NULL
+ /* means IMMEDIATE WEAPON */
+ && (((lpWeaponEvalDesc->ObjectPtr->state_flags & PLAYER_SHIP)
+ && ShipPtr->crew_level == 1)
+ || (PlotIntercept (lpWeaponEvalDesc->ObjectPtr, ShipPtr, 2, 0)
+ && lpWeaponEvalDesc->ObjectPtr->mass_points >=
+ ShipPtr->crew_level
+ && (TFB_Random () & 1))))))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+}
+
+static void
+shofixti_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags
+ ^ StarShipPtr->old_status_flags) & SPECIAL)
+ {
+ StarShipPtr->RaceDescPtr->ship_data.captain_control.special =
+ IncFrameIndex (StarShipPtr->RaceDescPtr->ship_data.
+ captain_control.special);
+ if (GetFrameCount (StarShipPtr->RaceDescPtr->ship_data.
+ captain_control.special)
+ - GetFrameIndex (StarShipPtr->RaceDescPtr->ship_data.
+ captain_control.special) == 3)
+ self_destruct (ElementPtr);
+ }
+}
+
+RACE_DESC*
+init_shofixti (void)
+{
+ RACE_DESC *RaceDescPtr;
+ // The caller of this func will copy the struct
+ static RACE_DESC new_shofixti_desc;
+
+ shofixti_desc.postprocess_func = shofixti_postprocess;
+ shofixti_desc.init_weapon_func = initialize_standard_missile;
+ shofixti_desc.cyborg_control.intelligence_func = shofixti_intelligence;
+
+ new_shofixti_desc = shofixti_desc;
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER
+ && !GET_GAME_STATE (SHOFIXTI_RECRUITED))
+ {
+ // Tanaka/Katana flies in a damaged ship.
+ COUNT i;
+
+ new_shofixti_desc.ship_data.ship_rsc[0] = OLDSHOF_BIG_MASK_PMAP_ANIM;
+ new_shofixti_desc.ship_data.ship_rsc[1] = OLDSHOF_MED_MASK_PMAP_ANIM;
+ new_shofixti_desc.ship_data.ship_rsc[2] = OLDSHOF_SML_MASK_PMAP_ANIM;
+ new_shofixti_desc.ship_data.special_rsc[0] = NULL_RESOURCE;
+ new_shofixti_desc.ship_data.special_rsc[1] = NULL_RESOURCE;
+ new_shofixti_desc.ship_data.special_rsc[2] = NULL_RESOURCE;
+ new_shofixti_desc.ship_data.captain_control.captain_rsc =
+ OLDSHOF_CAPTAIN_MASK_PMAP_ANIM;
+
+ /* Weapon doesn't work as well */
+ new_shofixti_desc.characteristics.weapon_wait = 10;
+
+ /* Simulate VUX limpets */
+ for (i = 0; i < NUM_LIMPETS; ++i)
+ {
+ if (++new_shofixti_desc.characteristics.turn_wait == 0)
+ --new_shofixti_desc.characteristics.turn_wait;
+ if (++new_shofixti_desc.characteristics.thrust_wait == 0)
+ --new_shofixti_desc.characteristics.thrust_wait;
+
+/* This should be the same as MIN_THRUST_INCREMENT in vux.c */
+#define MIN_THRUST_INCREMENT DISPLAY_TO_WORLD (1)
+
+ if (new_shofixti_desc.characteristics.thrust_increment <=
+ MIN_THRUST_INCREMENT)
+ {
+ new_shofixti_desc.characteristics.max_thrust =
+ new_shofixti_desc.characteristics.thrust_increment << 1;
+ }
+ else
+ {
+ COUNT num_thrusts;
+
+ num_thrusts = new_shofixti_desc.characteristics.max_thrust /
+ new_shofixti_desc.characteristics.thrust_increment;
+ --new_shofixti_desc.characteristics.thrust_increment;
+ new_shofixti_desc.characteristics.max_thrust =
+ new_shofixti_desc.characteristics.thrust_increment *
+ num_thrusts;
+ }
+ }
+ }
+
+ RaceDescPtr = &new_shofixti_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/shofixti/shofixti.h b/src/uqm/ships/shofixti/shofixti.h
new file mode 100644
index 0000000..e4fdd0c
--- /dev/null
+++ b/src/uqm/ships/shofixti/shofixti.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SHOFIXTI_H
+#define SHOFIXTI_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_shofixti (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SHOFIXTI_H */
+
diff --git a/src/uqm/ships/sis_ship/Makeinfo b/src/uqm/ships/sis_ship/Makeinfo
new file mode 100644
index 0000000..540e3a6
--- /dev/null
+++ b/src/uqm/ships/sis_ship/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="sis_ship.c"
+uqm_HFILES="icode.h resinst.h sis_ship.h"
diff --git a/src/uqm/ships/sis_ship/icode.h b/src/uqm/ships/sis_ship/icode.h
new file mode 100644
index 0000000..3d57449
--- /dev/null
+++ b/src/uqm/ships/sis_ship/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SIS_CODE "ship.flagship.code"
diff --git a/src/uqm/ships/sis_ship/resinst.h b/src/uqm/ships/sis_ship/resinst.h
new file mode 100644
index 0000000..cf8d074
--- /dev/null
+++ b/src/uqm/ships/sis_ship/resinst.h
@@ -0,0 +1,15 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define BLASTER_BIG_MASK_PMAP_ANIM "ship.flagship.graphics.blaster.large"
+#define BLASTER_MED_MASK_PMAP_ANIM "ship.flagship.graphics.blaster.medium"
+#define BLASTER_SML_MASK_PMAP_ANIM "ship.flagship.graphics.blaster.small"
+#define SIS_BIG_MASK_PMAP_ANIM "ship.flagship.graphics.flagship.large"
+#define SIS_CAPTAIN_MASK_PMAP_ANIM "ship.flagship.graphics.captain"
+#define SIS_HYPER_MASK_PMAP_ANIM "ship.flagship.graphics.hyperspace"
+#define SIS_ICON_MASK_PMAP_ANIM "ship.flagship.icons"
+#define SIS_MED_MASK_PMAP_ANIM "ship.flagship.graphics.flagship.medium"
+#define SIS_SHIP_SOUNDS "ship.flagship.sounds"
+#define SIS_SML_MASK_PMAP_ANIM "ship.flagship.graphics.flagship.small"
+#define SIS_VICTORY_SONG "ship.flagship.ditty"
diff --git a/src/uqm/ships/sis_ship/sis_ship.c b/src/uqm/ships/sis_ship/sis_ship.c
new file mode 100644
index 0000000..d589827
--- /dev/null
+++ b/src/uqm/ships/sis_ship/sis_ship.c
@@ -0,0 +1,1002 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "sis_ship.h"
+#include "resinst.h"
+
+#include "uqm/colors.h"
+#include "uqm/controls.h"
+#include "uqm/globdata.h"
+#include "uqm/hyper.h"
+#include "libs/mathlib.h"
+
+/* Core characteristics.
+ * All of these are changed at init time by some module, except for
+ * MAX_ENERGY, THRUST_INCREMENT, and SHIP_MASS. */
+
+#define MAX_CREW MAX_CREW_SIZE
+ /* This value gets thrown out - actual max crew is determined by the
+ * number of crew pods. The minimum value is 1 (just the Captain). */
+
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+ /* Shiva furnaces increase this by 1 each. */
+#define SHIVA_ENERGY_REGEN_INC 1
+
+#define ENERGY_WAIT 10
+ /* Dynamos decrease this by 2 each, to a minimum of 4. */
+#define MIN_ENERGY_WAIT 4
+#define DYNAMO_UNIT_ENERGY_WAIT_DEC 2
+
+#define MAX_THRUST 10
+ /* Thrusters increase this and decrease THRUST_WAIT based on
+ * THRUST_INCREMENT, see InitDriveSlots near the bottom of this file
+ * for details. */
+#define THRUST_INCREMENT 4
+#define THRUST_WAIT 6
+#define TURN_WAIT 17
+ /* Turning jets decrease by 2 each */
+#define SHIP_MASS MAX_SHIP_MASS
+
+
+/* Primary weapon - energy cost and damage change at init time based on
+ * the number and type of weapon modules installed. */
+
+#define BLASTER_DAMAGE 2
+ /* This is the damage value for the basic ion bolt guns. Fusion
+ * blasters and hellbore cannons end up doing (BLASTER_DAMAGE * 2)
+ * and (BLASTER_DAMAGE * 3) damage, respectively, but this depends
+ * on enum values. */
+
+#define BLASTER_HITS 2 /* Hitpoints for ion bolt guns, see BLASTER_DAMAGE */
+
+#define WEAPON_ENERGY_COST 1
+ /* This value gets thrown out and reset in an ugly manner based on
+ * the enum that is used for module IDs. Bigger gun = higher value.
+ */
+#define WEAPON_WAIT 6
+#define BLASTER_SPEED DISPLAY_TO_WORLD (24)
+#define BLASTER_LIFE 12
+ /* This value is greatly increased, based in part on the enum used
+ * for module IDs (bigger gun == longer life). See the first half of
+ * InitWeaponSlots */
+#define MAX_TRACKING 3
+#define TRACKER_ENERGY_COST 3
+#define BLASTER_OFFSET 8
+#define SIS_VERT_OFFSET 28
+ /* Used for foward, spread, and rear slots */
+#define SIS_HORZ_OFFSET 20
+ /* Used for side slot */
+
+
+/* Secondary weapon */
+#define SPECIAL_ENERGY_COST 0
+ /* Increased by 1 for each point defense module */
+#define ANTIMISSILE_ENERGY_INC 1
+#define SPECIAL_WAIT 9
+#define LASER_RANGE (UWORD)100
+#define MAX_DEFENSE 8
+
+
+static RACE_DESC sis_desc =
+{
+ { /* SHIP_INFO */
+ 0,
+ 16, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ NULL_RESOURCE,
+ SIS_ICON_MASK_PMAP_ANIM,
+ NULL_RESOURCE,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SIS_BIG_MASK_PMAP_ANIM,
+ SIS_MED_MASK_PMAP_ANIM,
+ SIS_SML_MASK_PMAP_ANIM,
+ },
+ {
+ BLASTER_BIG_MASK_PMAP_ANIM,
+ BLASTER_MED_MASK_PMAP_ANIM,
+ BLASTER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ SIS_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ SIS_VICTORY_SONG,
+ SIS_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ BLASTER_SPEED * BLASTER_LIFE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+// Private per-instance SIS data
+typedef struct
+{
+ COUNT num_trackers;
+ COUNT num_blasters;
+ MISSILE_BLOCK MissileBlock[6];
+
+} SIS_DATA;
+
+static void InitWeaponSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *ModuleSlots);
+static void InitModuleSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *ModuleSlots);
+static void InitDriveSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *DriveSlots);
+static void InitJetSlots (RACE_DESC *RaceDescPtr,
+ const BYTE *JetSlots);
+static void uninit_sis (RACE_DESC *pRaceDesc);
+
+
+// Local typedef
+typedef SIS_DATA CustomShipData_t;
+
+// Retrieve race-specific ship data from a race desc
+static CustomShipData_t *
+GetCustomShipData (RACE_DESC *pRaceDesc)
+{
+ return pRaceDesc->data;
+}
+
+// Set the race-specific data in a race desc
+// (Re)Allocates its own storage for the data.
+static void
+SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data)
+{
+ if (pRaceDesc->data == data)
+ return; // no-op
+
+ if (pRaceDesc->data) // Out with the old
+ {
+ HFree (pRaceDesc->data);
+ pRaceDesc->data = NULL;
+ }
+
+ if (data) // In with the new
+ {
+ CustomShipData_t* newData = HMalloc (sizeof (*data));
+ *newData = *data;
+ pRaceDesc->data = newData;
+ }
+}
+
+static void
+sis_hyper_preprocess (ELEMENT *ElementPtr)
+{
+ SIZE udx = 0, udy = 0;
+ SIZE dx = 0, dy = 0;
+ SIZE AccelerateDirection;
+ STARSHIP *StarShipPtr;
+
+ if (ElementPtr->state_flags & APPEARING)
+ ElementPtr->velocity = GLOBAL (velocity);
+
+ AccelerateDirection = 0;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ++StarShipPtr->weapon_counter; /* no shooting in hyperspace! */
+ if ((GLOBAL (autopilot)).x == ~0
+ || (GLOBAL (autopilot)).y == ~0
+ || (StarShipPtr->cur_status_flags & (LEFT | RIGHT | THRUST)))
+ {
+LeaveAutoPilot:
+ (GLOBAL (autopilot)).x =
+ (GLOBAL (autopilot)).y = ~0;
+ if (!(StarShipPtr->cur_status_flags & THRUST)
+ || (GLOBAL_SIS (FuelOnBoard) == 0
+ && GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1))
+ {
+ AccelerateDirection = -1;
+ GetCurrentVelocityComponents (&ElementPtr->velocity,
+ &dx, &dy);
+ udx = dx << 4;
+ udy = dy << 4;
+
+ StarShipPtr->cur_status_flags &= ~THRUST;
+ }
+ }
+ else
+ {
+ SIZE facing;
+ POINT universe;
+
+ universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
+ universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
+ udx = (GLOBAL (autopilot)).x - universe.x;
+ udy = -((GLOBAL (autopilot)).y - universe.y);
+ if ((dx = udx) < 0)
+ dx = -dx;
+ if ((dy = udy) < 0)
+ dy = -dy;
+ if (dx <= 1 && dy <= 1)
+ goto LeaveAutoPilot;
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (ARCTAN (udx, udy)));
+
+ /* This prevents ship from flying backwards on auto-pilot.
+ * It could also theoretically abort autopilot in a bad savegame */
+ if ((StarShipPtr->cur_status_flags & SHIP_AT_MAX_SPEED)
+ /*|| (ElementPtr->state_flags & APPEARING)*/ )
+ {
+ if (NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + ANGLE_TO_FACING (QUADRANT)
+ - facing) > ANGLE_TO_FACING (HALF_CIRCLE))
+ goto LeaveAutoPilot;
+
+ facing = StarShipPtr->ShipFacing;
+ }
+ else if ((int)facing != (int)StarShipPtr->ShipFacing
+ && ElementPtr->turn_wait == 0)
+ {
+ if (NORMALIZE_FACING (
+ StarShipPtr->ShipFacing - facing
+ ) >= ANGLE_TO_FACING (HALF_CIRCLE))
+ {
+ facing = NORMALIZE_FACING (facing - 1);
+ StarShipPtr->cur_status_flags |= RIGHT;
+ }
+ else if ((int)StarShipPtr->ShipFacing != (int)facing)
+ {
+ facing = NORMALIZE_FACING (facing + 1);
+ StarShipPtr->cur_status_flags |= LEFT;
+ }
+
+ if ((int)facing == (int)StarShipPtr->ShipFacing)
+ {
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ }
+ }
+
+ GetCurrentVelocityComponents (&ElementPtr->velocity, &dx, &dy);
+ if ((GLOBAL_SIS (FuelOnBoard)
+ || GET_GAME_STATE (ARILOU_SPACE_SIDE) > 1)
+ && (int)facing == (int)StarShipPtr->ShipFacing)
+ {
+ StarShipPtr->cur_status_flags |= SHIP_AT_MAX_SPEED;
+ AccelerateDirection = 1;
+ }
+ else
+ {
+ AccelerateDirection = -1;
+ udx = dx << 4;
+ udy = dy << 4;
+ }
+ }
+
+ if (ElementPtr->thrust_wait == 0 && AccelerateDirection)
+ {
+ COUNT dist;
+ SIZE speed, velocity_increment;
+
+ velocity_increment = WORLD_TO_VELOCITY (
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment);
+
+ if ((dist = square_root ((long)udx * udx + (long)udy * udy)) == 0)
+ dist = 1; /* prevent divide by zero */
+
+ speed = square_root ((long)dx * dx + (long)dy * dy);
+ if (AccelerateDirection < 0)
+ {
+ dy = (speed / velocity_increment - 1) * velocity_increment;
+ if (dy < speed - velocity_increment)
+ dy = speed - velocity_increment;
+ if ((speed = dy) < 0)
+ speed = 0;
+
+ StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED;
+ }
+ else
+ {
+ SIZE max_velocity;
+
+ AccelerateDirection = 0;
+
+ max_velocity = WORLD_TO_VELOCITY (
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust);
+
+ dy = (speed / velocity_increment + 1)
+ * velocity_increment;
+ if (dy < speed + velocity_increment)
+ dy = speed + velocity_increment;
+ if ((speed = dy) > max_velocity)
+ {
+ speed = max_velocity;
+ StarShipPtr->cur_status_flags |= SHIP_AT_MAX_SPEED;
+ }
+ }
+
+ dx = (SIZE)((long)udx * speed / (long)dist);
+ dy = (SIZE)((long)udy * speed / (long)dist);
+ SetVelocityComponents (&ElementPtr->velocity, dx, dy);
+
+ ElementPtr->thrust_wait =
+ StarShipPtr->RaceDescPtr->characteristics.thrust_wait;
+ }
+}
+
+static void
+sis_hyper_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GLOBAL (velocity) = ElementPtr->velocity;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (((StarShipPtr->cur_status_flags & WEAPON) ||
+ PulsedInputState.menu[KEY_MENU_CANCEL])
+ && StarShipPtr->special_counter == 0)
+ {
+#define MENU_DELAY 10
+ HyperspaceMenu ();
+ StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED;
+ StarShipPtr->special_counter = MENU_DELAY;
+ }
+}
+
+static void
+spawn_point_defense (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ HELEMENT hDefense;
+
+ hDefense = AllocElement ();
+ if (hDefense)
+ {
+ ELEMENT *DefensePtr;
+
+ LockElement (hDefense, &DefensePtr);
+ DefensePtr->playerNr = ElementPtr->playerNr;
+ DefensePtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
+ DefensePtr->death_func = spawn_point_defense;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ SetElementStarShip (DefensePtr, StarShipPtr);
+ UnlockElement (hDefense);
+
+ PutElement (hDefense);
+ }
+ }
+ else
+ {
+ BOOLEAN PaidFor;
+ HELEMENT hObject, hNextObject;
+ ELEMENT *ShipPtr;
+ Color LaserColor;
+ static const Color ColorRange[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7F),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x18, 0x00), 0x79),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1C, 0x00), 0x78),
+ };
+
+ PaidFor = FALSE;
+
+ LaserColor = ColorRange[
+ StarShipPtr->RaceDescPtr->characteristics.special_energy_cost
+ ];
+ LockElement (StarShipPtr->hShip, &ShipPtr);
+ for (hObject = GetTailElement (); hObject; hObject = hNextObject)
+ {
+ ELEMENT *ObjectPtr;
+
+ LockElement (hObject, &ObjectPtr);
+ hNextObject = GetPredElement (ObjectPtr);
+ if (ObjectPtr != ShipPtr && CollidingElement (ObjectPtr) &&
+ !OBJECT_CLOAKED (ObjectPtr))
+ {
+ SIZE delta_x, delta_y;
+
+ delta_x = ObjectPtr->next.location.x -
+ ShipPtr->next.location.x;
+ delta_y = ObjectPtr->next.location.y -
+ ShipPtr->next.location.y;
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x = WORLD_TO_DISPLAY (delta_x);
+ delta_y = WORLD_TO_DISPLAY (delta_y);
+ if ((UWORD)delta_x <= LASER_RANGE &&
+ (UWORD)delta_y <= LASER_RANGE &&
+ (UWORD)delta_x * (UWORD)delta_x +
+ (UWORD)delta_y * (UWORD)delta_y <=
+ LASER_RANGE * LASER_RANGE)
+ {
+ HELEMENT hPointDefense;
+ LASER_BLOCK LaserBlock;
+
+ if (!PaidFor)
+ {
+ if (!DeltaEnergy (ShipPtr,
+ -(StarShipPtr->RaceDescPtr->characteristics.special_energy_cost
+ << 2)))
+ break;
+
+ ProcessSound (SetAbsSoundIndex (
+ /* POINT_DEFENSE_LASER */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ PaidFor = TRUE;
+ }
+
+ LaserBlock.cx = ShipPtr->next.location.x;
+ LaserBlock.cy = ShipPtr->next.location.y;
+ LaserBlock.face = 0;
+ LaserBlock.ex = ObjectPtr->next.location.x
+ - ShipPtr->next.location.x;
+ LaserBlock.ey = ObjectPtr->next.location.y
+ - ShipPtr->next.location.y;
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = 0;
+ LaserBlock.color = LaserColor;
+ hPointDefense = initialize_laser (&LaserBlock);
+ if (hPointDefense)
+ {
+ ELEMENT *PDPtr;
+
+ LockElement (hPointDefense, &PDPtr);
+ PDPtr->mass_points =
+ StarShipPtr->RaceDescPtr->characteristics.special_energy_cost;
+ SetElementStarShip (PDPtr, StarShipPtr);
+ PDPtr->hTarget = 0;
+ UnlockElement (hPointDefense);
+
+ PutElement (hPointDefense);
+ }
+ }
+ }
+ UnlockElement (hObject);
+ }
+ UnlockElement (StarShipPtr->hShip);
+ }
+}
+
+static void
+sis_battle_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->RaceDescPtr->characteristics.special_energy_cost == 0)
+ {
+ StarShipPtr->cur_status_flags &= ~SPECIAL;
+ StarShipPtr->special_counter = 2;
+ }
+ if (!(StarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & (FIRES_FORE | FIRES_RIGHT | FIRES_AFT | FIRES_LEFT)))
+ {
+ StarShipPtr->cur_status_flags &= ~WEAPON;
+ StarShipPtr->weapon_counter = 2;
+ }
+}
+
+static void
+sis_battle_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->characteristics.special_energy_cost)
+ {
+ spawn_point_defense (ElementPtr);
+ }
+}
+
+static void
+blaster_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ HELEMENT hBlastElement;
+
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ if (hBlastElement)
+ {
+ ELEMENT *BlastElementPtr;
+
+ LockElement (hBlastElement, &BlastElementPtr);
+ switch (ElementPtr0->mass_points)
+ {
+ case BLASTER_DAMAGE * 1:
+ BlastElementPtr->life_span = 2;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr0->current.image.frame, 0);
+ BlastElementPtr->preprocess_func = NULL;
+ break;
+ case BLASTER_DAMAGE * 2:
+ BlastElementPtr->life_span = 6;
+ BlastElementPtr->current.image.frame =
+ IncFrameIndex (ElementPtr0->current.image.frame);
+ break;
+ case BLASTER_DAMAGE * 3:
+ BlastElementPtr->life_span = 7;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr0->current.image.frame, 20);
+ break;
+ }
+ UnlockElement (hBlastElement);
+ }
+}
+
+static void
+blaster_preprocess (ELEMENT *ElementPtr)
+{
+ BYTE wait;
+
+ switch (ElementPtr->mass_points)
+ {
+ case BLASTER_DAMAGE * 1:
+ if (GetFrameIndex (ElementPtr->current.image.frame) < 8)
+ {
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+ break;
+ case BLASTER_DAMAGE * 3:
+ if (GetFrameIndex (ElementPtr->current.image.frame) < 19)
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->current.image.frame);
+ else
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame, 16);
+ ElementPtr->state_flags |= CHANGING;
+ break;
+ }
+
+ if (LONIBBLE (ElementPtr->turn_wait))
+ --ElementPtr->turn_wait;
+ else if ((wait = HINIBBLE (ElementPtr->turn_wait)))
+ {
+ COUNT facing;
+
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)));
+ if (TrackShip (ElementPtr, &facing) > 0)
+ SetVelocityVector (&ElementPtr->velocity, BLASTER_SPEED, facing);
+
+ ElementPtr->turn_wait = MAKE_BYTE (wait, wait);
+ }
+}
+
+static COUNT
+initialize_blasters (ELEMENT *ShipPtr, HELEMENT BlasterArray[])
+{
+ BYTE nt;
+ COUNT i;
+ STARSHIP *StarShipPtr;
+ SIS_DATA *SisData;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ SisData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+
+ nt = (BYTE)((4 - SisData->num_trackers) & 3);
+
+ for (i = 0; i < SisData->num_blasters; ++i)
+ {
+ MISSILE_BLOCK MissileBlock = SisData->MissileBlock[i];
+
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.face = NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + MissileBlock.face);
+
+ BlasterArray[i] = initialize_missile (&MissileBlock);
+ if (BlasterArray[i])
+ {
+ ELEMENT *BlasterPtr;
+
+ LockElement (BlasterArray[i], &BlasterPtr);
+ BlasterPtr->collision_func = blaster_collision;
+ BlasterPtr->turn_wait = MAKE_BYTE (nt, nt);
+ UnlockElement (BlasterArray[i]);
+ }
+
+ }
+
+ return SisData->num_blasters;
+}
+
+static void
+sis_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+ SIS_DATA *SisData;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ SisData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+ if (StarShipPtr->RaceDescPtr->characteristics.special_energy_cost)
+ {
+ if (StarShipPtr->special_counter == 0
+ && ((lpEvalDesc->ObjectPtr
+ && lpEvalDesc->which_turn <= 2)
+ || (ObjectsOfConcern[ENEMY_SHIP_INDEX].ObjectPtr != NULL
+ && ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn <= 4)))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ lpEvalDesc->ObjectPtr = NULL;
+ }
+ else if (MANEUVERABILITY (&StarShipPtr->RaceDescPtr->cyborg_control)
+ < MEDIUM_SHIP
+ && lpEvalDesc->MoveState == ENTICE
+ && (!(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT)
+ || lpEvalDesc->which_turn <= 8)
+ && (!(lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE)
+ || (lpEvalDesc->ObjectPtr->mass_points >= 4
+ && lpEvalDesc->which_turn == 2
+ && ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 16)))
+ lpEvalDesc->MoveState = PURSUE;
+ }
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (SisData->num_trackers
+ && StarShipPtr->weapon_counter == 0
+ && !(StarShipPtr->ship_input_state & WEAPON)
+ && lpEvalDesc->ObjectPtr
+ && lpEvalDesc->which_turn <= 16)
+ {
+ COUNT direction_facing;
+ SIZE delta_x, delta_y;
+ UWORD fire_flags, ship_flags;
+ COUNT facing;
+
+ delta_x = lpEvalDesc->ObjectPtr->current.location.x
+ - ShipPtr->current.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->current.location.y
+ - ShipPtr->current.location.y;
+ direction_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)));
+
+ ship_flags = StarShipPtr->RaceDescPtr->ship_info.ship_flags;
+ for (fire_flags = FIRES_FORE, facing = StarShipPtr->ShipFacing;
+ fire_flags <= FIRES_LEFT;
+ fire_flags <<= 1, facing += QUADRANT)
+ {
+ if ((ship_flags & fire_flags) && NORMALIZE_FACING (
+ direction_facing - facing + ANGLE_TO_FACING (OCTANT)
+ ) <= ANGLE_TO_FACING (QUADRANT))
+ {
+ StarShipPtr->ship_input_state |= WEAPON;
+ break;
+ }
+ }
+ }
+}
+
+static void
+InitWeaponSlots (RACE_DESC *RaceDescPtr, const BYTE *ModuleSlots)
+{
+ COUNT i;
+ SIS_DATA *SisData = GetCustomShipData (RaceDescPtr);
+ MISSILE_BLOCK *lpMB = SisData->MissileBlock;
+
+ SisData->num_blasters = 0;
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ COUNT which_gun;
+
+ if (i == 3)
+ i = NUM_MODULE_SLOTS - 1;
+
+ which_gun = ModuleSlots[(NUM_MODULE_SLOTS - 1) - i];
+
+ if (which_gun < GUN_WEAPON || which_gun > CANNON_WEAPON)
+ continue; /* not a gun */
+
+ which_gun -= GUN_WEAPON - 1;
+ RaceDescPtr->characteristics.weapon_energy_cost +=
+ which_gun * 2;
+
+ lpMB->flags = IGNORE_SIMILAR;
+ lpMB->blast_offs = BLASTER_OFFSET;
+ lpMB->speed = BLASTER_SPEED;
+ lpMB->preprocess_func = blaster_preprocess;
+ lpMB->hit_points = BLASTER_HITS * which_gun;
+ lpMB->damage = BLASTER_DAMAGE * which_gun;
+ lpMB->life = BLASTER_LIFE + ((BLASTER_LIFE >> 2) * (which_gun - 1));
+
+ if (which_gun == 1)
+ lpMB->index = 0;
+ else if (which_gun == 2)
+ lpMB->index = 9;
+ else
+ lpMB->index = 16;
+
+ switch (i)
+ {
+ case 0: /* NOSE WEAPON */
+ RaceDescPtr->ship_info.ship_flags |= FIRES_FORE;
+ lpMB->pixoffs = SIS_VERT_OFFSET;
+ lpMB->face = 0;
+ break;
+ case 1: /* SPREAD WEAPON */
+ RaceDescPtr->ship_info.ship_flags |= FIRES_FORE;
+ lpMB->pixoffs = SIS_VERT_OFFSET;
+ lpMB->face = +1;
+ /* copy it because there are two */
+ lpMB[1] = lpMB[0];
+ ++lpMB;
+ ++SisData->num_blasters;
+ lpMB->face = NORMALIZE_FACING (-1);
+ break;
+ case 2: /* SIDE WEAPON */
+ RaceDescPtr->ship_info.ship_flags |=
+ FIRES_LEFT | FIRES_RIGHT;
+ lpMB->pixoffs = SIS_HORZ_OFFSET;
+ lpMB->face = ANGLE_TO_FACING (QUADRANT);
+ /* copy it because there are two */
+ lpMB[1] = lpMB[0];
+ ++lpMB;
+ ++SisData->num_blasters;
+ lpMB->face = NORMALIZE_FACING (-ANGLE_TO_FACING (QUADRANT));
+ break;
+ case NUM_MODULE_SLOTS - 1: /* TAIL WEAPON */
+ RaceDescPtr->ship_info.ship_flags |= FIRES_AFT;
+ lpMB->pixoffs = SIS_VERT_OFFSET;
+ lpMB->face = ANGLE_TO_FACING (HALF_CIRCLE);
+ break;
+ }
+
+ ++lpMB;
+ ++SisData->num_blasters;
+ }
+}
+
+static void
+InitModuleSlots (RACE_DESC *RaceDescPtr, const BYTE *ModuleSlots)
+{
+ COUNT i;
+ COUNT num_trackers;
+ SIS_DATA *SisData = GetCustomShipData (RaceDescPtr);
+
+ RaceDescPtr->ship_info.max_crew = 0;
+ num_trackers = 0;
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ BYTE which_mod;
+
+ which_mod = ModuleSlots[(NUM_MODULE_SLOTS - 1) - i];
+ switch (which_mod)
+ {
+ case CREW_POD:
+ RaceDescPtr->ship_info.max_crew += CREW_POD_CAPACITY;
+ break;
+ case TRACKING_SYSTEM:
+ ++num_trackers;
+ break;
+ case ANTIMISSILE_DEFENSE:
+ RaceDescPtr->characteristics.special_energy_cost +=
+ ANTIMISSILE_ENERGY_INC;
+ break;
+ case SHIVA_FURNACE:
+ RaceDescPtr->characteristics.energy_regeneration +=
+ SHIVA_ENERGY_REGEN_INC;
+ break;
+ case DYNAMO_UNIT:
+ RaceDescPtr->characteristics.energy_wait -=
+ DYNAMO_UNIT_ENERGY_WAIT_DEC;
+ if (RaceDescPtr->characteristics.energy_wait < MIN_ENERGY_WAIT)
+ RaceDescPtr->characteristics.energy_wait = MIN_ENERGY_WAIT;
+ break;
+ }
+ }
+
+ if (num_trackers > MAX_TRACKING)
+ num_trackers = MAX_TRACKING;
+ RaceDescPtr->characteristics.weapon_energy_cost +=
+ num_trackers * TRACKER_ENERGY_COST;
+ SisData->num_trackers = num_trackers;
+ if (RaceDescPtr->characteristics.special_energy_cost)
+ {
+ RaceDescPtr->ship_info.ship_flags |= POINT_DEFENSE;
+ if (RaceDescPtr->characteristics.special_energy_cost > MAX_DEFENSE)
+ RaceDescPtr->characteristics.special_energy_cost = MAX_DEFENSE;
+ }
+}
+
+static void
+InitDriveSlots (RACE_DESC *RaceDescPtr, const BYTE *DriveSlots)
+{
+ COUNT i;
+
+ // NB. RaceDescPtr->characteristics.max_thrust is already initialised.
+ RaceDescPtr->characteristics.thrust_wait = 0;
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ switch (DriveSlots[i])
+ {
+ case FUSION_THRUSTER:
+ RaceDescPtr->characteristics.max_thrust += 2;
+ ++RaceDescPtr->characteristics.thrust_wait;
+ break;
+ }
+ }
+ RaceDescPtr->characteristics.thrust_wait = (BYTE)(
+ THRUST_WAIT - (RaceDescPtr->characteristics.thrust_wait >> 1));
+ RaceDescPtr->characteristics.max_thrust =
+ ((RaceDescPtr->characteristics.max_thrust /
+ RaceDescPtr->characteristics.thrust_increment) + 1)
+ * RaceDescPtr->characteristics.thrust_increment;
+}
+
+static void
+InitJetSlots (RACE_DESC *RaceDescPtr, const BYTE *JetSlots)
+{
+ COUNT i;
+
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ switch (JetSlots[i])
+ {
+ case TURNING_JETS:
+ RaceDescPtr->characteristics.turn_wait -= 2;
+ break;
+ }
+ }
+}
+
+RACE_DESC*
+init_sis (void)
+{
+ RACE_DESC *RaceDescPtr;
+ COUNT i;
+ // The caller of this func will copy the struct
+ static RACE_DESC new_sis_desc;
+ SIS_DATA empty_data;
+ memset (&empty_data, 0, sizeof (empty_data));
+
+ /* copy initial ship settings to new_sis_desc */
+ new_sis_desc = sis_desc;
+
+ new_sis_desc.uninit_func = uninit_sis;
+
+ if (inHQSpace ())
+ {
+ for (i = 0; i < NUM_VIEWS; ++i)
+ {
+ new_sis_desc.ship_data.ship_rsc[i] = NULL_RESOURCE;
+ new_sis_desc.ship_data.weapon_rsc[i] = NULL_RESOURCE;
+ new_sis_desc.ship_data.special_rsc[i] = NULL_RESOURCE;
+ }
+ new_sis_desc.ship_info.icons_rsc = NULL_RESOURCE;
+ new_sis_desc.ship_data.captain_control.captain_rsc = NULL_RESOURCE;
+ new_sis_desc.ship_data.victory_ditty_rsc = NULL_RESOURCE;
+ new_sis_desc.ship_data.ship_sounds_rsc = NULL_RESOURCE;
+
+ new_sis_desc.ship_data.ship_rsc[0] = SIS_HYPER_MASK_PMAP_ANIM;
+
+ new_sis_desc.preprocess_func = sis_hyper_preprocess;
+ new_sis_desc.postprocess_func = sis_hyper_postprocess;
+
+ new_sis_desc.characteristics.max_thrust -= 4;
+ }
+ else
+ {
+ new_sis_desc.preprocess_func = sis_battle_preprocess;
+ new_sis_desc.postprocess_func = sis_battle_postprocess;
+ new_sis_desc.init_weapon_func = initialize_blasters;
+ new_sis_desc.cyborg_control.intelligence_func = sis_intelligence;
+
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 3)
+ SET_GAME_STATE (BOMB_CARRIER, 1);
+ }
+
+ SetCustomShipData (&new_sis_desc, &empty_data);
+ InitModuleSlots (&new_sis_desc, GLOBAL_SIS (ModuleSlots));
+ InitWeaponSlots (&new_sis_desc, GLOBAL_SIS (ModuleSlots));
+ InitDriveSlots (&new_sis_desc, GLOBAL_SIS (DriveSlots));
+ InitJetSlots (&new_sis_desc, GLOBAL_SIS (JetSlots));
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ {
+ new_sis_desc.ship_info.crew_level = new_sis_desc.ship_info.max_crew;
+ }
+ else
+ {
+ // Count the captain too.
+ new_sis_desc.ship_info.max_crew++;
+ new_sis_desc.ship_info.crew_level = GLOBAL_SIS (CrewEnlisted) + 1;
+ new_sis_desc.ship_info.ship_flags |= PLAYER_CAPTAIN;
+ }
+
+ new_sis_desc.ship_info.energy_level = new_sis_desc.ship_info.max_energy;
+
+ RaceDescPtr = &new_sis_desc;
+
+ return (RaceDescPtr);
+}
+
+static void
+uninit_sis (RACE_DESC *pRaceDesc)
+{
+ if (!inHQSpace ())
+ {
+ GLOBAL_SIS (CrewEnlisted) = pRaceDesc->ship_info.crew_level;
+ if (pRaceDesc->ship_info.ship_flags & PLAYER_CAPTAIN)
+ GLOBAL_SIS (CrewEnlisted)--;
+ }
+
+ SetCustomShipData (pRaceDesc, NULL);
+}
+
+
diff --git a/src/uqm/ships/sis_ship/sis_ship.h b/src/uqm/ships/sis_ship/sis_ship.h
new file mode 100644
index 0000000..ffe0776
--- /dev/null
+++ b/src/uqm/ships/sis_ship/sis_ship.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SIS_SHIP_H
+#define SIS_SHIP_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_sis (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SIS_SHIP_H */
+
diff --git a/src/uqm/ships/slylandr/Makeinfo b/src/uqm/ships/slylandr/Makeinfo
new file mode 100644
index 0000000..309b1d4
--- /dev/null
+++ b/src/uqm/ships/slylandr/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="slylandr.c"
+uqm_HFILES="icode.h resinst.h slylandr.h"
diff --git a/src/uqm/ships/slylandr/icode.h b/src/uqm/ships/slylandr/icode.h
new file mode 100644
index 0000000..9897234
--- /dev/null
+++ b/src/uqm/ships/slylandr/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SLYLANDRO_CODE "ship.slylandro.code"
diff --git a/src/uqm/ships/slylandr/resinst.h b/src/uqm/ships/slylandr/resinst.h
new file mode 100644
index 0000000..00df833
--- /dev/null
+++ b/src/uqm/ships/slylandr/resinst.h
@@ -0,0 +1,13 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SLYLANDRO_BIG_MASK_PMAP_ANIM "ship.slylandro.graphics.probe.large"
+#define SLYLANDRO_CAPTAIN_MASK_PMAP_ANIM "ship.slylandro.graphics.captain"
+#define SLYLANDRO_ICON_MASK_PMAP_ANIM "ship.slylandro.icons"
+#define SLYLANDRO_MED_MASK_PMAP_ANIM "ship.slylandro.graphics.probe.medium"
+#define SLYLANDRO_MICON_MASK_PMAP_ANIM "ship.slylandro.meleeicons"
+#define SLYLANDRO_RACE_STRINGS "ship.slylandro.text"
+#define SLYLANDRO_SHIP_SOUNDS "ship.slylandro.sounds"
+#define SLYLANDRO_SML_MASK_PMAP_ANIM "ship.slylandro.graphics.probe.small"
+#define SLYLANDRO_VICTORY_SONG "ship.slylandro.ditty"
diff --git a/src/uqm/ships/slylandr/slylandr.c b/src/uqm/ships/slylandr/slylandr.c
new file mode 100644
index 0000000..8c4ca95
--- /dev/null
+++ b/src/uqm/ships/slylandr/slylandr.c
@@ -0,0 +1,438 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "slylandr.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 12
+#define MAX_ENERGY 20
+#define ENERGY_REGENERATION 0
+#define ENERGY_WAIT 10
+#define MAX_THRUST 60
+#define THRUST_INCREMENT MAX_THRUST
+#define THRUST_WAIT 0
+#define TURN_WAIT 0
+#define SHIP_MASS 1
+
+// Lightning weapon
+#define WEAPON_ENERGY_COST 2
+#define WEAPON_WAIT 17
+#define SLYLANDRO_OFFSET 9
+#define LASER_LENGTH 32
+ /* Total length of lighting bolts. Actual range is usually less than
+ * this, since the lightning rarely is straight. */
+
+// Harvester
+#define SPECIAL_ENERGY_COST 0
+#define SPECIAL_WAIT 20
+#define HARVEST_RANGE (208 * 3 / 8)
+ /* Was originally (SPACE_HEIGHT * 3 / 8) */
+
+static RACE_DESC slylandro_desc =
+{
+ { /* SHIP_INFO */
+ SEEKING_WEAPON | CREW_IMMUNE,
+ 17, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ SLYLANDRO_RACE_STRINGS,
+ SLYLANDRO_ICON_MASK_PMAP_ANIM,
+ SLYLANDRO_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ INFINITE_RADIUS, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 333, 9812,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SLYLANDRO_BIG_MASK_PMAP_ANIM,
+ SLYLANDRO_MED_MASK_PMAP_ANIM,
+ SLYLANDRO_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ SLYLANDRO_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ SLYLANDRO_VICTORY_SONG,
+ SLYLANDRO_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON << 1,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT initialize_lightning (ELEMENT *ElementPtr,
+ HELEMENT LaserArray[]);
+
+static void
+lightning_postprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait
+ && !(ElementPtr->state_flags & COLLISION))
+ {
+ HELEMENT Lightning;
+
+ initialize_lightning (ElementPtr, &Lightning);
+ if (Lightning)
+ PutElement (Lightning);
+ }
+}
+
+static void
+lightning_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ if (StarShipPtr->weapon_counter > WEAPON_WAIT >> 1)
+ StarShipPtr->weapon_counter =
+ WEAPON_WAIT - StarShipPtr->weapon_counter;
+ StarShipPtr->weapon_counter -= ElementPtr0->turn_wait;
+ ElementPtr0->turn_wait = 0;
+
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+}
+
+static COUNT
+initialize_lightning (ELEMENT *ElementPtr, HELEMENT LaserArray[])
+{
+ LASER_BLOCK LaserBlock;
+
+ LaserBlock.cx = ElementPtr->next.location.x;
+ LaserBlock.cy = ElementPtr->next.location.y;
+ LaserBlock.ex = 0;
+ LaserBlock.ey = 0;
+
+ LaserBlock.sender = ElementPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.face = 0;
+ LaserBlock.pixoffs = 0;
+ LaserArray[0] = initialize_laser (&LaserBlock);
+
+ if (LaserArray[0])
+ {
+ SIZE delta;
+ COUNT angle, facing;
+ DWORD rand_val;
+ ELEMENT *LaserPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ LockElement (LaserArray[0], &LaserPtr);
+ LaserPtr->postprocess_func = lightning_postprocess;
+ LaserPtr->collision_func = lightning_collision;
+
+ rand_val = TFB_Random ();
+
+ if (!(ElementPtr->state_flags & PLAYER_SHIP))
+ {
+ angle = GetVelocityTravelAngle (&ElementPtr->velocity);
+ facing = NORMALIZE_FACING (ANGLE_TO_FACING (angle));
+ delta = TrackShip (ElementPtr, &facing);
+
+ LaserPtr->turn_wait = ElementPtr->turn_wait - 1;
+
+ SetPrimColor (&(GLOBAL (DisplayArray))[LaserPtr->PrimIndex],
+ GetPrimColor (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex]));
+ }
+ else
+ {
+ facing = StarShipPtr->ShipFacing;
+ ElementPtr->hTarget = 0;
+ delta = TrackShip (ElementPtr, &facing);
+ ElementPtr->hTarget = 0;
+ angle = FACING_TO_ANGLE (facing);
+
+ if ((LaserPtr->turn_wait = StarShipPtr->weapon_counter) == 0)
+ LaserPtr->turn_wait = WEAPON_WAIT;
+
+ if (LaserPtr->turn_wait > WEAPON_WAIT >> 1)
+ LaserPtr->turn_wait = WEAPON_WAIT - LaserPtr->turn_wait;
+
+ switch (HIBYTE (LOWORD (rand_val)) & 3)
+ {
+ case 0:
+ SetPrimColor (
+ &(GLOBAL (DisplayArray))[LaserPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+ );
+ break;
+ case 1:
+ SetPrimColor (
+ &(GLOBAL (DisplayArray))[LaserPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x16, 0x17, 0x1F), 0x42)
+ );
+ break;
+ case 2:
+ SetPrimColor (
+ &(GLOBAL (DisplayArray))[LaserPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x06, 0x07, 0x1F), 0x4A)
+ );
+ break;
+ case 3:
+ SetPrimColor (
+ &(GLOBAL (DisplayArray))[LaserPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x18), 0x50)
+ );
+ break;
+ }
+ }
+
+ if (delta == -1 || delta == ANGLE_TO_FACING (HALF_CIRCLE))
+ angle += LOWORD (rand_val);
+ else if (delta == 0)
+ angle += LOWORD (rand_val) & 1 ? -1 : 1;
+ else if (delta < ANGLE_TO_FACING (HALF_CIRCLE))
+ angle += LOWORD (rand_val) & (QUADRANT - 1);
+ else
+ angle -= LOWORD (rand_val) & (QUADRANT - 1);
+ delta = WORLD_TO_VELOCITY (
+ DISPLAY_TO_WORLD ((HIWORD (rand_val) & (LASER_LENGTH - 1)) + 4)
+ );
+ SetVelocityComponents (&LaserPtr->velocity,
+ COSINE (angle, delta), SINE (angle, delta));
+
+ SetElementStarShip (LaserPtr, StarShipPtr);
+ UnlockElement (LaserArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+slylandro_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER)
+ /* no dodging in role playing game */
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr = 0;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter == 0
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level == 0
+ && ObjectsOfConcern[GRAVITY_MASS_INDEX].ObjectPtr == 0)
+ ConcernCounter = FIRST_EMPTY_INDEX + 1;
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == PURSUE
+ && lpEvalDesc->which_turn <= 6)
+ lpEvalDesc->MoveState = ENTICE;
+
+ ++ShipPtr->thrust_wait;
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+ --ShipPtr->thrust_wait;
+
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->which_turn <= 14)
+ StarShipPtr->ship_input_state |= WEAPON;
+ else
+ StarShipPtr->ship_input_state &= ~WEAPON;
+
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level <
+ StarShipPtr->RaceDescPtr->ship_info.max_energy)
+ {
+ lpEvalDesc = &ObjectsOfConcern[FIRST_EMPTY_INDEX];
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->which_turn <= 14)
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+}
+
+static BOOLEAN
+harvest_space_junk (ELEMENT *ElementPtr)
+{
+ BOOLEAN retval;
+ HELEMENT hElement, hNextElement;
+
+ retval = FALSE;
+ for (hElement = GetHeadElement ();
+ hElement; hElement = hNextElement)
+ {
+ ELEMENT *ObjPtr;
+
+ LockElement (hElement, &ObjPtr);
+ hNextElement = GetSuccElement (ObjPtr);
+
+ if (!(ObjPtr->state_flags & (APPEARING | PLAYER_SHIP | FINITE_LIFE))
+ && ObjPtr->playerNr == NEUTRAL_PLAYER_NUM
+ && !GRAVITY_MASS (ObjPtr->mass_points)
+ && CollisionPossible (ObjPtr, ElementPtr))
+ {
+ SIZE dx, dy;
+
+ if ((dx = ObjPtr->next.location.x
+ - ElementPtr->next.location.x) < 0)
+ dx = -dx;
+ if ((dy = ObjPtr->next.location.y
+ - ElementPtr->next.location.y) < 0)
+ dy = -dy;
+ dx = WORLD_TO_DISPLAY (dx);
+ dy = WORLD_TO_DISPLAY (dy);
+ if (dx <= HARVEST_RANGE && dy <= HARVEST_RANGE
+ && dx * dx + dy * dy <= HARVEST_RANGE * HARVEST_RANGE)
+ {
+ ObjPtr->life_span = 0;
+ ObjPtr->state_flags |= NONSOLID;
+
+ if (!retval)
+ {
+ STARSHIP *StarShipPtr;
+
+ retval = TRUE;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ DeltaEnergy (ElementPtr, MAX_ENERGY);
+ }
+ }
+ }
+
+ UnlockElement (hElement);
+ }
+
+ return (retval);
+}
+
+static void
+slylandro_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->weapon_counter
+ && StarShipPtr->weapon_counter < WEAPON_WAIT)
+ {
+ HELEMENT Lightning;
+
+ initialize_lightning (ElementPtr, &Lightning);
+ if (Lightning)
+ PutElement (Lightning);
+ }
+
+ if (StarShipPtr->special_counter == 0
+ && (StarShipPtr->cur_status_flags & SPECIAL)
+ && harvest_space_junk (ElementPtr))
+ {
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+static void
+slylandro_preprocess (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & (APPEARING | NONSOLID)))
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & THRUST)
+ && !(StarShipPtr->old_status_flags & THRUST))
+ StarShipPtr->ShipFacing += ANGLE_TO_FACING (HALF_CIRCLE);
+
+ if (ElementPtr->turn_wait == 0)
+ {
+ ElementPtr->turn_wait +=
+ StarShipPtr->RaceDescPtr->characteristics.turn_wait + 1;
+ if (StarShipPtr->cur_status_flags & LEFT)
+ --StarShipPtr->ShipFacing;
+ else if (StarShipPtr->cur_status_flags & RIGHT)
+ ++StarShipPtr->ShipFacing;
+ }
+
+ StarShipPtr->ShipFacing = NORMALIZE_FACING (StarShipPtr->ShipFacing);
+
+ if (ElementPtr->thrust_wait == 0)
+ {
+ ElementPtr->thrust_wait +=
+ StarShipPtr->RaceDescPtr->characteristics.thrust_wait + 1;
+
+ SetVelocityVector (&ElementPtr->velocity,
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust,
+ StarShipPtr->ShipFacing);
+ StarShipPtr->cur_status_flags |= SHIP_AT_MAX_SPEED;
+ StarShipPtr->cur_status_flags &= ~SHIP_IN_GRAVITY_WELL;
+ }
+
+ ElementPtr->next.image.frame = IncFrameIndex (ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+RACE_DESC*
+init_slylandro (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ slylandro_desc.preprocess_func = slylandro_preprocess;
+ slylandro_desc.postprocess_func = slylandro_postprocess;
+ slylandro_desc.init_weapon_func = initialize_lightning;
+ slylandro_desc.cyborg_control.intelligence_func = slylandro_intelligence;
+
+ RaceDescPtr = &slylandro_desc;
+
+ return (RaceDescPtr);
+}
diff --git a/src/uqm/ships/slylandr/slylandr.h b/src/uqm/ships/slylandr/slylandr.h
new file mode 100644
index 0000000..a55362d
--- /dev/null
+++ b/src/uqm/ships/slylandr/slylandr.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SLYLANDR_H
+#define SLYLANDR_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_slylandro (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SLYLANDR_H */
+
diff --git a/src/uqm/ships/spathi/Makeinfo b/src/uqm/ships/spathi/Makeinfo
new file mode 100644
index 0000000..48dc3f9
--- /dev/null
+++ b/src/uqm/ships/spathi/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="spathi.c"
+uqm_HFILES="icode.h resinst.h spathi.h"
diff --git a/src/uqm/ships/spathi/icode.h b/src/uqm/ships/spathi/icode.h
new file mode 100644
index 0000000..aa5f6ef
--- /dev/null
+++ b/src/uqm/ships/spathi/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SPATHI_CODE "ship.spathi.code"
diff --git a/src/uqm/ships/spathi/resinst.h b/src/uqm/ships/spathi/resinst.h
new file mode 100644
index 0000000..1d4ecf6
--- /dev/null
+++ b/src/uqm/ships/spathi/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define DISCRIM_BIG_MASK_PMAP_ANIM "ship.spathi.graphics.butt.large"
+#define DISCRIM_MED_MASK_PMAP_ANIM "ship.spathi.graphics.butt.medium"
+#define DISCRIM_SML_MASK_PMAP_ANIM "ship.spathi.graphics.butt.small"
+#define MISSILE_BIG_MASK_PMAP_ANIM "ship.spathi.graphics.missile.large"
+#define MISSILE_MED_MASK_PMAP_ANIM "ship.spathi.graphics.missile.medium"
+#define MISSILE_SML_MASK_PMAP_ANIM "ship.spathi.graphics.missile.small"
+#define SPATHI_BIG_MASK_PMAP_ANIM "ship.spathi.graphics.eluder.large"
+#define SPATHI_CAPTAIN_MASK_PMAP_ANIM "ship.spathi.graphics.captain"
+#define SPATHI_ICON_MASK_PMAP_ANIM "ship.spathi.icons"
+#define SPATHI_MED_MASK_PMAP_ANIM "ship.spathi.graphics.eluder.medium"
+#define SPATHI_MICON_MASK_PMAP_ANIM "ship.spathi.meleeicons"
+#define SPATHI_RACE_STRINGS "ship.spathi.text"
+#define SPATHI_SHIP_SOUNDS "ship.spathi.sounds"
+#define SPATHI_SML_MASK_PMAP_ANIM "ship.spathi.graphics.eluder.small"
+#define SPATHI_VICTORY_SONG "ship.spathi.ditty"
diff --git a/src/uqm/ships/spathi/spathi.c b/src/uqm/ships/spathi/spathi.c
new file mode 100644
index 0000000..aa4bd00
--- /dev/null
+++ b/src/uqm/ships/spathi/spathi.c
@@ -0,0 +1,301 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "spathi.h"
+#include "resinst.h"
+
+// Core characteristics
+#define MAX_CREW 30
+#define MAX_ENERGY 10
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 10
+#define MAX_THRUST 48
+#define THRUST_INCREMENT 12
+#define THRUST_WAIT 1
+#define TURN_WAIT 1
+#define SHIP_MASS 5
+
+// Forward gun
+#define WEAPON_ENERGY_COST 2
+#define WEAPON_WAIT 0
+#define SPATHI_FORWARD_OFFSET 16
+#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define MISSILE_LIFE 10
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+#define MISSILE_OFFSET 1
+#define MISSILE_RANGE (MISSILE_SPEED * MISSILE_LIFE)
+ /* This is for the cyborg only. */
+
+// B.U.T.T.
+#define SPECIAL_ENERGY_COST 3
+#define SPECIAL_WAIT 7
+#define SPATHI_REAR_OFFSET 20
+#define DISCRIMINATOR_SPEED DISPLAY_TO_WORLD (8)
+#define DISCRIMINATOR_LIFE 30
+#define DISCRIMINATOR_HITS 1
+#define DISCRIMINATOR_DAMAGE 2
+#define DISCRIMINATOR_OFFSET 4
+#define TRACK_WAIT 1
+
+static RACE_DESC spathi_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | FIRES_AFT | SEEKING_SPECIAL | DONT_CHASE,
+ 18, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ SPATHI_RACE_STRINGS,
+ SPATHI_ICON_MASK_PMAP_ANIM,
+ SPATHI_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 1000 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 2549, 3600,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SPATHI_BIG_MASK_PMAP_ANIM,
+ SPATHI_MED_MASK_PMAP_ANIM,
+ SPATHI_SML_MASK_PMAP_ANIM,
+ },
+ {
+ MISSILE_BIG_MASK_PMAP_ANIM,
+ MISSILE_MED_MASK_PMAP_ANIM,
+ MISSILE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ DISCRIM_BIG_MASK_PMAP_ANIM,
+ DISCRIM_MED_MASK_PMAP_ANIM,
+ DISCRIM_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SPATHI_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ SPATHI_VICTORY_SONG,
+ SPATHI_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_RANGE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+butt_missile_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT facing;
+
+ facing = GetFrameIndex (ElementPtr->next.image.frame);
+ if (TrackShip (ElementPtr, &facing) > 0)
+ {
+ ElementPtr->next.image.frame =
+ SetAbsFrameIndex (ElementPtr->next.image.frame,
+ facing);
+ ElementPtr->state_flags |= CHANGING;
+
+ SetVelocityVector (&ElementPtr->velocity,
+ DISCRIMINATOR_SPEED, facing);
+ }
+
+ ElementPtr->turn_wait = TRACK_WAIT;
+ }
+}
+
+static void
+spawn_butt_missile (ELEMENT *ShipPtr)
+{
+ HELEMENT ButtMissile;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK ButtMissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ ButtMissileBlock.cx = ShipPtr->next.location.x;
+ ButtMissileBlock.cy = ShipPtr->next.location.y;
+ ButtMissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ ButtMissileBlock.face = ButtMissileBlock.index =
+ NORMALIZE_FACING (StarShipPtr->ShipFacing
+ + ANGLE_TO_FACING (HALF_CIRCLE));
+ ButtMissileBlock.sender = ShipPtr->playerNr;
+ ButtMissileBlock.flags = 0;
+ ButtMissileBlock.pixoffs = SPATHI_REAR_OFFSET;
+ ButtMissileBlock.speed = DISCRIMINATOR_SPEED;
+ ButtMissileBlock.hit_points = DISCRIMINATOR_HITS;
+ ButtMissileBlock.damage = DISCRIMINATOR_DAMAGE;
+ ButtMissileBlock.life = DISCRIMINATOR_LIFE;
+ ButtMissileBlock.preprocess_func = butt_missile_preprocess;
+ ButtMissileBlock.blast_offs = DISCRIMINATOR_OFFSET;
+ ButtMissile = initialize_missile (&ButtMissileBlock);
+ if (ButtMissile)
+ {
+ ELEMENT *ButtPtr;
+
+ LockElement (ButtMissile, &ButtPtr);
+ ButtPtr->turn_wait = TRACK_WAIT;
+ SetElementStarShip (ButtPtr, StarShipPtr);
+
+ ProcessSound (SetAbsSoundIndex (
+ /* LAUNCH_BUTT_MISSILE */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ButtPtr);
+
+ UnlockElement (ButtMissile);
+ PutElement (ButtMissile);
+ }
+}
+
+static void
+spathi_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr
+ && lpEvalDesc->which_turn <= 24)
+ {
+ COUNT travel_facing, direction_facing;
+ SIZE delta_x, delta_y;
+
+ travel_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (GetVelocityTravelAngle (&ShipPtr->velocity)
+ + HALF_CIRCLE)
+ );
+ delta_x = lpEvalDesc->ObjectPtr->current.location.x
+ - ShipPtr->current.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->current.location.y
+ - ShipPtr->current.location.y;
+ direction_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+
+ if (NORMALIZE_FACING (direction_facing
+ - (StarShipPtr->ShipFacing + ANGLE_TO_FACING (HALF_CIRCLE))
+ + ANGLE_TO_FACING (QUADRANT))
+ <= ANGLE_TO_FACING (HALF_CIRCLE)
+ && (lpEvalDesc->which_turn <= 8
+ || NORMALIZE_FACING (direction_facing
+ + ANGLE_TO_FACING (HALF_CIRCLE)
+ - ANGLE_TO_FACING (GetVelocityTravelAngle (
+ &lpEvalDesc->ObjectPtr->velocity
+ ))
+ + ANGLE_TO_FACING (QUADRANT))
+ <= ANGLE_TO_FACING (HALF_CIRCLE))
+ && (!(StarShipPtr->cur_status_flags &
+ (SHIP_BEYOND_MAX_SPEED | SHIP_IN_GRAVITY_WELL))
+ || NORMALIZE_FACING (direction_facing
+ - travel_facing + ANGLE_TO_FACING (QUADRANT))
+ <= ANGLE_TO_FACING (HALF_CIRCLE)))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+}
+
+static COUNT
+initialize_standard_missile (ELEMENT *ShipPtr, HELEMENT MissileArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = SPATHI_FORWARD_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ MissileArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void
+spathi_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ spawn_butt_missile (ElementPtr);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+RACE_DESC*
+init_spathi (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ spathi_desc.postprocess_func = spathi_postprocess;
+ spathi_desc.init_weapon_func = initialize_standard_missile;
+ spathi_desc.cyborg_control.intelligence_func = spathi_intelligence;
+
+ RaceDescPtr = &spathi_desc;
+
+ return (RaceDescPtr);
+}
diff --git a/src/uqm/ships/spathi/spathi.h b/src/uqm/ships/spathi/spathi.h
new file mode 100644
index 0000000..900d05c
--- /dev/null
+++ b/src/uqm/ships/spathi/spathi.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SPATHI_H
+#define SPATHI_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_spathi (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SPATHI_H */
+
diff --git a/src/uqm/ships/supox/Makeinfo b/src/uqm/ships/supox/Makeinfo
new file mode 100644
index 0000000..9105cf6
--- /dev/null
+++ b/src/uqm/ships/supox/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="supox.c"
+uqm_HFILES="icode.h resinst.h supox.h"
diff --git a/src/uqm/ships/supox/icode.h b/src/uqm/ships/supox/icode.h
new file mode 100644
index 0000000..d2f82f9
--- /dev/null
+++ b/src/uqm/ships/supox/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SUPOX_CODE "ship.supox.code"
diff --git a/src/uqm/ships/supox/resinst.h b/src/uqm/ships/supox/resinst.h
new file mode 100644
index 0000000..8984c6c
--- /dev/null
+++ b/src/uqm/ships/supox/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define GOB_BIG_MASK_PMAP_ANIM "ship.supox.graphics.glob.large"
+#define GOB_MED_MASK_PMAP_ANIM "ship.supox.graphics.glob.medium"
+#define GOB_SML_MASK_PMAP_ANIM "ship.supox.graphics.glob.small"
+#define SUPOX_BIG_MASK_PMAP_ANIM "ship.supox.graphics.blade.large"
+#define SUPOX_CAPTAIN_MASK_PMAP_ANIM "ship.supox.graphics.captain"
+#define SUPOX_ICON_MASK_PMAP_ANIM "ship.supox.icons"
+#define SUPOX_MED_MASK_PMAP_ANIM "ship.supox.graphics.blade.medium"
+#define SUPOX_MICON_MASK_PMAP_ANIM "ship.supox.meleeicons"
+#define SUPOX_RACE_STRINGS "ship.supox.text"
+#define SUPOX_SHIP_SOUNDS "ship.supox.sounds"
+#define SUPOX_SML_MASK_PMAP_ANIM "ship.supox.graphics.blade.small"
+#define SUPOX_VICTORY_SONG "ship.supox.ditty"
diff --git a/src/uqm/ships/supox/supox.c b/src/uqm/ships/supox/supox.c
new file mode 100644
index 0000000..854c5b3
--- /dev/null
+++ b/src/uqm/ships/supox/supox.c
@@ -0,0 +1,288 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "supox.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 12
+#define MAX_ENERGY 16
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST 40
+#define THRUST_INCREMENT 8
+#define THRUST_WAIT 0
+#define TURN_WAIT 1
+#define SHIP_MASS 4
+
+// Gob launcher
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 2
+#define SUPOX_OFFSET 23
+#define MISSILE_OFFSET 2
+#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define MISSILE_LIFE 10
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+
+// Lateral/reverse thrust
+#define SPECIAL_ENERGY_COST 1
+ /* Unused - uncomment below to enable. */
+#define SPECIAL_WAIT 0
+ /* Unused except to initialize supox_desc.special_wait */
+
+static RACE_DESC supox_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 16, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ SUPOX_RACE_STRINGS,
+ SUPOX_ICON_MASK_PMAP_ANIM,
+ SUPOX_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 333 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 7468, 9246,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SUPOX_BIG_MASK_PMAP_ANIM,
+ SUPOX_MED_MASK_PMAP_ANIM,
+ SUPOX_SML_MASK_PMAP_ANIM,
+ },
+ {
+ GOB_BIG_MASK_PMAP_ANIM,
+ GOB_MED_MASK_PMAP_ANIM,
+ GOB_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ SUPOX_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ SUPOX_VICTORY_SONG,
+ SUPOX_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ (MISSILE_SPEED * MISSILE_LIFE) >> 1,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+supox_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (StarShipPtr->special_counter || lpEvalDesc->ObjectPtr == 0)
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ else
+ {
+ BOOLEAN LinedUp;
+ COUNT direction_angle;
+ SIZE delta_x, delta_y;
+
+ delta_x = lpEvalDesc->ObjectPtr->next.location.x
+ - ShipPtr->next.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->next.location.y
+ - ShipPtr->next.location.y;
+ direction_angle = ARCTAN (delta_x, delta_y);
+
+ LinedUp = (BOOLEAN)(NORMALIZE_ANGLE (NORMALIZE_ANGLE (direction_angle
+ - FACING_TO_ANGLE (StarShipPtr->ShipFacing))
+ + QUADRANT) <= HALF_CIRCLE);
+
+ if (!LinedUp
+ || lpEvalDesc->which_turn > 20
+ || NORMALIZE_ANGLE (
+ lpEvalDesc->facing
+ - (FACING_TO_ANGLE (StarShipPtr->ShipFacing)
+ + HALF_CIRCLE) + OCTANT
+ ) > QUADRANT)
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ else if (LinedUp && lpEvalDesc->which_turn <= 12)
+ StarShipPtr->ship_input_state |= SPECIAL;
+
+ if (StarShipPtr->ship_input_state & SPECIAL)
+ lpEvalDesc->MoveState = PURSUE;
+ }
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ if (StarShipPtr->ship_input_state & SPECIAL)
+ StarShipPtr->ship_input_state |= THRUST | WEAPON;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr
+ && lpEvalDesc->MoveState == AVOID
+ && ShipPtr->turn_wait == 0)
+ {
+ StarShipPtr->ship_input_state &= ~THRUST;
+ StarShipPtr->ship_input_state |= SPECIAL;
+ if (!(StarShipPtr->cur_status_flags & (LEFT | RIGHT)))
+ StarShipPtr->ship_input_state |= 1 << ((BYTE)TFB_Random () & 1);
+ else
+ StarShipPtr->ship_input_state |=
+ StarShipPtr->cur_status_flags & (LEFT | RIGHT);
+ }
+}
+
+static COUNT
+initialize_horn (ELEMENT *ShipPtr, HELEMENT HornArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = SUPOX_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ HornArray[0] = initialize_missile (&MissileBlock);
+ return (1);
+}
+
+static void
+supox_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+/*
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST)
+*/
+ )
+ {
+ SIZE add_facing;
+
+ add_facing = 0;
+ if (StarShipPtr->cur_status_flags & THRUST)
+ {
+ if (ElementPtr->thrust_wait == 0)
+ ++ElementPtr->thrust_wait;
+
+ add_facing = ANGLE_TO_FACING (HALF_CIRCLE);
+ }
+ if (StarShipPtr->cur_status_flags & LEFT)
+ {
+ if (ElementPtr->turn_wait == 0)
+ ++ElementPtr->turn_wait;
+
+ if (add_facing)
+ add_facing += ANGLE_TO_FACING (OCTANT);
+ else
+ add_facing = -ANGLE_TO_FACING (QUADRANT);
+ }
+ else if (StarShipPtr->cur_status_flags & RIGHT)
+ {
+ if (ElementPtr->turn_wait == 0)
+ ++ElementPtr->turn_wait;
+
+ if (add_facing)
+ add_facing -= ANGLE_TO_FACING (OCTANT);
+ else
+ add_facing = ANGLE_TO_FACING (QUADRANT);
+ }
+
+ if (add_facing)
+ {
+ COUNT facing;
+ STATUS_FLAGS thrust_status;
+
+ facing = StarShipPtr->ShipFacing;
+ StarShipPtr->ShipFacing = NORMALIZE_FACING (
+ facing + add_facing
+ );
+ thrust_status = inertial_thrust (ElementPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED
+ | SHIP_BEYOND_MAX_SPEED
+ | SHIP_IN_GRAVITY_WELL);
+ StarShipPtr->cur_status_flags |= thrust_status;
+ StarShipPtr->ShipFacing = facing;
+ }
+ }
+}
+
+RACE_DESC*
+init_supox (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ supox_desc.preprocess_func = supox_preprocess;
+ supox_desc.init_weapon_func = initialize_horn;
+ supox_desc.cyborg_control.intelligence_func = supox_intelligence;
+
+ RaceDescPtr = &supox_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/supox/supox.h b/src/uqm/ships/supox/supox.h
new file mode 100644
index 0000000..b066320
--- /dev/null
+++ b/src/uqm/ships/supox/supox.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SUPOX_H
+#define SUPOX_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_supox (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SUPOX_H */
+
diff --git a/src/uqm/ships/syreen/Makeinfo b/src/uqm/ships/syreen/Makeinfo
new file mode 100644
index 0000000..9485c78
--- /dev/null
+++ b/src/uqm/ships/syreen/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="syreen.c"
+uqm_HFILES="icode.h resinst.h syreen.h"
diff --git a/src/uqm/ships/syreen/icode.h b/src/uqm/ships/syreen/icode.h
new file mode 100644
index 0000000..66f3ca4
--- /dev/null
+++ b/src/uqm/ships/syreen/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SYREEN_CODE "ship.syreen.code"
diff --git a/src/uqm/ships/syreen/resinst.h b/src/uqm/ships/syreen/resinst.h
new file mode 100644
index 0000000..7a7cc24
--- /dev/null
+++ b/src/uqm/ships/syreen/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define DAGGER_BIG_MASK_PMAP_ANIM "ship.syreen.graphics.dagger.large"
+#define DAGGER_MED_MASK_PMAP_ANIM "ship.syreen.graphics.dagger.medium"
+#define DAGGER_SML_MASK_PMAP_ANIM "ship.syreen.graphics.dagger.small"
+#define SYREEN_BIG_MASK_PMAP_ANIM "ship.syreen.graphics.penetrator.large"
+#define SYREEN_CAPTAIN_MASK_PMAP_ANIM "ship.syreen.graphics.captain"
+#define SYREEN_ICON_MASK_PMAP_ANIM "ship.syreen.icons"
+#define SYREEN_MED_MASK_PMAP_ANIM "ship.syreen.graphics.penetrator.medium"
+#define SYREEN_MICON_MASK_PMAP_ANIM "ship.syreen.meleeicons"
+#define SYREEN_RACE_STRINGS "ship.syreen.text"
+#define SYREEN_SHIP_SOUNDS "ship.syreen.sounds"
+#define SYREEN_SML_MASK_PMAP_ANIM "ship.syreen.graphics.penetrator.small"
+#define SYREEN_VICTORY_SONG "ship.syreen.ditty"
diff --git a/src/uqm/ships/syreen/syreen.c b/src/uqm/ships/syreen/syreen.c
new file mode 100644
index 0000000..c65ecd2
--- /dev/null
+++ b/src/uqm/ships/syreen/syreen.c
@@ -0,0 +1,284 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "syreen.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define SYREEN_MAX_CREW_SIZE MAX_CREW_SIZE
+#define MAX_CREW 12
+#define MAX_ENERGY 16
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 6
+#define MAX_THRUST /* DISPLAY_TO_WORLD (8) */ 36
+#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 9
+#define THRUST_WAIT 1
+#define TURN_WAIT 1
+#define SHIP_MASS 2
+
+// Particle Beam Stiletto
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 8
+#define SYREEN_OFFSET 30
+#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define MISSILE_LIFE 10
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 2
+#define MISSILE_OFFSET 3
+
+// Syreen song
+#define SPECIAL_ENERGY_COST 5
+#define SPECIAL_WAIT 20
+#define ABANDONER_RANGE 208 /* originally SPACE_HEIGHT */
+#define MAX_ABANDONERS 8
+
+static RACE_DESC syreen_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 13, /* Super Melee cost */
+ MAX_CREW, SYREEN_MAX_CREW_SIZE,
+ MAX_ENERGY, MAX_ENERGY,
+ SYREEN_RACE_STRINGS,
+ SYREEN_ICON_MASK_PMAP_ANIM,
+ SYREEN_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 0, /* Initial sphere of influence radius */
+ { /* Known location (center of SoI) */
+ 0, 0,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ SYREEN_BIG_MASK_PMAP_ANIM,
+ SYREEN_MED_MASK_PMAP_ANIM,
+ SYREEN_SML_MASK_PMAP_ANIM,
+ },
+ {
+ DAGGER_BIG_MASK_PMAP_ANIM,
+ DAGGER_MED_MASK_PMAP_ANIM,
+ DAGGER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ SYREEN_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ SYREEN_VICTORY_SONG,
+ SYREEN_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ (MISSILE_SPEED * MISSILE_LIFE * 2 / 3),
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT
+initialize_dagger (ELEMENT *ShipPtr, HELEMENT DaggerArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = SYREEN_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ DaggerArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void
+spawn_crew (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ HELEMENT hCrew;
+
+ hCrew = AllocElement ();
+ if (hCrew != 0)
+ {
+ ELEMENT *CrewPtr;
+
+ LockElement (hCrew, &CrewPtr);
+ CrewPtr->next.location = ElementPtr->next.location;
+ CrewPtr->playerNr = ElementPtr->playerNr;
+ CrewPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
+ CrewPtr->life_span = 0;
+ CrewPtr->death_func = spawn_crew;
+ CrewPtr->pParent = ElementPtr->pParent;
+ CrewPtr->hTarget = 0;
+ UnlockElement (hCrew);
+
+ PutElement (hCrew);
+ }
+ }
+ else
+ {
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = GetHeadElement ();
+ hElement != 0; hElement = hNextElement)
+ {
+ ELEMENT *ObjPtr;
+
+ LockElement (hElement, &ObjPtr);
+ hNextElement = GetSuccElement (ObjPtr);
+
+ if ((ObjPtr->state_flags & PLAYER_SHIP)
+ && !elementsOfSamePlayer (ObjPtr, ElementPtr)
+ && ObjPtr->crew_level > 1)
+ {
+ SIZE dx, dy;
+ DWORD d_squared;
+
+ dx = ObjPtr->next.location.x - ElementPtr->next.location.x;
+ if (dx < 0)
+ dx = -dx;
+ dy = ObjPtr->next.location.y - ElementPtr->next.location.y;
+ if (dy < 0)
+ dy = -dy;
+
+ dx = WORLD_TO_DISPLAY (dx);
+ dy = WORLD_TO_DISPLAY (dy);
+ if (dx <= ABANDONER_RANGE && dy <= ABANDONER_RANGE
+ && (d_squared = (DWORD)((UWORD)dx * (UWORD)dx)
+ + (DWORD)((UWORD)dy * (UWORD)dy)) <=
+ (DWORD)((UWORD)ABANDONER_RANGE * (UWORD)ABANDONER_RANGE))
+ {
+ COUNT crew_loss;
+
+ crew_loss = ((MAX_ABANDONERS
+ * (ABANDONER_RANGE - square_root (d_squared)))
+ / ABANDONER_RANGE) + 1;
+ if (crew_loss >= ObjPtr->crew_level)
+ crew_loss = ObjPtr->crew_level - 1;
+
+ AbandonShip (ObjPtr, ElementPtr, crew_loss);
+ }
+ }
+
+ UnlockElement (hElement);
+ }
+ }
+}
+
+static void
+syreen_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr != NULL)
+ {
+ STARSHIP *StarShipPtr;
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (!(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & CREW_IMMUNE)
+ && StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr->crew_level > 1
+ && lpEvalDesc->which_turn <= 14)
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ }
+}
+
+static void
+syreen_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* SYREEN_SONG */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ spawn_crew (ElementPtr);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+RACE_DESC*
+init_syreen (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ syreen_desc.postprocess_func = syreen_postprocess;
+ syreen_desc.init_weapon_func = initialize_dagger;
+ syreen_desc.cyborg_control.intelligence_func = syreen_intelligence;
+
+ RaceDescPtr = &syreen_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/syreen/syreen.h b/src/uqm/ships/syreen/syreen.h
new file mode 100644
index 0000000..1930a1a
--- /dev/null
+++ b/src/uqm/ships/syreen/syreen.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SYREEN_H
+#define SYREEN_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_syreen (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SYREEN_H */
+
diff --git a/src/uqm/ships/thradd/Makeinfo b/src/uqm/ships/thradd/Makeinfo
new file mode 100644
index 0000000..f555509
--- /dev/null
+++ b/src/uqm/ships/thradd/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="thradd.c"
+uqm_HFILES="icode.h resinst.h thradd.h"
diff --git a/src/uqm/ships/thradd/icode.h b/src/uqm/ships/thradd/icode.h
new file mode 100644
index 0000000..070353a
--- /dev/null
+++ b/src/uqm/ships/thradd/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define THRADDASH_CODE "ship.thraddash.code"
diff --git a/src/uqm/ships/thradd/resinst.h b/src/uqm/ships/thradd/resinst.h
new file mode 100644
index 0000000..191d263
--- /dev/null
+++ b/src/uqm/ships/thradd/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define HORN_BIG_MASK_PMAP_ANIM "ship.thraddash.graphics.horn.large"
+#define HORN_MED_MASK_PMAP_ANIM "ship.thraddash.graphics.horn.medium"
+#define HORN_SML_MASK_PMAP_ANIM "ship.thraddash.graphics.horn.small"
+#define NAPALM_BIG_MASK_PMAP_ANIM "ship.thraddash.graphics.napalm.large"
+#define NAPALM_MED_MASK_PMAP_ANIM "ship.thraddash.graphics.napalm.medium"
+#define NAPALM_SML_MASK_PMAP_ANIM "ship.thraddash.graphics.napalm.small"
+#define THRADDASH_BIG_MASK_PMAP_ANIM "ship.thraddash.graphics.torch.large"
+#define THRADDASH_CAPTAIN_MASK_PMAP_ANIM "ship.thraddash.graphics.captain"
+#define THRADDASH_ICON_MASK_PMAP_ANIM "ship.thraddash.icons"
+#define THRADDASH_MED_MASK_PMAP_ANIM "ship.thraddash.graphics.torch.medium"
+#define THRADDASH_MICON_MASK_PMAP_ANIM "ship.thraddash.meleeicons"
+#define THRADDASH_RACE_STRINGS "ship.thraddash.text"
+#define THRADDASH_SHIP_SOUNDS "ship.thraddash.sounds"
+#define THRADDASH_SML_MASK_PMAP_ANIM "ship.thraddash.graphics.torch.small"
+#define THRADDASH_VICTORY_SONG "ship.thraddash.ditty"
diff --git a/src/uqm/ships/thradd/thradd.c b/src/uqm/ships/thradd/thradd.c
new file mode 100644
index 0000000..0d7a8e2
--- /dev/null
+++ b/src/uqm/ships/thradd/thradd.c
@@ -0,0 +1,400 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "thradd.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+
+// Core characteristics
+#define MAX_CREW 8
+#define MAX_ENERGY 24
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 6
+#define MAX_THRUST 28
+#define THRUST_INCREMENT 7
+#define THRUST_WAIT 0
+#define TURN_WAIT 1
+#define SHIP_MASS 7
+
+// Ion Blasters
+#define WEAPON_ENERGY_COST 2
+#define WEAPON_WAIT 12
+#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define MISSILE_LIFE 15
+#define MISSILE_OFFSET 3
+#define THRADDASH_OFFSET 9
+#define MISSILE_HITS 2
+#define MISSILE_DAMAGE 1
+
+// Afterburner
+#define SPECIAL_ENERGY_COST 1
+#define SPECIAL_WAIT 0
+#define SPECIAL_THRUST_INCREMENT 12
+#define SPECIAL_MAX_THRUST 72
+#define NAPALM_LIFE 48
+#define NAPALM_OFFSET 0
+#define NAPALM_HITS 1
+#define NAPALM_DAMAGE 2
+#define NAPALM_DECAY_RATE 5
+ /* Controls the speed of the afterburner "decay" animation; it will
+ * decay one step (one animation frame) per NAPALM_DECAY_RATE
+ * frames. */
+#define NUM_NAPALM_FADES 6
+
+
+static RACE_DESC thraddash_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 10, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ THRADDASH_RACE_STRINGS,
+ THRADDASH_ICON_MASK_PMAP_ANIM,
+ THRADDASH_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 833 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 2535, 8358,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ THRADDASH_BIG_MASK_PMAP_ANIM,
+ THRADDASH_MED_MASK_PMAP_ANIM,
+ THRADDASH_SML_MASK_PMAP_ANIM,
+ },
+ {
+ HORN_BIG_MASK_PMAP_ANIM,
+ HORN_MED_MASK_PMAP_ANIM,
+ HORN_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NAPALM_BIG_MASK_PMAP_ANIM,
+ NAPALM_MED_MASK_PMAP_ANIM,
+ NAPALM_SML_MASK_PMAP_ANIM,
+ },
+ {
+ THRADDASH_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ THRADDASH_VICTORY_SONG,
+ THRADDASH_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ (MISSILE_SPEED * MISSILE_LIFE) >> 1,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+thraddash_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEvalDesc->ObjectPtr)
+ {
+#define STATIONARY_SPEED WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (4))
+ SIZE dx, dy;
+
+ GetCurrentVelocityComponents (
+ &lpEvalDesc->ObjectPtr->velocity, &dx, &dy
+ );
+ if (lpEvalDesc->which_turn > 8
+ || (long)dx * dx + (long)dy * dy <=
+ (long)STATIONARY_SPEED * STATIONARY_SPEED)
+ lpEvalDesc->MoveState = PURSUE;
+ else
+ lpEvalDesc->MoveState = ENTICE;
+ }
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState == ENTICE)
+ {
+ if ((StarShipPtr->ship_input_state & THRUST)
+ || (ShipPtr->turn_wait == 0
+ && !(StarShipPtr->ship_input_state & (LEFT | RIGHT)))
+ || NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (
+ &ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr->velocity
+ ) + HALF_CIRCLE + OCTANT)
+ - StarShipPtr->ShipFacing) > ANGLE_TO_FACING (QUADRANT))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ else if (lpEvalDesc->ObjectPtr)
+ {
+ if (lpEvalDesc->MoveState == PURSUE)
+ {
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level >= WEAPON_ENERGY_COST
+ + SPECIAL_ENERGY_COST
+ && ShipPtr->turn_wait == 0
+ && !(StarShipPtr->ship_input_state & (LEFT | RIGHT))
+ && (!(StarShipPtr->cur_status_flags & SPECIAL)
+ || !(StarShipPtr->cur_status_flags
+ & (SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED))))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ else if (lpEvalDesc->MoveState == ENTICE)
+ {
+ COUNT direction_angle;
+ SIZE delta_x, delta_y;
+
+ delta_x = lpEvalDesc->ObjectPtr->next.location.x
+ - ShipPtr->next.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->next.location.y
+ - ShipPtr->next.location.y;
+ direction_angle = ARCTAN (delta_x, delta_y);
+
+ if ((lpEvalDesc->which_turn > 24
+ && !(StarShipPtr->ship_input_state & (LEFT | RIGHT)))
+ || (lpEvalDesc->which_turn <= 16
+ && NORMALIZE_ANGLE (direction_angle
+ - (FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE)
+ + QUADRANT) <= HALF_CIRCLE
+ && (lpEvalDesc->which_turn < 12
+ || NORMALIZE_ANGLE (direction_angle
+ - (GetVelocityTravelAngle (
+ &lpEvalDesc->ObjectPtr->velocity
+ ) + HALF_CIRCLE)
+ + (OCTANT + 2)) <= ((OCTANT + 2) << 1))))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ }
+
+ if ((StarShipPtr->ship_input_state & SPECIAL)
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
+ SPECIAL_ENERGY_COST)
+ StarShipPtr->ship_input_state &= ~THRUST;
+ }
+}
+
+static void
+flame_napalm_preprocess (ELEMENT *ElementPtr)
+{
+ ZeroVelocityComponents (&ElementPtr->velocity);
+
+ if (ElementPtr->state_flags & NONSOLID)
+ {
+ ElementPtr->state_flags &= ~NONSOLID;
+ ElementPtr->state_flags |= APPEARING;
+ SetPrimType (&(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ STAMP_PRIM);
+
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectFrame (ElementPtr);
+ }
+ /* turn_wait is abused here to store the speed of the decay animation */
+ else if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ if (ElementPtr->life_span <= NUM_NAPALM_FADES * (NAPALM_DECAY_RATE + 1)
+ || GetFrameIndex (
+ ElementPtr->current.image.frame
+ ) != NUM_NAPALM_FADES)
+ ElementPtr->next.image.frame =
+ DecFrameIndex (ElementPtr->current.image.frame);
+ else if (ElementPtr->life_span > NUM_NAPALM_FADES * (NAPALM_DECAY_RATE + 1))
+ ElementPtr->next.image.frame = SetAbsFrameIndex (
+ ElementPtr->current.image.frame,
+ GetFrameCount (ElementPtr->current.image.frame) - 1
+ );
+
+ /* turn_wait is abused here to store the speed of the decay
+ * animation. */
+ ElementPtr->turn_wait = NAPALM_DECAY_RATE;
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static COUNT
+initialize_horn (ELEMENT *ShipPtr, HELEMENT HornArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = THRADDASH_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ HornArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void
+thraddash_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (!(StarShipPtr->cur_status_flags & SPECIAL))
+ {
+ if ((StarShipPtr->old_status_flags & SPECIAL)
+ && (StarShipPtr->cur_status_flags & SHIP_AT_MAX_SPEED))
+ StarShipPtr->cur_status_flags |= SHIP_BEYOND_MAX_SPEED;
+ }
+ else if (DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ COUNT max_thrust, thrust_increment;
+ STATUS_FLAGS thrust_status;
+ HELEMENT hTrailElement;
+
+ if (!(StarShipPtr->old_status_flags & SPECIAL))
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+
+ if (ElementPtr->thrust_wait == 0)
+ ++ElementPtr->thrust_wait;
+
+ thrust_increment =
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment;
+ max_thrust = StarShipPtr->RaceDescPtr->characteristics.max_thrust;
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment =
+ SPECIAL_THRUST_INCREMENT;
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust =
+ SPECIAL_MAX_THRUST;
+
+ thrust_status = inertial_thrust (ElementPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED
+ | SHIP_BEYOND_MAX_SPEED
+ | SHIP_IN_GRAVITY_WELL);
+ StarShipPtr->cur_status_flags |= thrust_status;
+
+ StarShipPtr->RaceDescPtr->characteristics.thrust_increment =
+ thrust_increment;
+ StarShipPtr->RaceDescPtr->characteristics.max_thrust = max_thrust;
+
+ {
+ MISSILE_BLOCK MissileBlock;
+
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ MissileBlock.face = 0;
+ MissileBlock.index = GetFrameCount (
+ StarShipPtr->RaceDescPtr->ship_data.special[0]
+ ) - 1;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = 0;
+ MissileBlock.speed = 0;
+ MissileBlock.hit_points = NAPALM_HITS;
+ MissileBlock.damage = NAPALM_DAMAGE;
+ MissileBlock.life = NAPALM_LIFE;
+ MissileBlock.preprocess_func = flame_napalm_preprocess;
+ MissileBlock.blast_offs = NAPALM_OFFSET;
+
+ hTrailElement = initialize_missile (&MissileBlock);
+ if (hTrailElement)
+ {
+ ELEMENT *TrailElementPtr;
+
+ LockElement (hTrailElement, &TrailElementPtr);
+ SetElementStarShip (TrailElementPtr, StarShipPtr);
+ TrailElementPtr->hTarget = 0;
+
+ /* turn_wait is abused here to store the speed of the decay
+ * animation */
+ TrailElementPtr->turn_wait = NAPALM_DECAY_RATE;
+
+ TrailElementPtr->state_flags |= NONSOLID;
+ SetPrimType (
+ &(GLOBAL (DisplayArray))[TrailElementPtr->PrimIndex],
+ NO_PRIM
+ );
+
+ /* normally done during preprocess, but because
+ * object is being inserted at head rather than
+ * appended after tail it may never get preprocessed.
+ */
+ TrailElementPtr->next = TrailElementPtr->current;
+ TrailElementPtr->state_flags |= PRE_PROCESS;
+
+ UnlockElement (hTrailElement);
+ InsertElement (hTrailElement, GetHeadElement ());
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ }
+ }
+ }
+}
+
+RACE_DESC*
+init_thraddash (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ thraddash_desc.preprocess_func = thraddash_preprocess;
+ thraddash_desc.init_weapon_func = initialize_horn;
+ thraddash_desc.cyborg_control.intelligence_func = thraddash_intelligence;
+
+ RaceDescPtr = &thraddash_desc;
+
+ return (RaceDescPtr);
+}
diff --git a/src/uqm/ships/thradd/thradd.h b/src/uqm/ships/thradd/thradd.h
new file mode 100644
index 0000000..fb2a542
--- /dev/null
+++ b/src/uqm/ships/thradd/thradd.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef THRADD_H
+#define THRADD_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_thraddash (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* THRADD_H */
+
diff --git a/src/uqm/ships/umgah/Makeinfo b/src/uqm/ships/umgah/Makeinfo
new file mode 100644
index 0000000..a66b4ce
--- /dev/null
+++ b/src/uqm/ships/umgah/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="umgah.c"
+uqm_HFILES="icode.h resinst.h umgah.h"
diff --git a/src/uqm/ships/umgah/icode.h b/src/uqm/ships/umgah/icode.h
new file mode 100644
index 0000000..103f5d2
--- /dev/null
+++ b/src/uqm/ships/umgah/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define UMGAH_CODE "ship.umgah.code"
diff --git a/src/uqm/ships/umgah/resinst.h b/src/uqm/ships/umgah/resinst.h
new file mode 100644
index 0000000..4df4b07
--- /dev/null
+++ b/src/uqm/ships/umgah/resinst.h
@@ -0,0 +1,17 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define CONE_BIG_MASK_ANIM "ship.umgah.graphics.cone.large"
+#define CONE_MED_MASK_ANIM "ship.umgah.graphics.cone.medium"
+#define CONE_SML_MASK_ANIM "ship.umgah.graphics.cone.small"
+#define SPRITZ_MASK_PMAP_ANIM "ship.umgah.graphics.spritz"
+#define UMGAH_BIG_MASK_PMAP_ANIM "ship.umgah.graphics.drone.large"
+#define UMGAH_CAPTAIN_MASK_PMAP_ANIM "ship.umgah.graphics.captain"
+#define UMGAH_ICON_MASK_PMAP_ANIM "ship.umgah.icons"
+#define UMGAH_MED_MASK_PMAP_ANIM "ship.umgah.graphics.drone.medium"
+#define UMGAH_MICON_MASK_PMAP_ANIM "ship.umgah.meleeicons"
+#define UMGAH_RACE_STRINGS "ship.umgah.text"
+#define UMGAH_SHIP_SOUNDS "ship.umgah.sounds"
+#define UMGAH_SML_MASK_PMAP_ANIM "ship.umgah.graphics.drone.small"
+#define UMGAH_VICTORY_SONG "ship.umgah.ditty"
diff --git a/src/uqm/ships/umgah/umgah.c b/src/uqm/ships/umgah/umgah.c
new file mode 100644
index 0000000..370ad40
--- /dev/null
+++ b/src/uqm/ships/umgah/umgah.c
@@ -0,0 +1,434 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "umgah.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 10
+#define MAX_ENERGY 30
+#define ENERGY_REGENERATION MAX_ENERGY
+#define ENERGY_WAIT 150
+#define MAX_THRUST /* DISPLAY_TO_WORLD (5) */ 18
+#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 6
+#define THRUST_WAIT 3
+#define TURN_WAIT 4
+#define SHIP_MASS 1
+
+// Antimatter cone
+#define WEAPON_ENERGY_COST 0
+#define WEAPON_WAIT 0
+#define UMGAH_OFFSET 0
+#define CONE_OFFSET 0
+#define CONE_SPEED 0
+#define CONE_HITS 100
+#define CONE_DAMAGE 1
+#define CONE_LIFE 1
+
+// Retropropulsion
+#define SPECIAL_ENERGY_COST 1
+#define SPECIAL_WAIT 2
+#define JUMP_DIST DISPLAY_TO_WORLD (40)
+
+static RACE_DESC umgah_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | IMMEDIATE_WEAPON,
+ 7, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ UMGAH_RACE_STRINGS,
+ UMGAH_ICON_MASK_PMAP_ANIM,
+ UMGAH_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 833 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 1798, 6000,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ UMGAH_BIG_MASK_PMAP_ANIM,
+ UMGAH_MED_MASK_PMAP_ANIM,
+ UMGAH_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SPRITZ_MASK_PMAP_ANIM,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ CONE_BIG_MASK_ANIM,
+ CONE_MED_MASK_ANIM,
+ CONE_SML_MASK_ANIM,
+ },
+ {
+ UMGAH_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ UMGAH_VICTORY_SONG,
+ UMGAH_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ (LONG_RANGE_WEAPON << 2),
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+
+// Private per-instance ship data
+typedef struct
+{
+ UWORD prevFacing;
+} UMGAH_DATA;
+
+// Local typedef
+typedef UMGAH_DATA CustomShipData_t;
+
+// Retrieve race-specific ship data from a race desc
+static CustomShipData_t *
+GetCustomShipData (RACE_DESC *pRaceDesc)
+{
+ return pRaceDesc->data;
+}
+
+// Set the race-specific data in a race desc
+// (Re)Allocates its own storage for the data.
+static void
+SetCustomShipData (RACE_DESC *pRaceDesc, const CustomShipData_t *data)
+{
+ if (pRaceDesc->data == data)
+ return; // no-op
+
+ if (pRaceDesc->data) // Out with the old
+ {
+ HFree (pRaceDesc->data);
+ pRaceDesc->data = NULL;
+ }
+
+ if (data) // In with the new
+ {
+ CustomShipData_t* newData = HMalloc (sizeof (*data));
+ *newData = *data;
+ pRaceDesc->data = newData;
+ }
+}
+
+
+static void
+cone_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ StarShipPtr->RaceDescPtr->ship_data.special[0] =
+ SetRelFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0],
+ ANGLE_TO_FACING (FULL_CIRCLE));
+
+ ElementPtr->state_flags |= APPEARING;
+}
+
+static void
+cone_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ HELEMENT hBlastElement;
+
+ hBlastElement = weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ if (hBlastElement)
+ {
+ RemoveElement (hBlastElement);
+ FreeElement (hBlastElement);
+
+ ElementPtr0->state_flags &= ~DISAPPEARING;
+ }
+}
+
+static void
+umgah_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE)
+ {
+ if (lpEvalDesc->which_turn > 3
+ || (StarShipPtr->old_status_flags & SPECIAL))
+ lpEvalDesc->ObjectPtr = 0;
+ else if ((lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE)
+ && !(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
+ lpEvalDesc->MoveState = AVOID;
+ else
+ lpEvalDesc->MoveState = PURSUE;
+ }
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (StarShipPtr->special_counter
+ || ObjectsOfConcern[GRAVITY_MASS_INDEX].ObjectPtr
+ || lpEvalDesc->ObjectPtr == 0)
+ {
+ StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = CLOSE_RANGE_WEAPON;
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ if (lpEvalDesc->which_turn < 16)
+ StarShipPtr->ship_input_state |= WEAPON;
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ }
+ else
+ {
+ BYTE this_turn;
+ SIZE delta_x, delta_y;
+ BOOLEAN EnemyBehind, EnoughJuice;
+
+ if (lpEvalDesc->which_turn >= 0xFF + 1)
+ this_turn = 0xFF;
+ else
+ this_turn = (BYTE)lpEvalDesc->which_turn;
+
+ EnoughJuice = (BOOLEAN)(WORLD_TO_TURN (
+ JUMP_DIST * StarShipPtr->RaceDescPtr->ship_info.energy_level
+ / SPECIAL_ENERGY_COST
+ ) > this_turn);
+ delta_x = lpEvalDesc->ObjectPtr->next.location.x -
+ ShipPtr->next.location.x;
+ delta_y = lpEvalDesc->ObjectPtr->next.location.y -
+ ShipPtr->next.location.y;
+ EnemyBehind = (BOOLEAN)(NORMALIZE_ANGLE (
+ ARCTAN (delta_x, delta_y)
+ - (FACING_TO_ANGLE (StarShipPtr->ShipFacing)
+ + HALF_CIRCLE) + (OCTANT + (OCTANT >> 2))
+ ) <= ((OCTANT + (OCTANT >> 2)) << 1));
+
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (EnoughJuice
+ && ((StarShipPtr->old_status_flags & SPECIAL)
+ || EnemyBehind
+ || (this_turn > 6
+ && MANEUVERABILITY (
+ &EnemyStarShipPtr->RaceDescPtr->cyborg_control
+ ) <= SLOW_SHIP)
+ || (this_turn >= 16 && this_turn <= 24)))
+ StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = (LONG_RANGE_WEAPON << 3);
+ else
+ StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange = CLOSE_RANGE_WEAPON;
+
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+
+ if (StarShipPtr->RaceDescPtr->cyborg_control.WeaponRange == CLOSE_RANGE_WEAPON)
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ else
+ {
+ BOOLEAN LinedUp;
+
+ StarShipPtr->ship_input_state &= ~THRUST;
+ LinedUp = (BOOLEAN)(ShipPtr->turn_wait == 0
+ && !(StarShipPtr->old_status_flags & (LEFT | RIGHT)));
+ if (((StarShipPtr->old_status_flags & SPECIAL)
+ && this_turn <= StarShipPtr->RaceDescPtr->characteristics.special_wait)
+ || (!(StarShipPtr->old_status_flags & SPECIAL)
+ && EnemyBehind && (LinedUp || this_turn < 16)))
+ {
+ StarShipPtr->ship_input_state |= SPECIAL;
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = this_turn;
+
+ /* don't want him backing straight into ship */
+ if (this_turn <= 8 && LinedUp)
+ {
+ if (TFB_Random () & 1)
+ StarShipPtr->ship_input_state |= LEFT;
+ else
+ StarShipPtr->ship_input_state |= RIGHT;
+ }
+ }
+ else if (StarShipPtr->old_status_flags & SPECIAL)
+ {
+ StarShipPtr->ship_input_state &= ~(SPECIAL | LEFT | RIGHT);
+ StarShipPtr->ship_input_state |= THRUST;
+ }
+ }
+
+ if (this_turn < 16 && !EnemyBehind)
+ StarShipPtr->ship_input_state |= WEAPON;
+ }
+
+ if (!(StarShipPtr->ship_input_state & SPECIAL))
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = 0xFF;
+}
+
+static COUNT
+initialize_cone (ELEMENT *ShipPtr, HELEMENT ConeArray[])
+{
+ STARSHIP *StarShipPtr;
+ UMGAH_DATA* UmgahData;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = UMGAH_OFFSET;
+ MissileBlock.speed = CONE_SPEED;
+ MissileBlock.hit_points = CONE_HITS;
+ MissileBlock.damage = CONE_DAMAGE;
+ MissileBlock.life = CONE_LIFE;
+ MissileBlock.preprocess_func = cone_preprocess;
+ MissileBlock.blast_offs = CONE_OFFSET;
+
+ // This func is called every frame while the player is holding down WEAPON
+ // Don't reset the cone FRAME to the first image every time
+ UmgahData = GetCustomShipData (StarShipPtr->RaceDescPtr);
+ if (!UmgahData || StarShipPtr->ShipFacing != UmgahData->prevFacing)
+ {
+ const UMGAH_DATA shipData = {StarShipPtr->ShipFacing};
+
+ SetCustomShipData (StarShipPtr->RaceDescPtr, &shipData);
+
+ StarShipPtr->RaceDescPtr->ship_data.special[0] =
+ SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.special[0],
+ StarShipPtr->ShipFacing);
+ }
+
+ MissileBlock.index = GetFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0]);
+ ConeArray[0] = initialize_missile (&MissileBlock);
+
+ if (ConeArray[0])
+ {
+ ELEMENT *ConePtr;
+
+ LockElement (ConeArray[0], &ConePtr);
+ ConePtr->collision_func = cone_collision;
+ ConePtr->state_flags &= ~APPEARING;
+ ConePtr->next = ConePtr->current;
+ InitIntersectStartPoint (ConePtr);
+ InitIntersectEndPoint (ConePtr);
+ ConePtr->IntersectControl.IntersectStamp.frame =
+ StarShipPtr->RaceDescPtr->ship_data.special[0];
+ UnlockElement (ConeArray[0]);
+ }
+
+ return (1);
+}
+
+static void
+umgah_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter > 0)
+ {
+ StarShipPtr->special_counter = 0;
+
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ }
+}
+
+static void
+umgah_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ // Reset the value just in case
+ SetCustomShipData (StarShipPtr->RaceDescPtr, NULL);
+ }
+ else
+ {
+ if (ElementPtr->thrust_wait == 0
+ && (StarShipPtr->cur_status_flags & SPECIAL)
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ COUNT facing;
+
+ ProcessSound (SetAbsSoundIndex (
+ /* ZIP_BACKWARDS */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ facing = FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE;
+ DeltaVelocityComponents (&ElementPtr->velocity,
+ COSINE (facing, WORLD_TO_VELOCITY (JUMP_DIST)),
+ SINE (facing, WORLD_TO_VELOCITY (JUMP_DIST)));
+ StarShipPtr->cur_status_flags &=
+ ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
+
+ StarShipPtr->special_counter = SPECIAL_WAIT;
+ }
+ }
+}
+
+static void
+uninit_umgah (RACE_DESC *pRaceDesc)
+{
+ SetCustomShipData (pRaceDesc, NULL);
+}
+
+RACE_DESC*
+init_umgah (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ umgah_desc.uninit_func = uninit_umgah;
+ umgah_desc.preprocess_func = umgah_preprocess;
+ umgah_desc.postprocess_func = umgah_postprocess;
+ umgah_desc.init_weapon_func = initialize_cone;
+ umgah_desc.cyborg_control.intelligence_func = umgah_intelligence;
+
+ RaceDescPtr = &umgah_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/umgah/umgah.h b/src/uqm/ships/umgah/umgah.h
new file mode 100644
index 0000000..8c706bb
--- /dev/null
+++ b/src/uqm/ships/umgah/umgah.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UMGAH_H
+#define UMGAH_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_umgah (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UMGAH_H */
+
diff --git a/src/uqm/ships/urquan/Makeinfo b/src/uqm/ships/urquan/Makeinfo
new file mode 100644
index 0000000..a1d130d
--- /dev/null
+++ b/src/uqm/ships/urquan/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="urquan.c"
+uqm_HFILES="icode.h resinst.h urquan.h"
diff --git a/src/uqm/ships/urquan/icode.h b/src/uqm/ships/urquan/icode.h
new file mode 100644
index 0000000..b26e84a
--- /dev/null
+++ b/src/uqm/ships/urquan/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define URQUAN_CODE "ship.urquan.code"
diff --git a/src/uqm/ships/urquan/resinst.h b/src/uqm/ships/urquan/resinst.h
new file mode 100644
index 0000000..a7b9ecd
--- /dev/null
+++ b/src/uqm/ships/urquan/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define FIGHTER_BIG_MASK_PMAP_ANIM "ship.urquan.graphics.fighter.large"
+#define FIGHTER_MED_MASK_PMAP_ANIM "ship.urquan.graphics.fighter.medium"
+#define FIGHTER_SML_MASK_PMAP_ANIM "ship.urquan.graphics.fighter.small"
+#define FUSION_BIG_MASK_PMAP_ANIM "ship.urquan.graphics.fusion.large"
+#define FUSION_MED_MASK_PMAP_ANIM "ship.urquan.graphics.fusion.medium"
+#define FUSION_SML_MASK_PMAP_ANIM "ship.urquan.graphics.fusion.small"
+#define URQUAN_BIG_MASK_PMAP_ANIM "ship.urquan.graphics.dreadnought.large"
+#define URQUAN_CAPTAIN_MASK_PMAP_ANIM "ship.urquan.graphics.captain"
+#define URQUAN_ICON_MASK_PMAP_ANIM "ship.urquan.icons"
+#define URQUAN_MED_MASK_PMAP_ANIM "ship.urquan.graphics.dreadnought.medium"
+#define URQUAN_MICON_MASK_PMAP_ANIM "ship.urquan.meleeicons"
+#define URQUAN_RACE_STRINGS "ship.urquan.text"
+#define URQUAN_SHIP_SOUNDS "ship.urquan.sounds"
+#define URQUAN_SML_MASK_PMAP_ANIM "ship.urquan.graphics.dreadnought.small"
+#define URQUAN_VICTORY_SONG "ship.urquan.ditty"
diff --git a/src/uqm/ships/urquan/urquan.c b/src/uqm/ships/urquan/urquan.c
new file mode 100644
index 0000000..9df87d0
--- /dev/null
+++ b/src/uqm/ships/urquan/urquan.c
@@ -0,0 +1,554 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "urquan.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+
+#include <stdlib.h>
+
+// Core characteristics
+#define MAX_CREW MAX_CREW_SIZE
+#define MAX_ENERGY MAX_ENERGY_SIZE
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST 30
+#define THRUST_INCREMENT 6
+#define THRUST_WAIT 6
+#define TURN_WAIT 4
+#define SHIP_MASS 10
+
+// Fusion blast
+#define WEAPON_ENERGY_COST 6
+#define WEAPON_WAIT 6
+#define MISSILE_SPEED DISPLAY_TO_WORLD (20)
+#define MISSILE_LIFE 20
+#define MISSILE_HITS 10
+#define MISSILE_DAMAGE 6
+#define MISSILE_OFFSET 8
+#define URQUAN_OFFSET 32
+
+// Fighters
+#define SPECIAL_ENERGY_COST 8
+#define SPECIAL_WAIT 9
+#define FIGHTER_OFFSET 4
+#define FIGHTER_SPEED DISPLAY_TO_WORLD (8)
+#define ONE_WAY_FLIGHT 125
+#define TRACK_THRESHOLD 6
+#define FIGHTER_LIFE (ONE_WAY_FLIGHT + ONE_WAY_FLIGHT + 150)
+#define FIGHTER_HITS 1
+#define FIGHTER_MASS 0
+#define FIGHTER_WEAPON_WAIT 8
+#define FIGHTER_LASER_RANGE DISPLAY_TO_WORLD (40 + FIGHTER_OFFSET)
+
+static RACE_DESC urquan_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_SPECIAL,
+ 30, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ URQUAN_RACE_STRINGS,
+ URQUAN_ICON_MASK_PMAP_ANIM,
+ URQUAN_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 2666 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 5750, 6000,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ URQUAN_BIG_MASK_PMAP_ANIM,
+ URQUAN_MED_MASK_PMAP_ANIM,
+ URQUAN_SML_MASK_PMAP_ANIM,
+ },
+ {
+ FUSION_BIG_MASK_PMAP_ANIM,
+ FUSION_MED_MASK_PMAP_ANIM,
+ FUSION_SML_MASK_PMAP_ANIM,
+ },
+ {
+ FIGHTER_BIG_MASK_PMAP_ANIM,
+ FIGHTER_MED_MASK_PMAP_ANIM,
+ FIGHTER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ URQUAN_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ URQUAN_VICTORY_SONG,
+ URQUAN_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_SPEED * MISSILE_LIFE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT
+initialize_fusion (ELEMENT *ShipPtr, HELEMENT FusionArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = URQUAN_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ FusionArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void
+fighter_postprocess (ELEMENT *ElementPtr)
+{
+ HELEMENT Laser;
+ STARSHIP *StarShipPtr;
+ LASER_BLOCK LaserBlock;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LaserBlock.cx = ElementPtr->next.location.x;
+ LaserBlock.cy = ElementPtr->next.location.y;
+ LaserBlock.face = ElementPtr->thrust_wait;
+ LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), FIGHTER_LASER_RANGE);
+ LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), FIGHTER_LASER_RANGE);
+ LaserBlock.sender = ElementPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = FIGHTER_OFFSET;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E);
+ Laser = initialize_laser (&LaserBlock);
+ if (Laser)
+ {
+ ELEMENT *LaserPtr;
+
+ LockElement (Laser, &LaserPtr);
+ SetElementStarShip (LaserPtr, StarShipPtr);
+
+ ProcessSound (SetAbsSoundIndex (
+ /* FIGHTER_ZAP */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), LaserPtr);
+
+ UnlockElement (Laser);
+ PutElement (Laser);
+ }
+
+ ElementPtr->postprocess_func = 0;
+ ElementPtr->thrust_wait = FIGHTER_WEAPON_WAIT;
+}
+
+static void
+fighter_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+
+ ++StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ if (FIGHTER_LIFE - ElementPtr->life_span > TRACK_THRESHOLD
+ && !(ElementPtr->state_flags & CHANGING))
+ {
+ BOOLEAN Enroute;
+ COUNT orig_facing, facing;
+ SIZE delta_x, delta_y;
+ ELEMENT *eptr;
+
+ Enroute = TRUE;
+
+ delta_x = StarShipPtr->RaceDescPtr->ship_info.crew_level;
+ delta_y = ElementPtr->life_span;
+
+ orig_facing = facing =
+ GetFrameIndex (ElementPtr->current.image.frame);
+ if (((delta_y & 1) || ElementPtr->hTarget
+ || TrackShip (ElementPtr, &facing) >= 0)
+ && (delta_x == 0 || delta_y >= ONE_WAY_FLIGHT))
+ ElementPtr->state_flags |= IGNORE_SIMILAR;
+ else if (delta_x)
+ {
+ LockElement (StarShipPtr->hShip, &eptr);
+ delta_x = eptr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_y = eptr->current.location.y
+ - ElementPtr->current.location.y;
+ UnlockElement (StarShipPtr->hShip);
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+ facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+
+#ifdef NEVER
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ if (delta_x <= LASER_RANGE && delta_y <= LASER_RANGE)
+#endif /* NEVER */
+ ElementPtr->state_flags &= ~IGNORE_SIMILAR;
+
+ Enroute = FALSE;
+ }
+
+ if (ElementPtr->thrust_wait > 0)
+ --ElementPtr->thrust_wait;
+
+ if (ElementPtr->hTarget)
+ {
+ LockElement (ElementPtr->hTarget, &eptr);
+ delta_x = eptr->current.location.x
+ - ElementPtr->current.location.x;
+ delta_y = eptr->current.location.y
+ - ElementPtr->current.location.y;
+ UnlockElement (ElementPtr->hTarget);
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+
+ if (ElementPtr->thrust_wait == 0
+ && abs (delta_x) < FIGHTER_LASER_RANGE * 3 / 4
+ && abs (delta_y) < FIGHTER_LASER_RANGE * 3 / 4
+ && delta_x * delta_x + delta_y * delta_y <
+ (FIGHTER_LASER_RANGE * 3 / 4) * (FIGHTER_LASER_RANGE * 3 / 4))
+ {
+ ElementPtr->thrust_wait =
+ (BYTE)NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+ ElementPtr->postprocess_func = fighter_postprocess;
+ }
+
+ if (Enroute)
+ {
+ facing = GetFrameIndex (eptr->current.image.frame);
+ if (ElementPtr->turn_wait & LEFT)
+ {
+ delta_x += COSINE (FACING_TO_ANGLE (facing - 4),
+ DISPLAY_TO_WORLD (30));
+ delta_y += SINE (FACING_TO_ANGLE (facing - 4),
+ DISPLAY_TO_WORLD (30));
+ }
+ else
+ {
+ delta_x += COSINE (FACING_TO_ANGLE (facing + 4),
+ DISPLAY_TO_WORLD (30));
+ delta_y += SINE (FACING_TO_ANGLE (facing + 4),
+ DISPLAY_TO_WORLD (30));
+ }
+ facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ );
+ }
+ }
+ ElementPtr->state_flags |= CHANGING;
+
+ if (facing != orig_facing)
+ ElementPtr->next.image.frame = SetAbsFrameIndex (
+ ElementPtr->next.image.frame, facing
+ );
+ SetVelocityVector (
+ &ElementPtr->velocity, FIGHTER_SPEED, facing
+ );
+ }
+}
+
+static void
+fighter_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ if (GRAVITY_MASS (ElementPtr1->mass_points))
+ {
+ HELEMENT hFighterElement;
+
+ hFighterElement = AllocElement ();
+ if (hFighterElement)
+ {
+ COUNT primIndex, travel_facing;
+ SIZE delta_facing;
+ ELEMENT *FighterElementPtr;
+
+ LockElement (hFighterElement, &FighterElementPtr);
+ primIndex = FighterElementPtr->PrimIndex;
+ *FighterElementPtr = *ElementPtr0;
+ FighterElementPtr->PrimIndex = primIndex;
+ (GLOBAL (DisplayArray))[primIndex] =
+ (GLOBAL (DisplayArray))[ElementPtr0->PrimIndex];
+ FighterElementPtr->state_flags &= ~PRE_PROCESS;
+ FighterElementPtr->state_flags |= CHANGING;
+ FighterElementPtr->next = FighterElementPtr->current;
+ travel_facing = GetVelocityTravelAngle (
+ &FighterElementPtr->velocity
+ );
+ delta_facing = NORMALIZE_ANGLE (
+ ARCTAN (pPt1->x - pPt0->x, pPt1->y - pPt0->y)
+ - travel_facing);
+ if (delta_facing == 0)
+ {
+ if (FighterElementPtr->turn_wait & LEFT)
+ travel_facing -= QUADRANT;
+ else
+ travel_facing += QUADRANT;
+ }
+ else if (delta_facing <= HALF_CIRCLE)
+ travel_facing -= QUADRANT;
+ else
+ travel_facing += QUADRANT;
+
+ travel_facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ NORMALIZE_ANGLE (travel_facing)
+ ));
+ FighterElementPtr->next.image.frame =
+ SetAbsFrameIndex (FighterElementPtr->next.image.frame,
+ travel_facing);
+ SetVelocityVector (&FighterElementPtr->velocity,
+ FIGHTER_SPEED, travel_facing);
+ UnlockElement (hFighterElement);
+
+ PutElement (hFighterElement);
+ }
+
+ ElementPtr0->state_flags |= DISAPPEARING | COLLISION;
+ }
+ else if (ElementPtr0->pParent != ElementPtr1->pParent)
+ {
+ ElementPtr0->blast_offset = 0;
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+ ElementPtr0->state_flags |= DISAPPEARING | COLLISION;
+ }
+ else if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* FIGHTERS_RETURN */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 3), ElementPtr1);
+ DeltaCrew (ElementPtr1, 1);
+ ElementPtr0->state_flags |= DISAPPEARING | COLLISION;
+ }
+
+ if (ElementPtr0->state_flags & DISAPPEARING)
+ {
+ ElementPtr0->state_flags &= ~DISAPPEARING;
+
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ ElementPtr0->state_flags |= NONSOLID;
+
+ --StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+static void
+spawn_fighters (ELEMENT *ElementPtr)
+{
+ SIZE i;
+ COUNT facing;
+ SIZE delta_x, delta_y;
+ HELEMENT hFighterElement;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ facing = StarShipPtr->ShipFacing + ANGLE_TO_FACING (HALF_CIRCLE);
+ delta_x = COSINE (FACING_TO_ANGLE (facing), DISPLAY_TO_WORLD (14));
+ delta_y = SINE (FACING_TO_ANGLE (facing), DISPLAY_TO_WORLD (14));
+
+ i = ElementPtr->crew_level > 2 ? 2 : 1;
+ while (i-- && (hFighterElement = AllocElement ()))
+ {
+ SIZE sx, sy;
+ COUNT fighter_facing;
+ ELEMENT *FighterElementPtr;
+
+ DeltaCrew (ElementPtr, -1);
+
+ PutElement (hFighterElement);
+ LockElement (hFighterElement, &FighterElementPtr);
+ FighterElementPtr->hit_points = FIGHTER_HITS;
+ FighterElementPtr->mass_points = FIGHTER_MASS;
+ FighterElementPtr->thrust_wait = TRACK_THRESHOLD + 1;
+ FighterElementPtr->playerNr = ElementPtr->playerNr;
+ FighterElementPtr->state_flags = APPEARING | FINITE_LIFE
+ | CREW_OBJECT | IGNORE_SIMILAR;
+ FighterElementPtr->life_span = FIGHTER_LIFE;
+ SetPrimType (&(GLOBAL (DisplayArray))[FighterElementPtr->PrimIndex],
+ STAMP_PRIM);
+ {
+ FighterElementPtr->preprocess_func = fighter_preprocess;
+ FighterElementPtr->postprocess_func = 0;
+ FighterElementPtr->collision_func = fighter_collision;
+ FighterElementPtr->death_func = NULL;
+ }
+
+ FighterElementPtr->current.location = ElementPtr->next.location;
+ if (i == 1)
+ {
+ FighterElementPtr->turn_wait = LEFT;
+ fighter_facing = NORMALIZE_FACING (facing + 2);
+ FighterElementPtr->current.location.x += delta_x - delta_y;
+ FighterElementPtr->current.location.y += delta_y + delta_x;
+ }
+ else
+ {
+ FighterElementPtr->turn_wait = RIGHT;
+ fighter_facing = NORMALIZE_FACING (facing - 2);
+ FighterElementPtr->current.location.x += delta_x + delta_y;
+ FighterElementPtr->current.location.y += delta_y - delta_x;
+ }
+ sx = COSINE (FACING_TO_ANGLE (fighter_facing),
+ WORLD_TO_VELOCITY (FIGHTER_SPEED));
+ sy = SINE (FACING_TO_ANGLE (fighter_facing),
+ WORLD_TO_VELOCITY (FIGHTER_SPEED));
+ SetVelocityComponents (&FighterElementPtr->velocity, sx, sy);
+ FighterElementPtr->current.location.x -= VELOCITY_TO_WORLD (sx);
+ FighterElementPtr->current.location.y -= VELOCITY_TO_WORLD (sy);
+
+ FighterElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ FighterElementPtr->current.image.frame =
+ SetAbsFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0],
+ fighter_facing);
+ SetElementStarShip (FighterElementPtr, StarShipPtr);
+ UnlockElement (hFighterElement);
+ }
+}
+
+static void
+urquan_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ ObjectsOfConcern[ENEMY_SHIP_INDEX].MoveState = PURSUE;
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr
+ && lpEvalDesc->MoveState == ENTICE
+ && (!(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT)
+ || lpEvalDesc->which_turn <= 8)
+ && (!(lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE)
+ || (lpEvalDesc->ObjectPtr->mass_points >= 4
+ && lpEvalDesc->which_turn == 2
+ && ObjectsOfConcern[ENEMY_SHIP_INDEX].which_turn > 16)))
+ lpEvalDesc->MoveState = PURSUE;
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ {
+ STARSHIP *EnemyStarShipPtr = NULL;
+
+ if (lpEvalDesc->ObjectPtr)
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr
+ && StarShipPtr->RaceDescPtr->ship_info.crew_level >
+ (StarShipPtr->RaceDescPtr->ship_info.max_crew >> 2)
+ && !(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & POINT_DEFENSE)
+ && (StarShipPtr->RaceDescPtr->characteristics.special_wait < 6
+ || (MANEUVERABILITY (
+ &EnemyStarShipPtr->RaceDescPtr->cyborg_control
+ ) <= SLOW_SHIP
+ && !(EnemyStarShipPtr->cur_status_flags & SHIP_BEYOND_MAX_SPEED))
+ || (lpEvalDesc->which_turn <= 12
+ && (StarShipPtr->ship_input_state & (LEFT | RIGHT))
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
+ (BYTE)(StarShipPtr->RaceDescPtr->ship_info.max_energy >> 1))))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ }
+
+ StarShipPtr->RaceDescPtr->characteristics.special_wait = 0;
+}
+
+static void
+urquan_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && ElementPtr->crew_level > 1
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* LAUNCH_FIGHTERS */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ spawn_fighters (ElementPtr);
+
+ StarShipPtr->special_counter = SPECIAL_WAIT;
+ }
+}
+
+RACE_DESC*
+init_urquan (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ urquan_desc.postprocess_func = urquan_postprocess;
+ urquan_desc.init_weapon_func = initialize_fusion;
+ urquan_desc.cyborg_control.intelligence_func = urquan_intelligence;
+
+ RaceDescPtr = &urquan_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/urquan/urquan.h b/src/uqm/ships/urquan/urquan.h
new file mode 100644
index 0000000..937d93f
--- /dev/null
+++ b/src/uqm/ships/urquan/urquan.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef URQUAN_H
+#define URQUAN_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_urquan (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* URQUAN_H */
+
diff --git a/src/uqm/ships/utwig/Makeinfo b/src/uqm/ships/utwig/Makeinfo
new file mode 100644
index 0000000..84b1d8c
--- /dev/null
+++ b/src/uqm/ships/utwig/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="utwig.c"
+uqm_HFILES="icode.h resinst.h utwig.h"
diff --git a/src/uqm/ships/utwig/icode.h b/src/uqm/ships/utwig/icode.h
new file mode 100644
index 0000000..4762b89
--- /dev/null
+++ b/src/uqm/ships/utwig/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define UTWIG_CODE "ship.utwig.code"
diff --git a/src/uqm/ships/utwig/resinst.h b/src/uqm/ships/utwig/resinst.h
new file mode 100644
index 0000000..384862e
--- /dev/null
+++ b/src/uqm/ships/utwig/resinst.h
@@ -0,0 +1,16 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define LANCE_BIG_MASK_PMAP_ANIM "ship.utwig.graphics.lance.large"
+#define LANCE_MED_MASK_PMAP_ANIM "ship.utwig.graphics.lance.medium"
+#define LANCE_SML_MASK_PMAP_ANIM "ship.utwig.graphics.lance.small"
+#define UTWIG_BIG_MASK_PMAP_ANIM "ship.utwig.graphics.jugger.large"
+#define UTWIG_CAPTAIN_MASK_PMAP_ANIM "ship.utwig.graphics.captain"
+#define UTWIG_ICON_MASK_PMAP_ANIM "ship.utwig.icons"
+#define UTWIG_MED_MASK_PMAP_ANIM "ship.utwig.graphics.jugger.medium"
+#define UTWIG_MICON_MASK_PMAP_ANIM "ship.utwig.meleeicons"
+#define UTWIG_RACE_STRINGS "ship.utwig.text"
+#define UTWIG_SHIP_SOUNDS "ship.utwig.sounds"
+#define UTWIG_SML_MASK_PMAP_ANIM "ship.utwig.graphics.jugger.small"
+#define UTWIG_VICTORY_SONG "ship.utwig.ditty"
diff --git a/src/uqm/ships/utwig/utwig.c b/src/uqm/ships/utwig/utwig.c
new file mode 100644
index 0000000..cb5f2fd
--- /dev/null
+++ b/src/uqm/ships/utwig/utwig.c
@@ -0,0 +1,380 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "utwig.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 20
+#define ENERGY_REGENERATION 0
+#define ENERGY_WAIT 255
+#define MAX_THRUST 36
+#define THRUST_INCREMENT 6
+#define THRUST_WAIT 6
+#define TURN_WAIT 1
+#define SHIP_MASS 8
+
+// Weapon
+#define WEAPON_ENERGY_COST 0
+#define WEAPON_WAIT 7
+#define UTWIG_OFFSET 9
+#define MISSILE_SPEED DISPLAY_TO_WORLD (30)
+#define MISSILE_LIFE 10
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+#define MISSILE_OFFSET 1
+#define LAUNCH_XOFFS0 DISPLAY_TO_WORLD (5)
+#define LAUNCH_YOFFS0 -DISPLAY_TO_WORLD (18)
+#define LAUNCH_XOFFS1 DISPLAY_TO_WORLD (13)
+#define LAUNCH_YOFFS1 -DISPLAY_TO_WORLD (9)
+#define LAUNCH_XOFFS2 DISPLAY_TO_WORLD (17)
+#define LAUNCH_YOFFS2 -DISPLAY_TO_WORLD (4)
+
+// Shield
+#define SPECIAL_ENERGY_COST 1
+#define SPECIAL_WAIT 12
+
+static RACE_DESC utwig_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | POINT_DEFENSE | SHIELD_DEFENSE,
+ 22, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY >> 1, MAX_ENERGY,
+ UTWIG_RACE_STRINGS,
+ UTWIG_ICON_MASK_PMAP_ANIM,
+ UTWIG_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 666 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 8534, 8797,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ UTWIG_BIG_MASK_PMAP_ANIM,
+ UTWIG_MED_MASK_PMAP_ANIM,
+ UTWIG_SML_MASK_PMAP_ANIM,
+ },
+ {
+ LANCE_BIG_MASK_PMAP_ANIM,
+ LANCE_MED_MASK_PMAP_ANIM,
+ LANCE_SML_MASK_PMAP_ANIM,
+ },
+ {
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ UTWIG_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ UTWIG_VICTORY_SONG,
+ UTWIG_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT
+initialize_lance (ELEMENT *ShipPtr, HELEMENT WeaponArray[])
+{
+ COUNT i;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ MissileBlock.pixoffs = 0;
+
+ for (i = 0; i < 3; ++i)
+ {
+ COUNT angle;
+ SIZE sin0 = 0, cos0 = 0;
+ SIZE sin1sin0, cos1sin0, cos1cos0, sin1cos0;
+
+ switch (i)
+ {
+ case 0:
+ cos0 = LAUNCH_XOFFS0;
+ sin0 = LAUNCH_YOFFS0;
+ break;
+ case 1:
+ cos0 = LAUNCH_XOFFS1;
+ sin0 = LAUNCH_YOFFS1;
+ break;
+ case 2:
+ cos0 = LAUNCH_XOFFS2;
+ sin0 = LAUNCH_YOFFS2;
+ break;
+ }
+ angle = FACING_TO_ANGLE (MissileBlock.face) + QUADRANT;
+ cos1cos0 = COSINE (angle, cos0);
+ sin1sin0 = SINE (angle, sin0);
+ sin1cos0 = SINE (angle, cos0);
+ cos1sin0 = COSINE (angle, sin0);
+
+ cos0 = cos1cos0 - sin1sin0;
+ sin0 = sin1cos0 + cos1sin0;
+ MissileBlock.cx = ShipPtr->next.location.x + cos0;
+ MissileBlock.cy = ShipPtr->next.location.y + sin0;
+ WeaponArray[(i << 1)] = initialize_missile (&MissileBlock);
+
+ cos0 = -cos1cos0 - sin1sin0;
+ sin0 = -sin1cos0 + cos1sin0;
+ MissileBlock.cx = ShipPtr->next.location.x + cos0;
+ MissileBlock.cy = ShipPtr->next.location.y + sin0;
+ WeaponArray[(i << 1) + 1] = initialize_missile (&MissileBlock);
+ }
+
+ return (6);
+}
+
+static void
+utwig_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ SIZE ShieldStatus;
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level == 0)
+ ShieldStatus = 0;
+ else
+ {
+ ShieldStatus = -1;
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE)
+ {
+ ShieldStatus = 0;
+ if (!(lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE))
+ lpEvalDesc->MoveState = PURSUE;
+ else if (lpEvalDesc->ObjectPtr->mass_points
+ || (lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
+ {
+ if ((lpEvalDesc->which_turn >>= 1) == 0)
+ lpEvalDesc->which_turn = 1;
+
+ if (lpEvalDesc->ObjectPtr->mass_points)
+ lpEvalDesc->ObjectPtr = 0;
+ else
+ lpEvalDesc->MoveState = PURSUE;
+ ShieldStatus = 1;
+ }
+ }
+ }
+
+ if (StarShipPtr->special_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (ShieldStatus)
+ {
+ if ((ShieldStatus > 0 || lpEvalDesc->ObjectPtr)
+ && lpEvalDesc->which_turn <= 2
+ && (ShieldStatus > 0
+ || (lpEvalDesc->ObjectPtr->state_flags
+ & PLAYER_SHIP) /* means IMMEDIATE WEAPON */
+ || PlotIntercept (lpEvalDesc->ObjectPtr,
+ ShipPtr, 2, 0))
+ && (TFB_Random () & 3))
+ {
+ StarShipPtr->ship_input_state |= SPECIAL;
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ }
+
+ lpEvalDesc->ObjectPtr = 0;
+ }
+ }
+
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level
+ && (lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX])->ObjectPtr)
+ {
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (!(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & IMMEDIATE_WEAPON))
+ lpEvalDesc->MoveState = PURSUE;
+ }
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+}
+
+static void
+utwig_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr0->life_span > NORMAL_LIFE
+ && (ElementPtr1->state_flags & FINITE_LIFE)
+ && ElementPtr1->mass_points)
+ ElementPtr0->life_span += ElementPtr1->mass_points;
+
+ collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+}
+
+static void
+utwig_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ PRIMITIVE *lpPrim;
+
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ ElementPtr->collision_func = utwig_collision;
+ }
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (ElementPtr->life_span > (NORMAL_LIFE + 1))
+ {
+ DeltaEnergy (ElementPtr,
+ ElementPtr->life_span - (NORMAL_LIFE + 1));
+
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1),
+ ElementPtr);
+ }
+
+ if (!(StarShipPtr->cur_status_flags & SPECIAL))
+ StarShipPtr->special_counter = 0;
+ else if (StarShipPtr->special_counter % (SPECIAL_WAIT >> 1) == 0)
+ {
+ if (!DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ StarShipPtr->RaceDescPtr->ship_info.ship_flags &=
+ ~(POINT_DEFENSE | SHIELD_DEFENSE);
+ else if (StarShipPtr->special_counter == 0)
+ {
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ ProcessSound (SetAbsSoundIndex (
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2),
+ ElementPtr);
+ }
+ }
+
+ lpPrim = &(GLOBAL (DisplayArray))[ElementPtr->PrimIndex];
+ if (StarShipPtr->special_counter == 0)
+ {
+ // The shield is off.
+ SetPrimColor (lpPrim,
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1C, 0x00), 0x78));
+ ElementPtr->colorCycleIndex = 0;
+ ElementPtr->life_span = NORMAL_LIFE;
+ SetPrimType (lpPrim, STAMP_PRIM);
+ }
+ else
+ {
+ // The shield is on.
+
+ /* Originally, this table also contained the now commented out
+ * entries. It then used some if statements to skip over these.
+ * The current behaviour is the same as the old behavior,
+ * but I am not sure that the old behavior was intended. - SvdB
+ */
+ static const Color colorTable[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7a),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7b),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7c),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7d),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7e),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7f),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x2a),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2b),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2c),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2d),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2e),
+ //BUILD_COLOR (MAKE_RGB15_INIT (0x0B, 0x00, 0x00), 0x2f),
+ };
+ const size_t colorTableCount =
+ sizeof colorTable / sizeof colorTable[0];
+
+ if (StarShipPtr->weapon_counter == 0)
+ ++StarShipPtr->weapon_counter;
+
+ // colorCycleIndex is actually 1 higher than the entry in colorTable
+ // which is currently used, as it is 0 when the shield is off,
+ // and we don't want to skip over the first entry of the table.
+ ElementPtr->colorCycleIndex++;
+ if (ElementPtr->colorCycleIndex == colorTableCount + 1)
+ ElementPtr->colorCycleIndex = 1;
+
+ SetPrimColor (lpPrim, colorTable[ElementPtr->colorCycleIndex - 1]);
+
+ ElementPtr->life_span = NORMAL_LIFE + 1;
+ SetPrimType (lpPrim, STAMPFILL_PRIM);
+ }
+}
+
+RACE_DESC*
+init_utwig (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ utwig_desc.preprocess_func = utwig_preprocess;
+ utwig_desc.init_weapon_func = initialize_lance;
+ utwig_desc.cyborg_control.intelligence_func = utwig_intelligence;
+
+ RaceDescPtr = &utwig_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/utwig/utwig.h b/src/uqm/ships/utwig/utwig.h
new file mode 100644
index 0000000..83ab97e
--- /dev/null
+++ b/src/uqm/ships/utwig/utwig.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UTWIG_H
+#define UTWIG_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_utwig (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UTWIG_H */
+
diff --git a/src/uqm/ships/vux/Makeinfo b/src/uqm/ships/vux/Makeinfo
new file mode 100644
index 0000000..13c9264
--- /dev/null
+++ b/src/uqm/ships/vux/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="vux.c"
+uqm_HFILES="icode.h resinst.h vux.h"
diff --git a/src/uqm/ships/vux/icode.h b/src/uqm/ships/vux/icode.h
new file mode 100644
index 0000000..0bd37d7
--- /dev/null
+++ b/src/uqm/ships/vux/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define VUX_CODE "ship.vux.code"
diff --git a/src/uqm/ships/vux/resinst.h b/src/uqm/ships/vux/resinst.h
new file mode 100644
index 0000000..c3e04bf
--- /dev/null
+++ b/src/uqm/ships/vux/resinst.h
@@ -0,0 +1,17 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define LIMPETS_BIG_MASK_PMAP_ANIM "ship.vux.graphics.limpets.large"
+#define LIMPETS_MED_MASK_PMAP_ANIM "ship.vux.graphics.limpets.medium"
+#define LIMPETS_SML_MASK_PMAP_ANIM "ship.vux.graphics.limpets.small"
+#define SLIME_MASK_PMAP_ANIM "ship.vux.graphics.slime"
+#define VUX_BIG_MASK_PMAP_ANIM "ship.vux.graphics.intruder.large"
+#define VUX_CAPTAIN_MASK_PMAP_ANIM "ship.vux.graphics.captain"
+#define VUX_ICON_MASK_PMAP_ANIM "ship.vux.icons"
+#define VUX_MED_MASK_PMAP_ANIM "ship.vux.graphics.intruder.medium"
+#define VUX_MICON_MASK_PMAP_ANIM "ship.vux.meleeicons"
+#define VUX_RACE_STRINGS "ship.vux.text"
+#define VUX_SHIP_SOUNDS "ship.vux.sounds"
+#define VUX_SML_MASK_PMAP_ANIM "ship.vux.graphics.intruder.small"
+#define VUX_VICTORY_SONG "ship.vux.ditty"
diff --git a/src/uqm/ships/vux/vux.c b/src/uqm/ships/vux/vux.c
new file mode 100644
index 0000000..83e6c47
--- /dev/null
+++ b/src/uqm/ships/vux/vux.c
@@ -0,0 +1,398 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "vux.h"
+#include "resinst.h"
+
+#include "uqm/globdata.h"
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 40
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 8
+#define MAX_THRUST /* DISPLAY_TO_WORLD (5) */ 21
+#define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 7
+#define THRUST_WAIT 4
+#define TURN_WAIT 6
+#define SHIP_MASS 6
+
+// Laser
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 0
+#define VUX_OFFSET 12
+#define LASER_BASE 150
+#define LASER_RANGE DISPLAY_TO_WORLD (LASER_BASE + VUX_OFFSET)
+
+// Limpet
+#define SPECIAL_ENERGY_COST 2
+#define SPECIAL_WAIT 7
+#define LIMPET_SPEED 25
+#define LIMPET_OFFSET 8
+#define LIMPET_LIFE 80
+#define LIMPET_HITS 1
+#define LIMPET_DAMAGE 0
+#define MIN_THRUST_INCREMENT DISPLAY_TO_WORLD (1)
+
+// Aggressive Entry
+#define WARP_OFFSET 46
+ /* How far outside of the laser range can the ship warp in. */
+#define MAXX_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1)
+#define MAXY_ENTRY_DIST DISPLAY_TO_WORLD ((LASER_BASE + VUX_OFFSET + WARP_OFFSET) << 1)
+ /* Originally, the warp distance was:
+ * DISPLAY_TO_WORLD (SPACE_HEIGHT << 1)
+ * where SPACE_HEIGHT = SCREEN_HEIGHT - (SAFE_Y * 2)
+ * But in reality this should be relative to the laser-range. */
+
+static RACE_DESC vux_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SEEKING_SPECIAL | IMMEDIATE_WEAPON,
+ 12, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ VUX_RACE_STRINGS,
+ VUX_ICON_MASK_PMAP_ANIM,
+ VUX_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 900 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 4412, 1558,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ VUX_BIG_MASK_PMAP_ANIM,
+ VUX_MED_MASK_PMAP_ANIM,
+ VUX_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SLIME_MASK_PMAP_ANIM,
+ NULL_RESOURCE,
+ NULL_RESOURCE,
+ },
+ {
+ LIMPETS_BIG_MASK_PMAP_ANIM,
+ LIMPETS_MED_MASK_PMAP_ANIM,
+ LIMPETS_SML_MASK_PMAP_ANIM,
+ },
+ {
+ VUX_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ VUX_VICTORY_SONG,
+ VUX_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ CLOSE_RANGE_WEAPON,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+
+static void
+limpet_preprocess (ELEMENT *ElementPtr)
+{
+ COUNT facing, orig_facing;
+ SIZE delta_facing;
+
+ facing = orig_facing = NORMALIZE_FACING (ANGLE_TO_FACING (
+ GetVelocityTravelAngle (&ElementPtr->velocity)
+ ));
+ if ((delta_facing = TrackShip (ElementPtr, &facing)) > 0)
+ {
+ facing = orig_facing + delta_facing;
+ SetVelocityVector (&ElementPtr->velocity, LIMPET_SPEED, facing);
+ }
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->next.image.frame);
+
+ ElementPtr->state_flags |= CHANGING;
+}
+
+static void
+limpet_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ if (ElementPtr1->state_flags & PLAYER_SHIP)
+ {
+ STAMP s;
+ STARSHIP *StarShipPtr;
+ RACE_DESC *RDPtr;
+
+ GetElementStarShip (ElementPtr1, &StarShipPtr);
+ RDPtr = StarShipPtr->RaceDescPtr;
+
+ if (++RDPtr->characteristics.turn_wait == 0)
+ --RDPtr->characteristics.turn_wait;
+ if (++RDPtr->characteristics.thrust_wait == 0)
+ --RDPtr->characteristics.thrust_wait;
+ if (RDPtr->characteristics.thrust_increment <= MIN_THRUST_INCREMENT)
+ {
+ RDPtr->characteristics.max_thrust =
+ RDPtr->characteristics.thrust_increment << 1;
+ }
+ else
+ {
+ COUNT num_thrusts;
+
+ num_thrusts = RDPtr->characteristics.max_thrust /
+ RDPtr->characteristics.thrust_increment;
+ --RDPtr->characteristics.thrust_increment;
+ RDPtr->characteristics.max_thrust =
+ RDPtr->characteristics.thrust_increment * num_thrusts;
+ }
+ RDPtr->cyborg_control.ManeuverabilityIndex = 0;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ ProcessSound (SetAbsSoundIndex (
+ /* LIMPET_AFFIXES */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), ElementPtr1);
+ s.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_data.weapon[0], (COUNT)TFB_Random ()
+ );
+ ModifySilhouette (ElementPtr1, &s, MODIFY_IMAGE);
+ }
+
+ ElementPtr0->hit_points = 0;
+ ElementPtr0->life_span = 0;
+ ElementPtr0->state_flags |= COLLISION | DISAPPEARING;
+
+ (void) pPt0; /* Satisfying compiler (unused parameter) */
+ (void) pPt1; /* Satisfying compiler (unused parameter) */
+}
+
+static void
+spawn_limpets (ELEMENT *ElementPtr)
+{
+ HELEMENT Limpet;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ MissileBlock.face = StarShipPtr->ShipFacing + HALF_CIRCLE;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ElementPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = LIMPET_OFFSET;
+ MissileBlock.speed = LIMPET_SPEED;
+ MissileBlock.hit_points = LIMPET_HITS;
+ MissileBlock.damage = LIMPET_DAMAGE;
+ MissileBlock.life = LIMPET_LIFE;
+ MissileBlock.preprocess_func = limpet_preprocess;
+ MissileBlock.blast_offs = 0;
+
+ MissileBlock.cx = ElementPtr->next.location.x;
+ MissileBlock.cy = ElementPtr->next.location.y;
+ Limpet = initialize_missile (&MissileBlock);
+ if (Limpet)
+ {
+ ELEMENT *LimpetPtr;
+
+ LockElement (Limpet, &LimpetPtr);
+ LimpetPtr->collision_func = limpet_collision;
+ SetElementStarShip (LimpetPtr, StarShipPtr);
+ UnlockElement (Limpet);
+
+ PutElement (Limpet);
+ }
+}
+
+static COUNT
+initialize_horrific_laser (ELEMENT *ShipPtr, HELEMENT LaserArray[])
+{
+ STARSHIP *StarShipPtr;
+ LASER_BLOCK LaserBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ LaserBlock.face = StarShipPtr->ShipFacing;
+ LaserBlock.cx = ShipPtr->next.location.x;
+ LaserBlock.cy = ShipPtr->next.location.y;
+ LaserBlock.ex = COSINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.ey = SINE (FACING_TO_ANGLE (LaserBlock.face), LASER_RANGE);
+ LaserBlock.sender = ShipPtr->playerNr;
+ LaserBlock.flags = IGNORE_SIMILAR;
+ LaserBlock.pixoffs = VUX_OFFSET;
+ LaserBlock.color = BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x0A), 0x0A);
+ LaserArray[0] = initialize_laser (&LaserBlock);
+
+ return (1);
+}
+
+static void
+vux_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ EVALUATE_DESC *lpEvalDesc;
+ STARSHIP *StarShipPtr;
+
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ lpEvalDesc->MoveState = PURSUE;
+ if (ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr != 0
+ && ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState == ENTICE)
+ {
+ if ((ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr->state_flags
+ & FINITE_LIFE)
+ && !(ObjectsOfConcern[ENEMY_WEAPON_INDEX].ObjectPtr->state_flags
+ & CREW_OBJECT))
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState = AVOID;
+ else
+ ObjectsOfConcern[ENEMY_WEAPON_INDEX].MoveState = PURSUE;
+ }
+
+ ship_intelligence (ShipPtr,
+ ObjectsOfConcern, ConcernCounter);
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter == 0
+ && lpEvalDesc->ObjectPtr != 0
+ && lpEvalDesc->which_turn <= 12
+ && (StarShipPtr->ship_input_state & (LEFT | RIGHT))
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >=
+ (BYTE)(StarShipPtr->RaceDescPtr->ship_info.max_energy >> 1))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ else
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+}
+
+static void
+vux_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* LAUNCH_LIMPET */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ spawn_limpets (ElementPtr);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+}
+
+static void
+vux_preprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ COUNT facing;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ facing = StarShipPtr->ShipFacing;
+ if (LOBYTE (GLOBAL (CurrentActivity)) != IN_ENCOUNTER
+ && TrackShip (ElementPtr, &facing) >= 0)
+ {
+ ELEMENT *OtherShipPtr;
+
+ LockElement (ElementPtr->hTarget, &OtherShipPtr);
+
+ do
+ {
+ SIZE dx, dy;
+
+ ElementPtr->current.location.x =
+ (OtherShipPtr->current.location.x -
+ (MAXX_ENTRY_DIST >> 1)) +
+ ((COUNT)TFB_Random () % MAXX_ENTRY_DIST);
+ ElementPtr->current.location.y =
+ (OtherShipPtr->current.location.y -
+ (MAXY_ENTRY_DIST >> 1)) +
+ ((COUNT)TFB_Random () % MAXY_ENTRY_DIST);
+ dx = OtherShipPtr->current.location.x -
+ ElementPtr->current.location.x;
+ dy = OtherShipPtr->current.location.y -
+ ElementPtr->current.location.y;
+ facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (dx, dy))
+ );
+ ElementPtr->current.image.frame =
+ SetAbsFrameIndex (ElementPtr->current.image.frame,
+ facing);
+
+ ElementPtr->current.location.x =
+ WRAP_X (DISPLAY_ALIGN (ElementPtr->current.location.x));
+ ElementPtr->current.location.y =
+ WRAP_Y (DISPLAY_ALIGN (ElementPtr->current.location.y));
+ } while (CalculateGravity (ElementPtr)
+ || TimeSpaceMatterConflict (ElementPtr));
+
+ UnlockElement (ElementPtr->hTarget);
+ ElementPtr->hTarget = 0;
+
+ ElementPtr->next = ElementPtr->current;
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectFrame (ElementPtr);
+
+ StarShipPtr->ShipFacing = facing;
+ }
+
+ StarShipPtr->RaceDescPtr->preprocess_func = 0;
+ }
+}
+
+RACE_DESC*
+init_vux (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ vux_desc.preprocess_func = vux_preprocess;
+ vux_desc.postprocess_func = vux_postprocess;
+ vux_desc.init_weapon_func = initialize_horrific_laser;
+ vux_desc.cyborg_control.intelligence_func = vux_intelligence;
+
+ RaceDescPtr = &vux_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/vux/vux.h b/src/uqm/ships/vux/vux.h
new file mode 100644
index 0000000..3fa2f3f
--- /dev/null
+++ b/src/uqm/ships/vux/vux.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef VUX_H
+#define VUX_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_vux (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* VUX_H */
+
diff --git a/src/uqm/ships/yehat/Makeinfo b/src/uqm/ships/yehat/Makeinfo
new file mode 100644
index 0000000..73d70c3
--- /dev/null
+++ b/src/uqm/ships/yehat/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="yehat.c"
+uqm_HFILES="icode.h resinst.h yehat.h"
diff --git a/src/uqm/ships/yehat/icode.h b/src/uqm/ships/yehat/icode.h
new file mode 100644
index 0000000..81cac0e
--- /dev/null
+++ b/src/uqm/ships/yehat/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define YEHAT_CODE "ship.yehat.code"
diff --git a/src/uqm/ships/yehat/resinst.h b/src/uqm/ships/yehat/resinst.h
new file mode 100644
index 0000000..ad325a5
--- /dev/null
+++ b/src/uqm/ships/yehat/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SHIELD_BIG_MASK_ANIM "ship.yehat.graphics.shield.large"
+#define SHIELD_MED_MASK_ANIM "ship.yehat.graphics.shield.medium"
+#define SHIELD_SML_MASK_ANIM "ship.yehat.graphics.shield.small"
+#define YEHAT_BIG_MASK_PMAP_ANIM "ship.yehat.graphics.terminator.large"
+#define YEHAT_CANNON_BIG_MASK_PMAP_ANIM "ship.yehat.graphics.missile.large"
+#define YEHAT_CANNON_MED_MASK_PMAP_ANIM "ship.yehat.graphics.missile.medium"
+#define YEHAT_CANNON_SML_MASK_PMAP_ANIM "ship.yehat.graphics.missile.small"
+#define YEHAT_CAPTAIN_MASK_PMAP_ANIM "ship.yehat.graphics.captain"
+#define YEHAT_ICON_MASK_PMAP_ANIM "ship.yehat.icons"
+#define YEHAT_MED_MASK_PMAP_ANIM "ship.yehat.graphics.terminator.medium"
+#define YEHAT_MICON_MASK_PMAP_ANIM "ship.yehat.meleeicons"
+#define YEHAT_RACE_STRINGS "ship.yehat.text"
+#define YEHAT_SHIP_SOUNDS "ship.yehat.sounds"
+#define YEHAT_SML_MASK_PMAP_ANIM "ship.yehat.graphics.terminator.small"
+#define YEHAT_VICTORY_SONG "ship.yehat.ditty"
diff --git a/src/uqm/ships/yehat/yehat.c b/src/uqm/ships/yehat/yehat.c
new file mode 100644
index 0000000..f3d0fb8
--- /dev/null
+++ b/src/uqm/ships/yehat/yehat.c
@@ -0,0 +1,369 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "yehat.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 20
+#define MAX_ENERGY 10
+#define ENERGY_REGENERATION 2
+#define ENERGY_WAIT 6
+#define MAX_THRUST 30
+#define THRUST_INCREMENT 6
+#define THRUST_WAIT 2
+#define TURN_WAIT 2
+#define SHIP_MASS 3
+
+// Twin Pulse Cannon
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 0
+#define YEHAT_OFFSET 16
+#define LAUNCH_OFFS DISPLAY_TO_WORLD (8)
+#define MISSILE_SPEED DISPLAY_TO_WORLD (20)
+#define MISSILE_LIFE 10
+#define MISSILE_HITS 1
+#define MISSILE_DAMAGE 1
+#define MISSILE_OFFSET 1
+
+// Force Shield
+#define SPECIAL_ENERGY_COST 3
+#define SPECIAL_WAIT 2
+#define SHIELD_LIFE 10
+
+static RACE_DESC yehat_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE | SHIELD_DEFENSE,
+ 23, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ YEHAT_RACE_STRINGS,
+ YEHAT_ICON_MASK_PMAP_ANIM,
+ YEHAT_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 750 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 4970, 40,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ YEHAT_BIG_MASK_PMAP_ANIM,
+ YEHAT_MED_MASK_PMAP_ANIM,
+ YEHAT_SML_MASK_PMAP_ANIM,
+ },
+ {
+ YEHAT_CANNON_BIG_MASK_PMAP_ANIM,
+ YEHAT_CANNON_MED_MASK_PMAP_ANIM,
+ YEHAT_CANNON_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SHIELD_BIG_MASK_ANIM,
+ SHIELD_MED_MASK_ANIM,
+ SHIELD_SML_MASK_ANIM,
+ },
+ {
+ YEHAT_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ YEHAT_VICTORY_SONG,
+ YEHAT_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_SPEED * MISSILE_LIFE / 3,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static COUNT
+initialize_standard_missiles (ELEMENT *ShipPtr, HELEMENT MissileArray[])
+{
+ SIZE offs_x, offs_y;
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = YEHAT_OFFSET;
+ MissileBlock.speed = MISSILE_SPEED;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = NULL;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+
+ offs_x = -SINE (FACING_TO_ANGLE (MissileBlock.face), LAUNCH_OFFS);
+ offs_y = COSINE (FACING_TO_ANGLE (MissileBlock.face), LAUNCH_OFFS);
+
+ MissileBlock.cx = ShipPtr->next.location.x + offs_x;
+ MissileBlock.cy = ShipPtr->next.location.y + offs_y;
+ MissileArray[0] = initialize_missile (&MissileBlock);
+
+ MissileBlock.cx = ShipPtr->next.location.x - offs_x;
+ MissileBlock.cy = ShipPtr->next.location.y - offs_y;
+ MissileArray[1] = initialize_missile (&MissileBlock);
+
+ return (2);
+}
+
+static void
+yehat_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ SIZE ShieldStatus;
+ STARSHIP *StarShipPtr;
+ EVALUATE_DESC *lpEvalDesc;
+
+ ShieldStatus = -1;
+ lpEvalDesc = &ObjectsOfConcern[ENEMY_WEAPON_INDEX];
+ if (lpEvalDesc->ObjectPtr && lpEvalDesc->MoveState == ENTICE)
+ {
+ ShieldStatus = 0;
+ if (!(lpEvalDesc->ObjectPtr->state_flags & (FINITE_LIFE | CREW_OBJECT)))
+ lpEvalDesc->MoveState = PURSUE;
+ else if (lpEvalDesc->ObjectPtr->mass_points
+ || (lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
+ {
+ if (!(lpEvalDesc->ObjectPtr->state_flags & FINITE_LIFE))
+ lpEvalDesc->which_turn <<= 1;
+ else
+ {
+ if ((lpEvalDesc->which_turn >>= 1) == 0)
+ lpEvalDesc->which_turn = 1;
+
+ if (lpEvalDesc->ObjectPtr->mass_points)
+ lpEvalDesc->ObjectPtr = 0;
+ else
+ lpEvalDesc->MoveState = PURSUE;
+ }
+ ShieldStatus = 1;
+ }
+ }
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->special_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+ if (ShieldStatus)
+ {
+ if (ShipPtr->life_span <= NORMAL_LIFE + 1
+ && (ShieldStatus > 0 || lpEvalDesc->ObjectPtr)
+ && lpEvalDesc->which_turn <= 2
+ && (ShieldStatus > 0
+ || (lpEvalDesc->ObjectPtr->state_flags
+ & PLAYER_SHIP) /* means IMMEDIATE WEAPON */
+ || PlotIntercept (lpEvalDesc->ObjectPtr,
+ ShipPtr, 2, 0))
+ && (TFB_Random () & 3))
+ StarShipPtr->ship_input_state |= SPECIAL;
+
+ if (lpEvalDesc->ObjectPtr
+ && !(lpEvalDesc->ObjectPtr->state_flags & CREW_OBJECT))
+ lpEvalDesc->ObjectPtr = 0;
+ }
+ }
+
+ if ((lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX])->ObjectPtr)
+ {
+ STARSHIP *EnemyStarShipPtr;
+
+ GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
+ if (!(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags
+ & IMMEDIATE_WEAPON))
+ lpEvalDesc->MoveState = PURSUE;
+ }
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+/*
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level <= SPECIAL_ENERGY_COST)
+ StarShipPtr->ship_input_state &= ~WEAPON;
+*/
+}
+
+static void
+yehat_postprocess (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & NONSOLID))
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ /* take care of shield effect */
+ if (StarShipPtr->special_counter > 0)
+ {
+ if (ElementPtr->life_span == NORMAL_LIFE)
+ StarShipPtr->special_counter = 0;
+ else
+ {
+#ifdef OLD
+ SetPrimColor (
+ &(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F)
+ );
+ SetPrimType (
+ &(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ STAMPFILL_PRIM
+ );
+#endif /* OLD */
+
+ ProcessSound (SetAbsSoundIndex (
+ /* YEHAT_SHIELD_ON */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+ DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST);
+ }
+ }
+
+#ifdef OLD
+ if (ElementPtr->life_span > NORMAL_LIFE)
+ {
+ HELEMENT hShipElement;
+
+ if (hShipElement = AllocElement ())
+ {
+ ELEMENT *ShipElementPtr;
+
+ InsertElement (hShipElement, GetSuccElement (ElementPtr));
+ LockElement (hShipElement, &ShipElementPtr);
+ ShipElementPtr->playerNr = ElementPtr->playerNr;
+ ShipElementPtr->state_flags =
+ /* in place of APPEARING */
+ (CHANGING | PRE_PROCESS | POST_PROCESS)
+ | FINITE_LIFE | NONSOLID;
+ SetPrimType (
+ &(GLOBAL (DisplayArray))[ShipElementPtr->PrimIndex],
+ STAMP_PRIM
+ );
+
+ ShipElementPtr->life_span = 0; /* because preprocessing
+ * will not be done
+ */
+ ShipElementPtr->current.location = ElementPtr->next.location;
+ ShipElementPtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.ship;
+ ShipElementPtr->current.image.frame =
+ SetAbsFrameIndex (StarShipPtr->RaceDescPtr->ship_data.ship[0],
+ StarShipPtr->ShipFacing);
+ ShipElementPtr->next = ShipElementPtr->current;
+ ShipElementPtr->preprocess_func =
+ ShipElementPtr->postprocess_func =
+ ShipElementPtr->death_func = NULL;
+ ZeroVelocityComponents (&ShipElementPtr->velocity);
+
+ UnlockElement (hShipElement);
+ }
+ }
+#endif /* OLD */
+ }
+}
+
+static void
+yehat_preprocess (ELEMENT *ElementPtr)
+{
+ if (!(ElementPtr->state_flags & APPEARING))
+ {
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((ElementPtr->life_span > NORMAL_LIFE
+ /* take care of shield effect */
+ && --ElementPtr->life_span == NORMAL_LIFE)
+ || (ElementPtr->life_span == NORMAL_LIFE
+ && ElementPtr->next.image.farray
+ == StarShipPtr->RaceDescPtr->ship_data.special))
+ {
+#ifdef NEVER
+ SetPrimType (
+ &(GLOBAL (DisplayArray))[ElementPtr->PrimIndex],
+ STAMP_PRIM
+ );
+#endif /* NEVER */
+
+ ElementPtr->next.image.farray = StarShipPtr->RaceDescPtr->ship_data.ship;
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (StarShipPtr->RaceDescPtr->ship_data.ship[0],
+ ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+ }
+
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0)
+ {
+ if (StarShipPtr->RaceDescPtr->ship_info.energy_level < SPECIAL_ENERGY_COST)
+ DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST); /* so text will flash */
+ else
+ {
+ ElementPtr->life_span = SHIELD_LIFE + NORMAL_LIFE;
+
+ ElementPtr->next.image.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (StarShipPtr->RaceDescPtr->ship_data.special[0],
+ ElementPtr->next.image.frame);
+ ElementPtr->state_flags |= CHANGING;
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+ }
+ }
+}
+
+RACE_DESC*
+init_yehat (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ yehat_desc.preprocess_func = yehat_preprocess;
+ yehat_desc.postprocess_func = yehat_postprocess;
+ yehat_desc.init_weapon_func = initialize_standard_missiles;
+ yehat_desc.cyborg_control.intelligence_func = yehat_intelligence;
+
+ RaceDescPtr = &yehat_desc;
+
+ return (RaceDescPtr);
+}
diff --git a/src/uqm/ships/yehat/yehat.h b/src/uqm/ships/yehat/yehat.h
new file mode 100644
index 0000000..8b3dd5a
--- /dev/null
+++ b/src/uqm/ships/yehat/yehat.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef YEHAT_H
+#define YEHAT_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_yehat (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* YEHAT_H */
+
diff --git a/src/uqm/ships/zoqfot/Makeinfo b/src/uqm/ships/zoqfot/Makeinfo
new file mode 100644
index 0000000..e795e78
--- /dev/null
+++ b/src/uqm/ships/zoqfot/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="zoqfot.c"
+uqm_HFILES="icode.h resinst.h zoqfot.h"
diff --git a/src/uqm/ships/zoqfot/icode.h b/src/uqm/ships/zoqfot/icode.h
new file mode 100644
index 0000000..0fe635f
--- /dev/null
+++ b/src/uqm/ships/zoqfot/icode.h
@@ -0,0 +1,5 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define ZOQFOTPIK_CODE "ship.zoqfotpik.code"
diff --git a/src/uqm/ships/zoqfot/resinst.h b/src/uqm/ships/zoqfot/resinst.h
new file mode 100644
index 0000000..5939142
--- /dev/null
+++ b/src/uqm/ships/zoqfot/resinst.h
@@ -0,0 +1,19 @@
+/* This file was auto-generated by the gen_resfiles utility and
+ should not be edited directly. Modify the master resource list
+ instead and regenerate. */
+
+#define SPIT_BIG_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.spit.large"
+#define SPIT_MED_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.spit.medium"
+#define SPIT_SML_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.spit.small"
+#define STINGER_BIG_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.proboscis.large"
+#define STINGER_MED_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.proboscis.medium"
+#define STINGER_SML_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.proboscis.small"
+#define ZOQFOTPIK_BIG_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.stinger.large"
+#define ZOQFOTPIK_CAPTAIN_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.captain"
+#define ZOQFOTPIK_ICON_MASK_PMAP_ANIM "ship.zoqfotpik.icons"
+#define ZOQFOTPIK_MED_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.stinger.medium"
+#define ZOQFOTPIK_MICON_MASK_PMAP_ANIM "ship.zoqfotpik.meleeicons"
+#define ZOQFOTPIK_RACE_STRINGS "ship.zoqfotpik.text"
+#define ZOQFOTPIK_SHIP_SOUNDS "ship.zoqfotpik.sounds"
+#define ZOQFOTPIK_SML_MASK_PMAP_ANIM "ship.zoqfotpik.graphics.stinger.small"
+#define ZOQFOTPIK_VICTORY_SONG "ship.zoqfotpik.ditty"
diff --git a/src/uqm/ships/zoqfot/zoqfot.c b/src/uqm/ships/zoqfot/zoqfot.c
new file mode 100644
index 0000000..15a6024
--- /dev/null
+++ b/src/uqm/ships/zoqfot/zoqfot.c
@@ -0,0 +1,377 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "../ship.h"
+#include "zoqfot.h"
+#include "resinst.h"
+
+#include "libs/mathlib.h"
+
+// Core characteristics
+#define MAX_CREW 10
+#define MAX_ENERGY 10
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 4
+#define MAX_THRUST 40
+#define THRUST_INCREMENT 10
+#define THRUST_WAIT 0
+#define TURN_WAIT 1
+#define SHIP_MASS 5
+
+// Main weapon
+#define WEAPON_ENERGY_COST 1
+#define WEAPON_WAIT 0
+#define ZOQFOTPIK_OFFSET 13
+#define MISSILE_OFFSET 0
+#define MISSILE_SPEED DISPLAY_TO_WORLD (10)
+ /* Used by the cyborg only. */
+#define MISSILE_LIFE 10
+#define MISSILE_RANGE (MISSILE_SPEED * MISSILE_LIFE)
+#define MISSILE_DAMAGE 1
+#define MISSILE_HITS 1
+#define SPIT_WAIT 2
+ /* Controls the main weapon color change animation's speed.
+ * The animation advances one frame every SPIT_WAIT frames. */
+
+// Tongue
+#define SPECIAL_ENERGY_COST (MAX_ENERGY * 3 / 4)
+#define SPECIAL_WAIT 6
+#define TONGUE_SPEED 0
+#define TONGUE_HITS 1
+#define TONGUE_DAMAGE 12
+#define TONGUE_OFFSET 4
+
+static RACE_DESC zoqfotpik_desc =
+{
+ { /* SHIP_INFO */
+ FIRES_FORE,
+ 6, /* Super Melee cost */
+ MAX_CREW, MAX_CREW,
+ MAX_ENERGY, MAX_ENERGY,
+ ZOQFOTPIK_RACE_STRINGS,
+ ZOQFOTPIK_ICON_MASK_PMAP_ANIM,
+ ZOQFOTPIK_MICON_MASK_PMAP_ANIM,
+ NULL, NULL, NULL
+ },
+ { /* FLEET_STUFF */
+ 320 / SPHERE_RADIUS_INCREMENT * 2, /* Initial SoI radius */
+ { /* Known location (center of SoI) */
+ 3761, 5333,
+ },
+ },
+ {
+ MAX_THRUST,
+ THRUST_INCREMENT,
+ ENERGY_REGENERATION,
+ WEAPON_ENERGY_COST,
+ SPECIAL_ENERGY_COST,
+ ENERGY_WAIT,
+ TURN_WAIT,
+ THRUST_WAIT,
+ WEAPON_WAIT,
+ SPECIAL_WAIT,
+ SHIP_MASS,
+ },
+ {
+ {
+ ZOQFOTPIK_BIG_MASK_PMAP_ANIM,
+ ZOQFOTPIK_MED_MASK_PMAP_ANIM,
+ ZOQFOTPIK_SML_MASK_PMAP_ANIM,
+ },
+ {
+ SPIT_BIG_MASK_PMAP_ANIM,
+ SPIT_MED_MASK_PMAP_ANIM,
+ SPIT_SML_MASK_PMAP_ANIM,
+ },
+ {
+ STINGER_BIG_MASK_PMAP_ANIM,
+ STINGER_MED_MASK_PMAP_ANIM,
+ STINGER_SML_MASK_PMAP_ANIM,
+ },
+ {
+ ZOQFOTPIK_CAPTAIN_MASK_PMAP_ANIM,
+ NULL, NULL, NULL, NULL, NULL
+ },
+ ZOQFOTPIK_VICTORY_SONG,
+ ZOQFOTPIK_SHIP_SOUNDS,
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ { NULL, NULL, NULL },
+ NULL, NULL
+ },
+ {
+ 0,
+ MISSILE_RANGE,
+ NULL,
+ },
+ (UNINIT_FUNC *) NULL,
+ (PREPROCESS_FUNC *) NULL,
+ (POSTPROCESS_FUNC *) NULL,
+ (INIT_WEAPON_FUNC *) NULL,
+ 0,
+ 0, /* CodeRef */
+};
+
+static void
+spit_preprocess (ELEMENT *ElementPtr)
+{
+ /* turn_wait is abused here to control the animation speed. */
+ if (ElementPtr->turn_wait > 0)
+ --ElementPtr->turn_wait;
+ else
+ {
+ COUNT index, angle, speed;
+
+ ElementPtr->next.image.frame =
+ IncFrameIndex (ElementPtr->next.image.frame);
+ angle = GetVelocityTravelAngle (&ElementPtr->velocity);
+ if ((index = GetFrameIndex (ElementPtr->next.image.frame)) == 1)
+ angle = angle + (((COUNT)TFB_Random () % 3) - 1);
+
+ speed = WORLD_TO_VELOCITY (DISPLAY_TO_WORLD (
+ GetFrameCount (ElementPtr->next.image.frame) - index) << 1);
+ SetVelocityComponents (&ElementPtr->velocity,
+ (SIZE)COSINE (angle, speed),
+ (SIZE)SINE (angle, speed));
+
+ /* turn_wait is abused here to control the animation speed. */
+ ElementPtr->turn_wait = SPIT_WAIT;
+ ElementPtr->state_flags |= CHANGING;
+ }
+}
+
+static COUNT
+initialize_spit (ELEMENT *ShipPtr, HELEMENT SpitArray[])
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK MissileBlock;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ MissileBlock.cx = ShipPtr->next.location.x;
+ MissileBlock.cy = ShipPtr->next.location.y;
+ MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
+ MissileBlock.face = StarShipPtr->ShipFacing;
+ MissileBlock.index = 0;
+ MissileBlock.sender = ShipPtr->playerNr;
+ MissileBlock.flags = IGNORE_SIMILAR;
+ MissileBlock.pixoffs = ZOQFOTPIK_OFFSET;
+ MissileBlock.speed = DISPLAY_TO_WORLD (
+ GetFrameCount (StarShipPtr->RaceDescPtr->ship_data.weapon[0])) << 1;
+ MissileBlock.hit_points = MISSILE_HITS;
+ MissileBlock.damage = MISSILE_DAMAGE;
+ MissileBlock.life = MISSILE_LIFE;
+ MissileBlock.preprocess_func = spit_preprocess;
+ MissileBlock.blast_offs = MISSILE_OFFSET;
+ SpitArray[0] = initialize_missile (&MissileBlock);
+
+ return (1);
+}
+
+static void spawn_tongue (ELEMENT *ElementPtr);
+
+static void
+tongue_postprocess (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->turn_wait)
+ spawn_tongue (ElementPtr);
+}
+
+static void
+tongue_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr0, &StarShipPtr);
+ if (StarShipPtr->special_counter ==
+ StarShipPtr->RaceDescPtr->characteristics.special_wait)
+ weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1);
+
+ StarShipPtr->special_counter -= ElementPtr0->turn_wait;
+ ElementPtr0->turn_wait = 0;
+ ElementPtr0->state_flags |= NONSOLID;
+}
+
+static void
+spawn_tongue (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+ MISSILE_BLOCK TongueBlock;
+ HELEMENT Tongue;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ TongueBlock.cx = ElementPtr->next.location.x;
+ TongueBlock.cy = ElementPtr->next.location.y;
+ TongueBlock.farray = StarShipPtr->RaceDescPtr->ship_data.special;
+ TongueBlock.face = TongueBlock.index = StarShipPtr->ShipFacing;
+ TongueBlock.sender = ElementPtr->playerNr;
+ TongueBlock.flags = IGNORE_SIMILAR;
+ TongueBlock.pixoffs = 0;
+ TongueBlock.speed = TONGUE_SPEED;
+ TongueBlock.hit_points = TONGUE_HITS;
+ TongueBlock.damage = TONGUE_DAMAGE;
+ TongueBlock.life = 1;
+ TongueBlock.preprocess_func = 0;
+ TongueBlock.blast_offs = TONGUE_OFFSET;
+ Tongue = initialize_missile (&TongueBlock);
+ if (Tongue)
+ {
+ ELEMENT *TonguePtr;
+
+ LockElement (Tongue, &TonguePtr);
+ TonguePtr->postprocess_func = tongue_postprocess;
+ TonguePtr->collision_func = tongue_collision;
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ TonguePtr->turn_wait = StarShipPtr->special_counter;
+ else
+ {
+ COUNT angle;
+ RECT r;
+ SIZE x_offs, y_offs;
+
+ TonguePtr->turn_wait = ElementPtr->turn_wait - 1;
+
+ GetFrameRect (TonguePtr->current.image.frame, &r);
+ x_offs = DISPLAY_TO_WORLD (r.extent.width >> 1);
+ y_offs = DISPLAY_TO_WORLD (r.extent.height >> 1);
+
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+ if (angle > HALF_CIRCLE && angle < FULL_CIRCLE)
+ TonguePtr->current.location.x -= x_offs;
+ else if (angle > 0 && angle < HALF_CIRCLE)
+ TonguePtr->current.location.x += x_offs;
+ if (angle < QUADRANT || angle > FULL_CIRCLE - QUADRANT)
+ TonguePtr->current.location.y -= y_offs;
+ else if (angle > QUADRANT && angle < FULL_CIRCLE - QUADRANT)
+ TonguePtr->current.location.y += y_offs;
+ }
+
+ SetElementStarShip (TonguePtr, StarShipPtr);
+ UnlockElement (Tongue);
+ PutElement (Tongue);
+ }
+}
+
+static void
+zoqfotpik_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
+ COUNT ConcernCounter)
+{
+ BOOLEAN GiveTongueJob;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ GiveTongueJob = FALSE;
+ if (StarShipPtr->special_counter == 0)
+ {
+ EVALUATE_DESC *lpEnemyEvalDesc;
+
+ StarShipPtr->ship_input_state &= ~SPECIAL;
+
+ lpEnemyEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
+ if (lpEnemyEvalDesc->ObjectPtr
+ && lpEnemyEvalDesc->which_turn <= 4
+#ifdef NEVER
+ && StarShipPtr->RaceDescPtr->ship_info.energy_level >= SPECIAL_ENERGY_COST
+#endif /* NEVER */
+ )
+ {
+ SIZE delta_x, delta_y;
+
+ GiveTongueJob = TRUE;
+
+ lpEnemyEvalDesc->MoveState = PURSUE;
+ delta_x = lpEnemyEvalDesc->ObjectPtr->next.location.x
+ - ShipPtr->next.location.x;
+ delta_y = lpEnemyEvalDesc->ObjectPtr->next.location.y
+ - ShipPtr->next.location.y;
+ if (StarShipPtr->ShipFacing == NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y))
+ ))
+ StarShipPtr->ship_input_state |= SPECIAL;
+ }
+ }
+
+ ++StarShipPtr->weapon_counter;
+ ship_intelligence (ShipPtr, ObjectsOfConcern, ConcernCounter);
+ --StarShipPtr->weapon_counter;
+
+ if (StarShipPtr->weapon_counter == 0)
+ {
+ StarShipPtr->ship_input_state &= ~WEAPON;
+ if (!GiveTongueJob)
+ {
+ ObjectsOfConcern += ConcernCounter;
+ while (ConcernCounter--)
+ {
+ --ObjectsOfConcern;
+ if (ObjectsOfConcern->ObjectPtr
+ && (ConcernCounter == ENEMY_SHIP_INDEX
+ || (ConcernCounter == ENEMY_WEAPON_INDEX
+ && ObjectsOfConcern->MoveState != AVOID
+#ifdef NEVER
+ && !(StarShipPtr->control & STANDARD_RATING)
+#endif /* NEVER */
+ ))
+ && ship_weapons (ShipPtr,
+ ObjectsOfConcern->ObjectPtr, DISPLAY_TO_WORLD (20)))
+ {
+ StarShipPtr->ship_input_state |= WEAPON;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void
+zoqfotpik_postprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if ((StarShipPtr->cur_status_flags & SPECIAL)
+ && StarShipPtr->special_counter == 0
+ && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
+ {
+ ProcessSound (SetAbsSoundIndex (
+ /* STICK_OUT_TONGUE */
+ StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
+
+ StarShipPtr->special_counter =
+ StarShipPtr->RaceDescPtr->characteristics.special_wait;
+ }
+
+ if (StarShipPtr->special_counter)
+ spawn_tongue (ElementPtr);
+}
+
+RACE_DESC*
+init_zoqfotpik (void)
+{
+ RACE_DESC *RaceDescPtr;
+
+ zoqfotpik_desc.postprocess_func = zoqfotpik_postprocess;
+ zoqfotpik_desc.init_weapon_func = initialize_spit;
+ zoqfotpik_desc.cyborg_control.intelligence_func = zoqfotpik_intelligence;
+
+ RaceDescPtr = &zoqfotpik_desc;
+
+ return (RaceDescPtr);
+}
+
diff --git a/src/uqm/ships/zoqfot/zoqfot.h b/src/uqm/ships/zoqfot/zoqfot.h
new file mode 100644
index 0000000..1bdcc85
--- /dev/null
+++ b/src/uqm/ships/zoqfot/zoqfot.h
@@ -0,0 +1,31 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ZOQFOT_H
+#define ZOQFOT_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+RACE_DESC *init_zoqfotpik (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ZOQFOT_H */
+
diff --git a/src/uqm/shipstat.c b/src/uqm/shipstat.c
new file mode 100644
index 0000000..0405426
--- /dev/null
+++ b/src/uqm/shipstat.c
@@ -0,0 +1,437 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "colors.h"
+#include "globdata.h"
+#include "options.h"
+#include "status.h"
+#include "setup.h"
+#include "libs/gfxlib.h"
+
+
+void
+DrawCrewFuelString (COORD y, SIZE state)
+{
+ STAMP Stamp;
+
+ Stamp.origin.y = y + GAUGE_YOFFS + STARCON_TEXT_HEIGHT;
+ if (state == 0)
+ {
+ Stamp.origin.x = CREW_XOFFS + (STAT_WIDTH >> 1) + 6;
+ if (optWhichMenu == OPT_PC)
+ Stamp.frame = SetAbsFrameIndex (StatusFrame, 4);
+ else
+ Stamp.frame = SetAbsFrameIndex (StatusFrame, 0);
+ DrawStamp (&Stamp);
+ }
+
+ Stamp.origin.x = ENERGY_XOFFS + (STAT_WIDTH >> 1) - 5;
+ if (optWhichMenu == OPT_PC)
+ Stamp.frame = SetAbsFrameIndex (StatusFrame, 5);
+ else
+ Stamp.frame = SetAbsFrameIndex (StatusFrame, 1);
+ if (state >= 0)
+ DrawStamp (&Stamp);
+ else
+ {
+#define LOW_FUEL_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E)
+ SetContextForeGroundColor (LOW_FUEL_COLOR);
+ DrawFilledStamp (&Stamp);
+ }
+}
+
+static void
+DrawShipNameString (UNICODE *pStr, COUNT CharCount, COORD y)
+{
+ TEXT Text;
+ FONT OldFont;
+
+ OldFont = SetContextFont (StarConFont);
+
+ Text.pStr = pStr;
+ Text.CharCount = CharCount;
+ Text.align = ALIGN_CENTER;
+
+ Text.baseline.y = STARCON_TEXT_HEIGHT + 3 + y;
+ Text.baseline.x = STATUS_WIDTH >> 1;
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19));
+ font_DrawText (&Text);
+ --Text.baseline.y;
+ SetContextForeGroundColor (BLACK_COLOR);
+ font_DrawText (&Text);
+
+ SetContextFont (OldFont);
+}
+
+void
+ClearShipStatus (COORD y)
+{
+ RECT r;
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ r.corner.x = 2;
+ r.corner.y = 3 + y;
+ r.extent.width = STATUS_WIDTH - 4;
+ r.extent.height = SHIP_INFO_HEIGHT - 3;
+ DrawFilledRectangle (&r);
+}
+
+void
+OutlineShipStatus (COORD y)
+{
+ RECT r;
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F));
+ r.corner.x = 0;
+ r.corner.y = 1 + y;
+ r.extent.width = STATUS_WIDTH;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ ++r.corner.y;
+ --r.extent.width;
+ DrawFilledRectangle (&r);
+ r.extent.width = 1;
+ r.extent.height = SHIP_INFO_HEIGHT - 2;
+ DrawFilledRectangle (&r);
+ ++r.corner.x;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19));
+ r.corner.x = STATUS_WIDTH - 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = STATUS_WIDTH - 2;
+ ++r.corner.y;
+ --r.extent.height;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.x = 0;
+ r.corner.y = y;
+ r.extent.width = STATUS_WIDTH;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+}
+
+void
+InitShipStatus (SHIP_INFO *SIPtr, STARSHIP *StarShipPtr, RECT *pClipRect)
+{
+ RECT r;
+ COORD y = 0; // default, for Melee menu
+ STAMP Stamp;
+ CONTEXT OldContext;
+ RECT oldClipRect;
+ POINT oldOrigin = {0, 0};
+
+ if (StarShipPtr) // set during battle
+ {
+ assert (StarShipPtr->playerNr >= 0);
+ y = status_y_offsets[StarShipPtr->playerNr];
+ }
+
+ OldContext = SetContext (StatusContext);
+ if (pClipRect)
+ {
+ GetContextClipRect (&oldClipRect);
+ r = oldClipRect;
+ r.corner.x += pClipRect->corner.x;
+ r.corner.y += (pClipRect->corner.y & ~1);
+ r.extent = pClipRect->extent;
+ r.extent.height += pClipRect->corner.y & 1;
+ SetContextClipRect (&r);
+ // Offset the origin so that we draw into the cliprect
+ oldOrigin = SetContextOrigin (MAKE_POINT (-pClipRect->corner.x,
+ -(pClipRect->corner.y & ~1)));
+ }
+
+ BatchGraphics ();
+
+ OutlineShipStatus (y);
+ ClearShipStatus (y);
+
+ Stamp.origin.x = (STATUS_WIDTH >> 1);
+ Stamp.origin.y = 31 + y;
+ Stamp.frame = IncFrameIndex (SIPtr->icons);
+ DrawStamp (&Stamp);
+
+ {
+ SIZE crew_height, energy_height;
+
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+ crew_height = ((MIN(SIPtr->max_crew, MAX_CREW_SIZE) + 1) & ~1) + 1;
+#undef MIN
+ energy_height = (((SIPtr->max_energy + 1) >> 1) << 1) + 1;
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F));
+ r.corner.x = CREW_XOFFS - 1;
+ r.corner.y = GAUGE_YOFFS + 1 + y;
+ r.extent.width = STAT_WIDTH + 2;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = ENERGY_XOFFS - 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = ENERGY_XOFFS + STAT_WIDTH;
+ r.corner.y -= energy_height;
+ r.extent.width = 1;
+ r.extent.height = energy_height;
+ DrawFilledRectangle (&r);
+ r.corner.x = CREW_XOFFS + STAT_WIDTH;
+ r.corner.y = (GAUGE_YOFFS + 1 + y) - crew_height;
+ r.extent.width = 1;
+ r.extent.height = crew_height;
+ DrawFilledRectangle (&r);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19));
+ r.corner.x = CREW_XOFFS - 1;
+ r.corner.y = GAUGE_YOFFS - crew_height + y;
+ r.extent.width = STAT_WIDTH + 2;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = ENERGY_XOFFS - 1;
+ r.corner.y = GAUGE_YOFFS - energy_height + y;
+ DrawFilledRectangle (&r);
+ r.extent.width = 1;
+ r.extent.height = energy_height + 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = CREW_XOFFS - 1;
+ r.corner.y = GAUGE_YOFFS - crew_height + y;
+ r.extent.height = crew_height + 1;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (BLACK_COLOR);
+
+ r.extent.width = STAT_WIDTH;
+ r.corner.x = CREW_XOFFS;
+ r.extent.height = crew_height;
+ r.corner.y = y - r.extent.height + GAUGE_YOFFS + 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = ENERGY_XOFFS;
+ r.extent.height = energy_height;
+ r.corner.y = y - r.extent.height + GAUGE_YOFFS + 1;
+ DrawFilledRectangle (&r);
+ }
+
+ if (!StarShipPtr || StarShipPtr->captains_name_index)
+ { // Any regular ship. SIS and Sa-Matra are separate.
+ // This includes Melee menu.
+ STRING locString;
+
+ DrawCrewFuelString (y, 0);
+
+ locString = SetAbsStringTableIndex (SIPtr->race_strings, 1);
+ DrawShipNameString (
+ (UNICODE *)GetStringAddress (locString),
+ GetStringLength (locString), y);
+
+ {
+ UNICODE buf[30];
+ TEXT Text;
+ FONT OldFont;
+
+ OldFont = SetContextFont (TinyFont);
+
+ if (!StarShipPtr)
+ { // In Melee menu
+ sprintf (buf, "%d", SIPtr->ship_cost);
+ Text.pStr = buf;
+ Text.CharCount = (COUNT)~0;
+ }
+ else
+ {
+ locString = SetAbsStringTableIndex (SIPtr->race_strings,
+ StarShipPtr->captains_name_index);
+ Text.pStr = (UNICODE *)GetStringAddress (locString);
+ Text.CharCount = GetStringLength (locString);
+ }
+ Text.align = ALIGN_CENTER;
+
+ Text.baseline.x = STATUS_WIDTH >> 1;
+ Text.baseline.y = y + GAUGE_YOFFS + 3;
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ font_DrawText (&Text);
+
+ SetContextFont (OldFont);
+ }
+ }
+ else if (StarShipPtr->playerNr == RPG_PLAYER_NUM)
+ { // This is SIS
+ DrawCrewFuelString (y, 0);
+ DrawShipNameString (GLOBAL_SIS (ShipName), (COUNT)~0, y);
+ }
+
+ {
+ SIZE crew_delta, energy_delta;
+
+ crew_delta = SIPtr->crew_level;
+ energy_delta = SIPtr->energy_level;
+ // DeltaStatistics() below will add specified values to these
+ SIPtr->crew_level = 0;
+ SIPtr->energy_level = 0;
+ DeltaStatistics (SIPtr, y, crew_delta, energy_delta);
+ }
+
+ UnbatchGraphics ();
+
+ if (pClipRect)
+ {
+ SetContextOrigin (oldOrigin);
+ SetContextClipRect (&oldClipRect);
+ }
+
+ SetContext (OldContext);
+}
+
+// Pre: -crew_delta <= ShipInfoPtr->crew_level
+// crew_delta <= ShipInfoPtr->max_crew - ShipInfoPtr->crew_level
+void
+DeltaStatistics (SHIP_INFO *ShipInfoPtr, COORD y_offs,
+ SIZE crew_delta, SIZE energy_delta)
+{
+ COORD x, y;
+ RECT r;
+
+ if (crew_delta == 0 && energy_delta == 0)
+ return;
+
+ x = 0;
+ y = GAUGE_YOFFS + y_offs;
+
+ r.extent.width = UNIT_WIDTH;
+ r.extent.height = UNIT_HEIGHT;
+
+ if (crew_delta != 0)
+ {
+ COUNT oldNumBlocks, newNumBlocks, blockI;
+ COUNT newCrewLevel;
+
+#define MIN(a, b) (((a) <= (b)) ? (a) : (b))
+ oldNumBlocks = MIN(ShipInfoPtr->crew_level, MAX_CREW_SIZE);
+ newCrewLevel = ShipInfoPtr->crew_level + crew_delta;
+ newNumBlocks = MIN(newCrewLevel, MAX_CREW_SIZE);
+#undef MIN
+
+ if (crew_delta > 0)
+ {
+ r.corner.y = (y + 1) -
+ (((oldNumBlocks + 1) >> 1) * (UNIT_HEIGHT + 1));
+#define CREW_UNIT_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02)
+#define ROBOT_UNIT_COLOR BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08)
+ SetContextForeGroundColor (
+ (ShipInfoPtr->ship_flags & CREW_IMMUNE) ?
+ ROBOT_UNIT_COLOR : CREW_UNIT_COLOR);
+ for (blockI = oldNumBlocks; blockI < newNumBlocks; blockI++)
+ {
+ r.corner.x = x + (CREW_XOFFS + 1);
+ if (!(blockI & 1))
+ {
+ r.corner.x += UNIT_WIDTH + 1;
+ r.corner.y -= UNIT_HEIGHT + 1;
+ }
+ DrawFilledRectangle (&r);
+ }
+ }
+ else /* crew_delta < 0 */
+ {
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.y = (y + 1) -
+ (((oldNumBlocks + 2) >> 1) * (UNIT_HEIGHT + 1));
+ for (blockI = oldNumBlocks; blockI > newNumBlocks; blockI--)
+ {
+ r.corner.x = x + (CREW_XOFFS + 1 + UNIT_WIDTH + 1);
+ if (!(blockI & 1))
+ {
+ r.corner.x -= UNIT_WIDTH + 1;
+ r.corner.y += UNIT_HEIGHT + 1;
+ }
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ if (ShipInfoPtr->ship_flags & PLAYER_CAPTAIN) {
+ if (((ShipInfoPtr->crew_level > MAX_CREW_SIZE) !=
+ (newCrewLevel > MAX_CREW_SIZE) ||
+ ShipInfoPtr->crew_level == 0) && newCrewLevel != 0)
+ {
+ // The block indicating the captain needs to change color.
+#define PLAYER_UNIT_COLOR BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+ SetContextForeGroundColor (
+ (newCrewLevel > MAX_CREW_SIZE) ?
+ CREW_UNIT_COLOR : PLAYER_UNIT_COLOR);
+ r.corner.x = x + (CREW_XOFFS + 1) + (UNIT_WIDTH + 1);
+ r.corner.y = y - UNIT_HEIGHT;
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ ShipInfoPtr->crew_level = newCrewLevel;
+ if (ShipInfoPtr->max_crew > MAX_CREW_SIZE ||
+ ShipInfoPtr->ship_flags & PLAYER_CAPTAIN)
+ {
+ // All crew doesn't fit in the graphics; print a number.
+ // Always print a number for the SIS in the full game.
+ DrawBattleCrewAmount (ShipInfoPtr, y_offs);
+ }
+ }
+
+ if (energy_delta != 0)
+ {
+ if (energy_delta > 0)
+ {
+#define FUEL_UNIT_COLOR BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x04)
+ SetContextForeGroundColor (FUEL_UNIT_COLOR);
+ r.corner.y = (y + 1) -
+ (((ShipInfoPtr->energy_level + 1) >> 1) * (UNIT_HEIGHT + 1));
+ do
+ {
+ r.corner.x = x + (ENERGY_XOFFS + 1);
+ if (!(ShipInfoPtr->energy_level & 1))
+ {
+ r.corner.x += UNIT_WIDTH + 1;
+ r.corner.y -= UNIT_HEIGHT + 1;
+ }
+ DrawFilledRectangle (&r);
+ ++ShipInfoPtr->energy_level;
+ } while (--energy_delta);
+ }
+ else
+ {
+ SetContextForeGroundColor (BLACK_COLOR);
+ r.corner.y = (y + 1) -
+ (((ShipInfoPtr->energy_level + 2) >> 1) * (UNIT_HEIGHT + 1));
+ do
+ {
+ r.corner.x = x + (ENERGY_XOFFS + 1 + UNIT_WIDTH + 1);
+ if (!(ShipInfoPtr->energy_level & 1))
+ {
+ r.corner.x -= UNIT_WIDTH + 1;
+ r.corner.y += UNIT_HEIGHT + 1;
+ }
+ DrawFilledRectangle (&r);
+ --ShipInfoPtr->energy_level;
+ } while (++energy_delta);
+ }
+ }
+}
+
+
diff --git a/src/uqm/shipyard.c b/src/uqm/shipyard.c
new file mode 100644
index 0000000..f317ba3
--- /dev/null
+++ b/src/uqm/shipyard.c
@@ -0,0 +1,1495 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "build.h"
+#include "colors.h"
+#include "controls.h"
+#include "menustat.h"
+#include "fmv.h"
+#include "gameopt.h"
+#include "gamestr.h"
+#include "supermelee/melee.h"
+#include "master.h"
+#include "options.h"
+#include "races.h"
+#include "nameref.h"
+#include "resinst.h"
+#include "settings.h"
+#include "starbase.h"
+#include "setup.h"
+#include "sis.h"
+#include "units.h"
+#include "sounds.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/inplib.h"
+
+
+#ifdef USE_3DO_HANGAR
+// 3DO 4x3 hangar layout
+# define HANGAR_SHIPS_ROW 4
+# define HANGAR_Y 64
+# define HANGAR_DY 44
+
+static const COORD hangar_x_coords[HANGAR_SHIPS_ROW] =
+{
+ 19, 60, 116, 157
+};
+
+#else // use PC hangar
+// modified PC 6x2 hangar layout
+# define HANGAR_SHIPS_ROW 6
+# define HANGAR_Y 88
+# define HANGAR_DY 84
+
+static const COORD hangar_x_coords[HANGAR_SHIPS_ROW] =
+{
+ 0, 38, 76, 131, 169, 207
+};
+#endif // USE_3DO_HANGAR
+
+#define HANGAR_SHIPS 12
+#define HANGAR_ROWS (HANGAR_SHIPS / HANGAR_SHIPS_ROW)
+#define HANGAR_ANIM_RATE 15 // fps
+
+enum
+{
+ SHIPYARD_CREW,
+ SHIPYARD_SAVELOAD,
+ SHIPYARD_EXIT
+};
+
+// Editing mode for DoModifyShips()
+typedef enum {
+ DMS_Mode_navigate, // Navigating the ship slots.
+ DMS_Mode_addEscort, // Selecting a ship to add to an empty slot.
+ DMS_Mode_editCrew, // Hiring or dismissing crew.
+ DMS_Mode_exit, // Leaving DoModifyShips() mode.
+} DMS_Mode;
+
+static COUNT ShipCost[] =
+{
+ RACE_SHIP_COST
+};
+
+
+static void
+animatePowerLines (MENU_STATE *pMS)
+{
+ static STAMP s;
+ static COLORMAP ColorMap;
+ static TimeCount NextTime = 0;
+ TimeCount Now = GetTimeCounter ();
+
+ if (pMS)
+ { // Init animation
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pMS->ModuleFrame, 24);
+ ColorMap = SetAbsColorMapIndex (pMS->CurString, 0);
+ }
+
+ if (Now >= NextTime || pMS)
+ {
+ NextTime = Now + (ONE_SECOND / HANGAR_ANIM_RATE);
+
+ SetColorMap (GetColorMapAddress (ColorMap));
+ DrawStamp (&s);
+ // Advance colomap cycle
+ ColorMap = SetRelColorMapIndex (ColorMap, 1);
+ }
+}
+
+static void
+on_input_frame (void)
+{
+ CONTEXT oldContext;
+
+ oldContext = SetContext (SpaceContext);
+ animatePowerLines (NULL);
+ SetContext (oldContext);
+}
+
+#ifdef WANT_SHIP_SPINS
+static void
+SpinStarShip (MENU_STATE *pMS, HFLEETINFO hStarShip)
+{
+ int Index;
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ Index = FindMasterShipIndex (FleetPtr->SpeciesID);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+
+ if (Index >= 0 && Index < NUM_MELEE_SHIPS)
+ {
+ DoShipSpin (Index, pMS->hMusic);
+ }
+}
+#endif
+
+// Count the ships which can be built by the player.
+static COUNT
+GetAvailableRaceCount (void)
+{
+ COUNT Index;
+ HFLEETINFO hStarShip, hNextShip;
+
+ Index = 0;
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ if (FleetPtr->allied_state == GOOD_GUY)
+ ++Index;
+
+ hNextShip = _GetSuccLink (FleetPtr);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ return Index;
+}
+
+static HFLEETINFO
+GetAvailableRaceFromIndex (BYTE Index)
+{
+ HFLEETINFO hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ if (FleetPtr->allied_state == GOOD_GUY && Index-- == 0)
+ {
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ return hStarShip;
+ }
+
+ hNextShip = _GetSuccLink (FleetPtr);
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+
+ return 0;
+}
+
+static void
+DrawRaceStrings (MENU_STATE *pMS, BYTE NewRaceItem)
+{
+ RECT r;
+ STAMP s;
+ CONTEXT OldContext;
+
+
+ OldContext = SetContext (StatusContext);
+ GetContextClipRect (&r);
+ s.origin.x = RADAR_X - r.corner.x;
+ s.origin.y = RADAR_Y - r.corner.y;
+ r.corner.x = s.origin.x - 1;
+ r.corner.y = s.origin.y - 11;
+ r.extent.width = RADAR_WIDTH + 2;
+ r.extent.height = 11;
+ BatchGraphics ();
+ ClearSISRect (CLEAR_SIS_RADAR);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ DrawFilledRectangle (&r);
+ r.corner = s.origin;
+ r.extent.width = RADAR_WIDTH;
+ r.extent.height = RADAR_HEIGHT;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ if (NewRaceItem != (BYTE)~0)
+ {
+ TEXT t;
+ HFLEETINFO hStarShip;
+ FLEET_INFO *FleetPtr;
+ UNICODE buf[30];
+
+ hStarShip = GetAvailableRaceFromIndex (NewRaceItem);
+ NewRaceItem = GetIndexFromStarShip (&GLOBAL (avail_race_q),
+ hStarShip);
+
+ // Draw the ship name, above the ship image.
+ s.frame = SetAbsFrameIndex (pMS->ModuleFrame, 3 + NewRaceItem);
+ DrawStamp (&s);
+
+ // Draw the ship image.
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ s.frame = FleetPtr->melee_icon;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
+ t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
+ s.origin.x += (RADAR_WIDTH >> 1);
+ s.origin.y += (RADAR_HEIGHT >> 1);
+ DrawStamp (&s);
+
+ // Print the ship cost.
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+ t.pStr = buf;
+ sprintf (buf, "%u", ShipCost[NewRaceItem]);
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x1F, 0x00), 0x02));
+ font_DrawText (&t);
+ }
+ UnbatchGraphics ();
+ SetContext (OldContext);
+
+}
+
+#define SHIP_WIN_WIDTH 34
+#define SHIP_WIN_HEIGHT (SHIP_WIN_WIDTH + 6)
+#define SHIP_WIN_FRAMES ((SHIP_WIN_WIDTH >> 1) + 1)
+
+// Print the crew count of an escort ship on top of its (already drawn)
+// image, either as '30' (full), '28/30' (partially full), or 'SCRAP'
+// (empty).
+// pRect is the rectangle of the ship image.
+static void
+ShowShipCrew (SHIP_FRAGMENT *StarShipPtr, const RECT *pRect)
+{
+ RECT r;
+ TEXT t;
+ UNICODE buf[80];
+ HFLEETINFO hTemplate;
+ FLEET_INFO *TemplatePtr;
+ COUNT maxCrewLevel;
+
+ hTemplate = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ StarShipPtr->race_id);
+ TemplatePtr = LockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ maxCrewLevel = TemplatePtr->crew_level;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+
+ if (StarShipPtr->crew_level >= maxCrewLevel)
+ sprintf (buf, "%u", StarShipPtr->crew_level);
+ else if (StarShipPtr->crew_level == 0)
+ // XXX: "SCRAP" needs to be moved to starcon.txt
+ utf8StringCopy (buf, sizeof (buf), "SCRAP");
+ else
+ sprintf (buf, "%u/%u", StarShipPtr->crew_level, maxCrewLevel);
+
+ r = *pRect;
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + r.extent.height - 1;
+ t.align = ALIGN_CENTER;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ if (r.corner.y)
+ {
+ r.corner.y = t.baseline.y - 6;
+ r.extent.width = SHIP_WIN_WIDTH;
+ r.extent.height = 6;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ }
+ SetContextForeGroundColor ((StarShipPtr->crew_level != 0) ?
+ (BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02)):
+ (BUILD_COLOR (MAKE_RGB15 (0x12, 0x00, 0x00), 0x2B)));
+ font_DrawText (&t);
+}
+
+static void
+ShowCombatShip (MENU_STATE *pMS, COUNT which_window,
+ SHIP_FRAGMENT *YankedStarShipPtr)
+{
+ COUNT i;
+ COUNT num_ships;
+ HSHIPFRAG hStarShip, hNextShip;
+ SHIP_FRAGMENT *StarShipPtr;
+ struct
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+ POINT finished_s;
+ STAMP ship_s;
+ STAMP lfdoor_s;
+ STAMP rtdoor_s;
+ } ship_win_info[MAX_BUILT_SHIPS], *pship_win_info;
+
+ num_ships = 1;
+ pship_win_info = &ship_win_info[0];
+ if (YankedStarShipPtr)
+ {
+ pship_win_info->StarShipPtr = YankedStarShipPtr;
+
+ pship_win_info->lfdoor_s.origin.x = -(SHIP_WIN_WIDTH >> 1);
+ pship_win_info->rtdoor_s.origin.x = (SHIP_WIN_WIDTH >> 1);
+ pship_win_info->lfdoor_s.origin.y = 0;
+ pship_win_info->rtdoor_s.origin.y = 0;
+ pship_win_info->lfdoor_s.frame = IncFrameIndex (pMS->ModuleFrame);
+ pship_win_info->rtdoor_s.frame =
+ IncFrameIndex (pship_win_info->lfdoor_s.frame);
+
+ pship_win_info->ship_s.origin.x = (SHIP_WIN_WIDTH >> 1) + 1;
+ pship_win_info->ship_s.origin.y = (SHIP_WIN_WIDTH >> 1);
+ pship_win_info->ship_s.frame = YankedStarShipPtr->melee_icon;
+
+ pship_win_info->finished_s.x = hangar_x_coords[
+ which_window % HANGAR_SHIPS_ROW];
+ pship_win_info->finished_s.y = HANGAR_Y + (HANGAR_DY *
+ (which_window / HANGAR_SHIPS_ROW));
+ }
+ else
+ {
+ if (which_window == (COUNT)~0)
+ {
+ hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
+ num_ships = CountLinks (&GLOBAL (built_ship_q));
+ }
+ else
+ {
+ HSHIPFRAG hTailShip;
+
+ hTailShip = GetTailLink (&GLOBAL (built_ship_q));
+ RemoveQueue (&GLOBAL (built_ship_q), hTailShip);
+
+ hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
+ while (hStarShip)
+ {
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ if (StarShipPtr->index > which_window)
+ {
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ break;
+ }
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ hStarShip = hNextShip;
+ }
+ InsertQueue (&GLOBAL (built_ship_q), hTailShip, hStarShip);
+
+ hStarShip = hTailShip;
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ StarShipPtr->index = which_window;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ for (i = 0; i < num_ships; ++i)
+ {
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+
+ pship_win_info->StarShipPtr = StarShipPtr;
+ // XXX BUG: this looks wrong according to the original
+ // semantics of LockShipFrag(): StarShipPtr is not valid
+ // anymore after UnlockShipFrag() is called, but it is
+ // used thereafter.
+
+ pship_win_info->lfdoor_s.origin.x = -1;
+ pship_win_info->rtdoor_s.origin.x = 1;
+ pship_win_info->lfdoor_s.origin.y = 0;
+ pship_win_info->rtdoor_s.origin.y = 0;
+ pship_win_info->lfdoor_s.frame = IncFrameIndex (pMS->ModuleFrame);
+ pship_win_info->rtdoor_s.frame =
+ IncFrameIndex (pship_win_info->lfdoor_s.frame);
+
+ pship_win_info->ship_s.origin.x = (SHIP_WIN_WIDTH >> 1) + 1;
+ pship_win_info->ship_s.origin.y = (SHIP_WIN_WIDTH >> 1);
+ pship_win_info->ship_s.frame = StarShipPtr->melee_icon;
+
+ which_window = StarShipPtr->index;
+ pship_win_info->finished_s.x = hangar_x_coords[
+ which_window % HANGAR_SHIPS_ROW];
+ pship_win_info->finished_s.y = HANGAR_Y + (HANGAR_DY *
+ (which_window / HANGAR_SHIPS_ROW));
+ ++pship_win_info;
+
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hStarShip = hNextShip;
+ }
+ }
+
+ if (num_ships)
+ {
+ BOOLEAN AllDoorsFinished;
+ DWORD TimeIn;
+ RECT r;
+ CONTEXT OldContext;
+ RECT OldClipRect;
+ int j;
+
+ AllDoorsFinished = FALSE;
+ r.corner.x = r.corner.y = 0;
+ r.extent.width = SHIP_WIN_WIDTH;
+ r.extent.height = SHIP_WIN_HEIGHT;
+ FlushInput ();
+ TimeIn = GetTimeCounter ();
+
+ for (j = 0; (j < SHIP_WIN_FRAMES) && !AllDoorsFinished; j++)
+ {
+ SleepThreadUntil (TimeIn + ONE_SECOND / 24);
+ TimeIn = GetTimeCounter ();
+ if (AnyButtonPress (FALSE))
+ {
+ if (YankedStarShipPtr != 0)
+ { // Fully close the doors
+ ship_win_info[0].lfdoor_s.origin.x = 0;
+ ship_win_info[0].rtdoor_s.origin.x = 0;
+ }
+ AllDoorsFinished = TRUE;
+ }
+
+ OldContext = SetContext (SpaceContext);
+ GetContextClipRect (&OldClipRect);
+ SetContextBackGroundColor (BLACK_COLOR);
+
+ BatchGraphics ();
+ pship_win_info = &ship_win_info[0];
+ for (i = 0; i < num_ships; ++i)
+ {
+ {
+ RECT ClipRect;
+
+ ClipRect.corner.x = SIS_ORG_X +
+ pship_win_info->finished_s.x;
+ ClipRect.corner.y = SIS_ORG_Y +
+ pship_win_info->finished_s.y;
+ ClipRect.extent.width = SHIP_WIN_WIDTH;
+ ClipRect.extent.height = SHIP_WIN_HEIGHT;
+ SetContextClipRect (&ClipRect);
+
+ ClearDrawable ();
+ DrawStamp (&pship_win_info->ship_s);
+ ShowShipCrew (pship_win_info->StarShipPtr, &r);
+ if (!AllDoorsFinished || YankedStarShipPtr)
+ {
+ DrawStamp (&pship_win_info->lfdoor_s);
+ DrawStamp (&pship_win_info->rtdoor_s);
+ if (YankedStarShipPtr)
+ { // Close the doors
+ ++pship_win_info->lfdoor_s.origin.x;
+ --pship_win_info->rtdoor_s.origin.x;
+ }
+ else
+ { // Open the doors
+ --pship_win_info->lfdoor_s.origin.x;
+ ++pship_win_info->rtdoor_s.origin.x;
+ }
+ }
+ }
+ ++pship_win_info;
+ }
+
+ SetContextClipRect (&OldClipRect);
+#ifndef USE_3DO_HANGAR
+ animatePowerLines (NULL);
+#endif
+ UnbatchGraphics ();
+ SetContext (OldContext);
+ }
+ }
+}
+
+static void
+CrewTransaction (SIZE crew_delta)
+{
+ if (crew_delta)
+ {
+ SIZE crew_bought;
+
+ crew_bought = (SIZE)MAKE_WORD (
+ GET_GAME_STATE (CREW_PURCHASED0),
+ GET_GAME_STATE (CREW_PURCHASED1)) + crew_delta;
+ if (crew_bought < 0)
+ {
+ if (crew_delta < 0)
+ crew_bought = 0;
+ else
+ crew_bought = 0x7FFF;
+ }
+ else if (crew_delta > 0)
+ {
+ if (crew_bought >= CREW_EXPENSE_THRESHOLD
+ && crew_bought - crew_delta < CREW_EXPENSE_THRESHOLD)
+ {
+ GLOBAL (CrewCost) += 2;
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+ }
+ }
+ else
+ {
+ if (crew_bought < CREW_EXPENSE_THRESHOLD
+ && crew_bought - crew_delta >= CREW_EXPENSE_THRESHOLD)
+ {
+ GLOBAL (CrewCost) -= 2;
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+ }
+ }
+ if (CheckAlliance (SHOFIXTI_SHIP) != GOOD_GUY)
+ {
+ SET_GAME_STATE (CREW_PURCHASED0, LOBYTE (crew_bought));
+ SET_GAME_STATE (CREW_PURCHASED1, HIBYTE (crew_bought));
+ }
+ }
+}
+
+static void
+DMS_FlashFlagShip (void)
+{
+ RECT r;
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = 61;
+ SetFlashRect (&r);
+}
+
+static void
+DMS_GetEscortShipRect (RECT *rOut, BYTE slotNr)
+{
+ BYTE row = slotNr / HANGAR_SHIPS_ROW;
+ BYTE col = slotNr % HANGAR_SHIPS_ROW;
+
+ rOut->corner.x = hangar_x_coords[col];
+ rOut->corner.y = HANGAR_Y + (HANGAR_DY * row);
+ rOut->extent.width = SHIP_WIN_WIDTH;
+ rOut->extent.height = SHIP_WIN_HEIGHT;
+}
+
+static void
+DMS_FlashEscortShip (BYTE slotNr)
+{
+ RECT r;
+ DMS_GetEscortShipRect (&r, slotNr);
+ SetFlashRect (&r);
+}
+
+static void
+DMS_FlashFlagShipCrewCount (void)
+{
+ RECT r;
+ SetContext (StatusContext);
+ GetGaugeRect (&r, TRUE);
+ SetFlashRect (&r);
+ SetContext (SpaceContext);
+}
+
+static void
+DMS_FlashEscortShipCrewCount (BYTE slotNr)
+{
+ RECT r;
+ BYTE row = slotNr / HANGAR_SHIPS_ROW;
+ BYTE col = slotNr % HANGAR_SHIPS_ROW;
+
+ r.corner.x = hangar_x_coords[col];
+ r.corner.y = (HANGAR_Y + (HANGAR_DY * row)) + (SHIP_WIN_HEIGHT - 6);
+ r.extent.width = SHIP_WIN_WIDTH;
+ r.extent.height = 5;
+
+ SetContext (SpaceContext);
+ SetFlashRect (&r);
+}
+
+// Helper function for DoModifyShips(). Called to change the flash
+// rectangle to the currently selected ship (flagship or escort ship).
+static void
+DMS_FlashActiveShip (MENU_STATE *pMS)
+{
+ if (HINIBBLE (pMS->CurState))
+ {
+ // Flash the flag ship.
+ DMS_FlashFlagShip ();
+ }
+ else
+ {
+ // Flash the current escort ship slot.
+ DMS_FlashEscortShip (pMS->CurState);
+ }
+}
+
+// Helper function for DoModifyShips(). Called to switch between
+// the various edit modes.
+// XXX: right now, this only switches the sound and flash rectangle.
+// Perhaps we should move more of the code to modify other aspects
+// here too.
+static void
+DMS_SetMode (MENU_STATE *pMS, DMS_Mode mode)
+{
+ switch (mode) {
+ case DMS_Mode_navigate:
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ DMS_FlashActiveShip (pMS);
+ break;
+ case DMS_Mode_addEscort:
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetFlashRect (SFR_MENU_ANY);
+ break;
+ case DMS_Mode_editCrew:
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN,
+ MENU_SOUND_SELECT | MENU_SOUND_CANCEL);
+ if (HINIBBLE (pMS->CurState))
+ {
+ // Enter crew editing mode for the flagship.
+ DMS_FlashFlagShipCrewCount ();
+ }
+ else
+ {
+ // Enter crew editing mode for an escort ship.
+ DMS_FlashEscortShipCrewCount (pMS->CurState);
+ }
+ break;
+ case DMS_Mode_exit:
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ SetFlashRect (SFR_MENU_3DO);
+ break;
+ }
+}
+
+#define MODIFY_CREW_FLAG (1 << 8)
+#ifdef WANT_SHIP_SPINS
+// Helper function for DoModifyShips(), called when the player presses the
+// special button.
+// It works both when the cursor is over an escort ship, while not editing
+// the crew, and when a new ship is added.
+// hStarShip is the ship in the slot under the cursor (or 0 if no such ship).
+static BOOLEAN
+DMS_SpinShip (MENU_STATE *pMS, HSHIPFRAG hStarShip)
+{
+ HFLEETINFO hSpinShip = 0;
+ CONTEXT OldContext;
+ RECT OldClipRect;
+
+ // No spinning the flagship.
+ if (HINIBBLE (pMS->CurState) != 0)
+ return FALSE;
+
+ // We must either be hovering over a used ship slot, or adding a new
+ // ship to the fleet.
+ if ((hStarShip == 0) == !(pMS->delta_item & MODIFY_CREW_FLAG))
+ return FALSE;
+
+ if (!hStarShip)
+ {
+ // Selecting a ship to build.
+ hSpinShip = GetAvailableRaceFromIndex (LOBYTE (pMS->delta_item));
+ if (!hSpinShip)
+ return FALSE;
+ }
+ else
+ {
+ // Hovering over an escort ship.
+ SHIP_FRAGMENT *FragPtr = LockShipFrag (
+ &GLOBAL (built_ship_q), hStarShip);
+ hSpinShip = GetStarShipFromIndex (
+ &GLOBAL (avail_race_q), FragPtr->race_id);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ SetFlashRect (NULL);
+
+ OldContext = SetContext (ScreenContext);
+ GetContextClipRect (&OldClipRect);
+
+ SpinStarShip (pMS, hSpinShip);
+
+ SetContextClipRect (&OldClipRect);
+ SetContext (OldContext);
+
+ return TRUE;
+}
+#endif /* WANT_SHIP_SPINS */
+
+// Helper function for DoModifyShips(), called when the player presses the
+// up button when modifying the crew of the flagship.
+// Buy crew for the flagship.
+// Returns the change in crew (1 on success, 0 on failure).
+static SIZE
+DMS_HireFlagShipCrew (void)
+{
+ RECT r;
+
+ if (GetCPodCapacity (&r.corner) <= GetCrewCount ())
+ {
+ // At capacity.
+ return 0;
+ }
+
+ if (GLOBAL_SIS (ResUnits) < (DWORD)GLOBAL (CrewCost))
+ {
+ // Not enough RUs.
+ return 0;
+ }
+
+ // Draw a crew member.
+ DrawPoint (&r.corner);
+
+ // Update the crew counter and RU. Note that the crew counter is
+ // flashing.
+ PreUpdateFlashRect ();
+ DeltaSISGauges (1, 0, -GLOBAL (CrewCost));
+ PostUpdateFlashRect ();
+
+ return 1;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// down button when modifying the crew of the flagship.
+// Dismiss crew from the flagship.
+// Returns the change in crew (-1 on success, 0 on failure).
+static SIZE
+DMS_DismissFlagShipCrew (void)
+{
+ SIZE crew_bought;
+ RECT r;
+
+ if (GetCrewCount () == 0)
+ {
+ // No crew to dismiss.
+ return 0;
+ }
+
+ crew_bought = (SIZE)MAKE_WORD (
+ GET_GAME_STATE (CREW_PURCHASED0),
+ GET_GAME_STATE (CREW_PURCHASED1));
+
+ // Update the crew counter and RU. Note that the crew counter is
+ // flashing.
+ PreUpdateFlashRect ();
+ DeltaSISGauges (-1, 0, GLOBAL (CrewCost) -
+ (crew_bought == CREW_EXPENSE_THRESHOLD ? 2 : 0));
+ PostUpdateFlashRect ();
+
+ // Remove the pixel representing the crew member.
+ GetCPodCapacity (&r.corner);
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawPoint (&r.corner);
+
+ return -1;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// up button when modifying the crew of an escort ship.
+// Buy crew for an escort ship
+// Returns the change in crew (1 on success, 0 on failure).
+static SIZE
+DMS_HireEscortShipCrew (SHIP_FRAGMENT *StarShipPtr)
+{
+ COUNT templateMaxCrew;
+ RECT r;
+
+ {
+ // XXX Split this off into a separate function?
+ HFLEETINFO hTemplate = GetStarShipFromIndex (&GLOBAL (avail_race_q),
+ StarShipPtr->race_id);
+ FLEET_INFO *TemplatePtr =
+ LockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ templateMaxCrew = TemplatePtr->crew_level;
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hTemplate);
+ }
+
+ if (GLOBAL_SIS (ResUnits) < (DWORD)GLOBAL (CrewCost))
+ {
+ // Not enough money to hire a crew member.
+ return 0;
+ }
+
+ if (StarShipPtr->crew_level >= StarShipPtr->max_crew)
+ {
+ // This ship cannot handle more crew.
+ return 0;
+ }
+
+ if (StarShipPtr->crew_level >= templateMaxCrew)
+ {
+ // A ship of this type cannot handle more crew.
+ return 0;
+ }
+
+ if (StarShipPtr->crew_level > 0)
+ {
+ DeltaSISGauges (0, 0, -GLOBAL (CrewCost));
+ }
+ else
+ {
+ // Buy a ship.
+ DeltaSISGauges (0, 0, -(COUNT)ShipCost[StarShipPtr->race_id]);
+ }
+
+ ++StarShipPtr->crew_level;
+
+ PreUpdateFlashRect ();
+ DMS_GetEscortShipRect (&r, StarShipPtr->index);
+ ShowShipCrew (StarShipPtr, &r);
+ PostUpdateFlashRect ();
+
+ return 1;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// down button when modifying the crew of an escort ship.
+// Dismiss crew from an escort ship
+// Returns the change in crew (-1 on success, 0 on failure).
+static SIZE
+DMS_DismissEscortShipCrew (SHIP_FRAGMENT *StarShipPtr)
+{
+ SIZE crew_delta = 0;
+ RECT r;
+
+ if (StarShipPtr->crew_level > 0)
+ {
+ if (StarShipPtr->crew_level > 1)
+ {
+ // The ship was not at 'scrap'.
+ // Give one crew member worth of RU.
+ SIZE crew_bought = (SIZE)MAKE_WORD (
+ GET_GAME_STATE (CREW_PURCHASED0),
+ GET_GAME_STATE (CREW_PURCHASED1));
+
+ DeltaSISGauges (0, 0, GLOBAL (CrewCost)
+ - (crew_bought == CREW_EXPENSE_THRESHOLD ? 2 : 0));
+ }
+ else
+ {
+ // With the last crew member, the ship will be scrapped.
+ // Give RU for the ship.
+ DeltaSISGauges (0, 0, (COUNT)ShipCost[StarShipPtr->race_id]);
+ }
+ crew_delta = -1;
+ --StarShipPtr->crew_level;
+ }
+ else
+ { // no crew to dismiss
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+
+ PreUpdateFlashRect ();
+ DMS_GetEscortShipRect (&r, StarShipPtr->index);
+ ShowShipCrew (StarShipPtr, &r);
+ PostUpdateFlashRect ();
+
+ return crew_delta;
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// up or down button when modifying the crew of the flagship or of an escort
+// ship.
+// 'hStarShip' is the currently escort ship, or 0 if no ship is
+// selected.
+// 'dy' is -1 if the 'up' button was pressed, or '1' if the down button was
+// pressed.
+static void
+DMS_ModifyCrew (MENU_STATE *pMS, HSHIPFRAG hStarShip, SBYTE dy)
+{
+ SIZE crew_delta = 0;
+ SHIP_FRAGMENT *StarShipPtr = NULL;
+
+ if (hStarShip)
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (hStarShip == 0)
+ {
+ // Add/Dismiss crew for the flagship.
+ if (dy < 0)
+ {
+ // Add crew for the flagship.
+ crew_delta = DMS_HireFlagShipCrew ();
+ }
+ else
+ {
+ // Dismiss crew from the flagship.
+ crew_delta = DMS_DismissFlagShipCrew ();
+ }
+
+ if (crew_delta != 0)
+ DMS_FlashFlagShipCrewCount ();
+ }
+ else
+ {
+ // Add/Dismiss crew for an escort ship.
+ if (dy < 0)
+ {
+ // Add crew for an escort ship.
+ crew_delta = DMS_HireEscortShipCrew (StarShipPtr);
+ }
+ else
+ {
+ // Dismiss crew from an escort ship.
+ crew_delta = DMS_DismissEscortShipCrew (StarShipPtr);
+ }
+
+ if (crew_delta != 0)
+ DMS_FlashEscortShipCrewCount (StarShipPtr->index);
+ }
+
+ if (crew_delta == 0)
+ PlayMenuSound (MENU_SOUND_FAILURE);
+
+ if (hStarShip)
+ {
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ // Clear out the bought ship index so that flash rects work
+ // correctly.
+ pMS->delta_item &= MODIFY_CREW_FLAG;
+ }
+
+ CrewTransaction (crew_delta);
+}
+
+// Helper function for DoModifyShips(), called when the player presses the
+// select button when the cursor is over an empty escort ship slot.
+// Try to add the currently selected ship as an escort ship.
+static void
+DMS_TryAddEscortShip (MENU_STATE *pMS)
+{
+ HFLEETINFO shipInfo = GetAvailableRaceFromIndex (
+ LOBYTE (pMS->delta_item));
+ COUNT Index = GetIndexFromStarShip (&GLOBAL (avail_race_q), shipInfo);
+
+ if (GLOBAL_SIS (ResUnits) >= (DWORD)ShipCost[Index]
+ && CloneShipFragment (Index, &GLOBAL (built_ship_q), 1))
+ {
+ ShowCombatShip (pMS, pMS->CurState, NULL);
+ // Reset flash rectangle
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA,
+ -((int)ShipCost[Index]));
+ DMS_SetMode (pMS, DMS_Mode_editCrew);
+ }
+ else
+ {
+ // not enough RUs to build, or cloning the ship failed.
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ }
+}
+
+// Helper function for DoModifyShips(), called when the player is in the
+// mode to add a new escort ship to the fleet (after pressing select on an
+// empty slot).
+// LOBYTE (pMS->delta_item) is used to store the currently highlighted ship.
+// Returns FALSE if the flash rectangle needs to be updated.
+static void
+DMS_AddEscortShip (MENU_STATE *pMS, BOOLEAN special, BOOLEAN select,
+ BOOLEAN cancel, SBYTE dx, SBYTE dy)
+{
+ assert (pMS->delta_item & MODIFY_CREW_FLAG);
+
+#ifdef WANT_SHIP_SPINS
+ if (special)
+ {
+ HSHIPFRAG hStarShip = GetEscortByStarShipIndex (pMS->delta_item);
+ if (DMS_SpinShip (pMS, hStarShip))
+ DMS_SetMode (pMS, DMS_Mode_addEscort);
+ return;
+ }
+#else
+ (void) special; // Satisfying compiler.
+#endif /* WANT_SHIP_SPINS */
+
+ if (cancel)
+ {
+ // Cancel selecting an escort ship.
+ pMS->delta_item &= ~MODIFY_CREW_FLAG;
+ SetFlashRect (NULL);
+ DrawMenuStateStrings (PM_CREW, SHIPYARD_CREW);
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else if (select)
+ {
+ // Selected a ship to be inserted in an empty escort
+ // ship slot.
+ DMS_TryAddEscortShip (pMS);
+ }
+ else if (dx || dy)
+ {
+ // Motion key pressed while selecting a ship to be
+ // inserted in an empty escort ship slot.
+ COUNT availableCount = GetAvailableRaceCount ();
+ BYTE currentShip = LOBYTE (pMS->delta_item);
+ if (dx < 0 || dy < 0)
+ {
+ if (currentShip-- == 0)
+ currentShip = availableCount - 1;
+ }
+ else if (dx > 0 || dy > 0)
+ {
+ if (++currentShip == availableCount)
+ currentShip = 0;
+ }
+
+ if (currentShip != LOBYTE (pMS->delta_item))
+ {
+ PreUpdateFlashRect ();
+ DrawRaceStrings (pMS, currentShip);
+ PostUpdateFlashRect ();
+ pMS->delta_item = currentShip | MODIFY_CREW_FLAG;
+ }
+ }
+}
+
+// Helper function for DoModifyShips(), called when the player presses
+// 'select' or 'cancel' after selling all the crew.
+static void
+DMS_ScrapEscortShip (MENU_STATE *pMS, HSHIPFRAG hStarShip)
+{
+ SHIP_FRAGMENT *StarShipPtr =
+ LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ BYTE slotNr;
+
+ SetFlashRect (NULL);
+ ShowCombatShip (pMS, pMS->CurState, StarShipPtr);
+
+ slotNr = StarShipPtr->index;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
+ FreeShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ // refresh SIS display
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+
+ SetContext (SpaceContext);
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+}
+
+// Helper function for DoModifyShips(), called when the player presses
+// one of the motion keys when not in crew modification mode.
+static BYTE
+DMS_MoveCursor (BYTE curState, SBYTE dx, SBYTE dy)
+{
+ BYTE row = LONIBBLE(curState) / HANGAR_SHIPS_ROW;
+ BYTE col = LONIBBLE(curState) % HANGAR_SHIPS_ROW;
+ BOOLEAN isFlagShipSelected = (HINIBBLE(curState) != 0);
+
+ if (dy)
+ {
+ // Vertical motion.
+
+ // We consider the flagship an extra row (on the bottom),
+ // to ease operations.
+ if (isFlagShipSelected)
+ row = HANGAR_ROWS;
+
+ // Move up/down, wrapping around:
+ row = (row + (HANGAR_ROWS + 1) + dy) % (HANGAR_ROWS + 1);
+
+ // If we moved to the 'extra row', this means the flag ship.
+ isFlagShipSelected = (row == HANGAR_ROWS);
+ if (isFlagShipSelected)
+ row = 0;
+ }
+ else if (dx)
+ {
+ // Horizontal motion.
+ if (!isFlagShipSelected)
+ {
+ // Moving horizontally through the escort ship slots,
+ // wrapping around if necessary.
+ col = (col + HANGAR_SHIPS_ROW + dx) % HANGAR_SHIPS_ROW;
+ }
+ }
+
+ return MAKE_BYTE(row * HANGAR_SHIPS_ROW + col,
+ isFlagShipSelected ? 0xf : 0);
+}
+
+// Helper function for DoModifyShips(), called every time DoModifyShip() is
+// called when we are in crew editing mode.
+static void
+DMS_EditCrewMode (MENU_STATE *pMS, HSHIPFRAG hStarShip,
+ BOOLEAN select, BOOLEAN cancel, SBYTE dy)
+{
+ if (select || cancel)
+ {
+ // Leave crew editing mode.
+ if (hStarShip != 0)
+ {
+ // Exiting crew editing mode for an escort ship.
+ SHIP_FRAGMENT *StarShipPtr = LockShipFrag (
+ &GLOBAL (built_ship_q), hStarShip);
+ COUNT crew_level = StarShipPtr->crew_level;
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ if (crew_level == 0)
+ {
+ // Scrapping the escort ship before exiting crew edit
+ // mode.
+ DMS_ScrapEscortShip (pMS, hStarShip);
+ }
+ }
+
+ pMS->delta_item &= ~MODIFY_CREW_FLAG;
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else if (dy)
+ {
+ // Hire or dismiss crew for the flagship or an escort
+ // ship.
+ DMS_ModifyCrew (pMS, hStarShip, dy);
+ }
+}
+
+// Helper function for DoModifyShips(), called every time DoModifyShip() is
+// called when we are in the mode where you can select a ship or empty slot.
+static void
+DMS_NavigateShipSlots (MENU_STATE *pMS, BOOLEAN special, BOOLEAN select,
+ BOOLEAN cancel, SBYTE dx, SBYTE dy)
+{
+ HSHIPFRAG hStarShip = GetEscortByStarShipIndex (pMS->CurState);
+
+ if (dx || dy)
+ {
+ // Moving through the ship slots.
+ BYTE NewState = DMS_MoveCursor (pMS->CurState, dx, dy);
+ if (NewState != pMS->CurState)
+ {
+ pMS->CurState = NewState;
+ DMS_FlashActiveShip(pMS);
+ }
+ }
+
+#ifndef WANT_SHIP_SPINS
+ (void) special; // Satisfying compiler.
+#else
+ if (special)
+ {
+ if (DMS_SpinShip (pMS, hStarShip))
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else
+#endif /* WANT_SHIP_SPINS */
+ if (select)
+ {
+ if (hStarShip == 0 && HINIBBLE (pMS->CurState) == 0)
+ {
+ // Select button was pressed over an empty escort
+ // ship slot. Switch to 'add escort ship' mode.
+ pMS->delta_item = MODIFY_CREW_FLAG;
+ DrawRaceStrings (pMS, 0);
+ DMS_SetMode (pMS, DMS_Mode_addEscort);
+ }
+ else
+ {
+ // Select button was pressed over an escort ship or
+ // the flagship. Entering crew editing mode
+ pMS->delta_item |= MODIFY_CREW_FLAG;
+ DMS_SetMode (pMS, DMS_Mode_editCrew);
+ }
+ }
+ else if (cancel)
+ {
+ // Leave escort ship editor.
+ pMS->InputFunc = DoShipyard;
+ pMS->CurState = SHIPYARD_CREW;
+ DrawMenuStateStrings (PM_CREW, pMS->CurState);
+ DMS_SetMode (pMS, DMS_Mode_exit);
+ }
+}
+
+/* In this routine, the least significant byte of pMS->CurState is used
+ * to store the current selected ship index
+ * a special case for the row is hi-nibble == -1 (0xf), which specifies
+ * SIS as the selected ship
+ * some bitwise math is still done to scroll through ships, for it to work
+ * ships per row number must divide 0xf0 without remainder
+ */
+static BOOLEAN
+DoModifyShips (MENU_STATE *pMS)
+{
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pMS->InputFunc = DoShipyard;
+ return TRUE;
+ }
+
+ if (!pMS->Initialized)
+ {
+ pMS->InputFunc = DoModifyShips;
+ pMS->Initialized = TRUE;
+ pMS->CurState = MAKE_BYTE (0, 0xF);
+ pMS->delta_item = 0;
+
+ SetContext (SpaceContext);
+ DMS_SetMode (pMS, DMS_Mode_navigate);
+ }
+ else
+ {
+ BOOLEAN special = (PulsedInputState.menu[KEY_MENU_SPECIAL] != 0);
+ BOOLEAN select = (PulsedInputState.menu[KEY_MENU_SELECT] != 0);
+ BOOLEAN cancel = (PulsedInputState.menu[KEY_MENU_CANCEL] != 0);
+ SBYTE dx = 0;
+ SBYTE dy = 0;
+
+ if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ dx = 1;
+ if (PulsedInputState.menu[KEY_MENU_LEFT])
+ dx = -1;
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ dy = -1;
+ if (PulsedInputState.menu[KEY_MENU_DOWN])
+ dy = 1;
+
+
+ if (!(pMS->delta_item & MODIFY_CREW_FLAG))
+ {
+ // Navigating through the ship slots.
+ DMS_NavigateShipSlots (pMS, special, select, cancel, dx, dy);
+ }
+ else
+ {
+ // Add an escort ship or edit the crew of a ship.
+ HSHIPFRAG hStarShip = GetEscortByStarShipIndex (pMS->CurState);
+
+ if (hStarShip == 0 && HINIBBLE (pMS->CurState) == 0)
+ {
+ // Cursor is over an empty escort ship slot, while we're
+ // in 'add escort ship' mode.
+ DMS_AddEscortShip (pMS, special, select, cancel, dx, dy);
+ }
+ else
+ {
+ // Crew editing mode.
+ DMS_EditCrewMode (pMS, hStarShip, select, cancel, dy);
+ }
+ }
+
+ }
+
+ SleepThread (ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static void
+DrawBluePrint (MENU_STATE *pMS)
+{
+ COUNT num_frames;
+ STAMP s;
+ FRAME ModuleFrame;
+
+ ModuleFrame = CaptureDrawable (LoadGraphic (SISBLU_MASK_ANIM));
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = DecFrameIndex (ModuleFrame);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x16), 0x01));
+ DrawFilledStamp (&s);
+
+ for (num_frames = 0; num_frames < NUM_DRIVE_SLOTS; ++num_frames)
+ {
+ DrawShipPiece (ModuleFrame, GLOBAL_SIS (DriveSlots[num_frames]),
+ num_frames, TRUE);
+ }
+ for (num_frames = 0; num_frames < NUM_JET_SLOTS; ++num_frames)
+ {
+ DrawShipPiece (ModuleFrame, GLOBAL_SIS (JetSlots[num_frames]),
+ num_frames, TRUE);
+ }
+ for (num_frames = 0; num_frames < NUM_MODULE_SLOTS; ++num_frames)
+ {
+ BYTE which_piece;
+
+ which_piece = GLOBAL_SIS (ModuleSlots[num_frames]);
+
+ if (!(pMS->CurState == SHIPYARD && which_piece == CREW_POD))
+ DrawShipPiece (ModuleFrame, which_piece, num_frames, TRUE);
+ }
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09));
+ for (num_frames = 0; num_frames < NUM_MODULE_SLOTS; ++num_frames)
+ {
+ BYTE which_piece;
+
+ which_piece = GLOBAL_SIS (ModuleSlots[num_frames]);
+ if (pMS->CurState == SHIPYARD && which_piece == CREW_POD)
+ DrawShipPiece (ModuleFrame, which_piece, num_frames, TRUE);
+ }
+
+ {
+ num_frames = GLOBAL_SIS (CrewEnlisted);
+ GLOBAL_SIS (CrewEnlisted) = 0;
+
+ while (num_frames--)
+ {
+ POINT pt;
+
+ GetCPodCapacity (&pt);
+ DrawPoint (&pt);
+
+ ++GLOBAL_SIS (CrewEnlisted);
+ }
+ }
+ {
+ RECT r;
+
+ num_frames = GLOBAL_SIS (TotalElementMass);
+ GLOBAL_SIS (TotalElementMass) = 0;
+
+ r.extent.width = 9;
+ r.extent.height = 1;
+ while (num_frames)
+ {
+ COUNT m;
+
+ m = num_frames < SBAY_MASS_PER_ROW ?
+ num_frames : SBAY_MASS_PER_ROW;
+ GLOBAL_SIS (TotalElementMass) += m;
+ GetSBayCapacity (&r.corner);
+ DrawFilledRectangle (&r);
+ num_frames -= m;
+ }
+ }
+ if (GLOBAL_SIS (FuelOnBoard) > FUEL_RESERVE)
+ {
+ DWORD FuelVolume;
+ RECT r;
+
+ FuelVolume = GLOBAL_SIS (FuelOnBoard) - FUEL_RESERVE;
+ GLOBAL_SIS (FuelOnBoard) = FUEL_RESERVE;
+
+ r.extent.width = 3;
+ r.extent.height = 1;
+ while (FuelVolume)
+ {
+ COUNT m;
+
+ GetFTankCapacity (&r.corner);
+ DrawPoint (&r.corner);
+ r.corner.x += r.extent.width + 1;
+ DrawPoint (&r.corner);
+ r.corner.x -= r.extent.width;
+ SetContextForeGroundColor (
+ SetContextBackGroundColor (BLACK_COLOR));
+ DrawFilledRectangle (&r);
+ m = FuelVolume < FUEL_VOLUME_PER_ROW ?
+ (COUNT)FuelVolume : FUEL_VOLUME_PER_ROW;
+ GLOBAL_SIS (FuelOnBoard) += m;
+ FuelVolume -= m;
+ }
+ }
+
+ DestroyDrawable (ReleaseDrawable (ModuleFrame));
+}
+
+BOOLEAN
+DoShipyard (MENU_STATE *pMS)
+{
+ BOOLEAN select, cancel;
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ goto ExitShipyard;
+
+ select = PulsedInputState.menu[KEY_MENU_SELECT];
+ cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ if (!pMS->Initialized)
+ {
+ pMS->InputFunc = DoShipyard;
+
+ {
+ STAMP s;
+ RECT r, old_r;
+
+ pMS->ModuleFrame = CaptureDrawable (
+ LoadGraphic (SHIPYARD_PMAP_ANIM));
+
+ pMS->CurString = CaptureColorMap (
+ LoadColorMap (HANGAR_COLOR_TAB));
+
+ pMS->hMusic = LoadMusic (SHIPYARD_MUSIC);
+
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ DrawSISFrame ();
+ DrawSISMessage (GAME_STRING (STARBASE_STRING_BASE + 3));
+ DrawSISTitle (GAME_STRING (STARBASE_STRING_BASE));
+ SetContext (SpaceContext);
+ DrawBluePrint (pMS);
+
+ pMS->CurState = SHIPYARD_CREW;
+ DrawMenuStateStrings (PM_CREW, pMS->CurState);
+
+ SetContext (SpaceContext);
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (pMS->ModuleFrame, 0);
+#ifdef USE_3DO_HANGAR
+ DrawStamp (&s);
+#else // PC hangar
+ // the PC ship dock needs to overwrite the border
+ // expand the clipping rect by 1 pixel
+ GetContextClipRect (&old_r);
+ r = old_r;
+ r.corner.x--;
+ r.extent.width += 2;
+ r.extent.height += 1;
+ SetContextClipRect (&r);
+ DrawStamp (&s);
+ SetContextClipRect (&old_r);
+ animatePowerLines (pMS);
+#endif // USE_3DO_HANGAR
+
+ SetContextFont (TinyFont);
+
+ ScreenTransition (3, NULL);
+ UnbatchGraphics ();
+
+ PlayMusic (pMS->hMusic, TRUE, 1);
+
+ ShowCombatShip (pMS, (COUNT)~0, NULL);
+
+ SetInputCallback (on_input_frame);
+
+ SetFlashRect (SFR_MENU_3DO);
+ }
+
+ pMS->Initialized = TRUE;
+ }
+ else if (cancel || (select && pMS->CurState == SHIPYARD_EXIT))
+ {
+ExitShipyard:
+ SetInputCallback (NULL);
+
+ DestroyDrawable (ReleaseDrawable (pMS->ModuleFrame));
+ pMS->ModuleFrame = 0;
+ DestroyColorMap (ReleaseColorMap (pMS->CurString));
+ pMS->CurString = 0;
+
+ return FALSE;
+ }
+ else if (select)
+ {
+ if (pMS->CurState != SHIPYARD_SAVELOAD)
+ {
+ pMS->Initialized = FALSE;
+ DoModifyShips (pMS);
+ }
+ else
+ {
+ // Clearing FlashRect is not necessary
+ if (!GameOptions ())
+ goto ExitShipyard;
+ DrawMenuStateStrings (PM_CREW, pMS->CurState);
+ SetFlashRect (SFR_MENU_3DO);
+ }
+ }
+ else
+ {
+ DoMenuChooser (pMS, PM_CREW);
+ }
+
+ return TRUE;
+}
+
diff --git a/src/uqm/sis.c b/src/uqm/sis.c
new file mode 100644
index 0000000..9f50d1d
--- /dev/null
+++ b/src/uqm/sis.c
@@ -0,0 +1,1741 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "sis.h"
+
+#include "colors.h"
+#include "races.h"
+#include "starmap.h"
+#include "units.h"
+#include "menustat.h"
+ // for DrawMenuStateStrings()
+#include "gamestr.h"
+#include "options.h"
+#include "battle.h"
+ // For BATTLE_FRAME_RATE
+#include "element.h"
+#include "setup.h"
+#include "state.h"
+#include "flash.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/tasklib.h"
+#include "libs/alarm.h"
+#include "libs/log.h"
+
+#include <stdio.h>
+
+static StatMsgMode curMsgMode = SMM_DEFAULT;
+
+static const UNICODE *describeWeapon (BYTE moduleType);
+
+void
+RepairSISBorder (void)
+{
+ RECT r;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (ScreenContext);
+
+ BatchGraphics ();
+
+ // Left border
+ r.corner.x = SIS_ORG_X - 1;
+ r.corner.y = SIS_ORG_Y - 1;
+ r.extent.width = 1;
+ r.extent.height = SIS_SCREEN_HEIGHT + 2;
+ SetContextForeGroundColor (SIS_LEFT_BORDER_COLOR);
+ DrawFilledRectangle (&r);
+
+ // Right border
+ SetContextForeGroundColor (SIS_BOTTOM_RIGHT_BORDER_COLOR);
+ r.corner.x += (SIS_SCREEN_WIDTH + 2) - 1;
+ DrawFilledRectangle (&r);
+
+ // Bottom border
+ r.corner.x = SIS_ORG_X - 1;
+ r.corner.y += (SIS_SCREEN_HEIGHT + 2) - 1;
+ r.extent.width = SIS_SCREEN_WIDTH + 2;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+void
+ClearSISRect (BYTE ClearFlags)
+{
+ RECT r;
+ Color OldColor;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ OldColor = SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+
+ r.corner.x = 2;
+ r.extent.width = STATUS_WIDTH - 4;
+
+ BatchGraphics ();
+ if (ClearFlags & DRAW_SIS_DISPLAY)
+ {
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+ }
+
+ if (ClearFlags & CLEAR_SIS_RADAR)
+ {
+ DrawMenuStateStrings ((BYTE)~0, 1);
+#ifdef NEVER
+ r.corner.x = RADAR_X - 1;
+ r.corner.y = RADAR_Y - 1;
+ r.extent.width = RADAR_WIDTH + 2;
+ r.extent.height = RADAR_HEIGHT + 2;
+
+ DrawStarConBox (&r, 1,
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19),
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F),
+ TRUE, BUILD_COLOR (MAKE_RGB15 (0x00, 0x0E, 0x00), 0x6C));
+#endif /* NEVER */
+ }
+ UnbatchGraphics ();
+
+ SetContextForeGroundColor (OldColor);
+ SetContext (OldContext);
+}
+
+// Draw the SIS title. This is the field at the top of the screen, on the
+// right hand side, containing the coordinates in HyperSpace, or the planet
+// name in IP.
+void
+DrawSISTitle (UNICODE *pStr)
+{
+ TEXT t;
+ CONTEXT OldContext;
+ RECT r;
+
+ t.baseline.x = SIS_TITLE_WIDTH >> 1;
+ t.baseline.y = SIS_TITLE_HEIGHT - 2;
+ t.align = ALIGN_CENTER;
+ t.pStr = pStr;
+ t.CharCount = (COUNT)~0;
+
+ OldContext = SetContext (OffScreenContext);
+ r.corner.x = SIS_ORG_X + SIS_SCREEN_WIDTH - SIS_TITLE_BOX_WIDTH + 1;
+ r.corner.y = SIS_ORG_Y - SIS_TITLE_HEIGHT;
+ r.extent.width = SIS_TITLE_WIDTH;
+ r.extent.height = SIS_TITLE_HEIGHT - 1;
+ SetContextFGFrame (Screen);
+ SetContextClipRect (&r);
+ SetContextFont (TinyFont);
+
+ BatchGraphics ();
+
+ // Background color
+ SetContextBackGroundColor (SIS_TITLE_BACKGROUND_COLOR);
+ ClearDrawable ();
+
+ // Text color
+ SetContextForeGroundColor (SIS_TITLE_TEXT_COLOR);
+ font_DrawText (&t);
+
+ UnbatchGraphics ();
+
+ SetContextClipRect (NULL);
+
+ SetContext (OldContext);
+}
+
+void
+DrawHyperCoords (POINT universe)
+{
+ UNICODE buf[100];
+
+ snprintf (buf, sizeof buf, "%03u.%01u : %03u.%01u",
+ universe.x / 10, universe.x % 10,
+ universe.y / 10, universe.y % 10);
+
+ DrawSISTitle (buf);
+}
+
+void
+DrawSISMessage (const UNICODE *pStr)
+{
+ DrawSISMessageEx (pStr, -1, -1, DSME_NONE);
+}
+
+// See sis.h for the allowed flags.
+BOOLEAN
+DrawSISMessageEx (const UNICODE *pStr, SIZE CurPos, SIZE ExPos, COUNT flags)
+{
+ UNICODE buf[256];
+ CONTEXT OldContext;
+ TEXT t;
+ RECT r;
+
+ OldContext = SetContext (OffScreenContext);
+ // prepare the context
+ r.corner.x = SIS_ORG_X + 1;
+ r.corner.y = SIS_ORG_Y - SIS_MESSAGE_HEIGHT;
+ r.extent.width = SIS_MESSAGE_WIDTH;
+ r.extent.height = SIS_MESSAGE_HEIGHT - 1;
+ SetContextFGFrame (Screen);
+ SetContextClipRect (&r);
+
+ BatchGraphics ();
+ SetContextBackGroundColor (SIS_MESSAGE_BACKGROUND_COLOR);
+
+ if (pStr == 0)
+ {
+ switch (LOBYTE (GLOBAL (CurrentActivity)))
+ {
+ default:
+ case IN_ENCOUNTER:
+ pStr = "";
+ break;
+ case IN_LAST_BATTLE:
+ case IN_INTERPLANETARY:
+ GetClusterName (CurStarDescPtr, buf);
+ pStr = buf;
+ break;
+ case IN_HYPERSPACE:
+ if (inHyperSpace ())
+ {
+ pStr = GAME_STRING (NAVIGATION_STRING_BASE);
+ // "HyperSpace"
+ }
+ else
+ {
+ pStr = GAME_STRING (NAVIGATION_STRING_BASE + 1);
+ // "QuasiSpace"
+ }
+ break;
+ }
+
+ }
+
+ if (!(flags & DSME_MYCOLOR))
+ SetContextForeGroundColor (SIS_MESSAGE_TEXT_COLOR);
+
+ t.baseline.y = SIS_MESSAGE_HEIGHT - 2;
+ t.pStr = pStr;
+ t.CharCount = (COUNT)~0;
+ SetContextFont (TinyFont);
+
+ if (flags & DSME_CLEARFR)
+ SetFlashRect (NULL);
+
+ if (CurPos < 0 && ExPos < 0)
+ { // normal state
+ ClearDrawable ();
+ t.baseline.x = SIS_MESSAGE_WIDTH >> 1;
+ t.align = ALIGN_CENTER;
+ font_DrawText (&t);
+ }
+ else
+ { // editing state
+ int i;
+ RECT text_r;
+ // XXX: 128 is currently safe, but it would be better to specify
+ // the size to TextRect()
+ BYTE char_deltas[128];
+ BYTE *pchar_deltas;
+
+ t.baseline.x = 3;
+ t.align = ALIGN_LEFT;
+
+ TextRect (&t, &text_r, char_deltas);
+ if (text_r.extent.width + t.baseline.x + 2 >= r.extent.width)
+ { // the text does not fit the input box size and so
+ // will not fit when displayed later
+ // disallow the change
+ UnbatchGraphics ();
+ SetContextClipRect (NULL);
+ SetContext (OldContext);
+ return (FALSE);
+ }
+
+ ClearDrawable ();
+
+ if (CurPos >= 0 && CurPos <= t.CharCount)
+ { // calc and draw the cursor
+ RECT cur_r = text_r;
+
+ for (i = CurPos, pchar_deltas = char_deltas; i > 0; --i)
+ cur_r.corner.x += (SIZE)*pchar_deltas++;
+ if (CurPos < t.CharCount) /* end of line */
+ --cur_r.corner.x;
+
+ if (flags & DSME_BLOCKCUR)
+ { // Use block cursor for keyboardless systems
+ if (CurPos == t.CharCount)
+ { // cursor at end-line -- use insertion point
+ cur_r.extent.width = 1;
+ }
+ else if (CurPos + 1 == t.CharCount)
+ { // extra pixel for last char margin
+ cur_r.extent.width = (SIZE)*pchar_deltas + 2;
+ }
+ else
+ { // normal mid-line char
+ cur_r.extent.width = (SIZE)*pchar_deltas + 1;
+ }
+ }
+ else
+ { // Insertion point cursor
+ cur_r.extent.width = 1;
+ }
+
+ cur_r.corner.y = 0;
+ cur_r.extent.height = r.extent.height;
+ SetContextForeGroundColor (SIS_MESSAGE_CURSOR_COLOR);
+ DrawFilledRectangle (&cur_r);
+ }
+
+ SetContextForeGroundColor (SIS_MESSAGE_TEXT_COLOR);
+
+ if (ExPos >= 0 && ExPos < t.CharCount)
+ { // handle extra characters
+ t.CharCount = ExPos;
+ font_DrawText (&t);
+
+ // print extra chars
+ SetContextForeGroundColor (SIS_MESSAGE_EXTRA_TEXT_COLOR);
+ for (i = ExPos, pchar_deltas = char_deltas; i > 0; --i)
+ t.baseline.x += (SIZE)*pchar_deltas++;
+ t.pStr = skipUTF8Chars (t.pStr, ExPos);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ }
+ else
+ { // just print the text
+ font_DrawText (&t);
+ }
+ }
+
+ if (flags & DSME_SETFR)
+ {
+ r.corner.x = 0;
+ r.corner.y = 0;
+ SetFlashRect (&r);
+ }
+
+ UnbatchGraphics ();
+
+ SetContextClipRect (NULL);
+ SetContext (OldContext);
+
+ return (TRUE);
+}
+
+void
+DateToString (char *buf, size_t bufLen,
+ BYTE month_index, BYTE day_index, COUNT year_index)
+{
+ snprintf (buf, bufLen, "%s %02d" STR_MIDDLE_DOT "%04d",
+ GAME_STRING (MONTHS_STRING_BASE + month_index - 1),
+ day_index, year_index);
+}
+
+void
+GetStatusMessageRect (RECT *r)
+{
+ r->corner.x = 2;
+ r->corner.y = 130;
+ r->extent.width = STATUS_MESSAGE_WIDTH;
+ r->extent.height = STATUS_MESSAGE_HEIGHT;
+}
+
+void
+DrawStatusMessage (const UNICODE *pStr)
+{
+ RECT r;
+ RECT ctxRect;
+ TEXT t;
+ UNICODE buf[128];
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ GetContextClipRect (&ctxRect);
+ // XXX: Technically, this does not need OffScreenContext. The only reason
+ // it is used is to avoid preserving StatusContext settings.
+ SetContext (OffScreenContext);
+ SetContextFGFrame (Screen);
+ GetStatusMessageRect (&r);
+ r.corner.x += ctxRect.corner.x;
+ r.corner.y += ctxRect.corner.y;
+ SetContextClipRect (&r);
+
+ BatchGraphics ();
+ SetContextBackGroundColor (STATUS_MESSAGE_BACKGROUND_COLOR);
+ ClearDrawable ();
+
+ if (!pStr)
+ {
+ if (curMsgMode == SMM_CREDITS)
+ {
+ snprintf (buf, sizeof buf, "%u %s", MAKE_WORD (
+ GET_GAME_STATE (MELNORME_CREDIT0),
+ GET_GAME_STATE (MELNORME_CREDIT1)
+ ), GAME_STRING (STATUS_STRING_BASE + 0)); // "Cr"
+ }
+ else if (curMsgMode == SMM_RES_UNITS)
+ {
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
+ {
+ snprintf (buf, sizeof buf, "%u %s", GLOBAL_SIS (ResUnits),
+ GAME_STRING (STATUS_STRING_BASE + 1)); // "RU"
+ }
+ else
+ {
+ snprintf (buf, sizeof buf, "%s %s",
+ (optWhichMenu == OPT_PC) ?
+ GAME_STRING (STATUS_STRING_BASE + 2)
+ : STR_INFINITY_SIGN, // "UNLIMITED"
+ GAME_STRING (STATUS_STRING_BASE + 1)); // "RU"
+ }
+ }
+ else
+ { // Just a date
+ DateToString (buf, sizeof buf,
+ GLOBAL (GameClock.month_index),
+ GLOBAL (GameClock.day_index),
+ GLOBAL (GameClock.year_index));
+ }
+ pStr = buf;
+ }
+
+ t.baseline.x = STATUS_MESSAGE_WIDTH >> 1;
+ t.baseline.y = STATUS_MESSAGE_HEIGHT - 1;
+ t.align = ALIGN_CENTER;
+ t.pStr = pStr;
+ t.CharCount = (COUNT)~0;
+
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (STATUS_MESSAGE_TEXT_COLOR);
+ font_DrawText (&t);
+ UnbatchGraphics ();
+
+ SetContextClipRect (NULL);
+
+ SetContext (OldContext);
+}
+
+StatMsgMode
+SetStatusMessageMode (StatMsgMode newMode)
+{
+ StatMsgMode oldMode = curMsgMode;
+ curMsgMode = newMode;
+ return oldMode;
+}
+
+void
+DrawCaptainsName (void)
+{
+ RECT r;
+ TEXT t;
+ CONTEXT OldContext;
+ FONT OldFont;
+ Color OldColor;
+
+ OldContext = SetContext (StatusContext);
+ OldFont = SetContextFont (TinyFont);
+ OldColor = SetContextForeGroundColor (CAPTAIN_NAME_BACKGROUND_COLOR);
+
+ r.corner.x = 2 + 1;
+ r.corner.y = 10;
+ r.extent.width = SHIP_NAME_WIDTH - 2;
+ r.extent.height = SHIP_NAME_HEIGHT;
+ DrawFilledRectangle (&r);
+
+ t.baseline.x = (STATUS_WIDTH >> 1) - 1;
+ t.baseline.y = r.corner.y + 6;
+ t.align = ALIGN_CENTER;
+ t.pStr = GLOBAL_SIS (CommanderName);
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (CAPTAIN_NAME_TEXT_COLOR);
+ font_DrawText (&t);
+
+ SetContextForeGroundColor (OldColor);
+ SetContextFont (OldFont);
+ SetContext (OldContext);
+}
+
+void
+DrawFlagshipName (BOOLEAN InStatusArea)
+{
+ RECT r;
+ TEXT t;
+ FONT OldFont;
+ Color OldColor;
+ CONTEXT OldContext;
+ FRAME OldFontEffect;
+ UNICODE buf[250];
+
+ if (InStatusArea)
+ {
+ OldContext = SetContext (StatusContext);
+ OldFont = SetContextFont (StarConFont);
+
+ r.corner.x = 2;
+ r.corner.y = 20;
+ r.extent.width = SHIP_NAME_WIDTH;
+ r.extent.height = SHIP_NAME_HEIGHT;
+
+ t.pStr = GLOBAL_SIS (ShipName);
+ }
+ else
+ {
+ OldContext = SetContext (SpaceContext);
+ OldFont = SetContextFont (MicroFont);
+
+ r.corner.x = 0;
+ r.corner.y = 1;
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = SHIP_NAME_HEIGHT;
+
+ t.pStr = buf;
+ snprintf (buf, sizeof buf, "%s %s",
+ GAME_STRING (NAMING_STRING_BASE + 1), GLOBAL_SIS (ShipName));
+ // XXX: this will not work with UTF-8 strings
+ strupr (buf);
+ }
+ OldFontEffect = SetContextFontEffect (NULL);
+ OldColor = SetContextForeGroundColor (FLAGSHIP_NAME_BACKGROUND_COLOR);
+ DrawFilledRectangle (&r);
+
+ t.baseline.x = r.corner.x + (r.extent.width >> 1);
+ t.baseline.y = r.corner.y + (SHIP_NAME_HEIGHT - InStatusArea);
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+ if (optWhichFonts == OPT_PC)
+ SetContextFontEffect (SetAbsFrameIndex (FontGradFrame,
+ InStatusArea ? 0 : 3));
+ else
+ SetContextForeGroundColor (THREEDO_FLAGSHIP_NAME_TEXT_COLOR);
+
+ font_DrawText (&t);
+
+ SetContextFontEffect (OldFontEffect);
+ SetContextForeGroundColor (OldColor);
+ SetContextFont (OldFont);
+ SetContext (OldContext);
+}
+
+void
+DrawFlagshipStats (void)
+{
+ RECT r;
+ TEXT t;
+ FONT OldFont;
+ Color OldColor;
+ FRAME OldFontEffect;
+ CONTEXT OldContext;
+ UNICODE buf[128];
+ SIZE leading;
+ BYTE i;
+ BYTE energy_regeneration, energy_wait, turn_wait;
+ COUNT max_thrust;
+ DWORD fuel;
+
+ /* collect stats */
+#define ENERGY_REGENERATION 1
+#define ENERGY_WAIT 10
+#define MAX_THRUST 10
+#define TURN_WAIT 17
+ energy_regeneration = ENERGY_REGENERATION;
+ energy_wait = ENERGY_WAIT;
+ max_thrust = MAX_THRUST;
+ turn_wait = TURN_WAIT;
+ fuel = 10 * FUEL_TANK_SCALE;
+
+ for (i = 0; i < NUM_MODULE_SLOTS; i++)
+ {
+ switch (GLOBAL_SIS (ModuleSlots[i])) {
+ case FUEL_TANK:
+ fuel += FUEL_TANK_CAPACITY;
+ break;
+ case HIGHEFF_FUELSYS:
+ fuel += HEFUEL_TANK_CAPACITY;
+ break;
+ case DYNAMO_UNIT:
+ energy_wait -= 2;
+ if (energy_wait < 4)
+ energy_wait = 4;
+ break;
+ case SHIVA_FURNACE:
+ energy_regeneration++;
+ break;
+ }
+ }
+
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ if (GLOBAL_SIS (DriveSlots[i]) == FUSION_THRUSTER)
+ max_thrust += 2;
+
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ if (GLOBAL_SIS (JetSlots[i]) == TURNING_JETS)
+ turn_wait -= 2;
+ /* END collect stats */
+
+ OldContext = SetContext (SpaceContext);
+ OldFont = SetContextFont (StarConFont);
+ OldFontEffect = SetContextFontEffect (NULL);
+ GetContextFontLeading (&leading);
+
+ /* we need room to play. full screen width, 4 lines tall */
+ r.corner.x = 0;
+ r.corner.y = SIS_SCREEN_HEIGHT - (4 * leading);
+ r.extent.width = SIS_SCREEN_WIDTH;
+ r.extent.height = (4 * leading);
+
+ OldColor = SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+
+ /*
+ now that we've cleared out our playground, compensate for the
+ fact that the leading is way more than is generally needed.
+ */
+ leading -= 3;
+ t.baseline.x = SIS_SCREEN_WIDTH / 6; //wild-assed guess, but it worked
+ t.baseline.y = r.corner.y + leading + 3;
+ t.align = ALIGN_RIGHT;
+ t.CharCount = (COUNT)~0;
+
+ SetContextFontEffect (SetAbsFrameIndex (FontGradFrame, 4));
+
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 0); // "nose:"
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 1); // "spread:"
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 2); // "side:"
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 3); // "tail:"
+ font_DrawText (&t);
+
+ t.baseline.x += 5;
+ t.baseline.y = r.corner.y + leading + 3;
+ t.align = ALIGN_LEFT;
+ t.pStr = buf;
+
+ snprintf (buf, sizeof buf, "%-7.7s",
+ describeWeapon (GLOBAL_SIS (ModuleSlots[15])));
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ snprintf (buf, sizeof buf,
+ "%-7.7s", describeWeapon (GLOBAL_SIS (ModuleSlots[14])));
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ snprintf (buf, sizeof buf,
+ "%-7.7s", describeWeapon (GLOBAL_SIS (ModuleSlots[13])));
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ snprintf (buf, sizeof buf,
+ "%-7.7s", describeWeapon (GLOBAL_SIS (ModuleSlots[0])));
+ font_DrawText (&t);
+
+ t.baseline.x = r.extent.width - 25;
+ t.baseline.y = r.corner.y + leading + 3;
+ t.align = ALIGN_RIGHT;
+
+ SetContextFontEffect (SetAbsFrameIndex (FontGradFrame, 5));
+
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 4); // "maximum velocity:"
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 5); // "turning rate:"
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 6); // "combat energy:"
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ t.pStr = GAME_STRING (FLAGSHIP_STRING_BASE + 7); // "maximum fuel:"
+ font_DrawText (&t);
+
+ t.baseline.x = r.extent.width - 2;
+ t.baseline.y = r.corner.y + leading + 3;
+ t.pStr = buf;
+
+ snprintf (buf, sizeof buf, "%4u", max_thrust * 4);
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ snprintf (buf, sizeof buf, "%4u", 1 + TURN_WAIT - turn_wait);
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ {
+ unsigned int energy_per_10_sec =
+ (((100 * ONE_SECOND * energy_regeneration) /
+ ((1 + energy_wait) * BATTLE_FRAME_RATE)) + 5) / 10;
+ snprintf (buf, sizeof buf, "%2u.%1u",
+ energy_per_10_sec / 10, energy_per_10_sec % 10);
+ }
+ font_DrawText (&t);
+ t.baseline.y += leading;
+ snprintf (buf, sizeof buf, "%4u", (fuel / FUEL_TANK_SCALE));
+ font_DrawText (&t);
+
+ SetContextFontEffect (OldFontEffect);
+ SetContextForeGroundColor (OldColor);
+ SetContextFont (OldFont);
+ SetContext (OldContext);
+}
+
+static const UNICODE *
+describeWeapon (BYTE moduleType)
+{
+ switch (moduleType)
+ {
+ case GUN_WEAPON:
+ return GAME_STRING (FLAGSHIP_STRING_BASE + 8); // "gun"
+ case BLASTER_WEAPON:
+ return GAME_STRING (FLAGSHIP_STRING_BASE + 9); // "blaster"
+ case CANNON_WEAPON:
+ return GAME_STRING (FLAGSHIP_STRING_BASE + 10); // "cannon"
+ case BOMB_MODULE_0:
+ case BOMB_MODULE_1:
+ case BOMB_MODULE_2:
+ case BOMB_MODULE_3:
+ case BOMB_MODULE_4:
+ case BOMB_MODULE_5:
+ return GAME_STRING (FLAGSHIP_STRING_BASE + 11); // "n/a"
+ default:
+ return GAME_STRING (FLAGSHIP_STRING_BASE + 12); // "none"
+ }
+}
+
+void
+DrawLanders (void)
+{
+ BYTE i;
+ SIZE width;
+ RECT r;
+ STAMP s;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+
+ s.frame = IncFrameIndex (FlagStatFrame);
+ GetFrameRect (s.frame, &r);
+
+ i = GLOBAL_SIS (NumLanders);
+ r.corner.x = (STATUS_WIDTH >> 1) - r.corner.x;
+ s.origin.x = r.corner.x - (((r.extent.width * i) + (2 * (i - 1))) >> 1);
+ s.origin.y = 29;
+
+ width = r.extent.width + 2;
+ r.extent.width = (r.extent.width * MAX_LANDERS)
+ + (2 * (MAX_LANDERS - 1)) + 2;
+ r.corner.x -= r.extent.width >> 1;
+ r.corner.y += s.origin.y;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ while (i--)
+ {
+ DrawStamp (&s);
+ s.origin.x += width;
+ }
+
+ SetContext (OldContext);
+}
+
+// Draw the storage bays, below the picture of the flagship.
+void
+DrawStorageBays (BOOLEAN Refresh)
+{
+ BYTE i;
+ RECT r;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+
+ r.extent.width = 2;
+ r.extent.height = 4;
+ r.corner.y = 123;
+ if (Refresh)
+ {
+ r.extent.width = NUM_MODULE_SLOTS * (r.extent.width + 1);
+ r.corner.x = (STATUS_WIDTH >> 1) - (r.extent.width >> 1);
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ r.extent.width = 2;
+ }
+
+ i = (BYTE)CountSISPieces (STORAGE_BAY);
+ if (i)
+ {
+ COUNT j;
+
+ r.corner.x = (STATUS_WIDTH >> 1)
+ - ((i * (r.extent.width + 1)) >> 1);
+ SetContextForeGroundColor (STORAGE_BAY_FULL_COLOR);
+ for (j = GLOBAL_SIS (TotalElementMass);
+ j >= STORAGE_BAY_CAPACITY; j -= STORAGE_BAY_CAPACITY)
+ {
+ DrawFilledRectangle (&r);
+ r.corner.x += r.extent.width + 1;
+
+ --i;
+ }
+
+ r.extent.height = (4 * j + (STORAGE_BAY_CAPACITY - 1)) /
+ STORAGE_BAY_CAPACITY;
+ if (r.extent.height)
+ {
+ r.corner.y += 4 - r.extent.height;
+ DrawFilledRectangle (&r);
+ r.extent.height = 4 - r.extent.height;
+ if (r.extent.height)
+ {
+ r.corner.y = 123;
+ SetContextForeGroundColor (STORAGE_BAY_EMPTY_COLOR);
+ DrawFilledRectangle (&r);
+ }
+ r.corner.x += r.extent.width + 1;
+
+ --i;
+ }
+ r.extent.height = 4;
+
+ SetContextForeGroundColor (STORAGE_BAY_EMPTY_COLOR);
+ while (i--)
+ {
+ DrawFilledRectangle (&r);
+ r.corner.x += r.extent.width + 1;
+ }
+ }
+
+ SetContext (OldContext);
+}
+
+void
+GetGaugeRect (RECT *pRect, BOOLEAN IsCrewRect)
+{
+ pRect->extent.width = 24;
+ pRect->corner.x = (STATUS_WIDTH >> 1) - (pRect->extent.width >> 1);
+ pRect->extent.height = 5;
+ pRect->corner.y = IsCrewRect ? 117 : 38;
+}
+
+static void
+DrawPC_SIS (void)
+{
+ TEXT t;
+ RECT r;
+
+ GetGaugeRect (&r, FALSE);
+ t.baseline.x = STATUS_WIDTH >> 1;
+ t.baseline.y = r.corner.y - 1;
+ t.align = ALIGN_CENTER;
+ t.CharCount = (COUNT)~0;
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (BLACK_COLOR);
+
+ r.corner.y -= 6;
+ r.corner.x--;
+ r.extent.width += 2;
+ DrawFilledRectangle (&r);
+
+ SetContextFontEffect (SetAbsFrameIndex (FontGradFrame, 1));
+ t.pStr = GAME_STRING (STATUS_STRING_BASE + 3); // "FUEL"
+ font_DrawText (&t);
+
+ r.corner.y += 79;
+ t.baseline.y += 79;
+ DrawFilledRectangle (&r);
+
+ SetContextFontEffect (SetAbsFrameIndex (FontGradFrame, 2));
+ t.pStr = GAME_STRING (STATUS_STRING_BASE + 4); // "CREW"
+ font_DrawText (&t);
+ SetContextFontEffect (NULL);
+
+ // Background of text "CAPTAIN".
+ r.corner.x = 2 + 1;
+ r.corner.y = 3;
+ r.extent.width = 58;
+ r.extent.height = 7;
+ SetContextForeGroundColor (PC_CAPTAIN_STRING_BACKGROUND_COLOR);
+ DrawFilledRectangle (&r);
+
+ // Text "CAPTAIN".
+ SetContextForeGroundColor (PC_CAPTAIN_STRING_TEXT_COLOR);
+ t.baseline.y = r.corner.y + 6;
+ t.pStr = GAME_STRING (STATUS_STRING_BASE + 5); // "CAPTAIN"
+ font_DrawText (&t);
+}
+
+static void
+DrawThrusters (void)
+{
+ STAMP s;
+ COUNT i;
+
+ s.origin.x = 1;
+ s.origin.y = 0;
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ BYTE which_piece = GLOBAL_SIS (DriveSlots[i]);
+ if (which_piece < EMPTY_SLOT)
+ {
+ s.frame = SetAbsFrameIndex (FlagStatFrame, which_piece + 1 + 0);
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ DrawStamp (&s);
+ }
+
+ s.origin.y -= 3;
+ }
+}
+
+static void
+DrawTurningJets (void)
+{
+ STAMP s;
+ COUNT i;
+
+ s.origin.x = 1;
+ s.origin.y = 0;
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ BYTE which_piece = GLOBAL_SIS (JetSlots[i]);
+ if (which_piece < EMPTY_SLOT)
+ {
+ s.frame = SetAbsFrameIndex (FlagStatFrame, which_piece + 1 + 1);
+ DrawStamp (&s);
+ s.frame = IncFrameIndex (s.frame);
+ DrawStamp (&s);
+ }
+
+ s.origin.y -= 3;
+ }
+}
+
+static void
+DrawModules (void)
+{
+ STAMP s;
+ COUNT i;
+
+ s.origin.x = 1; // This properly centers the modules.
+ s.origin.y = 1;
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ BYTE which_piece = GLOBAL_SIS (ModuleSlots[i]);
+ if (which_piece < EMPTY_SLOT)
+ {
+ s.frame = SetAbsFrameIndex (FlagStatFrame, which_piece + 1 + 2);
+ DrawStamp (&s);
+ }
+
+ s.origin.y -= 3;
+ }
+}
+
+static void
+DrawSupportShips (void)
+{
+ HSHIPFRAG hStarShip;
+ HSHIPFRAG hNextShip;
+ const POINT *pship_pos;
+ const POINT ship_pos[MAX_BUILT_SHIPS] =
+ {
+ SUPPORT_SHIP_PTS
+ };
+
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q)),
+ pship_pos = ship_pos;
+ hStarShip; hStarShip = hNextShip, ++pship_pos)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+ STAMP s;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+
+ s.origin = *pship_pos;
+ s.frame = StarShipPtr->icons;
+ DrawStamp (&s);
+
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+}
+
+static void
+DeltaSISGauges_crewDelta (SIZE crew_delta)
+{
+ if (crew_delta == 0)
+ return;
+
+ if (crew_delta != UNDEFINED_DELTA)
+ {
+ COUNT CrewCapacity;
+
+ if (crew_delta < 0
+ && GLOBAL_SIS (CrewEnlisted) <= (COUNT)-crew_delta)
+ GLOBAL_SIS (CrewEnlisted) = 0;
+ else
+ {
+ GLOBAL_SIS (CrewEnlisted) += crew_delta;
+ CrewCapacity = GetCrewPodCapacity ();
+ if (GLOBAL_SIS (CrewEnlisted) > CrewCapacity)
+ GLOBAL_SIS (CrewEnlisted) = CrewCapacity;
+ }
+ }
+
+ {
+ TEXT t;
+ UNICODE buf[60];
+ RECT r;
+
+ snprintf (buf, sizeof buf, "%u", GLOBAL_SIS (CrewEnlisted));
+
+ GetGaugeRect (&r, TRUE);
+
+ t.baseline.x = STATUS_WIDTH >> 1;
+ t.baseline.y = r.corner.y + r.extent.height;
+ t.align = ALIGN_CENTER;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x0E, 0x00), 0x6C));
+ font_DrawText (&t);
+ }
+}
+
+static void
+DeltaSISGauges_fuelDelta (SIZE fuel_delta)
+{
+ COUNT old_coarse_fuel;
+ COUNT new_coarse_fuel;
+
+ if (fuel_delta == 0)
+ return;
+
+ if (fuel_delta == UNDEFINED_DELTA)
+ old_coarse_fuel = (COUNT)~0;
+ else
+ {
+
+ old_coarse_fuel = (COUNT)(
+ GLOBAL_SIS (FuelOnBoard) / FUEL_TANK_SCALE);
+ if (fuel_delta < 0
+ && GLOBAL_SIS (FuelOnBoard) <= (DWORD)-fuel_delta)
+ {
+ GLOBAL_SIS (FuelOnBoard) = 0;
+ }
+ else
+ {
+ DWORD FuelCapacity = GetFuelTankCapacity ();
+ GLOBAL_SIS (FuelOnBoard) += fuel_delta;
+ if (GLOBAL_SIS (FuelOnBoard) > FuelCapacity)
+ GLOBAL_SIS (FuelOnBoard) = FuelCapacity;
+ }
+ }
+
+ new_coarse_fuel = (COUNT)(
+ GLOBAL_SIS (FuelOnBoard) / FUEL_TANK_SCALE);
+ if (new_coarse_fuel != old_coarse_fuel)
+ {
+ TEXT t;
+ UNICODE buf[60];
+ RECT r;
+
+ snprintf (buf, sizeof buf, "%u", new_coarse_fuel);
+
+ GetGaugeRect (&r, FALSE);
+
+ t.baseline.x = STATUS_WIDTH >> 1;
+ t.baseline.y = r.corner.y + r.extent.height;
+ t.align = ALIGN_CENTER;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2C));
+ font_DrawText (&t);
+ }
+}
+
+static void
+DeltaSISGauges_resunitDelta (SIZE resunit_delta)
+{
+ if (resunit_delta == 0)
+ return;
+
+ if (resunit_delta != UNDEFINED_DELTA)
+ {
+ if (resunit_delta < 0
+ && GLOBAL_SIS (ResUnits) <= (DWORD)-resunit_delta)
+ GLOBAL_SIS (ResUnits) = 0;
+ else
+ GLOBAL_SIS (ResUnits) += resunit_delta;
+
+ assert (curMsgMode == SMM_RES_UNITS);
+ }
+ else
+ {
+ RECT r;
+
+ r.corner.x = 2;
+ r.corner.y = 130;
+ r.extent.width = STATUS_MESSAGE_WIDTH;
+ r.extent.height = STATUS_MESSAGE_HEIGHT;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x08, 0x00), 0x6E));
+ DrawFilledRectangle (&r);
+ }
+
+ DrawStatusMessage (NULL);
+}
+
+void
+DeltaSISGauges (SIZE crew_delta, SIZE fuel_delta, int resunit_delta)
+{
+ CONTEXT OldContext;
+
+ if (crew_delta == 0 && fuel_delta == 0 && resunit_delta == 0)
+ return;
+
+ OldContext = SetContext (StatusContext);
+
+ BatchGraphics ();
+ if (crew_delta == UNDEFINED_DELTA)
+ {
+ STAMP s;
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = FlagStatFrame;
+ DrawStamp (&s);
+
+ if (optWhichFonts == OPT_PC)
+ DrawPC_SIS();
+
+ DrawThrusters ();
+ DrawTurningJets ();
+ DrawModules ();
+
+ DrawSupportShips ();
+ }
+
+ SetContextFont (TinyFont);
+
+ DeltaSISGauges_crewDelta (crew_delta);
+ DeltaSISGauges_fuelDelta (fuel_delta);
+
+ if (crew_delta == UNDEFINED_DELTA)
+ {
+ DrawFlagshipName (TRUE);
+ DrawCaptainsName ();
+ DrawLanders ();
+ DrawStorageBays (FALSE);
+ }
+
+ DeltaSISGauges_resunitDelta (resunit_delta);
+
+ UnbatchGraphics ();
+
+ SetContext (OldContext);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Crew
+////////////////////////////////////////////////////////////////////////////
+
+// Get the total amount of crew aboard the SIS.
+COUNT
+GetCrewCount (void)
+{
+ return GLOBAL_SIS (CrewEnlisted);
+}
+
+// Get the number of crew which fit in a module of a specified type.
+COUNT
+GetModuleCrewCapacity (BYTE moduleType)
+{
+ if (moduleType == CREW_POD)
+ return CREW_POD_CAPACITY;
+
+ return 0;
+}
+
+// Gets the amount of crew which currently fit in the ship's crew pods.
+COUNT
+GetCrewPodCapacity (void)
+{
+ COUNT capacity = 0;
+ COUNT slotI;
+
+ for (slotI = 0; slotI < NUM_MODULE_SLOTS; slotI++)
+ {
+ BYTE moduleType = GLOBAL_SIS (ModuleSlots[slotI]);
+ capacity += GetModuleCrewCapacity (moduleType);
+ }
+
+ return capacity;
+}
+
+// Find the slot number of the crew pod and "seat" number in that crew pod,
+// where the Nth crew member would be located.
+// If the crew member does not fit, false is returned, and *slotNr and
+// *seatNr are unchanged.
+static bool
+GetCrewPodForCrewMember (COUNT crewNr, COUNT *slotNr, COUNT *seatNr)
+{
+ COUNT slotI;
+ COUNT capacity = 0;
+
+ slotI = NUM_MODULE_SLOTS;
+ while (slotI--) {
+ BYTE moduleType = GLOBAL_SIS (ModuleSlots[slotI]);
+ COUNT moduleCapacity = GetModuleCrewCapacity (moduleType);
+
+ if (crewNr < capacity + moduleCapacity)
+ {
+ *slotNr = slotI;
+ *seatNr = crewNr - capacity;
+ return true;
+ }
+ capacity += moduleCapacity;
+ }
+
+ return false;
+}
+
+// Get the point where to draw the next crew member,
+// set the foreground color to the color for that crew member,
+// and return GetCrewPodCapacity ().
+// TODO: Split of the parts of this function into separate functions.
+COUNT
+GetCPodCapacity (POINT *ppt)
+{
+ COUNT crewCount;
+ COUNT slotNr;
+ COUNT seatNr;
+
+ COUNT rowNr;
+ COUNT colNr;
+
+ static const Color crewRows[] = PC_CREW_COLOR_TABLE;
+
+ crewCount = GetCrewCount ();
+ if (!GetCrewPodForCrewMember (crewCount, &slotNr, &seatNr))
+ {
+ // Crew does not fit. *ppt is unchanged.
+ return GetCrewPodCapacity ();
+ }
+
+ rowNr = seatNr / CREW_PER_ROW;
+ colNr = seatNr % CREW_PER_ROW;
+
+ if (optWhichFonts == OPT_PC)
+ SetContextForeGroundColor (crewRows[rowNr]);
+ else
+ SetContextForeGroundColor (THREEDO_CREW_COLOR);
+
+ ppt->x = 27 + (slotNr * SHIP_PIECE_OFFSET) - (colNr * 2);
+ ppt->y = 34 - (rowNr * 2);
+
+ return GetCrewPodCapacity ();
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Storage bays
+////////////////////////////////////////////////////////////////////////////
+
+// Get the total amount of minerals aboard the SIS.
+static COUNT
+GetElementMass (void)
+{
+ return GLOBAL_SIS (TotalElementMass);
+}
+
+// Get the number of crew which fit in a module of a specified type.
+COUNT
+GetModuleStorageCapacity (BYTE moduleType)
+{
+ if (moduleType == STORAGE_BAY)
+ return STORAGE_BAY_CAPACITY;
+
+ return 0;
+}
+
+// Gets the amount of minerals which currently fit in the ship's storage.
+COUNT
+GetStorageBayCapacity (void)
+{
+ COUNT capacity = 0;
+ COUNT slotI;
+
+ for (slotI = 0; slotI < NUM_MODULE_SLOTS; slotI++)
+ {
+ BYTE moduleType = GLOBAL_SIS (ModuleSlots[slotI]);
+ capacity += GetModuleStorageCapacity (moduleType);
+ }
+
+ return capacity;
+}
+
+// Find the slot number of the storage bay and "storage cell" number in that
+// storage bay, where the N-1th mineral unit would be located.
+// If the mineral unit does not fit, false is returned, and *slotNr and
+// *cellNr are unchanged.
+static bool
+GetStorageCellForMineralUnit (COUNT unitNr, COUNT *slotNr, COUNT *cellNr)
+{
+ COUNT slotI;
+ COUNT capacity = 0;
+
+ slotI = NUM_MODULE_SLOTS;
+ while (slotI--) {
+ BYTE moduleType = GLOBAL_SIS (ModuleSlots[slotI]);
+ COUNT moduleCapacity = GetModuleStorageCapacity (moduleType);
+
+ if (unitNr <= capacity + moduleCapacity)
+ {
+ *slotNr = slotI;
+ *cellNr = unitNr - capacity;
+ return true;
+ }
+ capacity += moduleCapacity;
+ }
+
+ return false;
+}
+
+// Get the point where to draw the next mineral unit,
+// set the foreground color to the color for that mineral unit,
+// and return GetStorageBayCapacity ().
+// TODO: Split of the parts of this function into separate functions.
+COUNT
+GetSBayCapacity (POINT *ppt)
+{
+ COUNT massCount;
+ COUNT slotNr;
+ COUNT cellNr;
+
+ COUNT rowNr;
+ COUNT colNr;
+
+ static const Color colorBars[] = STORAGE_BAY_COLOR_TABLE;
+
+ massCount = GetElementMass ();
+ if (!GetStorageCellForMineralUnit (massCount, &slotNr, &cellNr))
+ {
+ // Crew does not fit. *ppt is unchanged.
+ return GetStorageBayCapacity ();
+ }
+
+ rowNr = cellNr / SBAY_MASS_PER_ROW;
+ colNr = cellNr % SBAY_MASS_PER_ROW;
+
+ if (rowNr == 0)
+ SetContextForeGroundColor (BLACK_COLOR);
+ else
+ {
+ rowNr--;
+ SetContextForeGroundColor (colorBars[rowNr]);
+ }
+
+ ppt->x = 19 + (slotNr * SHIP_PIECE_OFFSET);
+ ppt->y = 34 - (rowNr * 2);
+
+ return GetStorageBayCapacity ();
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Fuel tanks
+////////////////////////////////////////////////////////////////////////////
+
+// Get the total amount of fuel aboard the SIS.
+static DWORD
+GetFuelTotal (void)
+{
+ return GLOBAL_SIS (FuelOnBoard);
+}
+
+// Get the amount of fuel which fits in a module of a specified type.
+DWORD
+GetModuleFuelCapacity (BYTE moduleType)
+{
+ if (moduleType == FUEL_TANK)
+ return FUEL_TANK_CAPACITY;
+
+ if (moduleType == HIGHEFF_FUELSYS)
+ return HEFUEL_TANK_CAPACITY;
+
+ return 0;
+}
+
+// Gets the amount of fuel which currently fits in the ship's fuel tanks.
+DWORD
+GetFuelTankCapacity (void)
+{
+ DWORD capacity = FUEL_RESERVE;
+ COUNT slotI;
+
+ for (slotI = 0; slotI < NUM_MODULE_SLOTS; slotI++)
+ {
+ BYTE moduleType = GLOBAL_SIS (ModuleSlots[slotI]);
+ capacity += GetModuleFuelCapacity (moduleType);
+ }
+
+ return capacity;
+}
+
+// Find the slot number of the fuel cell and "compartment" number in that
+// crew pod, where the Nth unit of fuel would be located.
+// If the unit does not fit, false is returned, and *slotNr and
+// *compartmentNr are unchanged.
+// Pre: unitNr >= FUEL_RESERER
+static bool
+GetFuelTankForFuelUnit (DWORD unitNr, COUNT *slotNr, DWORD *compartmentNr)
+{
+ COUNT slotI;
+ DWORD capacity = FUEL_RESERVE;
+
+ assert (unitNr >= FUEL_RESERVE);
+
+ slotI = NUM_MODULE_SLOTS;
+ while (slotI--) {
+ BYTE moduleType = GLOBAL_SIS (ModuleSlots[slotI]);
+
+ capacity += GetModuleFuelCapacity (moduleType);
+ if (unitNr < capacity)
+ {
+ *slotNr = slotI;
+ *compartmentNr = capacity - unitNr;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Get the point where to draw the next fuel unit,
+// set the foreground color to the color for that unit,
+// and return GetFuelTankCapacity ().
+// TODO: Split of the parts of this function into separate functions.
+DWORD
+GetFTankCapacity (POINT *ppt)
+{
+ DWORD capacity;
+ DWORD fuelAmount;
+ COUNT slotNr;
+ DWORD compartmentNr;
+ BYTE moduleType;
+ DWORD volume;
+
+ COUNT rowNr;
+
+ static const Color fuelColors[] = FUEL_COLOR_TABLE;
+
+ capacity = GetFuelTankCapacity ();
+ fuelAmount = GetFuelTotal ();
+ if (fuelAmount < FUEL_RESERVE)
+ {
+ // Fuel is in the SIS reserve, not in a fuel tank.
+ // *ppt is unchanged
+ return capacity;
+ }
+
+ if (!GetFuelTankForFuelUnit (fuelAmount, &slotNr, &compartmentNr))
+ {
+ // Fuel does not fit. *ppt is unchanged.
+ return capacity;
+ }
+
+ moduleType = GLOBAL_SIS (ModuleSlots[slotNr]);
+ volume = GetModuleFuelCapacity (moduleType);
+
+ rowNr = ((volume - compartmentNr) * MAX_FUEL_BARS / HEFUEL_TANK_CAPACITY);
+
+ ppt->x = 21 + (slotNr * SHIP_PIECE_OFFSET);
+ if (volume == FUEL_TANK_CAPACITY)
+ ppt->y = 27 - rowNr;
+ else
+ ppt->y = 30 - rowNr;
+
+ assert (rowNr + 1 < (COUNT) (sizeof fuelColors / sizeof fuelColors[0]));
+ SetContextForeGroundColor (fuelColors[rowNr]);
+ SetContextBackGroundColor (fuelColors[rowNr + 1]);
+
+ return capacity;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+COUNT
+CountSISPieces (BYTE piece_type)
+{
+ COUNT i, num_pieces;
+
+ num_pieces = 0;
+ if (piece_type == FUSION_THRUSTER)
+ {
+ for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (DriveSlots[i]) == piece_type)
+ ++num_pieces;
+ }
+ }
+ else if (piece_type == TURNING_JETS)
+ {
+ for (i = 0; i < NUM_JET_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (JetSlots[i]) == piece_type)
+ ++num_pieces;
+ }
+ }
+ else
+ {
+ for (i = 0; i < NUM_MODULE_SLOTS; ++i)
+ {
+ if (GLOBAL_SIS (ModuleSlots[i]) == piece_type)
+ ++num_pieces;
+ }
+ }
+
+ return num_pieces;
+}
+
+void
+DrawAutoPilotMessage (BOOLEAN Reset)
+{
+ static BOOLEAN LastPilot = FALSE;
+ static TimeCount NextTime = 0;
+ static DWORD cycle_index = 0;
+ BOOLEAN OnAutoPilot;
+
+ static const Color cycle_tab[] = AUTOPILOT_COLOR_CYCLE_TABLE;
+ const size_t cycleCount = sizeof cycle_tab / sizeof cycle_tab[0];
+#define BLINK_RATE (ONE_SECOND * 3 / 40) // 9 @ 120 ticks/second
+
+ if (Reset)
+ { // Just a reset, not drawing
+ LastPilot = FALSE;
+ return;
+ }
+
+ OnAutoPilot = (GLOBAL (autopilot.x) != ~0 && GLOBAL (autopilot.y) != ~0)
+ || GLOBAL_SIS (FuelOnBoard) == 0;
+
+ if (OnAutoPilot || LastPilot)
+ {
+ if (!OnAutoPilot)
+ { // AutoPilot aborted -- clear the AUTO-PILOT message
+ DrawSISMessage (NULL);
+ cycle_index = 0;
+ }
+ else if (GetTimeCounter () >= NextTime)
+ {
+ if (!(GLOBAL (CurrentActivity) & CHECK_ABORT)
+ && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
+ {
+ CONTEXT OldContext;
+
+ OldContext = SetContext (OffScreenContext);
+ SetContextForeGroundColor (cycle_tab[cycle_index]);
+ if (GLOBAL_SIS (FuelOnBoard) == 0)
+ {
+ DrawSISMessageEx (GAME_STRING (NAVIGATION_STRING_BASE + 2),
+ -1, -1, DSME_MYCOLOR); // "OUT OF FUEL"
+ }
+ else
+ {
+ DrawSISMessageEx (GAME_STRING (NAVIGATION_STRING_BASE + 3),
+ -1, -1, DSME_MYCOLOR); // "AUTO-PILOT"
+ }
+ SetContext (OldContext);
+ }
+
+ cycle_index = (cycle_index + 1) % cycleCount;
+ NextTime = GetTimeCounter () + BLINK_RATE;
+ }
+
+ LastPilot = OnAutoPilot;
+ }
+}
+
+
+static FlashContext *flashContext = NULL;
+static RECT flash_rect;
+static Alarm *flashAlarm = NULL;
+static BOOLEAN flashPaused = FALSE;
+
+static void scheduleFlashAlarm (void);
+
+static void
+updateFlashRect (void *arg)
+{
+ if (flashContext == NULL)
+ return;
+
+ Flash_process (flashContext);
+ scheduleFlashAlarm ();
+ (void) arg;
+}
+
+static void
+scheduleFlashAlarm (void)
+{
+ TimeCount nextTime = Flash_nextTime (flashContext);
+ DWORD nextTimeMs = (nextTime / ONE_SECOND) * 1000 +
+ ((nextTime % ONE_SECOND) * 1000 / ONE_SECOND);
+ // Overflow-safe conversion.
+ flashAlarm = Alarm_addAbsoluteMs (nextTimeMs, updateFlashRect, NULL);
+}
+
+void
+SetFlashRect (const RECT *pRect)
+{
+ RECT clip_r = {{0, 0}, {0, 0}};
+ RECT temp_r;
+
+ if (pRect != SFR_MENU_3DO && pRect != SFR_MENU_ANY)
+ {
+ // The caller specified their own flash area, or NULL (stop flashing).
+ GetContextClipRect (&clip_r);
+ }
+ else
+ {
+ if (optWhichMenu == OPT_PC && pRect != SFR_MENU_ANY)
+ {
+ // The player wants PC menus and this flash is not used
+ // for a PC menu.
+ // Don't flash.
+ pRect = 0;
+ }
+ else
+ {
+ // The player wants 3DO menus, or the flash is used in both
+ // 3DO and PC mode.
+ CONTEXT OldContext = SetContext (StatusContext);
+ GetContextClipRect (&clip_r);
+ pRect = &temp_r;
+ temp_r.corner.x = RADAR_X - clip_r.corner.x;
+ temp_r.corner.y = RADAR_Y - clip_r.corner.y;
+ temp_r.extent.width = RADAR_WIDTH;
+ temp_r.extent.height = RADAR_HEIGHT;
+ SetContext (OldContext);
+ }
+ }
+
+ if (pRect != 0 && pRect->extent.width != 0)
+ {
+ // Flash rectangle is not empty, start or continue flashing.
+ flash_rect = *pRect;
+ flash_rect.corner.x += clip_r.corner.x;
+ flash_rect.corner.y += clip_r.corner.y;
+
+ if (flashContext == NULL)
+ {
+ // Create a new flash context.
+ flashContext = Flash_createHighlight (ScreenContext, &flash_rect);
+ Flash_setMergeFactors(flashContext, 3, 2, 2);
+ Flash_setSpeed (flashContext, 0, ONE_SECOND / 16, 0, ONE_SECOND / 16);
+ Flash_setFrameTime (flashContext, ONE_SECOND / 16);
+ Flash_start (flashContext);
+ scheduleFlashAlarm ();
+ }
+ else
+ {
+ // Reuse an existing flash context
+ Flash_setRect (flashContext, &flash_rect);
+ }
+ }
+ else
+ {
+ // Flash rectangle is empty. Stop flashing.
+ if (flashContext != NULL)
+ {
+ Alarm_remove(flashAlarm);
+ flashAlarm = 0;
+
+ Flash_terminate (flashContext);
+ flashContext = NULL;
+ }
+ }
+}
+
+COUNT updateFlashRectRecursion = 0;
+// XXX This is necessary at least because DMS_AddEscortShip() calls
+// DrawRaceStrings() in an UpdateFlashRect block, which calls
+// ClearSISRect(), which calls DrawMenuStateStrings(), which starts its own
+// UpdateFlashRect block. This should probably be cleaned up.
+
+void
+PreUpdateFlashRect (void)
+{
+ if (flashAlarm)
+ {
+ updateFlashRectRecursion++;
+ if (updateFlashRectRecursion > 1)
+ return;
+ Flash_preUpdate (flashContext);
+ }
+}
+
+void
+PostUpdateFlashRect (void)
+{
+ if (flashAlarm)
+ {
+ updateFlashRectRecursion--;
+ if (updateFlashRectRecursion > 0)
+ return;
+
+ Flash_postUpdate (flashContext);
+ }
+}
+
+// Stop flashing if flashing is active.
+void
+PauseFlash (void)
+{
+ if (flashContext != NULL)
+ {
+ Alarm_remove(flashAlarm);
+ flashAlarm = 0;
+ flashPaused = TRUE;
+ }
+}
+
+// Continue flashing after PauseFlash (), if flashing was active.
+void
+ContinueFlash (void)
+{
+ if (flashPaused)
+ {
+ scheduleFlashAlarm ();
+ flashPaused = FALSE;
+ }
+}
+
+
diff --git a/src/uqm/sis.h b/src/uqm/sis.h
new file mode 100644
index 0000000..ee07a81
--- /dev/null
+++ b/src/uqm/sis.h
@@ -0,0 +1,241 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SIS_H_INCL__
+#define SIS_H_INCL__
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+#include "planets/elemdata.h"
+ // for NUM_ELEMENT_CATEGORIES
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define CLEAR_SIS_RADAR (1 << 2)
+#define DRAW_SIS_DISPLAY (1 << 3)
+
+#define UNDEFINED_DELTA 0x7FFF
+
+#define NUM_DRIVE_SLOTS 11
+#define NUM_JET_SLOTS 8
+#define NUM_MODULE_SLOTS 16
+
+#define CREW_POD_CAPACITY 50
+#define STORAGE_BAY_CAPACITY 500 /* km cubed */
+#define FUEL_TANK_SCALE 100
+#define FUEL_TANK_CAPACITY (50 * FUEL_TANK_SCALE)
+#define HEFUEL_TANK_CAPACITY (100 * FUEL_TANK_SCALE)
+#define MODULE_COST_SCALE 50
+
+#define CREW_EXPENSE_THRESHOLD 1000
+
+#define CREW_PER_ROW 5
+#define SBAY_MASS_PER_ROW 50
+
+#define MAX_FUEL_BARS 10
+#define FUEL_VOLUME_PER_ROW (HEFUEL_TANK_CAPACITY / MAX_FUEL_BARS)
+#define FUEL_RESERVE FUEL_VOLUME_PER_ROW
+
+#define IP_SHIP_THRUST_INCREMENT 8
+#define IP_SHIP_TURN_WAIT 17
+#define IP_SHIP_TURN_DECREMENT 2
+
+#define BIO_CREDIT_VALUE 2
+
+enum
+{
+ PLANET_LANDER = 0,
+ /* thruster types */
+ FUSION_THRUSTER,
+ /* jet types */
+ TURNING_JETS,
+ /* module types */
+ CREW_POD,
+ STORAGE_BAY,
+ FUEL_TANK,
+ HIGHEFF_FUELSYS,
+ DYNAMO_UNIT,
+ SHIVA_FURNACE,
+ GUN_WEAPON,
+ BLASTER_WEAPON,
+ CANNON_WEAPON,
+ TRACKING_SYSTEM,
+ ANTIMISSILE_DEFENSE,
+
+ NUM_PURCHASE_MODULES,
+
+ BOMB_MODULE_0 = NUM_PURCHASE_MODULES,
+ BOMB_MODULE_1,
+ BOMB_MODULE_2,
+ BOMB_MODULE_3,
+ BOMB_MODULE_4,
+ BOMB_MODULE_5,
+
+ NUM_MODULES /* must be last entry */
+};
+
+#define EMPTY_SLOT NUM_MODULES
+#define NUM_BOMB_MODULES 10
+
+#define DRIVE_SIDE_X 31
+#define DRIVE_SIDE_Y 56
+#define DRIVE_TOP_X 33
+#define DRIVE_TOP_Y (65 + 21)
+
+#define JET_SIDE_X 71
+#define JET_SIDE_Y 48
+#define JET_TOP_X 70
+#define JET_TOP_Y (73 + 21)
+
+#define MODULE_SIDE_X 17
+#define MODULE_SIDE_Y 14
+#define MODULE_TOP_X 17
+#define MODULE_TOP_Y (96 + 21)
+
+#define SHIP_PIECE_OFFSET 12
+
+#define MAX_BUILT_SHIPS 12
+ /* Maximum number of ships escorting the SIS */
+#define MAX_LANDERS 10
+
+#define SUPPORT_SHIP_PTS \
+ {3 + 0, 30 + (2 * 16)}, \
+ {3 + 42, 30 + (2 * 16)}, \
+ {3 + 0, 30 + (3 * 16)}, \
+ {3 + 42, 30 + (3 * 16)}, \
+ {3 + 0, 30 + (1 * 16)}, \
+ {3 + 42, 30 + (1 * 16)}, \
+ {3 + 0, 30 + (4 * 16)}, \
+ {3 + 42, 30 + (4 * 16)}, \
+ {3 + 0, 30 + (0 * 16)}, \
+ {3 + 42, 30 + (0 * 16)}, \
+ {3 + 0, 30 + (5 * 16)}, \
+ {3 + 42, 30 + (5 * 16)},
+
+#define SIS_NAME_SIZE 16
+
+typedef struct
+{
+ SDWORD log_x, log_y;
+
+ DWORD ResUnits;
+
+ DWORD FuelOnBoard;
+ COUNT CrewEnlisted;
+ // Number of crew on board, not counting the captain.
+ // Set to (COUNT) ~0 to indicate game over.
+ COUNT TotalElementMass, TotalBioMass;
+
+ BYTE ModuleSlots[NUM_MODULE_SLOTS];
+ BYTE DriveSlots[NUM_DRIVE_SLOTS];
+ BYTE JetSlots[NUM_JET_SLOTS];
+
+ BYTE NumLanders;
+
+ COUNT ElementAmounts[NUM_ELEMENT_CATEGORIES];
+
+ UNICODE ShipName[SIS_NAME_SIZE];
+ UNICODE CommanderName[SIS_NAME_SIZE];
+ UNICODE PlanetName[SIS_NAME_SIZE];
+} SIS_STATE;
+
+#define OVERRIDE_LANDER_FLAGS (1 << 7)
+#define AFTER_BOMB_INSTALLED (1 << 7)
+
+extern void RepairSISBorder (void);
+extern void InitSISContexts (void);
+extern void DrawSISFrame (void);
+extern void ClearSISRect (BYTE ClearFlags);
+extern void SetFlashRect (const RECT *pRect);
+extern void PreUpdateFlashRect (void);
+extern void PostUpdateFlashRect (void);
+extern void PauseFlash (void);
+extern void ContinueFlash (void);
+
+#define SFR_MENU_3DO ((RECT*)~0L)
+#define SFR_MENU_ANY ((RECT*)~1L)
+extern void DrawHyperCoords (POINT puniverse);
+extern void DrawSISTitle (UNICODE *pStr);
+
+// Flags for DrawSISMessageEx (may be OR'ed):
+#define DSME_NONE 0
+#define DSME_SETFR (1 << 0)
+ // Set the flash rectangle to the message area.
+#define DSME_CLEARFR (1 << 1)
+ // Disable the flash rectangle.
+#define DSME_BLOCKCUR (1 << 2)
+ // Use a block cursor instead of an insertion point cursor,
+ // when editing in the message field.
+#define DSME_MYCOLOR (1 << 3)
+ // Use the current foreground color, instead of the default.
+extern BOOLEAN DrawSISMessageEx (const UNICODE *pStr, SIZE CurPos,
+ SIZE ExPos, COUNT flags);
+
+extern void DrawSISMessage (const UNICODE *pStr);
+extern void DateToString (char *buf, size_t bufLen,
+ BYTE month_index, BYTE day_index, COUNT year_index);
+
+// Returned RECT is relative to the StatusContext
+extern void GetStatusMessageRect (RECT *r);
+extern void DrawStatusMessage (const UNICODE *pStr);
+typedef enum
+{
+ SMM_UNDEFINED = 0,
+ SMM_DATE,
+ SMM_RES_UNITS,
+ SMM_CREDITS,
+
+ SMM_DEFAULT = SMM_DATE,
+} StatMsgMode;
+// Sets the new mode and return the previous
+extern StatMsgMode SetStatusMessageMode (StatMsgMode);
+
+extern void DrawLanders (void);
+extern void DrawStorageBays (BOOLEAN Refresh);
+extern void GetGaugeRect (RECT *pRect, BOOLEAN IsCrewRect);
+extern void DrawFlagshipStats (void);
+void DrawAutoPilotMessage (BOOLEAN Reset);
+
+extern void DeltaSISGauges (SIZE crew_delta, SIZE fuel_delta, int
+ resunit_delta);
+
+extern COUNT GetCrewCount (void);
+extern COUNT GetModuleCrewCapacity (BYTE moduleType);
+
+extern COUNT GetCrewPodCapacity (void);
+extern COUNT GetCPodCapacity (POINT *ppt);
+
+extern COUNT GetModuleStorageCapacity (BYTE moduleType);
+extern COUNT GetStorageBayCapacity (void);
+extern COUNT GetSBayCapacity (POINT *ppt);
+
+extern DWORD GetModuleFuelCapacity (BYTE moduleType);
+extern DWORD GetFuelTankCapacity (void);
+extern DWORD GetFTankCapacity (POINT *ppt);
+
+extern COUNT CountSISPieces (BYTE piece_type);
+
+extern void DrawFlagshipName (BOOLEAN InStatusArea);
+extern void DrawCaptainsName (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* SIS_H_INCL__ */
+
diff --git a/src/uqm/sounds.c b/src/uqm/sounds.c
new file mode 100644
index 0000000..be14c84
--- /dev/null
+++ b/src/uqm/sounds.c
@@ -0,0 +1,199 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "settings.h"
+#include "sounds.h"
+#include "units.h"
+
+
+SOUND MenuSounds;
+SOUND GameSounds;
+
+#define MAX_SOUNDS 8
+static BYTE num_sounds = 0;
+static SOUND sound_buf[MAX_SOUNDS];
+static ELEMENT *sound_posobj[MAX_SOUNDS];
+
+void
+PlaySound (SOUND S, SoundPosition Pos, ELEMENT *PositionalObject,
+ BYTE Priority)
+{
+ BYTE chan, c;
+ static BYTE lru_channel[NUM_FX_CHANNELS] = {0, 1, 2, 3};
+ static SOUND channel[NUM_FX_CHANNELS] = {0, 0, 0, 0};
+
+ if (S == 0)
+ return;
+
+ for (chan = 0; chan < NUM_FX_CHANNELS; ++chan)
+ {
+ if (S == channel[chan])
+ break;
+ }
+
+ if (chan == NUM_FX_CHANNELS)
+ {
+ for (chan = 0; chan < NUM_FX_CHANNELS; ++chan)
+ {
+ if (!ChannelPlaying (chan + MIN_FX_CHANNEL))
+ break;
+ }
+
+ if (chan == NUM_FX_CHANNELS)
+ chan = lru_channel[0];
+ }
+
+ channel[chan] = S;
+
+ for (c = 0; c < NUM_FX_CHANNELS - 1; ++c)
+ {
+ if (lru_channel[c] == chan)
+ {
+ memmove (&lru_channel[c], &lru_channel[c + 1],
+ (NUM_FX_CHANNELS - 1) - c);
+ break;
+ }
+ }
+ lru_channel[NUM_FX_CHANNELS - 1] = chan;
+
+ PlaySoundEffect (S, chan + MIN_FX_CHANNEL, Pos, PositionalObject, Priority);
+}
+
+void
+PlayMenuSound (MENU_SOUND_EFFECT S)
+{
+ PlaySoundEffect (SetAbsSoundIndex (MenuSounds, S),
+ 0, NotPositional (), NULL,
+ GAME_SOUND_PRIORITY);
+}
+
+void
+ProcessSound (SOUND Sound, ELEMENT *PositionalObject)
+{
+ if (Sound == (SOUND)~0)
+ {
+ memset (sound_buf, 0, sizeof (sound_buf));
+ memset (sound_posobj, 0, sizeof (sound_posobj));
+ num_sounds = MAX_SOUNDS;
+ }
+ else if (num_sounds < MAX_SOUNDS)
+ {
+ sound_buf[num_sounds] = Sound;
+ sound_posobj[num_sounds++] = PositionalObject;
+ }
+}
+
+SoundPosition
+CalcSoundPosition (ELEMENT *ElementPtr)
+{
+ SoundPosition pos;
+
+ if (ElementPtr == NULL)
+ {
+ pos.x = pos.y = 0;
+ pos.positional = FALSE;
+ }
+ else
+ {
+ GRAPHICS_PRIM objtype;
+
+ objtype = GetPrimType (&DisplayArray[ElementPtr->PrimIndex]);
+ if (objtype == LINE_PRIM)
+ {
+ pos.x = DisplayArray[ElementPtr->PrimIndex].Object.Line.first.x;
+ pos.y = DisplayArray[ElementPtr->PrimIndex].Object.Line.first.y;
+ }
+ else
+ {
+ pos.x = DisplayArray[ElementPtr->PrimIndex].Object.Point.x;
+ pos.y = DisplayArray[ElementPtr->PrimIndex].Object.Point.y;
+ }
+
+ pos.x -= (SPACE_WIDTH >> 1);
+ pos.y -= (SPACE_HEIGHT >> 1);
+ pos.positional = TRUE;
+ }
+
+ return pos;
+}
+
+SoundPosition
+NotPositional (void)
+{
+ return CalcSoundPosition (NULL);
+}
+
+/* Updates positional sound effects */
+void
+UpdateSoundPositions (void)
+{
+ COUNT i;
+
+ for (i = FIRST_SFX_CHANNEL; i <= LAST_SFX_CHANNEL; ++i)
+ {
+ ELEMENT *posobj;
+ if (!ChannelPlaying(i))
+ continue;
+
+ posobj = GetPositionalObject (i);
+ if (posobj != NULL)
+ {
+ SoundPosition pos;
+ pos = CalcSoundPosition (posobj);
+ if (pos.positional)
+ UpdateSoundPosition (i, pos);
+ }
+ }
+}
+
+void
+FlushSounds (void)
+{
+ if (num_sounds > 0)
+ {
+ SOUND *pSound;
+ ELEMENT **pSoundPosObj;
+
+ pSound = sound_buf;
+ pSoundPosObj = sound_posobj;
+ do
+ {
+ SoundPosition pos = CalcSoundPosition (*pSoundPosObj);
+ PlaySound (*pSound++, pos, *pSoundPosObj++,
+ GAME_SOUND_PRIORITY);
+ } while (--num_sounds);
+ }
+}
+
+void
+RemoveSoundsForObject (ELEMENT *PosObj)
+{
+ int i;
+
+ for (i = FIRST_SFX_CHANNEL; i <= LAST_SFX_CHANNEL; ++i)
+ {
+ if (GetPositionalObject (i) == PosObj)
+ SetPositionalObject (i, NULL);
+ }
+
+ for (i = 0; i < num_sounds; ++i)
+ {
+ if (sound_posobj[i] == PosObj)
+ sound_posobj[i] = NULL;
+ }
+}
+
+
diff --git a/src/uqm/sounds.h b/src/uqm/sounds.h
new file mode 100644
index 0000000..622b3ba
--- /dev/null
+++ b/src/uqm/sounds.h
@@ -0,0 +1,85 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_SOUNDS_H_
+#define UQM_SOUNDS_H_
+
+#include "element.h"
+#include "libs/compiler.h"
+#include "libs/sndlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ GRAB_CREW = 0,
+ SHIP_EXPLODES,
+ TARGET_DAMAGED_FOR_1_PT,
+ TARGET_DAMAGED_FOR_2_3_PT,
+ TARGET_DAMAGED_FOR_4_5_PT,
+ TARGET_DAMAGED_FOR_6_PLUS_PT
+} SOUND_EFFECTS;
+
+typedef enum
+{
+ MENU_SOUND_MOVE = 0,
+ MENU_SOUND_SUCCESS,
+ MENU_SOUND_FAILURE,
+ MENU_SOUND_INVOKED,
+} MENU_SOUND_EFFECT;
+
+extern SOUND MenuSounds;
+extern SOUND GameSounds;
+
+/* Constants for DoInput */
+typedef UWORD MENU_SOUND_FLAGS;
+#define MENU_SOUND_UP ((MENU_SOUND_FLAGS)(1 << 0))
+#define MENU_SOUND_DOWN ((MENU_SOUND_FLAGS)(1 << 1))
+#define MENU_SOUND_LEFT ((MENU_SOUND_FLAGS)(1 << 2))
+#define MENU_SOUND_RIGHT ((MENU_SOUND_FLAGS)(1 << 3))
+#define MENU_SOUND_SELECT ((MENU_SOUND_FLAGS)(1 << 4))
+#define MENU_SOUND_CANCEL ((MENU_SOUND_FLAGS)(1 << 5))
+#define MENU_SOUND_SPECIAL ((MENU_SOUND_FLAGS)(1 << 6))
+#define MENU_SOUND_PAGEUP ((MENU_SOUND_FLAGS)(1 << 7))
+#define MENU_SOUND_PAGEDOWN ((MENU_SOUND_FLAGS)(1 << 8))
+#define MENU_SOUND_DELETE ((MENU_SOUND_FLAGS)(1 << 9))
+#define MENU_SOUND_ARROWS (MENU_SOUND_UP | MENU_SOUND_DOWN | MENU_SOUND_LEFT | MENU_SOUND_RIGHT)
+#define MENU_SOUND_NONE ((MENU_SOUND_FLAGS)0)
+
+extern void SetMenuSounds (MENU_SOUND_FLAGS sound_0,
+ MENU_SOUND_FLAGS sound_1);
+extern void GetMenuSounds (MENU_SOUND_FLAGS *sound_0,
+ MENU_SOUND_FLAGS *sound_1);
+
+extern void PlaySound (SOUND S, SoundPosition Pos,
+ ELEMENT *PositionalObject, BYTE Priority);
+extern void PlayMenuSound (MENU_SOUND_EFFECT S);
+extern void ProcessSound (SOUND Sound, ELEMENT *PositionalObject);
+extern SoundPosition CalcSoundPosition (ELEMENT *ElementPtr);
+extern SoundPosition NotPositional (void);
+extern void UpdateSoundPositions (void);
+extern void FlushSounds (void);
+extern void RemoveSoundsForObject (ELEMENT *PosObj);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SOUNDS_H_ */
diff --git a/src/uqm/starbase.c b/src/uqm/starbase.c
new file mode 100644
index 0000000..f119208
--- /dev/null
+++ b/src/uqm/starbase.c
@@ -0,0 +1,602 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "build.h"
+#include "colors.h"
+#include "controls.h"
+#include "starmap.h"
+#include "comm.h"
+#include "gamestr.h"
+#include "save.h"
+#include "starbase.h"
+#include "sis.h"
+#include "resinst.h"
+#include "nameref.h"
+#include "settings.h"
+#include "setup.h"
+#include "sounds.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/tasklib.h"
+
+
+static void CleanupAfterStarBase (void);
+
+static void
+DrawBaseStateStrings (STARBASE_STATE OldState, STARBASE_STATE NewState)
+{
+ TEXT t;
+ //STRING locString;
+
+ SetContext (ScreenContext);
+ SetContextFont (StarConFont);
+ SetContextForeGroundColor (BLACK_COLOR);
+
+ t.baseline.x = 73 - 4 + SAFE_X;
+ t.align = ALIGN_CENTER;
+
+ if (OldState == (STARBASE_STATE)~0)
+ {
+ t.baseline.y = 106 + 28 + (SAFE_Y + 4);
+ for (OldState = TALK_COMMANDER; OldState < DEPART_BASE; ++OldState)
+ {
+ if (OldState != NewState)
+ {
+ t.pStr = GAME_STRING (STARBASE_STRING_BASE + 1 + OldState);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ }
+ t.baseline.y += (23 - 4);
+ }
+ }
+
+ t.baseline.y = 106 + 28 + (SAFE_Y + 4) + ((23 - 4) * OldState);
+ t.pStr = GAME_STRING (STARBASE_STRING_BASE + 1 + OldState);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E));
+ t.baseline.y = 106 + 28 + (SAFE_Y + 4) + ((23 - 4) * NewState);
+ t.pStr = GAME_STRING (STARBASE_STRING_BASE + 1 + NewState);
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+}
+
+void
+DrawShipPiece (FRAME ModuleFrame, COUNT which_piece, COUNT which_slot,
+ BOOLEAN DrawBluePrint)
+{
+ Color OldColor = UNDEFINED_COLOR;
+ // Initialisation is just to keep the compiler silent.
+ RECT r;
+ STAMP Side, Top;
+ SBYTE RepairSlot;
+
+ RepairSlot = 0;
+ switch (which_piece)
+ {
+ case FUSION_THRUSTER:
+ case EMPTY_SLOT + 0:
+ Side.origin.x = DRIVE_SIDE_X;
+ Side.origin.y = DRIVE_SIDE_Y;
+ Top.origin.x = DRIVE_TOP_X;
+ Top.origin.y = DRIVE_TOP_Y;
+ break;
+ case TURNING_JETS:
+ case EMPTY_SLOT + 1:
+ Side.origin.x = JET_SIDE_X;
+ Side.origin.y = JET_SIDE_Y;
+ Top.origin.x = JET_TOP_X;
+ Top.origin.y = JET_TOP_Y;
+ break;
+ default:
+ if (which_piece < EMPTY_SLOT + 2)
+ {
+ RepairSlot = 1;
+ if (which_piece < EMPTY_SLOT
+ && (which_slot == 0
+ || GLOBAL_SIS (ModuleSlots[
+ which_slot - 1
+ ]) < EMPTY_SLOT))
+ ++RepairSlot;
+ }
+ else if (!DrawBluePrint)
+ {
+ if (which_slot == 0 || which_slot >= NUM_MODULE_SLOTS - 3)
+ ++which_piece;
+
+ if (which_slot < NUM_MODULE_SLOTS - 1
+ && GLOBAL_SIS (ModuleSlots[
+ which_slot + 1
+ ]) < EMPTY_SLOT)
+ {
+ RepairSlot = -1;
+ if (which_piece == EMPTY_SLOT + 3
+ || which_slot + 1 == NUM_MODULE_SLOTS - 3)
+ --RepairSlot;
+ }
+ }
+ Side.origin.x = MODULE_SIDE_X;
+ Side.origin.y = MODULE_SIDE_Y;
+ Top.origin.x = MODULE_TOP_X;
+ Top.origin.y = MODULE_TOP_Y;
+ break;
+ }
+
+ Side.origin.x += which_slot * SHIP_PIECE_OFFSET;
+ Side.frame = NULL;
+ if (RepairSlot < 0)
+ {
+ Side.frame = SetAbsFrameIndex (ModuleFrame,
+ ((NUM_MODULES - 1) + (6 - 2)) + (NUM_MODULES + 6)
+ - (RepairSlot + 1));
+ DrawStamp (&Side);
+ }
+ else if (RepairSlot)
+ {
+ r.corner = Side.origin;
+ r.extent.width = SHIP_PIECE_OFFSET;
+ r.extent.height = 1;
+ OldColor = SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledRectangle (&r);
+ r.corner.y += 23 - 1;
+ DrawFilledRectangle (&r);
+
+ r.extent.width = 1;
+ r.extent.height = 8;
+ if (RepairSlot == 2)
+ {
+ r.corner = Side.origin;
+ DrawFilledRectangle (&r);
+ r.corner.y += 15;
+ DrawFilledRectangle (&r);
+ }
+ if (which_slot < (NUM_MODULE_SLOTS - 1))
+ {
+ r.corner = Side.origin;
+ r.corner.x += SHIP_PIECE_OFFSET;
+ DrawFilledRectangle (&r);
+ r.corner.y += 15;
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ if (DrawBluePrint)
+ {
+ if (RepairSlot)
+ SetContextForeGroundColor (OldColor);
+ Side.frame = SetAbsFrameIndex (ModuleFrame, which_piece - 1);
+ DrawFilledStamp (&Side);
+ }
+ else
+ {
+ Top.origin.x += which_slot * SHIP_PIECE_OFFSET;
+ if (RepairSlot < 0)
+ {
+ Top.frame = SetRelFrameIndex (Side.frame, -((NUM_MODULES - 1) + 6));
+ DrawStamp (&Top);
+ }
+ else if (RepairSlot)
+ {
+ r.corner = Top.origin;
+ r.extent.width = SHIP_PIECE_OFFSET;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.y += 32 - 1;
+ DrawFilledRectangle (&r);
+
+ r.extent.width = 1;
+ r.extent.height = 12;
+ if (RepairSlot == 2)
+ {
+ r.corner = Top.origin;
+ DrawFilledRectangle (&r);
+ r.corner.y += 20;
+ DrawFilledRectangle (&r);
+ }
+ RepairSlot = (which_slot < NUM_MODULE_SLOTS - 1);
+ if (RepairSlot)
+ {
+ r.corner = Top.origin;
+ r.corner.x += SHIP_PIECE_OFFSET;
+ DrawFilledRectangle (&r);
+ r.corner.y += 20;
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ Top.frame = SetAbsFrameIndex (ModuleFrame, which_piece);
+ DrawStamp (&Top);
+
+ Side.frame = SetRelFrameIndex (Top.frame, (NUM_MODULES - 1) + 6);
+ DrawStamp (&Side);
+
+ if (which_slot == 1 && which_piece == EMPTY_SLOT + 2)
+ {
+ STAMP s;
+
+ s.origin = Top.origin;
+ s.origin.x -= SHIP_PIECE_OFFSET;
+ s.frame = SetAbsFrameIndex (ModuleFrame, NUM_MODULES + 5);
+ DrawStamp (&s);
+ s.origin = Side.origin;
+ s.origin.x -= SHIP_PIECE_OFFSET;
+ s.frame = SetRelFrameIndex (s.frame, (NUM_MODULES - 1) + 6);
+ DrawStamp (&s);
+ }
+
+ if (RepairSlot)
+ {
+ Top.origin.x += SHIP_PIECE_OFFSET;
+ Side.origin.x += SHIP_PIECE_OFFSET;
+ which_piece = GLOBAL_SIS (ModuleSlots[++which_slot]);
+ if (which_piece == EMPTY_SLOT + 2
+ && which_slot >= NUM_MODULE_SLOTS - 3)
+ ++which_piece;
+
+ Top.frame = SetAbsFrameIndex (ModuleFrame, which_piece);
+ DrawStamp (&Top);
+
+ Side.frame = SetRelFrameIndex (Top.frame, (NUM_MODULES - 1) + 6);
+ DrawStamp (&Side);
+ }
+ }
+}
+
+static void
+rotateStarbase (MENU_STATE *pMS, FRAME AniFrame)
+{
+ static TimeCount NextTime = 0;
+ TimeCount Now = GetTimeCounter ();
+
+ if (AniFrame)
+ { // Setup the animation
+ pMS->flash_frame0 = AniFrame;
+ pMS->flash_rect0.corner.x = SAFE_X;
+ pMS->flash_rect0.corner.y = SAFE_Y + 4;
+ }
+
+ if (Now >= NextTime || AniFrame)
+ {
+ STAMP s;
+
+ NextTime = Now + (ONE_SECOND / 20);
+
+ s.origin = pMS->flash_rect0.corner;
+ s.frame = pMS->flash_frame0;
+ DrawStamp (&s);
+
+ s.frame = IncFrameIndex (s.frame);
+ if (GetFrameIndex (s.frame) == 0)
+ { // Do not redraw the base frame, animation loops to frame 1
+ s.frame = IncFrameIndex (s.frame);
+ }
+ pMS->flash_frame0 = s.frame;
+ }
+}
+
+BOOLEAN
+DoStarBase (MENU_STATE *pMS)
+{
+ // XXX: This function is full of hacks and otherwise strange code
+
+ if (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ {
+ pMS->CurState = DEPART_BASE;
+ goto ExitStarBase;
+ }
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ if (!pMS->Initialized)
+ {
+ LastActivity &= ~CHECK_LOAD;
+ pMS->InputFunc = DoStarBase;
+
+ SetFlashRect (NULL);
+
+ if (pMS->hMusic)
+ {
+ StopMusic ();
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+
+ pMS->Initialized = TRUE;
+
+ pMS->CurFrame = CaptureDrawable (LoadGraphic (STARBASE_ANIM));
+ pMS->hMusic = LoadMusic (STARBASE_MUSIC);
+
+ SetContext (ScreenContext);
+ SetTransitionSource (NULL);
+ BatchGraphics ();
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+ rotateStarbase (pMS, pMS->CurFrame);
+ DrawBaseStateStrings ((STARBASE_STATE)~0, pMS->CurState);
+ ScreenTransition (3, NULL);
+ PlayMusic (pMS->hMusic, TRUE, 1);
+ UnbatchGraphics ();
+ }
+ else if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ExitStarBase:
+ DestroyDrawable (ReleaseDrawable (pMS->CurFrame));
+ pMS->CurFrame = 0;
+ StopMusic ();
+ if (pMS->hMusic)
+ {
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+
+ if (pMS->CurState == DEPART_BASE)
+ {
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ SET_GAME_STATE (STARBASE_VISITED, 0);
+ }
+ return (FALSE);
+ }
+
+ pMS->Initialized = FALSE;
+ if (pMS->CurState == TALK_COMMANDER)
+ {
+ FlushInput ();
+ InitCommunication (COMMANDER_CONVERSATION);
+ // XXX: InitCommunication() clears these flags, and we need them
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+ else
+ {
+ BYTE OldState;
+
+ switch (OldState = pMS->CurState)
+ {
+ case OUTFIT_STARSHIP:
+ pMS->InputFunc = DoOutfit;
+ break;
+ case SHIPYARD:
+ pMS->InputFunc = DoShipyard;
+ break;
+ }
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ DoInput (pMS, TRUE);
+
+ pMS->Initialized = FALSE;
+ pMS->CurState = OldState;
+ pMS->InputFunc = DoStarBase;
+ }
+ }
+ else
+ {
+ STARBASE_STATE NewState;
+
+ NewState = pMS->CurState;
+ if (PulsedInputState.menu[KEY_MENU_LEFT] || PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (NewState-- == TALK_COMMANDER)
+ NewState = DEPART_BASE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT] || PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ if (NewState++ == DEPART_BASE)
+ NewState = TALK_COMMANDER;
+ }
+
+ BatchGraphics ();
+ SetContext (ScreenContext);
+
+ if (NewState != pMS->CurState)
+ {
+ DrawBaseStateStrings (pMS->CurState, NewState);
+ pMS->CurState = NewState;
+ }
+
+ rotateStarbase (pMS, NULL);
+
+ UnbatchGraphics ();
+
+ SleepThread (ONE_SECOND / 30);
+ }
+
+ return (TRUE);
+}
+
+static void
+DoTimePassage (void)
+{
+#define LOST_DAYS 14
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND * 2));
+ MoveGameClockDays (LOST_DAYS);
+}
+
+void
+VisitStarBase (void)
+{
+ MENU_STATE MenuState;
+ CONTEXT OldContext;
+ StatMsgMode prevMsgMode = SMM_UNDEFINED;
+
+ // XXX: This should probably be moved out to Starcon2Main()
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ { // We were just transported by Chmmr to the Starbase
+ // Force a reload of the SolarSys
+ CurStarDescPtr = NULL;
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+
+ if (!GET_GAME_STATE (STARBASE_AVAILABLE))
+ {
+ HSHIPFRAG hStarShip;
+ SHIP_FRAGMENT *FragPtr;
+
+ // Unallied Starbase conversation
+ SetCommIntroMode (CIM_CROSSFADE_SCREEN, 0);
+ InitCommunication (COMMANDER_CONVERSATION);
+ if (!GET_GAME_STATE (PROBE_ILWRATH_ENCOUNTER)
+ || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ CleanupAfterStarBase ();
+ return;
+ }
+
+ /* Create an Ilwrath ship responding to the Ur-Quan
+ * probe's broadcast */
+ hStarShip = CloneShipFragment (ILWRATH_SHIP,
+ &GLOBAL (npc_built_ship_q), 7);
+ FragPtr = LockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+ /* Hack (sort of): Suppress the tally and salvage info
+ * after the battle */
+ FragPtr->race_id = (BYTE)~0;
+ UnlockShipFrag (&GLOBAL (npc_built_ship_q), hStarShip);
+
+ InitCommunication (ILWRATH_CONVERSATION);
+ if (GLOBAL_SIS (CrewEnlisted) == (COUNT)~0
+ || (GLOBAL (CurrentActivity) & CHECK_ABORT))
+ return; // Killed by Ilwrath
+
+ // After Ilwrath battle, about-to-ally Starbase conversation
+ SetCommIntroMode (CIM_CROSSFADE_SCREEN, 0);
+ InitCommunication (COMMANDER_CONVERSATION);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return;
+ // XXX: InitCommunication() clears these flags, and we need them
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+
+ prevMsgMode = SetStatusMessageMode (SMM_RES_UNITS);
+
+ if (GET_GAME_STATE (MOONBASE_ON_SHIP)
+ || GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ { // Go immediately into a conversation with the Commander when the
+ // Starbase becomes available for the first time, or after Chmmr
+ // install the bomb.
+ DoTimePassage ();
+ if (GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
+ return; // You are now dead! Thank you! (killed by Kohr-Ah)
+
+ SetCommIntroMode (CIM_FADE_IN_SCREEN, ONE_SECOND * 2);
+ InitCommunication (COMMANDER_CONVERSATION);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return;
+ // XXX: InitCommunication() clears these flags, and we need them
+ // This marks that we are in Starbase.
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, (BYTE)~0);
+ }
+
+ memset (&MenuState, 0, sizeof (MenuState));
+ MenuState.InputFunc = DoStarBase;
+
+ OldContext = SetContext (ScreenContext);
+ DoInput (&MenuState, TRUE);
+ SetContext (OldContext);
+
+ SetStatusMessageMode (prevMsgMode);
+ CleanupAfterStarBase ();
+}
+
+static void
+CleanupAfterStarBase (void)
+{
+ if (!(GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT)))
+ {
+ // Mark as not in Starbase anymore
+ SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 0);
+ // Fake a load so Starcon2Main takes us to IP
+ GLOBAL (CurrentActivity) = CHECK_LOAD;
+ NextActivity = MAKE_WORD (IN_INTERPLANETARY, 0)
+ | START_INTERPLANETARY;
+ }
+}
+
+void
+InstallBombAtEarth (void)
+{
+ DoTimePassage ();
+
+ SetContext (ScreenContext);
+ SetTransitionSource (NULL);
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+
+ SleepThreadUntil (FadeScreen (FadeAllToColor, 0));
+
+ SET_GAME_STATE (CHMMR_BOMB_STATE, 3); /* bomb processed */
+ GLOBAL (CurrentActivity) = CHECK_LOAD; /* fake a load game */
+ NextActivity = MAKE_WORD (IN_INTERPLANETARY, 0) | START_INTERPLANETARY;
+ CurStarDescPtr = 0; /* force SolarSys reload */
+}
+
+// XXX: Doesn't really belong in this file.
+COUNT
+WrapText (const UNICODE *pStr, COUNT len, TEXT *tarray, SIZE field_width)
+{
+ COUNT num_lines;
+
+ num_lines = 0;
+ do
+ {
+ RECT r;
+ COUNT OldCount;
+
+ tarray->align = ALIGN_LEFT; /* set alignment to something */
+ tarray->pStr = pStr;
+ tarray->CharCount = 1;
+ ++num_lines;
+
+ do
+ {
+ OldCount = tarray->CharCount;
+ while (*++pStr != ' ' && (COUNT)(pStr - tarray->pStr) < len)
+ ;
+ tarray->CharCount = pStr - tarray->pStr;
+ TextRect (tarray, &r, NULL);
+ } while (tarray->CharCount < len && r.extent.width < field_width);
+
+ if (r.extent.width >= field_width)
+ {
+ if ((tarray->CharCount = OldCount) == 1)
+ {
+ do
+ {
+ ++tarray->CharCount;
+ TextRect (tarray, &r, NULL);
+ } while (r.extent.width < field_width);
+ --tarray->CharCount;
+ }
+ }
+
+ pStr = tarray->pStr + tarray->CharCount;
+ len -= tarray->CharCount;
+ ++tarray;
+
+ if (len && (r.extent.width < field_width || OldCount > 1))
+ {
+ ++pStr; /* skip white space */
+ --len;
+ }
+
+ } while (len);
+
+ return (num_lines);
+}
+
diff --git a/src/uqm/starbase.h b/src/uqm/starbase.h
new file mode 100644
index 0000000..0bf5558
--- /dev/null
+++ b/src/uqm/starbase.h
@@ -0,0 +1,55 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_STARBASE_H_
+#define UQM_STARBASE_H_
+
+#include "menustat.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+enum
+{
+ TALK_COMMANDER = 0,
+ OUTFIT_STARSHIP,
+ SHIPYARD,
+ DEPART_BASE
+};
+typedef BYTE STARBASE_STATE;
+
+
+extern void InstallBombAtEarth (void);
+extern void VisitStarBase (void);
+extern BOOLEAN DoStarBase (MENU_STATE *pMS);
+extern BOOLEAN DoOutfit (MENU_STATE *pMS);
+extern BOOLEAN DoShipyard (MENU_STATE *pMS);
+
+extern void DrawShipPiece (FRAME ModuleFrame, COUNT which_piece, COUNT
+ which_slot, BOOLEAN DrawBluePrint);
+
+extern COUNT WrapText (const UNICODE *pStr, COUNT len, TEXT *tarray, SIZE
+ field_width);
+ // XXX: Doesn't really belong in this file.
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_STARBASE_H_ */
diff --git a/src/uqm/starcon.c b/src/uqm/starcon.c
new file mode 100644
index 0000000..5903e68
--- /dev/null
+++ b/src/uqm/starcon.c
@@ -0,0 +1,323 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+
+#include "comm.h"
+#include "battle.h"
+#include "fmv.h"
+#include "gameev.h"
+#include "types.h"
+#include "globdata.h"
+#include "resinst.h"
+#include "restart.h"
+#include "starbase.h"
+#include "save.h"
+#include "setup.h"
+#include "master.h"
+#include "controls.h"
+#include "starcon.h"
+#include "clock.h"
+ // for GameClockTick()
+#include "hyper.h"
+ // for SeedUniverse()
+#include "planets/planets.h"
+ // for ExploreSolarSys()
+#include "uqmdebug.h"
+#include "libs/tasklib.h"
+#include "libs/log.h"
+#include "libs/gfxlib.h"
+#include "libs/graphics/gfx_common.h"
+#include "libs/graphics/tfb_draw.h"
+#include "libs/misc.h"
+
+#include "uqmversion.h"
+#include "options.h"
+
+volatile int MainExited = FALSE;
+#ifdef DEBUG_SLEEP
+uint32 mainThreadId;
+extern uint32 SDL_ThreadID(void);
+#endif
+
+// Open or close the periodically occuring QuasiSpace portal.
+// It changes the appearant portal size when necessary.
+static void
+checkArilouGate (void)
+{
+ BYTE counter;
+
+ counter = GET_GAME_STATE (ARILOU_SPACE_COUNTER);
+ if (GET_GAME_STATE (ARILOU_SPACE) == OPENING)
+ { // The portal is opening or fully open
+ if (counter < 9)
+ ++counter;
+ }
+ else
+ { // The portal is closing or fully closed
+ if (counter > 0)
+ --counter;
+ }
+ SET_GAME_STATE (ARILOU_SPACE_COUNTER, counter);
+}
+
+// Battle frame callback function.
+static void
+on_battle_frame (void)
+{
+ GameClockTick ();
+ checkArilouGate ();
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ SeedUniverse ();
+
+ DrawAutoPilotMessage (FALSE);
+}
+
+static void
+BackgroundInitKernel (DWORD TimeOut)
+{
+ LoadMasterShipList (TaskSwitch);
+ TaskSwitch ();
+ InitGameKernel ();
+
+ while ((GetTimeCounter () <= TimeOut) &&
+ !(GLOBAL (CurrentActivity) & CHECK_ABORT))
+ {
+ UpdateInputState ();
+ TaskSwitch ();
+ }
+}
+
+// Executes on the main() thread
+void
+SignalStopMainThread (void)
+{
+ GamePaused = FALSE;
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ TaskSwitch ();
+}
+
+// Executes on the main() thread
+void
+ProcessUtilityKeys (void)
+{
+ if (ImmediateInputState.menu[KEY_ABORT])
+ {
+ log_showBox (false, false);
+ exit (EXIT_SUCCESS);
+ }
+
+ if (ImmediateInputState.menu[KEY_FULLSCREEN])
+ {
+ int flags = GfxFlags ^ TFB_GFXFLAGS_FULLSCREEN;
+ // clear ImmediateInputState so we don't repeat this next frame
+ FlushInput ();
+ TFB_DrawScreen_ReinitVideo (GraphicsDriver, flags, ScreenWidthActual,
+ ScreenHeightActual);
+ }
+
+#if defined(DEBUG) || defined(USE_DEBUG_KEY)
+ { // Only call the debug func on the rising edge of
+ // ImmediateInputState[KEY_DEBUG] so it does not execute repeatedly.
+ // This duplicates the PulsedInputState somewhat, but we cannot
+ // use PulsedInputState here because it is meant for another thread.
+ static int debugKeyState;
+
+ if (ImmediateInputState.menu[KEY_DEBUG] && debugKeyState == 0)
+ {
+ debugKeyPressed ();
+ }
+ debugKeyState = ImmediateInputState.menu[KEY_DEBUG];
+ }
+#endif /* DEBUG */
+}
+
+/* TODO: Remove these declarations once threading is gone. */
+extern int snddriver, soundflags;
+
+int
+Starcon2Main (void *threadArg)
+{
+#ifdef DEBUG_SLEEP
+ mainThreadId = SDL_ThreadID();
+#endif
+
+#if CREATE_JOURNAL
+{
+int ac = argc;
+char **av = argv;
+
+while (--ac > 0)
+{
+ ++av;
+ if ((*av)[0] == '-')
+ {
+ switch ((*av)[1])
+ {
+#if CREATE_JOURNAL
+ case 'j':
+ ++create_journal;
+ break;
+#endif //CREATE_JOURNAL
+ }
+ }
+}
+}
+#endif // CREATE_JOURNAL
+
+ {
+ /* TODO: Put initAudio back in main where it belongs once threading
+ * is gone.
+ */
+ extern sint32 initAudio (sint32 driver, sint32 flags);
+ initAudio (snddriver, soundflags);
+ }
+
+ if (!LoadKernel (0,0))
+ {
+ log_add (log_Fatal, "\n *** FATAL ERROR: Could not load basic content ***\n\nUQM requires at least the base content pack to run properly.");
+ log_add (log_Fatal, "This file is typically called uqm-%d.%d.0-content.uqm. UQM was expecting", UQM_MAJOR_VERSION, UQM_MINOR_VERSION);
+ log_add (log_Fatal, "it in the %s/packages directory.", baseContentPath);
+ log_add (log_Fatal, "Either your installation did not install the content pack at all, or it\ninstalled it in a different directory.\n\nFix your installation and rerun UQM.\n\n *******************\n");
+ log_showBox (true, true);
+
+ MainExited = TRUE;
+ return EXIT_FAILURE;
+ }
+ log_add (log_Info, "We've loaded the Kernel");
+
+ GLOBAL (CurrentActivity) = 0;
+ // show splash and init the kernel in the meantime
+ SplashScreen (BackgroundInitKernel);
+
+// OpenJournal ();
+ while (StartGame ())
+ {
+ // Initialise a new game
+ if (!SetPlayerInputAll ()) {
+ log_add (log_Fatal, "Could not set player input.");
+ explode (); // Does not return;
+ }
+ InitGameStructures ();
+ InitGameClock ();
+ AddInitialGameEvents();
+
+ do
+ {
+#ifdef DEBUG
+ if (debugHook != NULL)
+ {
+ void (*saveDebugHook) (void);
+ saveDebugHook = debugHook;
+ debugHook = NULL;
+ // No further debugHook calls unless the called
+ // function resets debugHook.
+ (*saveDebugHook) ();
+ continue;
+ }
+#endif
+ SetStatusMessageMode (SMM_DEFAULT);
+
+ if (!((GLOBAL (CurrentActivity) | NextActivity) & CHECK_LOAD))
+ ZeroVelocityComponents (&GLOBAL (velocity));
+ // not going into talking pet conversation
+ else if (GLOBAL (CurrentActivity) & CHECK_LOAD)
+ GLOBAL (CurrentActivity) = NextActivity;
+
+ if ((GLOBAL (CurrentActivity) & START_ENCOUNTER)
+ || GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ {
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) == 2
+ && !GET_GAME_STATE (STARBASE_AVAILABLE))
+ { /* BGD mode */
+ InstallBombAtEarth ();
+ }
+ else if (GET_GAME_STATE (GLOBAL_FLAGS_AND_DATA) == (BYTE)~0
+ || GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
+ {
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ VisitStarBase ();
+ }
+ else
+ {
+ GLOBAL (CurrentActivity) |= START_ENCOUNTER;
+ RaceCommunication ();
+ }
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
+ {
+ GLOBAL (CurrentActivity) &= ~START_ENCOUNTER;
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ }
+ }
+ else if (GLOBAL (CurrentActivity) & START_INTERPLANETARY)
+ {
+ GLOBAL (CurrentActivity) = MAKE_WORD (IN_INTERPLANETARY, 0);
+
+ DrawAutoPilotMessage (TRUE);
+ SetGameClockRate (INTERPLANETARY_CLOCK_RATE);
+ ExploreSolarSys ();
+ }
+ else
+ {
+ // Entering HyperSpace or QuasiSpace.
+ GLOBAL (CurrentActivity) = MAKE_WORD (IN_HYPERSPACE, 0);
+
+ DrawAutoPilotMessage (TRUE);
+ SetGameClockRate (HYPERSPACE_CLOCK_RATE);
+ Battle (&on_battle_frame);
+ }
+
+ SetFlashRect (NULL);
+
+ LastActivity = GLOBAL (CurrentActivity);
+
+ if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
+ && (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE
+ // if died for some reason
+ || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0))
+ {
+ if (GET_GAME_STATE (KOHR_AH_KILLED_ALL))
+ InitCommunication (BLACKURQ_CONVERSATION);
+ // surrendered to Ur-Quan
+ else if (GLOBAL (CurrentActivity) & CHECK_RESTART)
+ GLOBAL (CurrentActivity) &= ~CHECK_RESTART;
+ break;
+ }
+ } while (!(GLOBAL (CurrentActivity) & CHECK_ABORT));
+
+ StopSound ();
+ UninitGameClock ();
+ UninitGameStructures ();
+ ClearPlayerInputAll ();
+ }
+// CloseJournal ();
+
+ UninitGameKernel ();
+ FreeMasterShipList ();
+ FreeKernel ();
+
+ log_showBox (false, false);
+ MainExited = TRUE;
+
+ (void) threadArg; /* Satisfying compiler (unused parameter) */
+ return 0;
+}
+
diff --git a/src/uqm/starcon.h b/src/uqm/starcon.h
new file mode 100644
index 0000000..a8f6528
--- /dev/null
+++ b/src/uqm/starcon.h
@@ -0,0 +1,35 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_STARCON_H_
+#define UQM_STARCON_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern volatile int MainExited;
+extern void SignalStopMainThread (void);
+extern void ProcessUtilityKeys (void);
+
+extern int Starcon2Main (void *threadArg);
+extern void FreeGameData (void);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_STARCON_H_ */
diff --git a/src/uqm/starmap.c b/src/uqm/starmap.c
new file mode 100644
index 0000000..a209917
--- /dev/null
+++ b/src/uqm/starmap.c
@@ -0,0 +1,125 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "starmap.h"
+#include "gamestr.h"
+#include "globdata.h"
+#include "libs/gfxlib.h"
+
+
+STAR_DESC *star_array;
+STAR_DESC *CurStarDescPtr = 0;
+
+STAR_DESC*
+FindStar (STAR_DESC *LastSDPtr, POINT *puniverse, SIZE xbounds,
+ SIZE ybounds)
+{
+ COORD min_y, max_y;
+ SIZE lo, hi;
+ STAR_DESC *BaseSDPtr;
+
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ BaseSDPtr = star_array;
+ hi = NUM_SOLAR_SYSTEMS - 1;
+ }
+ else
+ {
+#define NUM_HYPER_VORTICES 15
+ BaseSDPtr = &star_array[NUM_SOLAR_SYSTEMS + 1];
+ hi = (NUM_HYPER_VORTICES + 1) - 1;
+ }
+
+ if (LastSDPtr == NULL)
+ lo = 0;
+ else if ((lo = LastSDPtr - BaseSDPtr + 1) > hi)
+ return (0);
+ else
+ hi = lo;
+
+ if (ybounds <= 0)
+ min_y = max_y = puniverse->y;
+ else
+ {
+ min_y = puniverse->y - ybounds;
+ max_y = puniverse->y + ybounds;
+ }
+
+ while (lo < hi)
+ {
+ SIZE mid;
+
+ mid = (lo + hi) >> 1;
+ if (BaseSDPtr[mid].star_pt.y >= min_y)
+ hi = mid - 1;
+ else
+ lo = mid + 1;
+ }
+
+ LastSDPtr = &BaseSDPtr[lo];
+ if (ybounds < 0 || LastSDPtr->star_pt.y <= max_y)
+ {
+ COORD min_x, max_x;
+
+ if (xbounds <= 0)
+ min_x = max_x = puniverse->x;
+ else
+ {
+ min_x = puniverse->x - xbounds;
+ max_x = puniverse->x + xbounds;
+ }
+
+ do
+ {
+ if ((ybounds < 0 || LastSDPtr->star_pt.y >= min_y)
+ && (xbounds < 0
+ || (LastSDPtr->star_pt.x >= min_x
+ && LastSDPtr->star_pt.x <= max_x))
+ )
+ return (LastSDPtr);
+ } while ((++LastSDPtr)->star_pt.y <= max_y);
+ }
+
+ return (0);
+}
+
+void
+GetClusterName (const STAR_DESC *pSD, UNICODE buf[])
+{
+ UNICODE *pBuf, *pStr;
+
+ pBuf = buf;
+ if (pSD->Prefix)
+ {
+ pStr = GAME_STRING (STAR_NUMBER_BASE + pSD->Prefix - 1);
+ if (pStr)
+ {
+ while ((*pBuf++ = *pStr++))
+ ;
+ pBuf[-1] = ' ';
+ }
+ }
+ if ((pStr = GAME_STRING (pSD->Postfix)) == 0)
+ *pBuf = '\0';
+ else
+ {
+ while ((*pBuf++ = *pStr++))
+ ;
+ }
+}
+
diff --git a/src/uqm/starmap.h b/src/uqm/starmap.h
new file mode 100644
index 0000000..0161830
--- /dev/null
+++ b/src/uqm/starmap.h
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef STARMAP_H_INCL_
+#define STARMAP_H_INCL_
+
+#include "libs/compiler.h"
+#include "planets/planets.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern STAR_DESC *CurStarDescPtr;
+extern STAR_DESC *star_array;
+
+#define NUM_SOLAR_SYSTEMS 502
+
+extern STAR_DESC* FindStar (STAR_DESC *pLastStar, POINT *puniverse,
+ SIZE xbounds, SIZE ybounds);
+
+extern void GetClusterName (const STAR_DESC *pSD, UNICODE buf[]);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* STARMAP_H_INCL_ */
+
diff --git a/src/uqm/state.c b/src/uqm/state.c
new file mode 100644
index 0000000..3358b32
--- /dev/null
+++ b/src/uqm/state.c
@@ -0,0 +1,354 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "state.h"
+
+#include "starmap.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <memory.h>
+
+// in-memory file i/o
+struct GAME_STATE_FILE
+{
+ const char *symname;
+ DWORD size_hint;
+ int open_count;
+ BYTE *data;
+ DWORD used;
+ DWORD size;
+ DWORD ptr;
+};
+#define STATE_FILE_ITRAILER 0, 0, 0, 0, 0
+
+#define NUM_STATE_FILES 3
+
+static GAME_STATE_FILE state_files[NUM_STATE_FILES] =
+{
+ {"STARINFO", STAR_BUFSIZE, STATE_FILE_ITRAILER},
+ {"RANDGRPINFO", RAND_BUFSIZE, STATE_FILE_ITRAILER},
+ {"DEFGRPINFO", DEF_BUFSIZE, STATE_FILE_ITRAILER}
+};
+
+
+GAME_STATE_FILE *
+OpenStateFile (int stateFile, const char *mode)
+{
+ GAME_STATE_FILE *fp;
+
+ if (stateFile < 0 || stateFile >= NUM_STATE_FILES)
+ return NULL;
+
+ fp = &state_files[stateFile];
+ fp->open_count++;
+ if (fp->open_count > 1)
+ log_add (log_Warning, "WARNING: "
+ "State file %s open count is %d after open()",
+ fp->symname, fp->open_count);
+
+ if (!fp->data)
+ {
+ fp->data = HMalloc (fp->size_hint);
+ if (!fp->data)
+ return NULL;
+ fp->size = fp->size_hint;
+ }
+
+ // we allow reading and writing for any open mode
+ // but the mode determines what happens to the file contents
+ if (mode[0] == 'w')
+ { // blow the file away
+ fp->used = 0;
+#ifdef DEBUG
+ // paint buffer for tracking writes
+ memset (fp->data, 0xCC, fp->size);
+#endif
+ }
+ else if (mode[0] == 'r')
+ { // nothing
+ }
+ else
+ {
+ log_add (log_Warning, "WARNING: "
+ "State file %s opened with unsupported mode '%s'",
+ fp->symname, mode);
+ }
+ fp->ptr = 0;
+
+ return fp;
+}
+
+void
+CloseStateFile (GAME_STATE_FILE *fp)
+{
+ fp->ptr = 0;
+ fp->open_count--;
+ if (fp->open_count < 0)
+ log_add (log_Warning, "WARNING: "
+ "State file %s open count is %d after close()",
+ fp->symname, fp->open_count);
+ // Erm, Ok, it's closed! Honest!
+}
+
+void
+DeleteStateFile (int stateFile)
+{
+ GAME_STATE_FILE *fp;
+
+ if (stateFile < 0 || stateFile >= NUM_STATE_FILES)
+ return;
+
+ fp = &state_files[stateFile];
+ if (fp->open_count != 0)
+ log_add (log_Warning, "WARNING: "
+ "State file %s open count is %d during delete()",
+ fp->symname, fp->open_count);
+
+ fp->used = 0;
+ fp->ptr = 0;
+ HFree (fp->data);
+ fp->data = 0;
+}
+
+DWORD
+LengthStateFile (GAME_STATE_FILE *fp)
+{
+ return fp->used;
+}
+
+int
+ReadStateFile (void *lpBuf, COUNT size, COUNT count, GAME_STATE_FILE *fp)
+{
+ DWORD bytes = size * count;
+
+ if (fp->ptr >= fp->size)
+ { // EOF
+ return 0;
+ }
+ else if (fp->ptr + bytes > fp->size)
+ { // dont have that much data
+ bytes = fp->size - fp->ptr;
+ bytes -= bytes % size;
+ }
+
+ if (bytes > 0)
+ {
+ memcpy (lpBuf, fp->data + fp->ptr, bytes);
+ fp->ptr += bytes;
+ }
+ return (bytes / size);
+}
+
+int
+WriteStateFile (const void *lpBuf, COUNT size, COUNT count, GAME_STATE_FILE *fp)
+{
+ DWORD bytes = size * count;
+
+ if (fp->ptr + bytes > fp->size)
+ { // dont have that much space available
+ DWORD newsize = fp->ptr + bytes;
+ // grab more space in advance
+ if (newsize < fp->size * 3 / 2)
+ newsize = fp->size * 3 / 2;
+
+ fp->data = HRealloc (fp->data, newsize);
+ if (!fp->data)
+ return 0;
+
+ fp->size = newsize;
+ if (newsize > fp->size_hint)
+ fp->size_hint = newsize;
+ }
+
+ if (bytes > 0)
+ {
+ memcpy (fp->data + fp->ptr, lpBuf, bytes);
+ fp->ptr += bytes;
+ if (fp->ptr > fp->used)
+ fp->used = fp->ptr;
+ }
+ return (bytes / size);
+}
+
+int
+SeekStateFile (GAME_STATE_FILE *fp, long offset, int whence)
+{
+ if (whence == SEEK_CUR)
+ offset += fp->ptr;
+ else if (whence == SEEK_END)
+ offset += fp->used;
+
+ if (offset < 0)
+ {
+ fp->ptr = 0;
+ return 0;
+ }
+ fp->ptr = offset;
+ return 1;
+}
+
+
+void
+InitPlanetInfo (void)
+{
+ GAME_STATE_FILE *fp;
+
+ fp = OpenStateFile (STARINFO_FILE, "wb");
+ if (fp)
+ {
+ STAR_DESC *pSD;
+
+ // Set record offsets for all stars to 0 (not present)
+ pSD = &star_array[0];
+ do
+ {
+ swrite_32 (fp, 0);
+ ++pSD;
+ } while (pSD->star_pt.x <= MAX_X_UNIVERSE
+ && pSD->star_pt.y <= MAX_Y_UNIVERSE);
+
+ CloseStateFile (fp);
+ }
+}
+
+void
+UninitPlanetInfo (void)
+{
+ DeleteStateFile (STARINFO_FILE);
+}
+
+#define OFFSET_SIZE (sizeof (DWORD))
+#define SCAN_RECORD_SIZE (sizeof (DWORD) * NUM_SCAN_TYPES)
+
+void
+GetPlanetInfo (void)
+{
+ GAME_STATE_FILE *fp;
+
+ pSolarSysState->SysInfo.PlanetInfo.ScanRetrieveMask[BIOLOGICAL_SCAN] = 0;
+ pSolarSysState->SysInfo.PlanetInfo.ScanRetrieveMask[MINERAL_SCAN] = 0;
+ pSolarSysState->SysInfo.PlanetInfo.ScanRetrieveMask[ENERGY_SCAN] = 0;
+
+ fp = OpenStateFile (STARINFO_FILE, "rb");
+ if (fp)
+ {
+ COUNT star_index, planet_index, moon_index;
+ DWORD offset;
+
+ star_index = (COUNT)(CurStarDescPtr - star_array);
+ planet_index = (COUNT)(pSolarSysState->pBaseDesc->pPrevDesc
+ - pSolarSysState->PlanetDesc);
+ if (pSolarSysState->pOrbitalDesc->pPrevDesc == pSolarSysState->SunDesc)
+ moon_index = 0;
+ else
+ moon_index = (COUNT)(pSolarSysState->pOrbitalDesc
+ - pSolarSysState->MoonDesc + 1);
+
+ SeekStateFile (fp, star_index * OFFSET_SIZE, SEEK_SET);
+ sread_32 (fp, &offset);
+
+ if (offset)
+ {
+ COUNT i;
+
+ // Skip scan records for all preceeding planets to the one we need
+ for (i = 0; i < planet_index; ++i)
+ offset += (pSolarSysState->PlanetDesc[i].NumPlanets + 1) *
+ SCAN_RECORD_SIZE;
+
+ // Skip scan records for all preceeding moons to the one we need
+ offset += moon_index * SCAN_RECORD_SIZE;
+
+ SeekStateFile (fp, offset, SEEK_SET);
+ sread_a32 (fp, pSolarSysState->SysInfo.PlanetInfo.ScanRetrieveMask,
+ NUM_SCAN_TYPES);
+ }
+
+ CloseStateFile (fp);
+ }
+}
+
+void
+PutPlanetInfo (void)
+{
+ GAME_STATE_FILE *fp;
+
+ fp = OpenStateFile (STARINFO_FILE, "r+b");
+ if (fp)
+ {
+ COUNT i;
+ COUNT star_index, planet_index, moon_index;
+ DWORD offset;
+
+ star_index = (COUNT)(CurStarDescPtr - star_array);
+ planet_index = (COUNT)(pSolarSysState->pBaseDesc->pPrevDesc
+ - pSolarSysState->PlanetDesc);
+ if (pSolarSysState->pOrbitalDesc->pPrevDesc == pSolarSysState->SunDesc)
+ moon_index = 0;
+ else
+ moon_index = (COUNT)(pSolarSysState->pOrbitalDesc
+ - pSolarSysState->MoonDesc + 1);
+
+ SeekStateFile (fp, star_index * OFFSET_SIZE, SEEK_SET);
+ sread_32 (fp, &offset);
+
+ if (offset == 0)
+ { // Scan record not present yet -- init it
+ DWORD ScanRetrieveMask[NUM_SCAN_TYPES] =
+ {
+ 0, 0, 0,
+ };
+
+ offset = LengthStateFile (fp);
+
+ // Write the record offset
+ SeekStateFile (fp, star_index * OFFSET_SIZE, SEEK_SET);
+ swrite_32 (fp, offset);
+
+ // Init scan records for all planets and moons in the system
+ SeekStateFile (fp, offset, SEEK_SET);
+ for (i = 0; i < pSolarSysState->SunDesc[0].NumPlanets; ++i)
+ {
+ COUNT j;
+
+ swrite_a32 (fp, ScanRetrieveMask, NUM_SCAN_TYPES);
+ // init moons
+ for (j = 0; j < pSolarSysState->PlanetDesc[i].NumPlanets; ++j)
+ swrite_a32 (fp, ScanRetrieveMask, NUM_SCAN_TYPES);
+ }
+ }
+
+ // Skip scan records for all preceeding planets to the one we need
+ for (i = 0; i < planet_index; ++i)
+ offset += (pSolarSysState->PlanetDesc[i].NumPlanets + 1) *
+ SCAN_RECORD_SIZE;
+
+ // Skip scan records for all preceeding moons to the one we need
+ offset += moon_index * SCAN_RECORD_SIZE;
+
+ SeekStateFile (fp, offset, SEEK_SET);
+ swrite_a32 (fp, pSolarSysState->SysInfo.PlanetInfo.ScanRetrieveMask,
+ NUM_SCAN_TYPES);
+
+ CloseStateFile (fp);
+ }
+}
+
diff --git a/src/uqm/state.h b/src/uqm/state.h
new file mode 100644
index 0000000..e469cba
--- /dev/null
+++ b/src/uqm/state.h
@@ -0,0 +1,166 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_STATE_H_
+#define UQM_STATE_H_
+
+#include "port.h"
+#include "libs/compiler.h"
+#include <assert.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void InitPlanetInfo (void);
+extern void UninitPlanetInfo (void);
+extern void GetPlanetInfo (void);
+extern void PutPlanetInfo (void);
+
+extern void InitGroupInfo (BOOLEAN FirstTime);
+extern void UninitGroupInfo (void);
+extern BOOLEAN GetGroupInfo (DWORD offset, BYTE which_group);
+extern DWORD PutGroupInfo (DWORD offset, BYTE which_group);
+#define GROUPS_RANDOM ((DWORD)(0L))
+#define GROUPS_ADD_NEW ((DWORD)(~0L))
+#define GROUP_LIST ((BYTE)0)
+#define GROUP_INIT_IP ((BYTE)~0)
+ // Initialize IP group list (ip_group_q) from the actual groups
+ // (not GROUP_LIST) in one of the state files
+#define GROUP_LOAD_IP GROUP_LIST
+ // Read IP group list into ip_group_q from the list entry
+ // (GROUP_LIST) in one of the state files
+#define GROUP_SAVE_IP ((BYTE)~0)
+ // Write IP group list from ip_group_q to the list entry
+ // (GROUP_LIST) in one of the state files
+extern void BuildGroups (void);
+
+typedef struct GAME_STATE_FILE GAME_STATE_FILE;
+
+#define STARINFO_FILE 0
+ //"starinfo.dat"
+#define STAR_BUFSIZE (NUM_SOLAR_SYSTEMS * sizeof (DWORD) \
+ + 3800 * (3 * sizeof (DWORD)))
+#define RANDGRPINFO_FILE 1
+ //"randgrp.dat"
+#define RAND_BUFSIZE (4 * 1024)
+#define DEFGRPINFO_FILE 2
+ //"defgrp.dat"
+#define DEF_BUFSIZE (10 * 1024)
+
+typedef enum
+{
+ STARINFO,
+ RANDGRPINFO,
+ DEFGRPINFO
+} INFO_TYPE;
+
+GAME_STATE_FILE* OpenStateFile (int stateFile, const char *mode);
+void CloseStateFile (GAME_STATE_FILE *fp);
+void DeleteStateFile (int stateFile);
+DWORD LengthStateFile (GAME_STATE_FILE *fp);
+int ReadStateFile (void *lpBuf, COUNT size, COUNT count, GAME_STATE_FILE *fp);
+int WriteStateFile (const void *lpBuf, COUNT size, COUNT count, GAME_STATE_FILE *fp);
+int SeekStateFile (GAME_STATE_FILE *fp, long offset, int whence);
+
+static inline COUNT
+sread_8 (GAME_STATE_FILE *fp, BYTE *v)
+{
+ BYTE t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadStateFile (v, 1, 1, fp);
+}
+
+static inline COUNT
+sread_16 (GAME_STATE_FILE *fp, UWORD *v)
+{
+ UWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadStateFile (v, 2, 1, fp);
+}
+
+static inline COUNT
+sread_16s (GAME_STATE_FILE *fp, SWORD *v)
+{
+ UWORD t;
+ COUNT ret;
+ ret = sread_16 (fp, &t);
+ // unsigned to signed conversion
+ if (v)
+ *v = t;
+ return ret;
+}
+
+static inline COUNT
+sread_32 (GAME_STATE_FILE *fp, DWORD *v)
+{
+ DWORD t;
+ if (!v) /* read value ignored */
+ v = &t;
+ return ReadStateFile (v, 4, 1, fp);
+}
+
+static inline COUNT
+sread_a32 (GAME_STATE_FILE *fp, DWORD *ar, COUNT count)
+{
+ assert (ar != NULL);
+
+ for ( ; count > 0; --count, ++ar)
+ {
+ if (sread_32 (fp, ar) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+static inline COUNT
+swrite_8 (GAME_STATE_FILE *fp, BYTE v)
+{
+ return WriteStateFile (&v, 1, 1, fp);
+}
+
+static inline COUNT
+swrite_16 (GAME_STATE_FILE *fp, UWORD v)
+{
+ return WriteStateFile (&v, 2, 1, fp);
+}
+
+static inline COUNT
+swrite_32 (GAME_STATE_FILE *fp, DWORD v)
+{
+ return WriteStateFile (&v, 4, 1, fp);
+}
+
+static inline COUNT
+swrite_a32 (GAME_STATE_FILE *fp, const DWORD *ar, COUNT count)
+{
+ for ( ; count > 0; --count, ++ar)
+ {
+ if (swrite_32 (fp, *ar) != 1)
+ return 0;
+ }
+ return 1;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_STATE_H_ */
diff --git a/src/uqm/status.c b/src/uqm/status.c
new file mode 100644
index 0000000..6a588a8
--- /dev/null
+++ b/src/uqm/status.c
@@ -0,0 +1,582 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "status.h"
+#include "colors.h"
+#include "globdata.h"
+#include "races.h"
+#include "ship.h"
+#include "setup.h"
+#include "options.h"
+#include "init.h"
+ // for NUM_PLAYERS
+
+#include <stdio.h>
+#include <string.h>
+
+
+COORD status_y_offsets[NUM_PLAYERS];
+
+
+void
+InitStatusOffsets (void)
+{
+ // XXX: We have to jump through these hoops because GOOD_GUY_YOFFS is
+ // not a constant, contrary to what its name suggests.
+ status_y_offsets[0] = GOOD_GUY_YOFFS; // bottom player
+ status_y_offsets[1] = BAD_GUY_YOFFS; // top player
+}
+
+static void
+CaptainsWindow (CAPTAIN_STUFF *CSPtr, COORD y,
+ STATUS_FLAGS delta_status_flags, STATUS_FLAGS cur_status_flags,
+ COUNT Pass)
+{
+ STAMP Stamp;
+
+ Stamp.origin.x = CAPTAIN_XOFFS;
+ Stamp.origin.y = y + CAPTAIN_YOFFS;
+
+ if (delta_status_flags & LEFT)
+ {
+ Stamp.frame = CSPtr->turn;
+ if (!(delta_status_flags & RIGHT))
+ {
+ Stamp.frame = SetRelFrameIndex (Stamp.frame, 3);
+ if (Pass == 2)
+ {
+ if (cur_status_flags & LEFT)
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ else
+ Stamp.frame = DecFrameIndex (Stamp.frame);
+ }
+ }
+ else if (cur_status_flags & RIGHT)
+ {
+ if (Pass == 1)
+ Stamp.frame = SetRelFrameIndex (Stamp.frame, 3);
+ else
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ DrawStamp (&Stamp);
+ Stamp.frame = DecFrameIndex (Stamp.frame);
+ }
+ else
+ {
+ if (Pass == 1)
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ else
+ Stamp.frame = SetRelFrameIndex (Stamp.frame, 3);
+ DrawStamp (&Stamp);
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ }
+ DrawStamp (&Stamp);
+ }
+ else if (delta_status_flags & RIGHT)
+ {
+ Stamp.frame = CSPtr->turn;
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ if (Pass == 2)
+ {
+ if (cur_status_flags & RIGHT)
+ Stamp.frame = DecFrameIndex (Stamp.frame);
+ else
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ }
+ DrawStamp (&Stamp);
+ }
+
+ if (delta_status_flags & THRUST)
+ {
+ Stamp.frame = CSPtr->thrust;
+ if (Pass == 1)
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ else if (cur_status_flags & THRUST)
+ Stamp.frame = SetRelFrameIndex (Stamp.frame, 2);
+ DrawStamp (&Stamp);
+ }
+ if (delta_status_flags & WEAPON)
+ {
+ Stamp.frame = CSPtr->weapon;
+ if (Pass == 1)
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ else if (cur_status_flags & WEAPON)
+ Stamp.frame = SetRelFrameIndex (Stamp.frame, 2);
+ DrawStamp (&Stamp);
+ }
+ if (delta_status_flags & SPECIAL)
+ {
+ Stamp.frame = CSPtr->special;
+ if (Pass == 1)
+ Stamp.frame = IncFrameIndex (Stamp.frame);
+ else if (cur_status_flags & SPECIAL)
+ Stamp.frame = SetRelFrameIndex (Stamp.frame, 2);
+ DrawStamp (&Stamp);
+ }
+}
+
+void
+DrawBattleCrewAmount (SHIP_INFO *ShipInfoPtr, COORD y_offs)
+{
+#define MAX_CREW_DIGITS 3
+ RECT r;
+ TEXT t;
+ UNICODE buf[40];
+
+ t.baseline.x = BATTLE_CREW_X + 2;
+ if (optWhichMenu == OPT_PC)
+ t.baseline.x -= 8;
+ t.baseline.y = BATTLE_CREW_Y + y_offs;
+ t.align = ALIGN_LEFT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+
+ r.corner.x = t.baseline.x;
+ r.corner.y = t.baseline.y - 5;
+ r.extent.width = 6 * MAX_CREW_DIGITS + 6;
+ r.extent.height = 5;
+
+ sprintf (buf, "%u", ShipInfoPtr->crew_level);
+ SetContextFont (StarConFont);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ DrawFilledRectangle (&r);
+ SetContextForeGroundColor (BLACK_COLOR);
+ font_DrawText (&t);
+}
+
+void
+DrawCaptainsWindow (STARSHIP *StarShipPtr)
+{
+ COORD y;
+ COORD y_offs;
+ RECT r;
+ STAMP s;
+ FRAME Frame;
+ RACE_DESC *RDPtr;
+
+ RDPtr = StarShipPtr->RaceDescPtr;
+ Frame = RDPtr->ship_data.captain_control.background;
+ if (Frame)
+ {
+ Frame = SetAbsFrameIndex (Frame, 0);
+ RDPtr->ship_data.captain_control.background = Frame;
+ Frame = SetRelFrameIndex (Frame, 1);
+ RDPtr->ship_data.captain_control.turn = Frame;
+ Frame = SetRelFrameIndex (Frame, 5);
+ RDPtr->ship_data.captain_control.thrust = Frame;
+ Frame = SetRelFrameIndex (Frame, 3);
+ RDPtr->ship_data.captain_control.weapon = Frame;
+ Frame = SetRelFrameIndex (Frame, 3);
+ RDPtr->ship_data.captain_control.special = Frame;
+ }
+
+ BatchGraphics ();
+
+ assert (StarShipPtr->playerNr >= 0);
+ y_offs = status_y_offsets[StarShipPtr->playerNr];
+
+ r.corner.x = CAPTAIN_XOFFS - 2;
+ r.corner.y = y_offs + SHIP_INFO_HEIGHT;
+ r.extent.width = STATUS_WIDTH - CAPTAIN_XOFFS;
+ r.extent.height = SHIP_STATUS_HEIGHT - CAPTAIN_YOFFS + 2;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F));
+ r.corner.x = 1;
+ r.corner.y = y_offs + SHIP_INFO_HEIGHT;
+ r.extent.width = 1;
+ r.extent.height = (SHIP_STATUS_HEIGHT - SHIP_INFO_HEIGHT - 2);
+ DrawFilledRectangle (&r);
+ r.corner.x = 0;
+ ++r.extent.height;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19));
+ r.corner.x = STATUS_WIDTH - 1;
+ r.corner.y = y_offs + SHIP_INFO_HEIGHT;
+ r.extent.width = 1;
+ r.extent.height = SHIP_STATUS_HEIGHT - SHIP_INFO_HEIGHT;
+ DrawFilledRectangle (&r);
+ r.corner.x = STATUS_WIDTH - 2;
+ DrawFilledRectangle (&r);
+ r.corner.x = 1;
+ r.extent.width = STATUS_WIDTH - 2;
+ r.corner.y = y_offs + (SHIP_STATUS_HEIGHT - 2);
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = 0;
+ ++r.extent.width;
+ ++r.corner.y;
+ DrawFilledRectangle (&r);
+
+ y = y_offs + CAPTAIN_YOFFS;
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x08, 0x08, 0x08), 0x1F));
+ r.corner.x = 59;
+ r.corner.y = y;
+ r.extent.width = 1;
+ r.extent.height = 30;
+ DrawFilledRectangle (&r);
+ r.corner.x = 3;
+ r.corner.y += 30;
+ r.extent.width = 57;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x10, 0x10, 0x10), 0x19));
+ r.corner.x = 3;
+ r.extent.width = 57;
+ r.corner.y = y - 1;
+ r.extent.height = 1;
+ DrawFilledRectangle (&r);
+ r.corner.x = 3;
+ r.extent.width = 1;
+ r.corner.y = y;
+ r.extent.height = 30;
+ DrawFilledRectangle (&r);
+
+ s.frame = RDPtr->ship_data.captain_control.background;
+ s.origin.x = CAPTAIN_XOFFS;
+ s.origin.y = y;
+ DrawStamp (&s);
+
+ if (StarShipPtr->captains_name_index == 0
+ && StarShipPtr->playerNr == RPG_PLAYER_NUM)
+ { // This is SIS
+ TEXT t;
+
+ t.baseline.x = STATUS_WIDTH >> 1;
+ t.baseline.y = y + 6;
+ t.align = ALIGN_CENTER;
+ t.pStr = GLOBAL_SIS (CommanderName);
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02));
+ SetContextFont (TinyFont);
+ font_DrawText (&t);
+ }
+ if (RDPtr->ship_info.max_crew > MAX_CREW_SIZE ||
+ RDPtr->ship_info.ship_flags & PLAYER_CAPTAIN)
+ {
+ // All crew doesn't fit in the graphics; print a number.
+ // Always print a number for the SIS in the full game.
+ DrawBattleCrewAmount (&RDPtr->ship_info, y_offs);
+ }
+
+ UnbatchGraphics ();
+}
+
+BOOLEAN
+DeltaEnergy (ELEMENT *ElementPtr, SIZE energy_delta)
+{
+ BOOLEAN retval;
+ STARSHIP *StarShipPtr;
+ SHIP_INFO *ShipInfoPtr;
+
+ retval = TRUE;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ShipInfoPtr = &StarShipPtr->RaceDescPtr->ship_info;
+ if (energy_delta >= 0)
+ {
+ if ((BYTE)(ShipInfoPtr->energy_level + (BYTE)energy_delta) >
+ ShipInfoPtr->max_energy)
+ energy_delta = ShipInfoPtr->max_energy
+ - ShipInfoPtr->energy_level;
+ }
+ else
+ {
+ if ((BYTE)-energy_delta > ShipInfoPtr->energy_level)
+ {
+ retval = FALSE;
+ }
+ }
+
+ if (!retval)
+ StarShipPtr->cur_status_flags |= LOW_ON_ENERGY;
+ else
+ {
+ StarShipPtr->cur_status_flags &= ~LOW_ON_ENERGY;
+ StarShipPtr->energy_counter =
+ StarShipPtr->RaceDescPtr->characteristics.energy_wait;
+
+ DeltaStatistics (ShipInfoPtr, status_y_offsets[StarShipPtr->playerNr],
+ 0, energy_delta);
+ }
+
+ return (retval);
+}
+
+BOOLEAN
+DeltaCrew (ELEMENT *ElementPtr, SIZE crew_delta)
+{
+ BOOLEAN retval;
+ STARSHIP *StarShipPtr;
+ SHIP_INFO *ShipInfoPtr;
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE
+ && ElementPtr->playerNr == NPC_PLAYER_NUM)
+ return (TRUE); /* Samatra can't be crew-modified */
+
+ retval = TRUE;
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ ShipInfoPtr = &StarShipPtr->RaceDescPtr->ship_info;
+ if (crew_delta > 0)
+ {
+ ElementPtr->crew_level += crew_delta;
+ if (ElementPtr->crew_level > ShipInfoPtr->max_crew)
+ {
+ crew_delta = ShipInfoPtr->max_crew - ShipInfoPtr->crew_level;
+ ElementPtr->crew_level = ShipInfoPtr->max_crew;
+ }
+ }
+ else if (crew_delta < 0)
+ {
+ if (ElementPtr->crew_level > (COUNT)-crew_delta)
+ ElementPtr->crew_level += crew_delta;
+ else
+ {
+ crew_delta = -(SIZE)ElementPtr->crew_level;
+ ElementPtr->crew_level = 0;
+ retval = FALSE;
+ }
+ }
+
+ DeltaStatistics (ShipInfoPtr, status_y_offsets[StarShipPtr->playerNr],
+ crew_delta, 0);
+
+ return (retval);
+}
+
+void
+PreProcessStatus (ELEMENT *ShipPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->captains_name_index
+ || StarShipPtr->playerNr == RPG_PLAYER_NUM)
+ { // All except Sa-Matra, no captain's window there
+ STATUS_FLAGS old_status_flags, cur_status_flags;
+ CAPTAIN_STUFF *CSPtr;
+
+ cur_status_flags = StarShipPtr->cur_status_flags;
+ old_status_flags = StarShipPtr->old_status_flags;
+ old_status_flags ^= cur_status_flags;
+
+ CSPtr = &StarShipPtr->RaceDescPtr->ship_data.captain_control;
+ old_status_flags &= (LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+ if (old_status_flags)
+ {
+ assert (StarShipPtr->playerNr >= 0);
+ CaptainsWindow (CSPtr, status_y_offsets[StarShipPtr->playerNr],
+ old_status_flags, cur_status_flags, 1);
+ }
+ }
+}
+
+void
+PostProcessStatus (ELEMENT *ShipPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+ if (StarShipPtr->captains_name_index
+ || StarShipPtr->playerNr == RPG_PLAYER_NUM)
+ { // All except Sa-Matra, no captain's window there
+ COORD y;
+ STATUS_FLAGS cur_status_flags, old_status_flags;
+
+ cur_status_flags = StarShipPtr->cur_status_flags;
+
+ assert (StarShipPtr->playerNr >= 0);
+ y = status_y_offsets[StarShipPtr->playerNr];
+
+ if (ShipPtr->crew_level == 0)
+ {
+ StarShipPtr->cur_status_flags &=
+ ~(LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+
+ if (StarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
+ {
+ BYTE i;
+ Color c;
+ RECT r;
+
+ i = (BYTE)(NUM_EXPLOSION_FRAMES * 3 - 1) - ShipPtr->life_span;
+ if (i <= 4)
+ {
+ static const Color flash_tab0[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x19, 0x19), 0x24),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1C, 0x00), 0x78),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x1F), 0x0F),
+ };
+
+ c = flash_tab0[i];
+ r.corner.x = CAPTAIN_XOFFS;
+ r.corner.y = y + CAPTAIN_YOFFS;
+ r.extent.width = CAPTAIN_WIDTH;
+ r.extent.height = CAPTAIN_HEIGHT;
+ }
+ else
+ {
+ SetContextForeGroundColor (BLACK_COLOR);
+ i -= 5;
+ if (i <= 14)
+ {
+ static const Color flash_tab1[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1E, 0x1F, 0x12), 0x70),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x0A), 0x0E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1F, 0x00), 0x71),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x1C, 0x00), 0x78),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x18, 0x00), 0x79),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7F),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D),
+ };
+
+ c = flash_tab1[i];
+ r.corner.x = CAPTAIN_XOFFS + i;
+ r.corner.y = y + CAPTAIN_YOFFS + i;
+ r.extent.width = CAPTAIN_WIDTH - (i << 1);
+ r.extent.height = CAPTAIN_HEIGHT - (i << 1);
+ if (r.extent.height == 2)
+ ++r.extent.height;
+ DrawRectangle (&r);
+ ++r.corner.x;
+ ++r.corner.y;
+ r.extent.width -= 2;
+ r.extent.height -= 2;
+ }
+ else if ((i -= 15) <= 4)
+ {
+ r.corner.y = y + (CAPTAIN_YOFFS + 15);
+ r.extent.width = i + 1;
+ r.extent.height = 1;
+ switch (i)
+ {
+ case 0:
+ r.corner.x = CAPTAIN_XOFFS + 15;
+ i = CAPTAIN_WIDTH - ((15 + 1) << 1);
+ c = BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2C);
+ break;
+ case 1:
+ r.corner.x = CAPTAIN_XOFFS + 16;
+ i = CAPTAIN_WIDTH - ((17 + 1) << 1);
+ c = BUILD_COLOR (MAKE_RGB15 (0x07, 0x00, 0x00), 0x2F);
+ break;
+ case 2:
+ r.corner.x = CAPTAIN_XOFFS + 18;
+ i = CAPTAIN_WIDTH - ((20 + 1) << 1);
+ c = BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x00), 0x2A);
+ break;
+ case 3:
+ r.corner.x = CAPTAIN_XOFFS + 21;
+ i = CAPTAIN_WIDTH - ((24 + 1) << 1);
+ c = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x00, 0x00), 0x29);
+ break;
+ case 4:
+ r.corner.x = CAPTAIN_XOFFS + 25;
+ i = 1;
+ r.extent.width = 2;
+ c = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x50, 0x05), 0x28);
+ break;
+ default:
+ // Should not happen.
+ c = UNDEFINED_COLOR; // Keeping compiler quiet.
+ break;
+ }
+ DrawFilledRectangle (&r);
+ r.corner.x += i + r.extent.width;
+ DrawFilledRectangle (&r);
+ r.corner.x -= i;
+ r.extent.width = i;
+ }
+ else
+ {
+ if ((i -= 5) > 2)
+ c = BLACK_COLOR;
+ else
+ {
+ static const Color flash_tab2[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0B, 0x00, 0x00), 0x2E),
+ };
+
+ c = flash_tab2[i];
+ }
+ r.corner.x = CAPTAIN_XOFFS
+ + (CAPTAIN_WIDTH >> 1);
+ r.corner.y = y + CAPTAIN_YOFFS
+ + ((CAPTAIN_HEIGHT + 1) >> 1);
+ r.extent.width = 1;
+ r.extent.height = 1;
+ }
+ }
+ SetContextForeGroundColor (c);
+ DrawFilledRectangle (&r);
+ }
+ }
+
+ old_status_flags = StarShipPtr->old_status_flags;
+ old_status_flags = (old_status_flags ^ cur_status_flags) &
+ (LEFT | RIGHT | THRUST | WEAPON | SPECIAL | LOW_ON_ENERGY);
+
+ if (old_status_flags)
+ {
+ if (old_status_flags & LOW_ON_ENERGY)
+ {
+ if (!(cur_status_flags & LOW_ON_ENERGY))
+ DrawCrewFuelString (y, 1);
+ else
+ DrawCrewFuelString (y, -1);
+ }
+
+ old_status_flags &= (LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+ if (old_status_flags)
+ {
+ CaptainsWindow (
+ &StarShipPtr->RaceDescPtr->ship_data.captain_control,
+ y, old_status_flags, cur_status_flags, 2);
+ }
+ }
+
+ StarShipPtr->old_status_flags = cur_status_flags;
+ }
+}
+
diff --git a/src/uqm/status.h b/src/uqm/status.h
new file mode 100644
index 0000000..f27965e
--- /dev/null
+++ b/src/uqm/status.h
@@ -0,0 +1,75 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_STATUS_H_INCL_
+#define UQM_STATUS_H_INCL_
+
+#include "races.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define CREW_XOFFS 4
+#define ENERGY_XOFFS 52
+#define GAUGE_YOFFS (SHIP_INFO_HEIGHT - 10)
+#define UNIT_WIDTH 2
+#define UNIT_HEIGHT 1
+#define STAT_WIDTH (1 + UNIT_WIDTH + 1 + UNIT_WIDTH + 1)
+
+#define SHIP_INFO_HEIGHT 65
+#define CAPTAIN_XOFFS 4
+#define CAPTAIN_YOFFS (SHIP_INFO_HEIGHT + 4)
+#define CAPTAIN_WIDTH 55
+#define CAPTAIN_HEIGHT 30
+#define SHIP_STATUS_HEIGHT (STATUS_HEIGHT >> 1)
+#define BAD_GUY_YOFFS 0
+#define GOOD_GUY_YOFFS SHIP_STATUS_HEIGHT
+#define STARCON_TEXT_HEIGHT 7
+#define TINY_TEXT_HEIGHT 9
+
+#define BATTLE_CREW_X 10
+#define BATTLE_CREW_Y (64 - SAFE_Y)
+
+extern COORD status_y_offsets[];
+
+extern void InitStatusOffsets (void);
+
+extern void DrawCrewFuelString (COORD y, SIZE state);
+extern void ClearShipStatus (COORD y);
+extern void OutlineShipStatus (COORD y);
+extern void InitShipStatus (SHIP_INFO *ShipInfoPtr, STARSHIP *StarShipPtr,
+ RECT *pClipRect);
+ // StarShipPtr or pClipRect can be NULL
+extern void DeltaStatistics (SHIP_INFO *ShipInfoPtr, COORD y_offs,
+ SIZE crew_delta, SIZE energy_delta);
+extern void DrawBattleCrewAmount (SHIP_INFO *ShipInfoPtr, COORD y_offs);
+
+extern void DrawCaptainsWindow (STARSHIP *StarShipPtr);
+extern BOOLEAN DeltaEnergy (ELEMENT *ElementPtr, SIZE energy_delta);
+extern BOOLEAN DeltaCrew (ELEMENT *ElementPtr, SIZE crew_delta);
+
+extern void PreProcessStatus (ELEMENT *ShipPtr);
+extern void PostProcessStatus (ELEMENT *ShipPtr);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_STATUS_H_INCL_ */
diff --git a/src/uqm/supermelee/Makeinfo b/src/uqm/supermelee/Makeinfo
new file mode 100644
index 0000000..897a1fe
--- /dev/null
+++ b/src/uqm/supermelee/Makeinfo
@@ -0,0 +1,5 @@
+uqm_CFILES="buildpick.c loadmele.c melee.c meleesetup.c pickmele.c"
+uqm_HFILES="buildpick.h loadmele.h melee.h meleesetup.h meleeship.h pickmele.h"
+if [ -n "$uqm_NETPLAY" ]; then
+ uqm_SUBDIRS="$uqm_SUBDIRS netplay"
+fi
diff --git a/src/uqm/supermelee/buildpick.c b/src/uqm/supermelee/buildpick.c
new file mode 100644
index 0000000..3f4731b
--- /dev/null
+++ b/src/uqm/supermelee/buildpick.c
@@ -0,0 +1,221 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "buildpick.h"
+
+#include "../controls.h"
+#include "../colors.h"
+#include "../fmv.h"
+#include "../master.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "libs/gfxlib.h"
+
+static FRAME BuildPickFrame;
+
+void
+BuildBuildPickFrame (void)
+{
+ STAMP s;
+ RECT r;
+ COUNT i;
+ CONTEXT OldContext = SetContext (OffScreenContext);
+
+ // create team building ship selection box
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (MeleeFrame, 27);
+ // 5x5 grid of ships to pick from
+ GetFrameRect (s.frame, &r);
+
+ BuildPickFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, r.extent.width, r.extent.height, 1));
+ SetContextFGFrame (BuildPickFrame);
+ SetFrameHot (s.frame, MAKE_HOT_SPOT (0, 0));
+ DrawStamp (&s);
+
+ for (i = 0; i < NUM_PICK_COLS * NUM_PICK_ROWS; ++i)
+ DrawPickIcon (i, true);
+
+ SetContext (OldContext);
+}
+
+void
+DestroyBuildPickFrame (void)
+{
+ DestroyDrawable (ReleaseDrawable (BuildPickFrame));
+ BuildPickFrame = 0;
+}
+
+// Draw a ship icon in the ship selection popup.
+void
+DrawPickIcon (MeleeShip ship, bool DrawErase)
+{
+ STAMP s;
+ RECT r;
+
+ GetFrameRect (BuildPickFrame, &r);
+
+ s.origin.x = r.corner.x + 20 + (ship % NUM_PICK_COLS) * 18;
+ s.origin.y = r.corner.y + 5 + (ship / NUM_PICK_COLS) * 18;
+ s.frame = GetShipIconsFromIndex (ship);
+ if (DrawErase)
+ { // draw icon
+ DrawStamp (&s);
+ }
+ else
+ { // erase icon
+ Color OldColor;
+
+ OldColor = SetContextForeGroundColor (BLACK_COLOR);
+ DrawFilledStamp (&s);
+ SetContextForeGroundColor (OldColor);
+ }
+}
+
+void
+DrawPickFrame (MELEE_STATE *pMS)
+{
+ RECT r, r0, r1, ship_r;
+ STAMP s;
+
+ GetShipBox (&r0, 0, 0, 0),
+ GetShipBox (&r1, 1, NUM_MELEE_ROWS - 1, NUM_MELEE_COLUMNS - 1),
+ BoxUnion (&r0, &r1, &ship_r);
+
+ s.frame = SetAbsFrameIndex (BuildPickFrame, 0);
+ GetFrameRect (s.frame, &r);
+ r.corner.x = -(ship_r.corner.x
+ + ((ship_r.extent.width - r.extent.width) >> 1));
+ if (pMS->side)
+ r.corner.y = -ship_r.corner.y;
+ else
+ r.corner.y = -(ship_r.corner.y
+ + (ship_r.extent.height - r.extent.height));
+ SetFrameHot (s.frame, MAKE_HOT_SPOT (r.corner.x, r.corner.y));
+ s.origin.x = 0;
+ s.origin.y = 0;
+ DrawStamp (&s);
+ DrawMeleeShipStrings (pMS, pMS->currentShip);
+}
+
+void
+GetBuildPickFrameRect (RECT *r)
+{
+ GetFrameRect (BuildPickFrame, r);
+}
+
+static BOOLEAN
+DoPickShip (MELEE_STATE *pMS)
+{
+ DWORD TimeIn = GetTimeCounter ();
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ {
+ pMS->buildPickConfirmed = false;
+ return FALSE;
+ }
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+
+ if (PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ // Confirm selection or cancel.
+ pMS->buildPickConfirmed = !PulsedInputState.menu[KEY_MENU_CANCEL];
+ return FALSE;
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_SPECIAL]
+ && (pMS->currentShip != MELEE_NONE))
+ {
+ // Show ship spin video.
+ DoShipSpin (pMS->currentShip, (MUSIC_REF) 0);
+ return TRUE;
+ }
+
+ {
+ MeleeShip newSelectedShip;
+
+ newSelectedShip = pMS->currentShip;
+
+ if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ if (newSelectedShip % NUM_PICK_COLS == 0)
+ newSelectedShip += NUM_PICK_COLS;
+ --newSelectedShip;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ ++newSelectedShip;
+ if (newSelectedShip % NUM_PICK_COLS == 0)
+ newSelectedShip -= NUM_PICK_COLS;
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (newSelectedShip >= NUM_PICK_COLS)
+ newSelectedShip -= NUM_PICK_COLS;
+ else
+ newSelectedShip += NUM_PICK_COLS * (NUM_PICK_ROWS - 1);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ if (newSelectedShip < NUM_PICK_COLS * (NUM_PICK_ROWS - 1))
+ newSelectedShip += NUM_PICK_COLS;
+ else
+ newSelectedShip -= NUM_PICK_COLS * (NUM_PICK_ROWS - 1);
+ }
+
+ if (newSelectedShip != pMS->currentShip)
+ {
+ // A new ship has been selected.
+ DrawPickIcon (pMS->currentShip, true);
+ pMS->currentShip = newSelectedShip;
+ DrawMeleeShipStrings (pMS, newSelectedShip);
+ }
+ }
+
+ Melee_flashSelection (pMS);
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+// Returns true if a ship has been selected, or false if the operation has
+// been cancelled or if the general abort key was pressed (in which case
+// 'GLOBAL (CurrentActivity) & CHECK_ABORT' is true as usual.
+// If a ship was selected, pMS->currentShip is set to the selected ship.
+bool
+BuildPickShip (MELEE_STATE *pMS)
+{
+ FlushInput ();
+
+ if (pMS->currentShip == MELEE_NONE)
+ pMS->currentShip = 0;
+
+ DrawPickFrame (pMS);
+
+ pMS->InputFunc = DoPickShip;
+ DoInput (pMS, FALSE);
+
+ return pMS->buildPickConfirmed;
+}
+
diff --git a/src/uqm/supermelee/buildpick.h b/src/uqm/supermelee/buildpick.h
new file mode 100644
index 0000000..43608e7
--- /dev/null
+++ b/src/uqm/supermelee/buildpick.h
@@ -0,0 +1,25 @@
+#ifndef BUILDPICK_H
+#define BUILDPICK_H
+
+#include "types.h"
+#include "melee.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void BuildBuildPickFrame (void);
+void DestroyBuildPickFrame (void);
+bool BuildPickShip (MELEE_STATE *pMS);
+void GetBuildPickFrameRect (RECT *r);
+
+void DrawPickFrame (MELEE_STATE *pMS);
+void DrawPickIcon (MeleeShip ship, bool DrawErase);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* BUILDPICK_H */
+
diff --git a/src/uqm/supermelee/loadmele.c b/src/uqm/supermelee/loadmele.c
new file mode 100644
index 0000000..d5917c3
--- /dev/null
+++ b/src/uqm/supermelee/loadmele.c
@@ -0,0 +1,826 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// This file handles loading of teams, but the UI and the actual loading.
+
+#define MELEESETUP_INTERNAL
+#include "melee.h"
+
+#include "../controls.h"
+#include "../gameopt.h"
+#include "../gamestr.h"
+#include "../globdata.h"
+#include "../master.h"
+#include "meleesetup.h"
+#include "../save.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "options.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+
+#define LOAD_TEAM_NAME_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0F, 0x10, 0x1B), 0x00)
+#define LOAD_TEAM_NAME_TEXT_COLOR_HILITE \
+ BUILD_COLOR (MAKE_RGB15 (0x17, 0x18, 0x1D), 0x00)
+
+
+#define LOAD_MELEE_BOX_WIDTH 34
+#define LOAD_MELEE_BOX_HEIGHT 34
+#define LOAD_MELEE_BOX_SPACE 1
+
+
+static void DrawFileStrings (MELEE_STATE *pMS);
+static bool FillFileView (MELEE_STATE *pMS);
+
+
+static bool
+LoadTeamImage (DIRENTRY DirEntry, MeleeTeam *team)
+{
+ const char *fileName;
+ uio_Stream *stream;
+
+ fileName = GetDirEntryAddress (DirEntry);
+
+ stream = uio_fopen (meleeDir, fileName, "rb");
+ if (stream == NULL)
+ return false;
+
+ if (MeleeTeam_deserialize (team, stream) == -1)
+ return false;
+
+ uio_fclose (stream);
+
+ return true;
+}
+
+#if 0 /* Not used */
+static void
+UnindexFleet (MELEE_STATE *pMS, COUNT index)
+{
+ assert (index < pMS->load.numIndices);
+ pMS->load.numIndices--;
+ memmove (&pMS->load.entryIndices[index],
+ &pMS->load.entryIndices[index + 1],
+ (pMS->load.numIndices - index) * sizeof pMS->load.entryIndices[0]);
+}
+#endif
+
+static void
+UnindexFleets (MELEE_STATE *pMS, COUNT index, COUNT count)
+{
+ assert (index + count <= pMS->load.numIndices);
+
+ pMS->load.numIndices -= count;
+ memmove (&pMS->load.entryIndices[index],
+ &pMS->load.entryIndices[index + count],
+ (pMS->load.numIndices - index) * sizeof pMS->load.entryIndices[0]);
+}
+
+static bool
+GetFleetByIndex (MELEE_STATE *pMS, COUNT index, MeleeTeam *result)
+{
+ COUNT firstIndex;
+
+ if (index < pMS->load.preBuiltCount)
+ {
+ MeleeTeam_copy (result, pMS->load.preBuiltList[index]);
+ return true;
+ }
+
+ index -= pMS->load.preBuiltCount;
+ firstIndex = index;
+
+ for ( ; index < pMS->load.numIndices; index++)
+ {
+ DIRENTRY entry = SetAbsDirEntryTableIndex (pMS->load.dirEntries,
+ pMS->load.entryIndices[index]);
+ if (LoadTeamImage (entry, result))
+ break; // Success
+
+ {
+ const char *fileName;
+ fileName = GetDirEntryAddress (entry);
+ log_add (log_Warning, "Warning: File '%s' is not a valid "
+ "SuperMelee team.", fileName);
+ }
+ }
+
+ if (index != firstIndex)
+ UnindexFleets (pMS, firstIndex, index - firstIndex);
+
+ return index < pMS->load.numIndices;
+}
+
+// returns (COUNT) -1 if not found
+static COUNT
+GetFleetIndexByFileName (MELEE_STATE *pMS, const char *fileName)
+{
+ COUNT index;
+
+ for (index = 0; index < pMS->load.numIndices; index++)
+ {
+ DIRENTRY entry = SetAbsDirEntryTableIndex (pMS->load.dirEntries,
+ pMS->load.entryIndices[index]);
+ const char *entryName = GetDirEntryAddress (entry);
+
+ if (strcasecmp ((const char *) entryName, fileName) == 0)
+ return pMS->load.preBuiltCount + index;
+ }
+
+ return (COUNT) -1;
+}
+
+// Auxiliary function for DrawFileStrings
+// If drawShips is set the ships themselves are drawn, in addition to the
+// fleet name and value; if not, only the fleet name and value are drawn.
+// If highlite is set the text is drawn in the color used for highlighting.
+static void
+DrawFileString (const MeleeTeam *team, const POINT *origin,
+ BOOLEAN drawShips, BOOLEAN highlite)
+{
+ SetContextForeGroundColor (highlite ?
+ LOAD_TEAM_NAME_TEXT_COLOR_HILITE : LOAD_TEAM_NAME_TEXT_COLOR);
+
+ // Print the name of the fleet
+ {
+ TEXT Text;
+
+ Text.baseline = *origin;
+ Text.align = ALIGN_LEFT;
+ Text.pStr = MeleeTeam_getTeamName(team);
+ Text.CharCount = (COUNT)~0;
+ font_DrawText (&Text);
+ }
+
+ // Print the value of the fleet
+ {
+ TEXT Text;
+ UNICODE buf[60];
+
+ sprintf (buf, "%u", MeleeTeam_getValue (team));
+ Text.baseline = *origin;
+ Text.baseline.x += NUM_MELEE_COLUMNS *
+ (LOAD_MELEE_BOX_WIDTH + LOAD_MELEE_BOX_SPACE) - 1;
+ Text.align = ALIGN_RIGHT;
+ Text.pStr = buf;
+ Text.CharCount = (COUNT)~0;
+ font_DrawText (&Text);
+ }
+
+ // Draw the ships for the fleet
+ if (drawShips)
+ {
+ STAMP s;
+ FleetShipIndex slotI;
+
+ s.origin.x = origin->x + 1;
+ s.origin.y = origin->y + 4;
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ {
+ BYTE StarShip;
+
+ StarShip = team->ships[slotI];
+ if (StarShip != MELEE_NONE)
+ {
+ s.frame = GetShipIconsFromIndex (StarShip);
+ DrawStamp (&s);
+ s.origin.x += 17;
+ }
+ }
+ }
+}
+
+// returns true if there are any entries in the view, in which case
+// pMS->load.bot gets set to the index just past the bottom entry in the view.
+// returns false if not, in which case, the entire view remains unchanged.
+static bool
+FillFileView (MELEE_STATE *pMS)
+{
+ COUNT viewI;
+
+ for (viewI = 0; viewI < LOAD_TEAM_VIEW_SIZE; viewI++)
+ {
+ bool success = GetFleetByIndex (pMS, pMS->load.top + viewI,
+ pMS->load.view[viewI]);
+ if (!success)
+ break;
+ }
+
+ if (viewI == 0)
+ return false;
+
+ pMS->load.bot = pMS->load.top + viewI;
+ return true;
+}
+
+#define FILE_STRING_ORIGIN_X 5
+#define FILE_STRING_ORIGIN_Y 34
+#define ENTRY_HEIGHT 32
+
+static void
+SelectFileString (MELEE_STATE *pMS, bool hilite)
+{
+ CONTEXT OldContext;
+ POINT origin;
+ COUNT viewI;
+
+ viewI = pMS->load.cur - pMS->load.top;
+
+ OldContext = SetContext (SpaceContext);
+ SetContextFont (MicroFont);
+ BatchGraphics ();
+
+ origin.x = FILE_STRING_ORIGIN_X;
+ origin.y = FILE_STRING_ORIGIN_Y + viewI * ENTRY_HEIGHT;
+ DrawFileString (pMS->load.view[viewI], &origin, FALSE, hilite);
+
+ UnbatchGraphics ();
+ SetContext (OldContext);
+}
+
+static void
+DrawFileStrings (MELEE_STATE *pMS)
+{
+ POINT origin;
+ CONTEXT OldContext;
+
+ origin.x = FILE_STRING_ORIGIN_X;
+ origin.y = FILE_STRING_ORIGIN_Y;
+
+ OldContext = SetContext (SpaceContext);
+ SetContextFont (MicroFont);
+ BatchGraphics ();
+
+ DrawMeleeIcon (28); /* The load team frame */
+
+ if (FillFileView (pMS))
+ {
+ COUNT i;
+ for (i = pMS->load.top; i < pMS->load.bot; i++) {
+ DrawFileString (pMS->load.view[i - pMS->load.top], &origin,
+ TRUE, FALSE);
+ origin.y += ENTRY_HEIGHT;
+ }
+ }
+
+ UnbatchGraphics ();
+ SetContext (OldContext);
+}
+
+static void
+RefocusView (MELEE_STATE *pMS, COUNT index)
+{
+ assert (index < pMS->load.preBuiltCount + pMS->load.numIndices);
+
+ pMS->load.cur = index;
+ if (index <= LOAD_TEAM_VIEW_SIZE / 2)
+ pMS->load.top = 0;
+ else
+ pMS->load.top = index - LOAD_TEAM_VIEW_SIZE / 2;
+}
+
+static void
+flashSelectedTeam (MELEE_STATE *pMS)
+{
+#define FLASH_RATE (ONE_SECOND / 9)
+ static TimeCount NextTime = 0;
+ static int hilite = 0;
+ TimeCount Now = GetTimeCounter ();
+
+ if (Now >= NextTime)
+ {
+ CONTEXT OldContext;
+
+ NextTime = Now + FLASH_RATE;
+ hilite ^= 1;
+
+ OldContext = SetContext (SpaceContext);
+ SelectFileString (pMS, hilite);
+ SetContext (OldContext);
+ }
+}
+
+BOOLEAN
+DoLoadTeam (MELEE_STATE *pMS)
+{
+ DWORD TimeIn = GetTimeCounter ();
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN | MENU_SOUND_PAGEUP |
+ MENU_SOUND_PAGEDOWN, MENU_SOUND_SELECT);
+
+ if (!pMS->Initialized)
+ {
+ DrawFileStrings (pMS);
+ SelectFileString (pMS, true);
+ pMS->Initialized = TRUE;
+ pMS->InputFunc = DoLoadTeam;
+ return TRUE;
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ // Copy the selected fleet to the player.
+ Melee_LocalChange_team (pMS, pMS->side,
+ pMS->load.view[pMS->load.cur - pMS->load.top]);
+ }
+
+ pMS->InputFunc = DoMelee;
+ pMS->LastInputTime = GetTimeCounter ();
+ {
+ RECT r;
+
+ GetFrameRect (SetAbsFrameIndex (MeleeFrame, 28), &r);
+ RepairMeleeFrame (&r);
+ }
+ return TRUE;
+ }
+
+ {
+ COUNT newTop = pMS->load.top;
+ COUNT newIndex = pMS->load.cur;
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (newIndex > 0)
+ {
+ newIndex--;
+ if (newIndex < newTop)
+ newTop = (newTop < LOAD_TEAM_VIEW_SIZE) ?
+ 0 : newTop - LOAD_TEAM_VIEW_SIZE;
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ COUNT numEntries = pMS->load.numIndices + pMS->load.preBuiltCount;
+ if (newIndex + 1 < numEntries)
+ {
+ newIndex++;
+ if (newIndex >= pMS->load.bot)
+ newTop = pMS->load.bot;
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_PAGE_UP])
+ {
+ newIndex = (newIndex < LOAD_TEAM_VIEW_SIZE) ?
+ 0 : newIndex - LOAD_TEAM_VIEW_SIZE;
+ newTop = (newTop < LOAD_TEAM_VIEW_SIZE) ?
+ 0 : newTop - LOAD_TEAM_VIEW_SIZE;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_PAGE_DOWN])
+ {
+ COUNT numEntries = pMS->load.numIndices + pMS->load.preBuiltCount;
+ if (newIndex + LOAD_TEAM_VIEW_SIZE < numEntries)
+ {
+ newIndex += LOAD_TEAM_VIEW_SIZE;
+ newTop += LOAD_TEAM_VIEW_SIZE;
+ }
+ else
+ {
+ newIndex = numEntries - 1;
+ if (newTop + LOAD_TEAM_VIEW_SIZE < numEntries &&
+ numEntries > LOAD_TEAM_VIEW_SIZE)
+ newTop = numEntries - LOAD_TEAM_VIEW_SIZE;
+ }
+ }
+
+ if (newIndex != pMS->load.cur)
+ {
+ // The cursor has been moved.
+ if (newTop == pMS->load.top)
+ {
+ // The view itself hasn't changed.
+ SelectFileString (pMS, false);
+ }
+ else
+ {
+ // The view is changed.
+ pMS->load.top = newTop;
+ DrawFileStrings (pMS);
+ }
+ pMS->load.cur = newIndex;
+ }
+ }
+
+ flashSelectedTeam (pMS);
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static void
+SelectTeamByFileName (MELEE_STATE *pMS, const char *fileName)
+{
+ COUNT index = GetFleetIndexByFileName (pMS, fileName);
+ if (index == (COUNT) -1)
+ return;
+
+ RefocusView (pMS, index);
+}
+
+void
+LoadTeamList (MELEE_STATE *pMS)
+{
+ COUNT i;
+
+ DestroyDirEntryTable (ReleaseDirEntryTable (pMS->load.dirEntries));
+ pMS->load.dirEntries = CaptureDirEntryTable (
+ LoadDirEntryTable (meleeDir, "", ".mle", match_MATCH_SUFFIX));
+
+ if (pMS->load.entryIndices != NULL)
+ HFree (pMS->load.entryIndices);
+ pMS->load.numIndices = GetDirEntryTableCount (pMS->load.dirEntries);
+ pMS->load.entryIndices = HMalloc (pMS->load.numIndices *
+ sizeof pMS->load.entryIndices[0]);
+ for (i = 0; i < pMS->load.numIndices; i++)
+ pMS->load.entryIndices[i] = i;
+}
+
+BOOLEAN
+DoSaveTeam (MELEE_STATE *pMS)
+{
+ STAMP MsgStamp;
+ char file[NAME_MAX];
+ uio_Stream *stream;
+ CONTEXT OldContext;
+ bool saveOk = false;
+
+ snprintf (file, sizeof file, "%s.mle",
+ MeleeSetup_getTeamName (pMS->meleeSetup, pMS->side));
+
+ OldContext = SetContext (ScreenContext);
+ ConfirmSaveLoad (&MsgStamp);
+ // Show the "Saving . . ." message.
+
+ stream = uio_fopen (meleeDir, file, "wb");
+ if (stream != NULL)
+ {
+ saveOk = (MeleeTeam_serialize (&pMS->meleeSetup->teams[pMS->side],
+ stream) == 0);
+ uio_fclose (stream);
+
+ if (!saveOk)
+ uio_unlink (meleeDir, file);
+ }
+
+ pMS->load.top = 0;
+ pMS->load.cur = 0;
+
+ // Undo the screen damage done by the "Saving . . ." message.
+ DrawStamp (&MsgStamp);
+ DestroyDrawable (ReleaseDrawable (MsgStamp.frame));
+ SetContext (OldContext);
+
+ if (!saveOk)
+ SaveProblem ();
+
+ // Update the team list; a previously existing team may have been
+ // deleted when save failed.
+ LoadTeamList (pMS);
+ SelectTeamByFileName (pMS, file);
+
+ return (stream != 0);
+}
+
+static void
+InitPreBuilt (MELEE_STATE *pMS)
+{
+ MeleeTeam **list;
+
+#define PREBUILT_COUNT 15
+ pMS->load.preBuiltList =
+ HMalloc (PREBUILT_COUNT * sizeof (MeleeTeam *));
+ pMS->load.preBuiltCount = PREBUILT_COUNT;
+#undef PREBUILT_COUNT
+
+ {
+ size_t fleetI;
+
+ for (fleetI = 0; fleetI < pMS->load.preBuiltCount; fleetI++)
+ pMS->load.preBuiltList[fleetI] = MeleeTeam_new ();
+ }
+
+ list = pMS->load.preBuiltList;
+
+ {
+ /* "Balanced Team 1" */
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, GAME_STRING (MELEE_STRING_BASE + 4));
+ MeleeTeam_setShip (*list, i++, MELEE_ANDROSYNTH);
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_DRUUGE);
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ MeleeTeam_setShip (*list, i++, MELEE_MELNORME);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ MeleeTeam_setShip (*list, i++, MELEE_SPATHI);
+ MeleeTeam_setShip (*list, i++, MELEE_SYREEN);
+ MeleeTeam_setShip (*list, i++, MELEE_UTWIG);
+ list++;
+ }
+
+ {
+ /* "Balanced Team 2" */
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, GAME_STRING (MELEE_STRING_BASE + 5));
+ MeleeTeam_setShip (*list, i++, MELEE_ARILOU);
+ MeleeTeam_setShip (*list, i++, MELEE_CHENJESU);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_MYCON);
+ MeleeTeam_setShip (*list, i++, MELEE_YEHAT);
+ MeleeTeam_setShip (*list, i++, MELEE_PKUNK);
+ MeleeTeam_setShip (*list, i++, MELEE_SUPOX);
+ MeleeTeam_setShip (*list, i++, MELEE_THRADDASH);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ MeleeTeam_setShip (*list, i++, MELEE_SHOFIXTI);
+ list++;
+ }
+
+ {
+ /* "200 points" */
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, GAME_STRING (MELEE_STRING_BASE + 6));
+ MeleeTeam_setShip (*list, i++, MELEE_ANDROSYNTH);
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_DRUUGE);
+ MeleeTeam_setShip (*list, i++, MELEE_MELNORME);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_SUPOX);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ MeleeTeam_setShip (*list, i++, MELEE_SPATHI);
+ MeleeTeam_setShip (*list, i++, MELEE_ILWRATH);
+ MeleeTeam_setShip (*list, i++, MELEE_VUX);
+ list++;
+ }
+
+ {
+ /* "Behemoth Zenith" */
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, GAME_STRING (MELEE_STRING_BASE + 7));
+ MeleeTeam_setShip (*list, i++, MELEE_CHENJESU);
+ MeleeTeam_setShip (*list, i++, MELEE_CHENJESU);
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ MeleeTeam_setShip (*list, i++, MELEE_UTWIG);
+ MeleeTeam_setShip (*list, i++, MELEE_UTWIG);
+ list++;
+ }
+
+ {
+ /* "The Peeled Eyes" */
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, GAME_STRING (MELEE_STRING_BASE + 8));
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ MeleeTeam_setShip (*list, i++, MELEE_CHENJESU);
+ MeleeTeam_setShip (*list, i++, MELEE_MYCON);
+ MeleeTeam_setShip (*list, i++, MELEE_SYREEN);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ MeleeTeam_setShip (*list, i++, MELEE_SHOFIXTI);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_MELNORME);
+ MeleeTeam_setShip (*list, i++, MELEE_DRUUGE);
+ MeleeTeam_setShip (*list, i++, MELEE_PKUNK);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "Ford's Fighters");
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ MeleeTeam_setShip (*list, i++, MELEE_MELNORME);
+ MeleeTeam_setShip (*list, i++, MELEE_SUPOX);
+ MeleeTeam_setShip (*list, i++, MELEE_UTWIG);
+ MeleeTeam_setShip (*list, i++, MELEE_UMGAH);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "Leyland's Lashers");
+ MeleeTeam_setShip (*list, i++, MELEE_ANDROSYNTH);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_MYCON);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "The Gregorizers 200");
+ MeleeTeam_setShip (*list, i++, MELEE_ANDROSYNTH);
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_DRUUGE);
+ MeleeTeam_setShip (*list, i++, MELEE_MELNORME);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_SUPOX);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ MeleeTeam_setShip (*list, i++, MELEE_PKUNK);
+ MeleeTeam_setShip (*list, i++, MELEE_SPATHI);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "300 point Armada!");
+ MeleeTeam_setShip (*list, i++, MELEE_ANDROSYNTH);
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_CHENJESU);
+ MeleeTeam_setShip (*list, i++, MELEE_DRUUGE);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_MELNORME);
+ MeleeTeam_setShip (*list, i++, MELEE_MYCON);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ MeleeTeam_setShip (*list, i++, MELEE_PKUNK);
+ MeleeTeam_setShip (*list, i++, MELEE_SPATHI);
+ MeleeTeam_setShip (*list, i++, MELEE_SUPOX);
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ MeleeTeam_setShip (*list, i++, MELEE_YEHAT);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "Little Dudes with Attitudes");
+ MeleeTeam_setShip (*list, i++, MELEE_UMGAH);
+ MeleeTeam_setShip (*list, i++, MELEE_THRADDASH);
+ MeleeTeam_setShip (*list, i++, MELEE_SHOFIXTI);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_VUX);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "New Alliance Ships");
+ MeleeTeam_setShip (*list, i++, MELEE_ARILOU);
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ MeleeTeam_setShip (*list, i++, MELEE_PKUNK);
+ MeleeTeam_setShip (*list, i++, MELEE_SHOFIXTI);
+ MeleeTeam_setShip (*list, i++, MELEE_SUPOX);
+ MeleeTeam_setShip (*list, i++, MELEE_SYREEN);
+ MeleeTeam_setShip (*list, i++, MELEE_UTWIG);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ MeleeTeam_setShip (*list, i++, MELEE_YEHAT);
+ MeleeTeam_setShip (*list, i++, MELEE_DRUUGE);
+ MeleeTeam_setShip (*list, i++, MELEE_THRADDASH);
+ MeleeTeam_setShip (*list, i++, MELEE_SPATHI);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "Old Alliance Ships");
+ MeleeTeam_setShip (*list, i++, MELEE_ARILOU);
+ MeleeTeam_setShip (*list, i++, MELEE_CHENJESU);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_MMRNMHRM);
+ MeleeTeam_setShip (*list, i++, MELEE_SHOFIXTI);
+ MeleeTeam_setShip (*list, i++, MELEE_SYREEN);
+ MeleeTeam_setShip (*list, i++, MELEE_YEHAT);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "Old Hierarchy Ships");
+ MeleeTeam_setShip (*list, i++, MELEE_ANDROSYNTH);
+ MeleeTeam_setShip (*list, i++, MELEE_ILWRATH);
+ MeleeTeam_setShip (*list, i++, MELEE_MYCON);
+ MeleeTeam_setShip (*list, i++, MELEE_SPATHI);
+ MeleeTeam_setShip (*list, i++, MELEE_UMGAH);
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ MeleeTeam_setShip (*list, i++, MELEE_VUX);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "Star Control 1");
+ MeleeTeam_setShip (*list, i++, MELEE_ANDROSYNTH);
+ MeleeTeam_setShip (*list, i++, MELEE_ARILOU);
+ MeleeTeam_setShip (*list, i++, MELEE_CHENJESU);
+ MeleeTeam_setShip (*list, i++, MELEE_EARTHLING);
+ MeleeTeam_setShip (*list, i++, MELEE_ILWRATH);
+ MeleeTeam_setShip (*list, i++, MELEE_MMRNMHRM);
+ MeleeTeam_setShip (*list, i++, MELEE_MYCON);
+ MeleeTeam_setShip (*list, i++, MELEE_SHOFIXTI);
+ MeleeTeam_setShip (*list, i++, MELEE_SPATHI);
+ MeleeTeam_setShip (*list, i++, MELEE_SYREEN);
+ MeleeTeam_setShip (*list, i++, MELEE_UMGAH);
+ MeleeTeam_setShip (*list, i++, MELEE_URQUAN);
+ MeleeTeam_setShip (*list, i++, MELEE_VUX);
+ MeleeTeam_setShip (*list, i++, MELEE_YEHAT);
+ list++;
+ }
+
+ {
+ FleetShipIndex i = 0;
+ MeleeTeam_setName (*list, "Star Control 2");
+ MeleeTeam_setShip (*list, i++, MELEE_CHMMR);
+ MeleeTeam_setShip (*list, i++, MELEE_DRUUGE);
+ MeleeTeam_setShip (*list, i++, MELEE_KOHR_AH);
+ MeleeTeam_setShip (*list, i++, MELEE_MELNORME);
+ MeleeTeam_setShip (*list, i++, MELEE_ORZ);
+ MeleeTeam_setShip (*list, i++, MELEE_PKUNK);
+ MeleeTeam_setShip (*list, i++, MELEE_SLYLANDRO);
+ MeleeTeam_setShip (*list, i++, MELEE_SUPOX);
+ MeleeTeam_setShip (*list, i++, MELEE_THRADDASH);
+ MeleeTeam_setShip (*list, i++, MELEE_UTWIG);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ MeleeTeam_setShip (*list, i++, MELEE_ZOQFOTPIK);
+ list++;
+ }
+
+ assert (list == pMS->load.preBuiltList + pMS->load.preBuiltCount);
+}
+
+static void
+UninitPreBuilt (MELEE_STATE *pMS)
+{
+ size_t fleetI;
+ for (fleetI = 0; fleetI < pMS->load.preBuiltCount; fleetI++)
+ MeleeTeam_delete (pMS->load.preBuiltList[fleetI]);
+ HFree (pMS->load.preBuiltList);
+ pMS->load.preBuiltCount = 0;
+}
+
+static void
+InitLoadView (MELEE_STATE *pMS)
+{
+ size_t viewI;
+ MeleeTeam **view = pMS->load.view;
+
+ for (viewI = 0; viewI < LOAD_TEAM_VIEW_SIZE; viewI++)
+ view[viewI] = MeleeTeam_new ();
+}
+
+static void
+UninitLoadView (MELEE_STATE *pMS)
+{
+ size_t viewI;
+ MeleeTeam **view = pMS->load.view;
+
+ for (viewI = 0; viewI < LOAD_TEAM_VIEW_SIZE; viewI++)
+ MeleeTeam_delete(view[viewI]);
+}
+
+void
+InitMeleeLoadState (MELEE_STATE *pMS)
+{
+ pMS->load.entryIndices = NULL;
+ InitPreBuilt (pMS);
+ InitLoadView (pMS);
+}
+
+void
+UninitMeleeLoadState (MELEE_STATE *pMS)
+{
+ UninitLoadView (pMS);
+ UninitPreBuilt (pMS);
+ if (pMS->load.entryIndices != NULL)
+ HFree (pMS->load.entryIndices);
+}
+
+
diff --git a/src/uqm/supermelee/loadmele.h b/src/uqm/supermelee/loadmele.h
new file mode 100644
index 0000000..529ef66
--- /dev/null
+++ b/src/uqm/supermelee/loadmele.h
@@ -0,0 +1,67 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_SUPERMELEE_LOADMELE_H_
+#define UQM_SUPERMELEE_LOADMELE_H_
+
+#define LOAD_TEAM_VIEW_SIZE 5
+
+struct melee_load_state;
+
+#include "melee.h"
+#include "meleesetup.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct melee_load_state
+{
+ MeleeTeam **preBuiltList;
+ COUNT preBuiltCount;
+
+ DIRENTRY dirEntries;
+ COUNT *entryIndices;
+ COUNT numIndices;
+
+ MeleeTeam *view[LOAD_TEAM_VIEW_SIZE];
+ COUNT top;
+ // Index of the first entry for the view.
+ COUNT bot;
+ // Index of the first entry past the end of the view.
+
+ COUNT cur;
+ // Index of the current position in the view.
+ COUNT viewSize;
+ // Number of entries in the view.
+};
+
+void InitMeleeLoadState (MELEE_STATE *pMS);
+void UninitMeleeLoadState (MELEE_STATE *pMS);
+
+BOOLEAN DoLoadTeam (MELEE_STATE *pMS);
+BOOLEAN DoSaveTeam (MELEE_STATE *pMS);
+bool ReadTeamImage (MeleeTeam *pTI, uio_Stream *load_fp);
+int WriteTeamImage (const MeleeTeam *pTI, uio_Stream *save_fp);
+void LoadTeamList (MELEE_STATE *pMS);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_LOADMELE_H_ */
diff --git a/src/uqm/supermelee/melee.c b/src/uqm/supermelee/melee.c
new file mode 100644
index 0000000..70f3acb
--- /dev/null
+++ b/src/uqm/supermelee/melee.c
@@ -0,0 +1,2640 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "melee.h"
+
+#include "options.h"
+#include "buildpick.h"
+#include "meleeship.h"
+#include "../battle.h"
+#include "../build.h"
+#include "../status.h"
+#include "../colors.h"
+#include "../comm.h"
+ // for getLineWithinWidth()
+#include "../cons_res.h"
+ // for load_gravity_well() and free_gravity_well()
+#include "../controls.h"
+#include "../gamestr.h"
+#include "../globdata.h"
+#include "../intel.h"
+#include "../master.h"
+#include "../nameref.h"
+#ifdef NETPLAY
+# include "netplay/netconnection.h"
+# include "netplay/netmelee.h"
+# include "netplay/notify.h"
+# include "netplay/notifyall.h"
+# include "libs/graphics/widgets.h"
+ // for DrawShadowedBox()
+# include "../cnctdlg.h"
+ // for MeleeConnectDialog()
+#endif /* defined (NETPLAY) */
+#include "../resinst.h"
+#include "../settings.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "../util.h"
+ // for DrawStarConBox()
+#include "../planets/planets.h"
+ // for NUMBER_OF_PLANET_TYPES
+#include "libs/gfxlib.h"
+#include "libs/mathlib.h"
+ // for TFB_Random()
+#include "libs/reslib.h"
+#include "libs/log.h"
+#include "libs/uio.h"
+
+
+#include <assert.h>
+#include <string.h>
+
+
+static void StartMelee (MELEE_STATE *pMS);
+#ifdef NETPLAY
+static ssize_t numPlayersReady (void);
+#endif /* NETPLAY */
+
+enum
+{
+#ifdef NETPLAY
+ NET_TOP,
+#endif
+ CONTROLS_TOP,
+ SAVE_TOP,
+ LOAD_TOP,
+ START_MELEE,
+ LOAD_BOT,
+ SAVE_BOT,
+ CONTROLS_BOT,
+#ifdef NETPLAY
+ NET_BOT,
+#endif
+ QUIT_BOT,
+ EDIT_MELEE, // Editing a fleet or the team name
+ BUILD_PICK // Selecting a ship to add to a fleet
+};
+
+#ifdef NETPLAY
+#define TOP_ENTRY NET_TOP
+#else
+#define TOP_ENTRY CONTROLS_TOP
+#endif
+
+#define MELEE_X_OFFS 2
+#define MELEE_Y_OFFS 21
+#define MELEE_BOX_WIDTH 34
+#define MELEE_BOX_HEIGHT 34
+#define MELEE_BOX_SPACE 1
+
+#define MENU_X_OFFS 29
+
+#define INFO_ORIGIN_X 4
+#define INFO_WIDTH 58
+#define TEAM_INFO_ORIGIN_Y 3
+#define TEAM_INFO_HEIGHT (SHIP_INFO_HEIGHT + 75)
+#define MODE_INFO_ORIGIN_Y (TEAM_INFO_HEIGHT + 6)
+#define MODE_INFO_HEIGHT ((STATUS_HEIGHT - 3) - MODE_INFO_ORIGIN_Y)
+#define RACE_INFO_ORIGIN_Y (SHIP_INFO_HEIGHT + 6)
+#define RACE_INFO_HEIGHT ((STATUS_HEIGHT - 3) - RACE_INFO_ORIGIN_Y)
+
+#define MELEE_STATUS_X_OFFS 1
+#define MELEE_STATUS_Y_OFFS 201
+#define MELEE_STATUS_WIDTH (NUM_MELEE_COLUMNS * \
+ (MELEE_BOX_WIDTH + MELEE_BOX_SPACE))
+#define MELEE_STATUS_HEIGHT 38
+
+#define MELEE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x04)
+#define MELEE_TITLE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C)
+#define MELEE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x0C)
+#define MELEE_TEAM_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E)
+
+#define STATE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x14), 0x01)
+#define STATE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x14), 0x03)
+#define ACTIVE_STATE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)
+#define UNAVAILABLE_STATE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+#define HI_STATE_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1F, 0x1F), 0x0B)
+#define HI_STATE_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+
+// XXX: The following entries are unused:
+#define LIST_INFO_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x14), 0x05)
+#define LIST_INFO_TITLE_COLOR \
+ WHITE_COLOR
+#define LIST_INFO_TEXT_COLOR \
+ LT_GRAY_COLOR
+#define LIST_INFO_CURENTRY_TEXT_COLOR \
+ WHITE_COLOR
+#define HI_LIST_INFO_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x00), 0x04)
+#define HI_LIST_INFO_BACKGROUND_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x1F), 0x0D)
+
+#define TEAM_NAME_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0F, 0x10, 0x1B), 0x00)
+#define TEAM_NAME_EDIT_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x17, 0x18, 0x1D), 0x00)
+#define TEAM_NAME_EDIT_RECT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x14, 0x00, 0x14), 0x05)
+#define TEAM_NAME_EDIT_CURS_COLOR \
+ WHITE_COLOR
+
+#define SHIPBOX_TOPLEFT_COLOR_NORMAL \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x09), 0x56)
+#define SHIPBOX_BOTTOMRIGHT_COLOR_NORMAL \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x0E), 0x54)
+#define SHIPBOX_INTERIOR_COLOR_NORMAL \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x00, 0x0C), 0x55)
+
+#define SHIPBOX_TOPLEFT_COLOR_HILITE \
+ BUILD_COLOR (MAKE_RGB15 (0x07, 0x00, 0x0C), 0x3E)
+#define SHIPBOX_BOTTOMRIGHT_COLOR_HILITE \
+ BUILD_COLOR (MAKE_RGB15 (0x0C, 0x00, 0x14), 0x3C)
+#define SHIPBOX_INTERIOR_COLOR_HILITE \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x00, 0x11), 0x3D)
+
+#define MELEE_STATUS_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02)
+
+
+FRAME MeleeFrame;
+ // Loaded from melee/melebkgd.ani
+MELEE_STATE *pMeleeState;
+
+BOOLEAN DoMelee (MELEE_STATE *pMS);
+static BOOLEAN DoEdit (MELEE_STATE *pMS);
+static BOOLEAN DoConfirmSettings (MELEE_STATE *pMS);
+
+#define DTSHS_NORMAL 0
+#define DTSHS_EDIT 1
+#define DTSHS_SELECTED 2
+#define DTSHS_REPAIR 4
+#define DTSHS_BLOCKCUR 8
+static BOOLEAN DrawTeamString (MELEE_STATE *pMS, COUNT side,
+ COUNT HiLiteState, const char *str);
+static void DrawFleetValue (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState);
+
+static void Melee_UpdateView_fleetValue (MELEE_STATE *pMS, COUNT side);
+static void Melee_UpdateView_ship (MELEE_STATE *pMS, COUNT side,
+ FleetShipIndex index);
+static void Melee_UpdateView_teamName (MELEE_STATE *pMS, COUNT side);
+
+
+// These icons come from melee/melebkgd.ani
+void
+DrawMeleeIcon (COUNT which_icon)
+{
+ STAMP s;
+
+ s.origin.x = 0;
+ s.origin.y = 0;
+ s.frame = SetAbsFrameIndex (MeleeFrame, which_icon);
+ DrawStamp (&s);
+}
+
+static FleetShipIndex
+GetShipIndex (BYTE row, BYTE col)
+{
+ return row * NUM_MELEE_COLUMNS + col;
+}
+
+static BYTE
+GetShipRow (FleetShipIndex index)
+{
+ return index / NUM_MELEE_COLUMNS;
+}
+
+static BYTE
+GetShipColumn (int index)
+{
+ return index % NUM_MELEE_COLUMNS;
+}
+
+// Get the rectangle containing the ship slot for the specified side, row,
+// and column.
+void
+GetShipBox (RECT *pRect, COUNT side, COUNT row, COUNT col)
+{
+ pRect->corner.x = MELEE_X_OFFS
+ + (col * (MELEE_BOX_WIDTH + MELEE_BOX_SPACE));
+ pRect->corner.y = MELEE_Y_OFFS
+ + (side * (MELEE_Y_OFFS + MELEE_BOX_SPACE
+ + (NUM_MELEE_ROWS * (MELEE_BOX_HEIGHT + MELEE_BOX_SPACE))))
+ + (row * (MELEE_BOX_HEIGHT + MELEE_BOX_SPACE));
+ pRect->extent.width = MELEE_BOX_WIDTH;
+ pRect->extent.height = MELEE_BOX_HEIGHT;
+}
+
+static void
+DrawShipBox (COUNT side, FleetShipIndex index, MeleeShip ship, BOOLEAN HiLite)
+{
+ RECT r;
+ BYTE row = GetShipRow (index);
+ BYTE col = GetShipColumn (index);
+
+ GetShipBox (&r, side, row, col);
+
+ BatchGraphics ();
+ if (HiLite)
+ DrawStarConBox (&r, 1,
+ SHIPBOX_TOPLEFT_COLOR_HILITE,
+ SHIPBOX_BOTTOMRIGHT_COLOR_HILITE,
+ (BOOLEAN)(ship != MELEE_NONE),
+ SHIPBOX_INTERIOR_COLOR_HILITE);
+ else
+ DrawStarConBox (&r, 1,
+ SHIPBOX_TOPLEFT_COLOR_NORMAL,
+ SHIPBOX_BOTTOMRIGHT_COLOR_NORMAL,
+ (BOOLEAN)(ship != MELEE_NONE),
+ SHIPBOX_INTERIOR_COLOR_NORMAL);
+
+ if (ship != MELEE_NONE)
+ {
+ STAMP s;
+ s.origin.x = r.corner.x + (r.extent.width >> 1);
+ s.origin.y = r.corner.y + (r.extent.height >> 1);
+ s.frame = GetShipMeleeIconsFromIndex (ship);
+
+ DrawStamp (&s);
+ }
+ UnbatchGraphics ();
+}
+
+static void
+ClearShipBox (COUNT side, FleetShipIndex index)
+{
+ RECT rect;
+ BYTE row = GetShipRow (index);
+ BYTE col = GetShipColumn (index);
+
+ GetShipBox (&rect, side, row, col);
+ RepairMeleeFrame (&rect);
+}
+
+static void
+DrawShipBoxCurrent (MELEE_STATE *pMS, BOOLEAN HiLite)
+{
+ FleetShipIndex slotI = GetShipIndex (pMS->row, pMS->col);
+ MeleeShip ship = MeleeSetup_getShip (pMS->meleeSetup, pMS->side, slotI);
+ DrawShipBox (pMS->side, slotI, ship, HiLite);
+}
+
+// Draw an image for one of the control method selection buttons.
+static void
+DrawControls (COUNT which_side, BOOLEAN HiLite)
+{
+ COUNT which_icon;
+
+ if (PlayerControl[which_side] & NETWORK_CONTROL)
+ {
+ DrawMeleeIcon (31 + (HiLite ? 1 : 0) + 2 * (1 - which_side));
+ /* "Network Control" */
+ return;
+ }
+
+ if (PlayerControl[which_side] & HUMAN_CONTROL)
+ which_icon = 0;
+ else
+ {
+ switch (PlayerControl[which_side]
+ & (STANDARD_RATING | GOOD_RATING | AWESOME_RATING))
+ {
+ case STANDARD_RATING:
+ which_icon = 1;
+ break;
+ case GOOD_RATING:
+ which_icon = 2;
+ break;
+ case AWESOME_RATING:
+ which_icon = 3;
+ break;
+ default:
+ // Should not happen. Satisfying compiler.
+ which_icon = 0;
+ break;
+ }
+ }
+
+ DrawMeleeIcon (1 + (8 * (1 - which_side)) + (HiLite ? 4 : 0) + which_icon);
+}
+
+static void
+DrawTeams (void)
+{
+ COUNT side;
+
+ for (side = 0; side < NUM_SIDES; side++)
+ {
+ FleetShipIndex index;
+
+ DrawControls (side, FALSE);
+
+ for (index = 0; index < MELEE_FLEET_SIZE; index++)
+ {
+ MeleeShip ship = MeleeSetup_getShip(pMeleeState->meleeSetup,
+ side, index);
+ DrawShipBox (side, index, ship, FALSE);
+ }
+
+ DrawTeamString (pMeleeState, side, DTSHS_NORMAL, NULL);
+ DrawFleetValue (pMeleeState, side, DTSHS_NORMAL);
+ }
+}
+
+void
+RepairMeleeFrame (const RECT *pRect)
+{
+ RECT r;
+ CONTEXT OldContext;
+ RECT OldRect;
+ POINT oldOrigin;
+
+ r.corner.x = pRect->corner.x + SAFE_X;
+ r.corner.y = pRect->corner.y + SAFE_Y;
+ r.extent = pRect->extent;
+ if (r.corner.y & 1)
+ {
+ --r.corner.y;
+ ++r.extent.height;
+ }
+
+ OldContext = SetContext (SpaceContext);
+ GetContextClipRect (&OldRect);
+ SetContextClipRect (&r);
+ // Offset the origin so that we draw the correct gfx in the cliprect
+ oldOrigin = SetContextOrigin (MAKE_POINT (-r.corner.x + SAFE_X,
+ -r.corner.y + SAFE_Y));
+ BatchGraphics ();
+
+ DrawMeleeIcon (0); /* Entire melee screen */
+#ifdef NETPLAY
+ DrawMeleeIcon (35); /* "Net..." (top, not highlighted) */
+ DrawMeleeIcon (37); /* "Net..." (bottom, not highlighted) */
+#endif
+ DrawMeleeIcon (26); /* "Battle!" (highlighted) */
+
+ DrawTeams ();
+
+ if (pMeleeState->MeleeOption == BUILD_PICK)
+ DrawPickFrame (pMeleeState);
+
+ UnbatchGraphics ();
+ SetContextOrigin (oldOrigin);
+ SetContextClipRect (&OldRect);
+ SetContext (OldContext);
+}
+
+static void
+RedrawMeleeFrame (void)
+{
+ RECT r;
+
+ r.corner.x = 0;
+ r.corner.y = 0;
+ r.extent.width = SCREEN_WIDTH;
+ r.extent.height = SCREEN_HEIGHT;
+
+ RepairMeleeFrame (&r);
+}
+
+static void
+GetTeamStringRect (COUNT side, RECT *r)
+{
+ r->corner.x = MELEE_X_OFFS - 1;
+ r->corner.y = (side + 1) * (MELEE_Y_OFFS
+ + ((MELEE_BOX_HEIGHT + MELEE_BOX_SPACE) * NUM_MELEE_ROWS + 2));
+ r->extent.width = NUM_MELEE_COLUMNS * (MELEE_BOX_WIDTH + MELEE_BOX_SPACE)
+ - 29;
+ r->extent.height = 13;
+}
+
+static void
+GetFleetValueRect (COUNT side, RECT *r)
+{
+ r->corner.x = MELEE_X_OFFS
+ + NUM_MELEE_COLUMNS * (MELEE_BOX_WIDTH + MELEE_BOX_SPACE) - 30;
+ r->corner.y = (side + 1) * (MELEE_Y_OFFS
+ + ((MELEE_BOX_HEIGHT + MELEE_BOX_SPACE) * NUM_MELEE_ROWS + 2));
+ r->extent.width = 29;
+ r->extent.height = 13;
+}
+
+static void
+DrawFleetValue (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState)
+{
+ RECT r;
+ TEXT rtText;
+ UNICODE buf[30];
+ COUNT fleetValue;
+
+ GetFleetValueRect (side ,&r);
+
+ if (HiLiteState == DTSHS_REPAIR)
+ {
+ RepairMeleeFrame (&r);
+ return;
+ }
+
+ SetContextFont (MicroFont);
+
+ fleetValue = MeleeSetup_getFleetValue (pMS->meleeSetup, side);
+ sprintf (buf, "%u", fleetValue);
+ rtText.pStr = buf;
+ rtText.align = ALIGN_RIGHT;
+ rtText.CharCount = (COUNT)~0;
+ rtText.baseline.y = r.corner.y + r.extent.height - 3;
+ rtText.baseline.x = r.corner.x + r.extent.width;
+
+ SetContextForeGroundColor (!(HiLiteState & DTSHS_SELECTED)
+ ? TEAM_NAME_TEXT_COLOR : TEAM_NAME_EDIT_TEXT_COLOR);
+ font_DrawText (&rtText);
+}
+
+// If teamName == NULL, the team name is taken from pMS->meleeSetup
+static BOOLEAN
+DrawTeamString (MELEE_STATE *pMS, COUNT side, COUNT HiLiteState,
+ const char *teamName)
+{
+ RECT r;
+ TEXT lfText;
+
+ GetTeamStringRect (side, &r);
+ if (HiLiteState == DTSHS_REPAIR)
+ {
+ RepairMeleeFrame (&r);
+ return TRUE;
+ }
+
+ SetContextFont (MicroFont);
+
+ lfText.pStr = (teamName != NULL) ? teamName :
+ MeleeSetup_getTeamName (pMS->meleeSetup, side);
+ lfText.baseline.y = r.corner.y + r.extent.height - 3;
+ lfText.baseline.x = r.corner.x + 1;
+ lfText.align = ALIGN_LEFT;
+ lfText.CharCount = strlen (lfText.pStr);
+
+ BatchGraphics ();
+ if (!(HiLiteState & DTSHS_EDIT))
+ { // normal or selected state
+ SetContextForeGroundColor (!(HiLiteState & DTSHS_SELECTED)
+ ? TEAM_NAME_TEXT_COLOR : TEAM_NAME_EDIT_TEXT_COLOR);
+ font_DrawText (&lfText);
+ }
+ else
+ { // editing state
+ COUNT i;
+ RECT text_r;
+ BYTE char_deltas[MAX_TEAM_CHARS];
+ BYTE *pchar_deltas;
+
+ TextRect (&lfText, &text_r, char_deltas);
+ if ((text_r.extent.width + 2) >= r.extent.width)
+ { // the text does not fit the input box size and so
+ // will not fit when displayed later
+ UnbatchGraphics ();
+ // disallow the change
+ return FALSE;
+ }
+
+ text_r = r;
+ SetContextForeGroundColor (TEAM_NAME_EDIT_RECT_COLOR);
+ DrawFilledRectangle (&text_r);
+
+ // calculate the cursor position and draw it
+ pchar_deltas = char_deltas;
+ for (i = pMS->CurIndex; i > 0; --i)
+ text_r.corner.x += (SIZE)*pchar_deltas++;
+ if (pMS->CurIndex < lfText.CharCount) /* cursor mid-line */
+ --text_r.corner.x;
+ if (HiLiteState & DTSHS_BLOCKCUR)
+ { // Use block cursor for keyboardless systems
+ if (pMS->CurIndex == lfText.CharCount)
+ { // cursor at end-line -- use insertion point
+ text_r.extent.width = 1;
+ }
+ else if (pMS->CurIndex + 1 == lfText.CharCount)
+ { // extra pixel for last char margin
+ text_r.extent.width = (SIZE)*pchar_deltas + 2;
+ }
+ else
+ { // normal mid-line char
+ text_r.extent.width = (SIZE)*pchar_deltas + 1;
+ }
+ }
+ else
+ { // Insertion point cursor
+ text_r.extent.width = 1;
+ }
+ // position cursor within input field rect
+ ++text_r.corner.x;
+ ++text_r.corner.y;
+ text_r.extent.height -= 2;
+ SetContextForeGroundColor (TEAM_NAME_EDIT_CURS_COLOR);
+ DrawFilledRectangle (&text_r);
+
+ SetContextForeGroundColor (BLACK_COLOR); // TEAM_NAME_EDIT_TEXT_COLOR);
+ font_DrawText (&lfText);
+ }
+ UnbatchGraphics ();
+
+ return TRUE;
+}
+
+#ifdef NETPLAY
+// This function is generic. It should probably be moved to elsewhere.
+static void
+multiLineDrawText (TEXT *textIn, RECT *clipRect) {
+ RECT oldRect;
+
+ SIZE leading;
+ TEXT text;
+ SIZE lineWidth;
+
+ GetContextClipRect (&oldRect);
+
+ SetContextClipRect (clipRect);
+ GetContextFontLeading (&leading);
+
+ text = *textIn;
+ text.baseline.x = 1;
+ text.baseline.y = 0;
+
+ if (clipRect->extent.width <= text.baseline.x)
+ goto out;
+
+ lineWidth = clipRect->extent.width - text.baseline.x;
+
+ while (*text.pStr != '\0') {
+ const char *nextLine;
+
+ text.baseline.y += leading;
+ text.CharCount = (COUNT) ~0;
+ getLineWithinWidth (&text, &nextLine, lineWidth, text.CharCount);
+ // This will also fill in text->CharCount.
+
+ font_DrawText (&text);
+
+ text.pStr = nextLine;
+ }
+
+out:
+ SetContextClipRect (&oldRect);
+}
+
+// Use an empty string to clear the status area.
+static void
+DrawMeleeStatusMessage (const char *message)
+{
+ CONTEXT oldContext;
+ RECT r;
+
+ oldContext = SetContext (SpaceContext);
+
+ r.corner.x = MELEE_STATUS_X_OFFS;
+ r.corner.y = MELEE_STATUS_Y_OFFS;
+ r.extent.width = MELEE_STATUS_WIDTH;
+ r.extent.height = MELEE_STATUS_HEIGHT;
+
+ RepairMeleeFrame (&r);
+
+ if (message[0] != '\0')
+ {
+ TEXT lfText;
+ lfText.pStr = message;
+ lfText.align = ALIGN_LEFT;
+ lfText.CharCount = (COUNT)~0;
+
+ SetContextFont (MicroFont);
+ SetContextForeGroundColor (MELEE_STATUS_COLOR);
+
+ BatchGraphics ();
+ multiLineDrawText (&lfText, &r);
+ UnbatchGraphics ();
+ }
+
+ SetContext (oldContext);
+}
+
+static void
+UpdateMeleeStatusMessage (ssize_t player)
+{
+ NetConnection *conn;
+
+ assert (player == -1 || (player >= 0 && player < NUM_PLAYERS));
+
+ if (player == -1)
+ {
+ DrawMeleeStatusMessage ("");
+ return;
+ }
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ {
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 0));
+ // "Unconnected. Press LEFT to connect."
+ return;
+ }
+
+ switch (NetConnection_getState (conn)) {
+ case NetState_unconnected:
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 0));
+ // "Unconnected. Press LEFT to connect."
+ break;
+ case NetState_connecting:
+ if (NetConnection_getPeerOptions (conn)->isServer)
+ DrawMeleeStatusMessage (
+ GAME_STRING (NETMELEE_STRING_BASE + 1));
+ // "Awaiting incoming connection...\n"
+ // "Press RIGHT to cancel."
+ else
+ DrawMeleeStatusMessage (
+ GAME_STRING (NETMELEE_STRING_BASE + 2));
+ // "Attempting outgoing connection...\n"
+ // "Press RIGHT to cancel."
+ break;
+ case NetState_init:
+ case NetState_inSetup:
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 3));
+ // "Connected. Press RIGHT to disconnect."
+ break;
+ default:
+ DrawMeleeStatusMessage ("");
+ break;
+ }
+}
+#endif /* NETPLAY */
+
+// XXX: this function is called when the current selection is blinking off.
+static void
+Deselect (BYTE opt)
+{
+ switch (opt)
+ {
+ case START_MELEE:
+ DrawMeleeIcon (25); /* "Battle!" (not highlighted) */
+ break;
+ case LOAD_TOP:
+ DrawMeleeIcon (17); /* "Load" (top, not highlighted) */
+ break;
+ case LOAD_BOT:
+ DrawMeleeIcon (22); /* "Load" (bottom, not highlighted) */
+ break;
+ case SAVE_TOP:
+ DrawMeleeIcon (18); /* "Save" (top, not highlighted) */
+ break;
+ case SAVE_BOT:
+ DrawMeleeIcon (21); /* "Save" (bottom, not highlighted) */
+ break;
+#ifdef NETPLAY
+ case NET_TOP:
+ DrawMeleeIcon (35); /* "Net..." (top, not highlighted) */
+ break;
+ case NET_BOT:
+ DrawMeleeIcon (37); /* "Net..." (bottom, not highlighted) */
+ break;
+#endif
+ case QUIT_BOT:
+ DrawMeleeIcon (29); /* "Quit" (not highlighted) */
+ break;
+ case CONTROLS_TOP:
+ case CONTROLS_BOT:
+ {
+ COUNT which_side;
+
+ which_side = opt == CONTROLS_TOP ? 1 : 0;
+ DrawControls (which_side, FALSE);
+ break;
+ }
+ case EDIT_MELEE:
+ if (pMeleeState->InputFunc == DoEdit)
+ {
+ if (pMeleeState->row < NUM_MELEE_ROWS)
+ DrawShipBoxCurrent (pMeleeState, FALSE);
+ else if (pMeleeState->CurIndex == MELEE_STATE_INDEX_DONE)
+ {
+ // Not currently editing the team name.
+ DrawTeamString (pMeleeState, pMeleeState->side,
+ DTSHS_NORMAL, NULL);
+ DrawFleetValue (pMeleeState, pMeleeState->side,
+ DTSHS_NORMAL);
+ }
+ }
+ break;
+ case BUILD_PICK:
+ DrawPickIcon (pMeleeState->currentShip, true);
+ break;
+ }
+}
+
+// XXX: this function is called when the current selection is blinking off.
+static void
+Select (BYTE opt)
+{
+ switch (opt)
+ {
+ case START_MELEE:
+ DrawMeleeIcon (26); /* "Battle!" (highlighted) */
+ break;
+ case LOAD_TOP:
+ DrawMeleeIcon (19); /* "Load" (top, highlighted) */
+ break;
+ case LOAD_BOT:
+ DrawMeleeIcon (24); /* "Load" (bottom, highlighted) */
+ break;
+ case SAVE_TOP:
+ DrawMeleeIcon (20); /* "Save" (top; highlighted) */
+ break;
+ case SAVE_BOT:
+ DrawMeleeIcon (23); /* "Save" (bottom; highlighted) */
+ break;
+#ifdef NETPLAY
+ case NET_TOP:
+ DrawMeleeIcon (36); /* "Net..." (top; highlighted) */
+ break;
+ case NET_BOT:
+ DrawMeleeIcon (38); /* "Net..." (bottom; highlighted) */
+ break;
+#endif
+ case QUIT_BOT:
+ DrawMeleeIcon (30); /* "Quit" (highlighted) */
+ break;
+ case CONTROLS_TOP:
+ case CONTROLS_BOT:
+ {
+ COUNT which_side;
+
+ which_side = (opt == CONTROLS_TOP) ? 1 : 0;
+ DrawControls (which_side, TRUE);
+ break;
+ }
+ case EDIT_MELEE:
+ if (pMeleeState->InputFunc == DoEdit)
+ {
+ if (pMeleeState->row < NUM_MELEE_ROWS)
+ DrawShipBoxCurrent (pMeleeState, TRUE);
+ else if (pMeleeState->CurIndex == MELEE_STATE_INDEX_DONE)
+ {
+ // Not currently editing the team name.
+ DrawTeamString (pMeleeState, pMeleeState->side,
+ DTSHS_SELECTED, NULL);
+ DrawFleetValue (pMeleeState, pMeleeState->side,
+ DTSHS_SELECTED);
+ }
+ }
+ break;
+ case BUILD_PICK:
+ DrawPickIcon (pMeleeState->currentShip, false);
+ break;
+ }
+}
+
+void
+Melee_flashSelection (MELEE_STATE *pMS)
+{
+#define FLASH_RATE (ONE_SECOND / 9)
+ static TimeCount NextTime = 0;
+ static bool select = false;
+ TimeCount Now = GetTimeCounter ();
+
+ if (Now >= NextTime)
+ {
+ CONTEXT OldContext;
+
+ NextTime = Now + FLASH_RATE;
+ select = !select;
+
+ OldContext = SetContext (SpaceContext);
+ if (select)
+ Select (pMS->MeleeOption);
+ else
+ Deselect (pMS->MeleeOption);
+ SetContext (OldContext);
+ }
+}
+
+static void
+InitMelee (MELEE_STATE *pMS)
+{
+ RECT r;
+
+ SetContext (SpaceContext);
+ SetContextFGFrame (Screen);
+ SetContextClipRect (NULL);
+ SetContextBackGroundColor (BLACK_COLOR);
+ ClearDrawable ();
+ r.corner.x = SAFE_X;
+ r.corner.y = SAFE_Y;
+ r.extent.width = SCREEN_WIDTH - (SAFE_X * 2);
+ r.extent.height = SCREEN_HEIGHT - (SAFE_Y * 2);
+ SetContextClipRect (&r);
+
+ r.corner.x = r.corner.y = 0;
+ RedrawMeleeFrame ();
+
+ (void) pMS;
+}
+
+void
+DrawMeleeShipStrings (MELEE_STATE *pMS, MeleeShip NewStarShip)
+{
+ RECT r, OldRect;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (StatusContext);
+ GetContextClipRect (&OldRect);
+ r = OldRect;
+ r.corner.x += ((SAFE_X << 1) - 32) + MENU_X_OFFS;
+ r.corner.y += 76;
+ r.extent.height = SHIP_INFO_HEIGHT;
+ SetContextClipRect (&r);
+ BatchGraphics ();
+
+ if (NewStarShip == MELEE_NONE)
+ {
+ RECT r;
+ TEXT t;
+
+ ClearShipStatus (0);
+ SetContextFont (StarConFont);
+ r.corner.x = 3;
+ r.corner.y = 4;
+ r.extent.width = 57;
+ r.extent.height = 60;
+ SetContextForeGroundColor (BLACK_COLOR);
+ DrawRectangle (&r);
+ t.baseline.x = STATUS_WIDTH >> 1;
+ t.baseline.y = 32;
+ t.align = ALIGN_CENTER;
+ if (pMS->row < NUM_MELEE_ROWS)
+ {
+ // A ship is selected (or an empty fleet position).
+ t.pStr = GAME_STRING (MELEE_STRING_BASE + 0); // "Empty"
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.pStr = GAME_STRING (MELEE_STRING_BASE + 1); // "Slot"
+ }
+ else
+ {
+ // The team name is selected.
+ t.pStr = GAME_STRING (MELEE_STRING_BASE + 2); // "Team"
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ t.pStr = GAME_STRING (MELEE_STRING_BASE + 3); // "Name"
+ }
+ t.baseline.y += TINY_TEXT_HEIGHT;
+ t.CharCount = (COUNT)~0;
+ font_DrawText (&t);
+ }
+ else
+ {
+ HMASTERSHIP hMasterShip;
+ MASTER_SHIP_INFO *MasterPtr;
+
+ hMasterShip = GetStarShipFromIndex (&master_q, NewStarShip);
+ MasterPtr = LockMasterShip (&master_q, hMasterShip);
+
+ InitShipStatus (&MasterPtr->ShipInfo, NULL, NULL);
+
+ UnlockMasterShip (&master_q, hMasterShip);
+ }
+
+ UnbatchGraphics ();
+ SetContextClipRect (&OldRect);
+ SetContext (OldContext);
+}
+
+// Set the currently displayed ship to the ship for the slot indicated by
+// pMS->row and pMS->col.
+static void
+UpdateCurrentShip (MELEE_STATE *pMS)
+{
+ if (pMS->row == NUM_MELEE_ROWS)
+ {
+ // The team name is selected.
+ pMS->currentShip = MELEE_NONE;
+ }
+ else
+ {
+ FleetShipIndex slotNr = GetShipIndex (pMS->row, pMS->col);
+ pMS->currentShip =
+ MeleeSetup_getShip (pMS->meleeSetup, pMS->side, slotNr);
+ }
+
+ DrawMeleeShipStrings (pMS, pMS->currentShip);
+}
+
+// returns (COUNT) ~0 for an invalid ship.
+COUNT
+GetShipValue (MeleeShip StarShip)
+{
+ COUNT val;
+
+ if (StarShip == MELEE_NONE)
+ return 0;
+
+ val = GetShipCostFromIndex (StarShip);
+ if (val == 0)
+ val = (COUNT)~0;
+
+ return val;
+}
+
+static void
+DeleteCurrentShip (MELEE_STATE *pMS)
+{
+ FleetShipIndex slotI = GetShipIndex (pMS->row, pMS->col);
+ Melee_LocalChange_ship (pMS, pMS->side, slotI, MELEE_NONE);
+}
+
+static bool
+isShipSlotSelected (MELEE_STATE *pMS, COUNT side, FleetShipIndex index)
+{
+ if (pMS->MeleeOption != EDIT_MELEE)
+ return false;
+
+ if (pMS->side != side)
+ return false;
+
+ return (index == GetShipIndex (pMS->row, pMS->col));
+}
+
+static void
+AdvanceCursor (MELEE_STATE *pMS)
+{
+ ++pMS->col;
+ if (pMS->col == NUM_MELEE_COLUMNS)
+ {
+ ++pMS->row;
+ if (pMS->row < NUM_MELEE_ROWS)
+ pMS->col = 0;
+ else
+ {
+ pMS->col = NUM_MELEE_COLUMNS - 1;
+ pMS->row = NUM_MELEE_ROWS - 1;
+ }
+ }
+}
+
+static BOOLEAN
+OnTeamNameChange (TEXTENTRY_STATE *pTES)
+{
+ MELEE_STATE *pMS = (MELEE_STATE*) pTES->CbParam;
+ BOOLEAN ret;
+ COUNT hl = DTSHS_EDIT;
+
+ pMS->CurIndex = pTES->CursorPos;
+ if (pTES->JoystickMode)
+ hl |= DTSHS_BLOCKCUR;
+
+ ret = DrawTeamString (pMS, pMS->side, hl, pTES->BaseStr);
+
+ return ret;
+}
+
+static BOOLEAN
+TeamNameFrameCallback (TEXTENTRY_STATE *pTES)
+{
+#ifdef NETPLAY
+ // Process incoming packets, so that remote changes are displayed
+ // while we are editing the team name.
+ // The team name itself isn't modified visually due to remote changes
+ // while it is being edited.
+ netInput ();
+#endif
+
+ (void) pTES;
+
+ return TRUE;
+ // Keep editing
+}
+
+static void
+BuildPickShipPopup (MELEE_STATE *pMS)
+{
+ bool buildOk;
+
+ pMS->MeleeOption = BUILD_PICK;
+
+ buildOk = BuildPickShip (pMS);
+ if (buildOk)
+ {
+ // A ship has been selected.
+ // Add the currently selected ship to the fleet.
+ FleetShipIndex index = GetShipIndex (pMS->row, pMS->col);
+ Melee_LocalChange_ship (pMS, pMS->side, index, pMS->currentShip);
+ AdvanceCursor (pMS);
+ }
+
+ pMS->MeleeOption = EDIT_MELEE;
+ // Must set this before the call to RepairMeleeFrame(), so that
+ // it will not redraw the BuildPickFrame.
+
+ {
+ RECT r;
+
+ GetBuildPickFrameRect (&r);
+ RepairMeleeFrame (&r);
+ }
+
+ UpdateCurrentShip (pMS);
+ pMS->InputFunc = DoEdit;
+}
+
+static BOOLEAN
+DoEdit (MELEE_STATE *pMS)
+{
+ DWORD TimeIn = GetTimeCounter ();
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT | MENU_SOUND_DELETE);
+ if (!pMS->Initialized)
+ {
+ UpdateCurrentShip (pMS);
+ pMS->Initialized = TRUE;
+ pMS->InputFunc = DoEdit;
+ return TRUE;
+ }
+
+#ifdef NETPLAY
+ netInput ();
+#endif
+ if ((pMS->row < NUM_MELEE_ROWS || pMS->currentShip == MELEE_NONE)
+ && (PulsedInputState.menu[KEY_MENU_CANCEL]
+ || (PulsedInputState.menu[KEY_MENU_RIGHT]
+ && (pMS->col == NUM_MELEE_COLUMNS - 1
+ || pMS->row == NUM_MELEE_ROWS))))
+ {
+ // Done editing the teams.
+ Deselect (EDIT_MELEE);
+ pMS->currentShip = MELEE_NONE;
+ pMS->MeleeOption = START_MELEE;
+ pMS->InputFunc = DoMelee;
+ pMS->LastInputTime = GetTimeCounter ();
+ }
+ else if (pMS->row < NUM_MELEE_ROWS
+ && PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ // Show a popup to add a new ship to the current team.
+ Deselect (EDIT_MELEE);
+ BuildPickShipPopup (pMS);
+ }
+ else if (pMS->row < NUM_MELEE_ROWS
+ && PulsedInputState.menu[KEY_MENU_SPECIAL])
+ {
+ // TODO: this is a stub; Should we display a ship spin?
+ Deselect (EDIT_MELEE);
+ if (pMS->currentShip != MELEE_NONE)
+ {
+ // Do something with pMS->currentShip here
+ }
+ }
+ else if (pMS->row < NUM_MELEE_ROWS &&
+ PulsedInputState.menu[KEY_MENU_DELETE])
+ {
+ // Remove the currently selected ship from the current team.
+ Deselect (EDIT_MELEE);
+ DeleteCurrentShip (pMS);
+ AdvanceCursor (pMS);
+ UpdateCurrentShip (pMS);
+ }
+ else
+ {
+ COUNT side = pMS->side;
+ COUNT row = pMS->row;
+ COUNT col = pMS->col;
+
+ if (row == NUM_MELEE_ROWS)
+ {
+ // Edit the name of the current team.
+ if (PulsedInputState.menu[KEY_MENU_SELECT])
+ {
+ TEXTENTRY_STATE tes;
+ char buf[MAX_TEAM_CHARS + 1];
+
+ // going to enter text
+ pMS->CurIndex = 0;
+ DrawTeamString (pMS, pMS->side, DTSHS_EDIT, NULL);
+
+ strncpy (buf, MeleeSetup_getTeamName (
+ pMS->meleeSetup, pMS->side), MAX_TEAM_CHARS);
+ buf[MAX_TEAM_CHARS] = '\0';
+
+ tes.Initialized = FALSE;
+ tes.BaseStr = buf;
+ tes.CursorPos = 0;
+ tes.MaxSize = MAX_TEAM_CHARS + 1;
+ tes.CbParam = pMS;
+ tes.ChangeCallback = OnTeamNameChange;
+ tes.FrameCallback = TeamNameFrameCallback;
+ DoTextEntry (&tes);
+
+ // done entering
+ pMS->CurIndex = MELEE_STATE_INDEX_DONE;
+ if (!tes.Success ||
+ !Melee_LocalChange_teamName (pMS, pMS->side, buf)) {
+ // The team name was not changed, so it was not redrawn.
+ // However, because we now leave edit mode, we still
+ // need to redraw.
+ Melee_UpdateView_teamName (pMS, pMS->side);
+ }
+
+ return TRUE;
+ }
+ }
+
+ {
+ if (PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ if (col > 0)
+ --col;
+ }
+ else if (PulsedInputState.menu[KEY_MENU_RIGHT])
+ {
+ if (col < NUM_MELEE_COLUMNS - 1)
+ ++col;
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ if (row-- == 0)
+ {
+ if (side == 0)
+ row = 0;
+ else
+ {
+ row = NUM_MELEE_ROWS;
+ side = !side;
+ }
+ }
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ if (row++ == NUM_MELEE_ROWS)
+ {
+ if (side == 1)
+ row = NUM_MELEE_ROWS;
+ else
+ {
+ row = 0;
+ side = !side;
+ }
+ }
+ }
+ }
+
+ if (col != pMS->col || row != pMS->row || side != pMS->side)
+ {
+ Deselect (EDIT_MELEE);
+ pMS->side = side;
+ pMS->row = row;
+ pMS->col = col;
+
+ UpdateCurrentShip (pMS);
+ }
+ }
+
+#ifdef NETPLAY
+ flushPacketQueues ();
+#endif
+
+ Melee_flashSelection (pMS);
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+#ifdef NETPLAY
+// Returns -1 if a connection has been aborted.
+static ssize_t
+numPlayersReady (void)
+{
+ size_t player;
+ size_t numDone;
+
+ numDone = 0;
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ if (!(PlayerControl[player] & NETWORK_CONTROL))
+ {
+ numDone++;
+ continue;
+ }
+
+ {
+ NetConnection *conn;
+
+ conn = netConnections[player];
+
+ if (conn == NULL || !NetConnection_isConnected (conn))
+ return -1;
+
+ if (NetConnection_getState (conn) > NetState_inSetup)
+ numDone++;
+ }
+ }
+
+ return numDone;
+}
+#endif /* NETPLAY */
+
+// The player has pressed "Start Game", and all Network players are
+// connected. We're now just waiting for the confirmation of the other
+// party.
+// When the other party changes something in the settings, the confirmation
+// is cancelled.
+static BOOLEAN
+DoConfirmSettings (MELEE_STATE *pMS)
+{
+#ifdef NETPLAY
+ ssize_t numDone;
+#endif
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ // The connection is explicitely cancelled, locally.
+ pMS->InputFunc = DoMelee;
+#ifdef NETPLAY
+ cancelConfirmations ();
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 4));
+ // "Confirmation cancelled. Press FIRE to reconfirm."
+#endif
+ return TRUE;
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_LEFT] ||
+ PulsedInputState.menu[KEY_MENU_UP] ||
+ PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ // The player moves the cursor; cancel the confirmation.
+ pMS->InputFunc = DoMelee;
+#ifdef NETPLAY
+ cancelConfirmations ();
+ DrawMeleeStatusMessage ("");
+#endif
+ return DoMelee (pMS);
+ // Let the pressed keys take effect immediately.
+ }
+
+#ifndef NETPLAY
+ pMS->InputFunc = DoMelee;
+ SeedRandomNumbers ();
+ pMS->meleeStarted = TRUE;
+ StartMelee (pMS);
+ pMS->meleeStarted = FALSE;
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+ return TRUE;
+#else
+ closeDisconnectedConnections ();
+ netInput ();
+ SleepThread (ONE_SECOND / 120);
+
+ numDone = numPlayersReady ();
+ if (numDone == -1)
+ {
+ // Connection aborted
+ cancelConfirmations ();
+ flushPacketQueues ();
+ pMS->InputFunc = DoMelee;
+ return TRUE;
+ }
+ else if (numDone != NUM_SIDES)
+ {
+ // Still waiting for some confirmation.
+ return TRUE;
+ }
+
+ // All sides have confirmed.
+
+ // Send our own prefered frame delay.
+ Netplay_NotifyAll_inputDelay (netplayOptions.inputDelay);
+
+ // Synchronise the RNGs:
+ {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+
+ if (!(PlayerControl[player] & NETWORK_CONTROL))
+ continue;
+
+ conn = netConnections[player];
+ assert (conn != NULL);
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ if (NetConnection_getDiscriminant (conn))
+ Netplay_Notify_seedRandom (conn, SeedRandomNumbers ());
+ }
+ flushPacketQueues ();
+ }
+
+ {
+ // One side will send the seed followed by 'Done' and wait
+ // for the other side to report 'Done'.
+ // The other side will report 'Done' and will wait for the other
+ // side to report 'Done', but before the reception of 'Done'
+ // it will have received the seed.
+ bool allOk = negotiateReadyConnections (true, NetState_interBattle);
+ if (!allOk)
+ return FALSE;
+ }
+
+ // The maximum value for all connections is used.
+ {
+ bool ok = setupInputDelay (netplayOptions.inputDelay);
+ if (!ok)
+ return FALSE;
+ }
+
+ pMS->InputFunc = DoMelee;
+
+ StartMelee (pMS);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ return TRUE;
+#endif /* defined (NETPLAY) */
+}
+
+static void
+LoadMeleeInfo (MELEE_STATE *pMS)
+{
+ BuildPickMeleeFrame ();
+ MeleeFrame = CaptureDrawable (LoadGraphic (MELEE_SCREEN_PMAP_ANIM));
+ BuildBuildPickFrame ();
+
+ InitSpace ();
+
+ LoadTeamList (pMS);
+}
+
+static void
+FreeMeleeInfo (MELEE_STATE *pMS)
+{
+ DestroyDirEntryTable (ReleaseDirEntryTable (pMS->load.dirEntries));
+ pMS->load.dirEntries = 0;
+
+ if (pMS->hMusic)
+ {
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+
+ UninitSpace ();
+
+ DestroyPickMeleeFrame ();
+ DestroyDrawable (ReleaseDrawable (MeleeFrame));
+ MeleeFrame = 0;
+ DestroyBuildPickFrame ();
+
+#ifdef NETPLAY
+ closeAllConnections ();
+ // Clear the input delay in case we will go into the full game later.
+ // Must be done after the net connections are closed.
+ setupInputDelay (0);
+#endif
+}
+
+static void
+BuildAndDrawShipList (MELEE_STATE *pMS)
+{
+ FillPickMeleeFrame (pMS->meleeSetup);
+ // XXX TODO: This also builds the race_q for each player.
+ // This should be split off.
+}
+
+static void
+StartMelee (MELEE_STATE *pMS)
+{
+ {
+ FadeMusic (0, ONE_SECOND / 2);
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2)
+ + ONE_SECOND / 60);
+ FlushColorXForms ();
+ StopMusic ();
+ }
+ FadeMusic (NORMAL_VOLUME, 0);
+ if (pMS->hMusic)
+ {
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+
+ do
+ {
+ if (!SetPlayerInputAll ())
+ break;
+ BuildAndDrawShipList (pMS);
+
+ WaitForSoundEnd (TFBSOUND_WAIT_ALL);
+
+ load_gravity_well ((BYTE)((COUNT)TFB_Random () %
+ NUMBER_OF_PLANET_TYPES));
+ Battle (NULL);
+ free_gravity_well ();
+ ClearPlayerInputAll ();
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return;
+
+ SleepThreadUntil (FadeScreen (FadeAllToBlack, ONE_SECOND / 2)
+ + ONE_SECOND / 60);
+ FlushColorXForms ();
+
+ } while (0 /* !(GLOBAL (CurrentActivity) & CHECK_ABORT) */);
+ GLOBAL (CurrentActivity) = SUPER_MELEE;
+
+ pMS->Initialized = FALSE;
+}
+
+static void
+StartMeleeButtonPressed (MELEE_STATE *pMS)
+{
+ // Either fleet must at least have one ship.
+ if (MeleeSetup_getFleetValue (pMS->meleeSetup, 0) == 0 ||
+ MeleeSetup_getFleetValue (pMS->meleeSetup, 1) == 0)
+ {
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return;
+ }
+
+#ifdef NETPLAY
+ if ((PlayerControl[0] & NETWORK_CONTROL) &&
+ (PlayerControl[1] & NETWORK_CONTROL))
+ {
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 32));
+ // "Only one side at a time can be network controlled."
+ return;
+ }
+
+ if (((PlayerControl[0] & NETWORK_CONTROL) &&
+ (PlayerControl[1] & COMPUTER_CONTROL)) ||
+ ((PlayerControl[0] & COMPUTER_CONTROL) &&
+ (PlayerControl[1] & NETWORK_CONTROL)))
+ {
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 33));
+ // "Netplay with a computer-controlled side is currently
+ // not possible."
+ return;
+ }
+
+ // Check whether all network parties are ready;
+ {
+ COUNT player;
+ bool netReady = true;
+
+ // We collect all error conditions, instead of only reporting
+ // the first one.
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+
+ if (!(PlayerControl[player] & NETWORK_CONTROL))
+ continue;
+
+ conn = netConnections[player];
+ if (conn == NULL || !NetConnection_isConnected (conn))
+ {
+ // Connection for player not established.
+ netReady = false;
+ if (player == 0)
+ DrawMeleeStatusMessage (
+ GAME_STRING (NETMELEE_STRING_BASE + 5));
+ // "Connection for bottom player not "
+ // "established."
+ else
+ DrawMeleeStatusMessage (
+ GAME_STRING (NETMELEE_STRING_BASE + 6));
+ // "Connection for top player not "
+ // "established."
+ }
+ else if (NetConnection_getState (conn) != NetState_inSetup)
+ {
+ // This side may be in the setup, but the network connection
+ // is not in a state that setup information can be sent.
+ netReady = false;
+ if (player == 0)
+ DrawMeleeStatusMessage (
+ GAME_STRING (NETMELEE_STRING_BASE + 14));
+ // "Connection for bottom player not ready."
+ else
+ DrawMeleeStatusMessage (
+ GAME_STRING (NETMELEE_STRING_BASE + 15));
+ // "Connection for top player not ready."
+
+ }
+ }
+ if (!netReady)
+ {
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ return;
+ }
+
+ if (numPlayersReady () != NUM_PLAYERS)
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 7));
+ // "Waiting for remote confirmation."
+ confirmConnections ();
+ }
+#endif
+
+ pMS->InputFunc = DoConfirmSettings;
+}
+
+#ifdef NETPLAY
+
+static BOOLEAN
+DoConnectingDialog (MELEE_STATE *pMS)
+{
+ DWORD TimeIn = GetTimeCounter ();
+ COUNT which_side = (pMS->MeleeOption == NET_TOP) ? 1 : 0;
+ NetConnection *conn;
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+ if (!pMS->Initialized)
+ {
+ RECT r;
+ FONT oldfont;
+ Color oldcolor;
+ TEXT t;
+
+ // Build a network connection.
+ if (netConnections[which_side] != NULL)
+ closePlayerNetworkConnection (which_side);
+
+ pMS->Initialized = TRUE;
+ conn = openPlayerNetworkConnection (which_side, pMS);
+ pMS->InputFunc = DoConnectingDialog;
+
+ /* Draw the dialog box here */
+ oldfont = SetContextFont (StarConFont);
+ oldcolor = SetContextForeGroundColor (BLACK_COLOR);
+ BatchGraphics ();
+ r.extent.width = 200;
+ r.extent.height = 30;
+ r.corner.x = (SCREEN_WIDTH - r.extent.width) >> 1;
+ r.corner.y = (SCREEN_HEIGHT - r.extent.height) >> 1;
+ DrawShadowedBox (&r, SHADOWBOX_BACKGROUND_COLOR,
+ SHADOWBOX_DARK_COLOR, SHADOWBOX_MEDIUM_COLOR);
+
+ if (NetConnection_getPeerOptions (conn)->isServer)
+ {
+ t.pStr = GAME_STRING (NETMELEE_STRING_BASE + 1);
+ /* "Awaiting incoming connection */
+ }
+ else
+ {
+ t.pStr = GAME_STRING (NETMELEE_STRING_BASE + 2);
+ /* "Awaiting outgoing connection */
+ }
+ t.baseline.y = r.corner.y + 10;
+ t.baseline.x = SCREEN_WIDTH >> 1;
+ t.align = ALIGN_CENTER;
+ t.CharCount = ~0;
+ font_DrawText (&t);
+
+ t.pStr = GAME_STRING (NETMELEE_STRING_BASE + 18);
+ /* "Press SPACE to cancel" */
+ t.baseline.y += 16;
+ font_DrawText (&t);
+
+ // Restore original graphics
+ SetContextFont (oldfont);
+ SetContextForeGroundColor (oldcolor);
+ UnbatchGraphics ();
+ }
+
+ netInput ();
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ // Terminate a network connection.
+ if (netConnections[which_side] != NULL) {
+ closePlayerNetworkConnection (which_side);
+ UpdateMeleeStatusMessage (which_side);
+ }
+ RedrawMeleeFrame ();
+ pMS->InputFunc = DoMelee;
+ pMS->LastInputTime = GetTimeCounter ();
+
+ flushPacketQueues ();
+
+ return TRUE;
+ }
+
+ conn = netConnections[which_side];
+ if (conn != NULL)
+ {
+ NetState status = NetConnection_getState (conn);
+ if ((status == NetState_init) ||
+ (status == NetState_inSetup))
+ {
+ /* Connection complete! */
+ PlayerControl[which_side] = NETWORK_CONTROL | STANDARD_RATING;
+ DrawControls (which_side, TRUE);
+
+ RedrawMeleeFrame ();
+
+ UpdateMeleeStatusMessage (which_side);
+ pMS->InputFunc = DoMelee;
+ pMS->LastInputTime = GetTimeCounter ();
+ Deselect (pMS->MeleeOption);
+ pMS->MeleeOption = START_MELEE;
+ }
+ }
+
+ flushPacketQueues ();
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+/* Check for disconnects, and revert to human control if there is one */
+static void
+check_for_disconnects (MELEE_STATE *pMS)
+{
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+
+ if (!(PlayerControl[player] & NETWORK_CONTROL))
+ continue;
+
+ conn = netConnections[player];
+ if (conn == NULL || !NetConnection_isConnected (conn))
+ {
+ PlayerControl[player] = HUMAN_CONTROL | STANDARD_RATING;
+ DrawControls (player, FALSE);
+ log_add (log_User, "Player %d has disconnected; shifting "
+ "controls\n", player);
+ }
+ }
+
+ (void) pMS;
+}
+
+#endif
+
+static void
+nextControlType (COUNT which_side)
+{
+ switch (PlayerControl[which_side])
+ {
+ case HUMAN_CONTROL | STANDARD_RATING:
+ PlayerControl[which_side] = COMPUTER_CONTROL | STANDARD_RATING;
+ break;
+ case COMPUTER_CONTROL | STANDARD_RATING:
+ PlayerControl[which_side] = COMPUTER_CONTROL | GOOD_RATING;
+ break;
+ case COMPUTER_CONTROL | GOOD_RATING:
+ PlayerControl[which_side] = COMPUTER_CONTROL | AWESOME_RATING;
+ break;
+ case COMPUTER_CONTROL | AWESOME_RATING:
+ PlayerControl[which_side] = HUMAN_CONTROL | STANDARD_RATING;
+ break;
+
+#ifdef NETPLAY
+ case NETWORK_CONTROL | STANDARD_RATING:
+ if (netConnections[which_side] != NULL)
+ closePlayerNetworkConnection (which_side);
+ UpdateMeleeStatusMessage (-1);
+ PlayerControl[which_side] = HUMAN_CONTROL | STANDARD_RATING;
+ break;
+#endif /* NETPLAY */
+ default:
+ log_add (log_Error, "Error: Bad control type (%d) in "
+ "nextControlType().\n", PlayerControl[which_side]);
+ PlayerControl[which_side] = HUMAN_CONTROL | STANDARD_RATING;
+ break;
+ }
+
+ DrawControls (which_side, TRUE);
+}
+
+static MELEE_OPTIONS
+MeleeOptionDown (MELEE_OPTIONS current) {
+ if (current == QUIT_BOT)
+ return QUIT_BOT;
+ return current + 1;
+}
+
+static MELEE_OPTIONS
+MeleeOptionUp (MELEE_OPTIONS current)
+{
+ if (current == TOP_ENTRY)
+ return TOP_ENTRY;
+ return current - 1;
+}
+
+static void
+MeleeOptionSelect (MELEE_STATE *pMS)
+{
+ switch (pMS->MeleeOption)
+ {
+ case START_MELEE:
+ StartMeleeButtonPressed (pMS);
+ break;
+ case LOAD_TOP:
+ case LOAD_BOT:
+ pMS->Initialized = FALSE;
+ pMS->side = pMS->MeleeOption == LOAD_TOP ? 0 : 1;
+ DoLoadTeam (pMS);
+ break;
+ case SAVE_TOP:
+ case SAVE_BOT:
+ pMS->side = pMS->MeleeOption == SAVE_TOP ? 0 : 1;
+ if (MeleeSetup_getFleetValue (pMS->meleeSetup, pMS->side) > 0)
+ DoSaveTeam (pMS);
+ else
+ PlayMenuSound (MENU_SOUND_FAILURE);
+ break;
+ case QUIT_BOT:
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ break;
+#ifdef NETPLAY
+ case NET_TOP:
+ case NET_BOT:
+ {
+ COUNT which_side;
+ BOOLEAN confirmed;
+
+ which_side = pMS->MeleeOption == NET_TOP ? 1 : 0;
+ confirmed = MeleeConnectDialog (which_side);
+ RedrawMeleeFrame ();
+ pMS->LastInputTime = GetTimeCounter ();
+ if (confirmed)
+ {
+ pMS->Initialized = FALSE;
+ pMS->InputFunc = DoConnectingDialog;
+ }
+ break;
+ }
+#endif /* NETPLAY */
+ case CONTROLS_TOP:
+ case CONTROLS_BOT:
+ {
+ COUNT which_side = (pMS->MeleeOption == CONTROLS_TOP) ? 1 : 0;
+ nextControlType (which_side);
+ break;
+ }
+ }
+}
+
+BOOLEAN
+DoMelee (MELEE_STATE *pMS)
+{
+ DWORD TimeIn = GetTimeCounter ();
+ BOOLEAN force_select = FALSE;
+
+ /* Cancel any presses of the Pause key. */
+ GamePaused = FALSE;
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ if (!pMS->Initialized)
+ {
+ if (pMS->hMusic)
+ {
+ StopMusic ();
+ DestroyMusic (pMS->hMusic);
+ pMS->hMusic = 0;
+ }
+ pMS->hMusic = LoadMusic (MELEE_MUSIC);
+ pMS->Initialized = TRUE;
+
+ pMS->MeleeOption = START_MELEE;
+ PlayMusic (pMS->hMusic, TRUE, 1);
+ InitMelee (pMS);
+
+ FadeScreen (FadeAllToColor, ONE_SECOND / 2);
+ pMS->LastInputTime = GetTimeCounter ();
+ return TRUE;
+ }
+
+#ifdef NETPLAY
+ netInput ();
+#endif
+
+ if (PulsedInputState.menu[KEY_MENU_CANCEL] ||
+ PulsedInputState.menu[KEY_MENU_LEFT])
+ {
+ // Start editing the teams.
+ pMS->LastInputTime = GetTimeCounter ();
+ Deselect (pMS->MeleeOption);
+ pMS->MeleeOption = EDIT_MELEE;
+ pMS->Initialized = FALSE;
+ if (PulsedInputState.menu[KEY_MENU_CANCEL])
+ {
+ pMS->side = 0;
+ pMS->row = 0;
+ pMS->col = 0;
+ }
+ else
+ {
+ pMS->side = 0;
+ pMS->row = NUM_MELEE_ROWS - 1;
+ pMS->col = NUM_MELEE_COLUMNS - 1;
+ }
+ DoEdit (pMS);
+ }
+ else
+ {
+ MELEE_OPTIONS NewMeleeOption;
+
+ NewMeleeOption = pMS->MeleeOption;
+ if (PulsedInputState.menu[KEY_MENU_UP])
+ {
+ pMS->LastInputTime = GetTimeCounter ();
+ NewMeleeOption = MeleeOptionUp (pMS->MeleeOption);
+ }
+ else if (PulsedInputState.menu[KEY_MENU_DOWN])
+ {
+ pMS->LastInputTime = GetTimeCounter ();
+ NewMeleeOption = MeleeOptionDown (pMS->MeleeOption);
+ }
+
+ if ((PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL)
+ && GetTimeCounter () - pMS->LastInputTime > ONE_SECOND * 10)
+ {
+ force_select = TRUE;
+ NewMeleeOption = START_MELEE;
+ }
+
+ if (NewMeleeOption != pMS->MeleeOption)
+ {
+#ifdef NETPLAY
+ if (pMS->MeleeOption == CONTROLS_TOP ||
+ pMS->MeleeOption == CONTROLS_BOT)
+ UpdateMeleeStatusMessage (-1);
+#endif
+ Deselect (pMS->MeleeOption);
+ pMS->MeleeOption = NewMeleeOption;
+ Select (pMS->MeleeOption);
+#ifdef NETPLAY
+ if (NewMeleeOption == CONTROLS_TOP ||
+ NewMeleeOption == CONTROLS_BOT)
+ {
+ COUNT side = (NewMeleeOption == CONTROLS_TOP) ? 1 : 0;
+ if (PlayerControl[side] & NETWORK_CONTROL)
+ UpdateMeleeStatusMessage (side);
+ else
+ UpdateMeleeStatusMessage (-1);
+ }
+#endif
+ }
+
+ if (PulsedInputState.menu[KEY_MENU_SELECT] || force_select)
+ {
+ MeleeOptionSelect (pMS);
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ return FALSE;
+ }
+ }
+
+#ifdef NETPLAY
+ flushPacketQueues ();
+
+ check_for_disconnects (pMS);
+#endif
+
+ Melee_flashSelection (pMS);
+
+ SleepThreadUntil (TimeIn + ONE_SECOND / 30);
+
+ return TRUE;
+}
+
+static int
+LoadMeleeConfig (MELEE_STATE *pMS)
+{
+ uio_Stream *stream;
+ int status;
+ COUNT side;
+
+ stream = uio_fopen (configDir, "melee.cfg", "rb");
+ if (stream == NULL)
+ goto err;
+
+ {
+ struct stat sb;
+
+ if (uio_fstat(uio_streamHandle(stream), &sb) == -1)
+ goto err;
+ if ((size_t) sb.st_size != (1 + MeleeTeam_serialSize) * NUM_SIDES)
+ goto err;
+ }
+
+ for (side = 0; side < NUM_SIDES; side++)
+ {
+ status = uio_getc (stream);
+ if (status == EOF)
+ goto err;
+ PlayerControl[side] = (BYTE) status;
+ // XXX: insert sanity check on PlanetControl here.
+
+ if (MeleeSetup_deserializeTeam (pMS->meleeSetup, side, stream) == -1)
+ goto err;
+
+ /* Do not allow netplay mode at the start. */
+ if (PlayerControl[side] & NETWORK_CONTROL)
+ PlayerControl[side] = HUMAN_CONTROL | STANDARD_RATING;
+ }
+
+ uio_fclose (stream);
+ return 0;
+
+err:
+ if (stream)
+ uio_fclose (stream);
+ return -1;
+}
+
+static int
+WriteMeleeConfig (MELEE_STATE *pMS)
+{
+ uio_Stream *stream;
+ COUNT side;
+
+ stream = res_OpenResFile (configDir, "melee.cfg", "wb");
+ if (stream == NULL)
+ goto err;
+
+ for (side = 0; side < NUM_SIDES; side++)
+ {
+ if (uio_putc (PlayerControl[side], stream) == EOF)
+ goto err;
+
+ if (MeleeSetup_serializeTeam (pMS->meleeSetup, side, stream) == -1)
+ goto err;
+ }
+
+ if (!res_CloseResFile (stream))
+ goto err;
+
+ return 0;
+
+err:
+ if (stream)
+ {
+ res_CloseResFile (stream);
+ DeleteResFile (configDir, "melee.cfg");
+ }
+ return -1;
+}
+
+void
+Melee (void)
+{
+ InitGlobData ();
+ {
+ MELEE_STATE MenuState;
+
+ pMeleeState = &MenuState;
+ memset (pMeleeState, 0, sizeof (*pMeleeState));
+
+ MenuState.InputFunc = DoMelee;
+ MenuState.Initialized = FALSE;
+
+ MenuState.meleeSetup = MeleeSetup_new ();
+
+ MenuState.randomContext = RandomContext_New ();
+ RandomContext_SeedRandom (MenuState.randomContext,
+ GetTimeCounter ());
+ // Using the current time still leaves the random state a bit
+ // predictable, but it is good enough.
+
+#ifdef NETPLAY
+ {
+ COUNT player;
+ for (player = 0; player < NUM_PLAYERS; player++)
+ netConnections[player] = NULL;
+ }
+#endif
+
+ MenuState.currentShip = MELEE_NONE;
+ MenuState.CurIndex = MELEE_STATE_INDEX_DONE;
+ InitMeleeLoadState (&MenuState);
+
+ GLOBAL (CurrentActivity) = SUPER_MELEE;
+
+ GameSounds = CaptureSound (LoadSound (GAME_SOUNDS));
+ LoadMeleeInfo (&MenuState);
+ if (LoadMeleeConfig (&MenuState) == -1)
+ {
+ PlayerControl[0] = HUMAN_CONTROL | STANDARD_RATING;
+ Melee_LocalChange_team (&MenuState, 0,
+ MenuState.load.preBuiltList[0]);
+ PlayerControl[1] = COMPUTER_CONTROL | STANDARD_RATING;
+ Melee_LocalChange_team (&MenuState, 1,
+ MenuState.load.preBuiltList[1]);
+ }
+
+ MenuState.side = 0;
+ SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
+ DoInput (&MenuState, TRUE);
+
+ StopMusic ();
+ WaitForSoundEnd (TFBSOUND_WAIT_ALL);
+
+ WriteMeleeConfig (&MenuState);
+ FreeMeleeInfo (&MenuState);
+ DestroySound (ReleaseSound (GameSounds));
+ GameSounds = 0;
+
+ UninitMeleeLoadState (&MenuState);
+
+ RandomContext_Delete (MenuState.randomContext);
+
+ MeleeSetup_delete (MenuState.meleeSetup);
+
+ FlushInput ();
+ }
+}
+
+#ifdef NETPLAY
+void
+updateRandomSeed (MELEE_STATE *pMS, COUNT side, DWORD seed)
+{
+ TFB_SeedRandom (seed);
+ (void) pMS;
+ (void) side;
+}
+
+// The remote player has done something which invalidates our confirmation.
+void
+confirmationCancelled (MELEE_STATE *pMS, COUNT side)
+{
+ if (side == 0)
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 16));
+ // "Bottom player changed something -- need to reconfirm."
+ else
+ DrawMeleeStatusMessage (GAME_STRING (NETMELEE_STRING_BASE + 17));
+ // "Top player changed something -- need to reconfirm."
+
+ if (pMS->InputFunc == DoConfirmSettings)
+ pMS->InputFunc = DoMelee;
+}
+
+static void
+connectionFeedback (NetConnection *conn, const char *str, bool forcePopup) {
+ struct battlestate_struct *bs = NetMelee_getBattleState (conn);
+
+ if (bs == NULL && !forcePopup)
+ {
+ // bs == NULL means the game has not started yet.
+ DrawMeleeStatusMessage (str);
+ }
+ else
+ {
+ DoPopupWindow (str);
+ }
+}
+
+void
+connectedFeedback (NetConnection *conn) {
+ if (NetConnection_getPlayerNr (conn) == 0)
+ connectionFeedback (conn, GAME_STRING (NETMELEE_STRING_BASE + 8),
+ false);
+ // "Bottom player is connected."
+ else
+ connectionFeedback (conn, GAME_STRING (NETMELEE_STRING_BASE + 9),
+ false);
+ // "Top player is connected."
+
+ PlayMenuSound (MENU_SOUND_INVOKED);
+}
+
+static const char *
+abortReasonString (NetplayAbortReason reason)
+{
+ switch (reason)
+ {
+ case AbortReason_unspecified:
+ return GAME_STRING (NETMELEE_STRING_BASE + 25);
+ // "Disconnect for an unspecified reason.'
+ case AbortReason_versionMismatch:
+ return GAME_STRING (NETMELEE_STRING_BASE + 26);
+ // "Connection aborted due to version mismatch."
+ case AbortReason_invalidHash:
+ return GAME_STRING (NETMELEE_STRING_BASE + 27);
+ // "Connection aborted because the remote side sent a "
+ // "fake signature."
+ case AbortReason_protocolError:
+ return GAME_STRING (NETMELEE_STRING_BASE + 28);
+ // "Connection aborted due to an internal protocol "
+ // "error."
+ }
+
+ return NULL;
+ // Should not happen.
+}
+
+void
+abortFeedback (NetConnection *conn, NetplayAbortReason reason)
+{
+ const char *msg;
+
+ msg = abortReasonString (reason);
+ if (msg != NULL)
+ connectionFeedback (conn, msg, true);
+}
+
+static const char *
+resetReasonString (NetplayResetReason reason)
+{
+ switch (reason)
+ {
+ case ResetReason_unspecified:
+ return GAME_STRING (NETMELEE_STRING_BASE + 29);
+ // "Game aborted for an unspecified reason."
+ case ResetReason_syncLoss:
+ return GAME_STRING (NETMELEE_STRING_BASE + 30);
+ // "Game aborted due to loss of synchronisation."
+ case ResetReason_manualReset:
+ return GAME_STRING (NETMELEE_STRING_BASE + 31);
+ // "Game aborted by the remote player."
+ }
+
+ return NULL;
+ // Should not happen.
+}
+
+void
+resetFeedback (NetConnection *conn, NetplayResetReason reason,
+ bool byRemote)
+{
+ const char *msg;
+
+ flushPacketQueues ();
+ // If the local side queued a reset packet as a result of a
+ // remote reset, that packet will not have been sent yet.
+ // We flush the queue now, so that the remote side won't be
+ // waiting for the reset packet while this side is waiting
+ // for an acknowledgement of the feedback message.
+
+ if (reason == ResetReason_manualReset && !byRemote) {
+ // No message needed, the player initiated the reset.
+ return;
+ }
+
+ msg = resetReasonString (reason);
+ if (msg != NULL)
+ connectionFeedback (conn, msg, false);
+
+ // End supermelee. This must not be done before connectionFeedback(),
+ // otherwise the message will immediately disappear.
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+}
+
+void
+errorFeedback (NetConnection *conn)
+{
+ if (NetConnection_getPlayerNr (conn) == 0)
+ connectionFeedback (conn, GAME_STRING (NETMELEE_STRING_BASE + 10),
+ false);
+ // "Bottom player: connection failed."
+ else
+ connectionFeedback (conn, GAME_STRING (NETMELEE_STRING_BASE + 11),
+ false);
+ // "Top player: connection failed."
+}
+
+void
+closeFeedback (NetConnection *conn)
+{
+ if (NetConnection_getPlayerNr (conn) == 0)
+ connectionFeedback (conn, GAME_STRING (NETMELEE_STRING_BASE + 12),
+ false);
+ // "Bottom player: connection closed."
+ else
+ connectionFeedback (conn, GAME_STRING (NETMELEE_STRING_BASE + 13),
+ false);
+ // "Top player: connection closed."
+}
+
+#endif /* NETPLAY */
+
+
+///////////////////////////////////////////////////////////////////////////
+
+// Melee_UpdateView_xxx() functions are called when some value in the
+// supermelee fleet setup screen needs to be updated visually.
+
+static void
+Melee_UpdateView_fleetValue (MELEE_STATE *pMS, COUNT side)
+{
+ if (pMS->meleeStarted)
+ return;
+
+ DrawFleetValue (pMS, side, DTSHS_REPAIR);
+ // BUG: The fleet value is always drawn as deselected.
+}
+
+static void
+Melee_UpdateView_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index)
+{
+ MeleeShip ship;
+
+ if (pMS->meleeStarted)
+ return;
+
+ ship = MeleeSetup_getShip (pMS->meleeSetup, side, index);
+
+ if (ship == MELEE_NONE)
+ {
+ ClearShipBox (side, index);
+ }
+ else
+ {
+ DrawShipBox (side, index, ship, FALSE);
+ }
+}
+
+static void
+Melee_UpdateView_teamName (MELEE_STATE *pMS, COUNT side)
+{
+ if (pMS->meleeStarted)
+ return;
+
+ DrawTeamString (pMS, side, DTSHS_REPAIR, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+// Melee_Change_xxx() functions are helper functions, called when some value
+// in the supermelee fleet setup screen has changed, eithed because of a
+// local change, or a remote change.
+
+static bool
+Melee_Change_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
+ MeleeShip ship)
+{
+ if (!MeleeSetup_setShip (pMS->meleeSetup, side, index, ship))
+ {
+ // No change.
+ return false;
+ }
+
+ // Update the view
+ Melee_UpdateView_ship (pMS, side, index);
+ Melee_UpdateView_fleetValue (pMS, side);
+
+ // If the modified slot is currently selected, display the new ship icon
+ // on the right of the screen.
+ if (isShipSlotSelected (pMS, side, index))
+ {
+ pMS->currentShip = ship;
+ DrawMeleeShipStrings (pMS, ship);
+ }
+
+ return true;
+}
+
+// Pre: 'name' is '\0'-terminated
+static bool
+Melee_Change_teamName (MELEE_STATE *pMS, COUNT side, const char *name)
+{
+ MeleeSetup *setup = pMS->meleeSetup;
+
+ if (!MeleeSetup_setTeamName (setup, side, name))
+ {
+ // No change.
+ return false;
+ }
+
+ if (pMS->row != NUM_MELEE_ROWS || pMS->side != side ||
+ pMeleeState->CurIndex == MELEE_STATE_INDEX_DONE)
+ {
+ // The team name is not currently being edited, so we can
+ // update it on screen. If it was edited, then this function
+ // will be called again after it is done.
+ Melee_UpdateView_teamName (pMS, side);
+ }
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+// Melee_LocalChange_xxx() functions are called when some value in the
+// supermelee fleet setup screen has changed because of a local action.
+// The behavior of these functions (and the comments therein) follow the
+// description in doc/devel/netplay/protocol.
+
+bool
+Melee_LocalChange_ship (MELEE_STATE *pMS, COUNT side, FleetShipIndex index,
+ MeleeShip ship)
+{
+ if (!Melee_Change_ship (pMS, side, index, ship))
+ return false;
+
+#ifdef NETPLAY
+ {
+ MeleeSetup *setup = pMS->meleeSetup;
+
+ MeleeShip sentShip = MeleeSetup_getSentShip (setup, side, index);
+ if (sentShip == MELEE_UNSET)
+ {
+ // State 1.
+ // Notify network connections of the change.
+ Netplay_NotifyAll_setShip (pMS, side, index);
+ MeleeSetup_setSentShip (setup, side, index, ship);
+ }
+ }
+#endif /* NETPLAY */
+
+ return true;
+}
+
+
+// Pre: 'name' is '\0'-terminated
+bool
+Melee_LocalChange_teamName (MELEE_STATE *pMS, COUNT side, const char *name)
+{
+ if (!Melee_Change_teamName (pMS, side, name))
+ return false;
+
+#ifdef NETPLAY
+ {
+ MeleeSetup *setup = pMS->meleeSetup;
+
+ const char *sentName = MeleeSetup_getSentTeamName (setup, side);
+ if (sentName == NULL)
+ {
+ // State 1.
+ // Notify network connections of the change.
+ Netplay_NotifyAll_setTeamName (pMS, side);
+ MeleeSetup_setSentTeamName (setup, side, name);
+ }
+ }
+#endif /* NETPLAY */
+
+ return true;
+}
+
+bool
+Melee_LocalChange_fleet (MELEE_STATE *pMS, size_t teamNr,
+ const MeleeShip *fleet)
+{
+ FleetShipIndex slotI;
+ bool changed = false;
+
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ {
+ if (Melee_LocalChange_ship (pMS, teamNr, slotI, fleet[slotI]))
+ changed = true;
+ }
+ return changed;
+}
+
+bool
+Melee_LocalChange_team (MELEE_STATE *pMS, size_t teamNr,
+ const MeleeTeam *team)
+{
+ const MeleeShip *fleet = MeleeTeam_getFleet (team);
+ const char *name = MeleeTeam_getTeamName (team);
+ bool changed = false;
+
+ if (Melee_LocalChange_fleet (pMS, teamNr, fleet))
+ changed = true;
+ if (Melee_LocalChange_teamName (pMS, teamNr, name))
+ changed = true;
+
+ return changed;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#ifdef NETPLAY
+
+// Send the entire team to the remote side. Used when the connection has
+// just been established, or after the setup menu is reentered after battle.
+void
+Melee_bootstrapSyncTeam (MELEE_STATE *meleeState, size_t teamNr)
+{
+ MeleeSetup *setup = meleeState->meleeSetup;
+ FleetShipIndex slotI;
+ const char *teamName;
+
+ // Send the current fleet.
+ Netplay_NotifyAll_setFleet(meleeState, teamNr);
+
+ // Update the last sent fleet.
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ {
+ MeleeShip ship = MeleeSetup_getShip (setup, teamNr, slotI);
+ assert (MeleeSetup_getSentShip (setup, teamNr, slotI) == MELEE_UNSET);
+ MeleeSetup_setSentShip (setup, teamNr, slotI, ship);
+ }
+
+ // Send the current team name.
+ Netplay_NotifyAll_setTeamName (meleeState, teamNr);
+
+ // Update the last sent team name.
+ teamName = MeleeSetup_getTeamName (setup, teamNr);
+ MeleeSetup_setSentTeamName (setup, teamNr, teamName);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+// Melee_RemoteChange_xxx() functions are called when some value in the
+// supermelee fleet setup screen has changed remotely.
+// The behavior of these functions (and the comments therein) follow the
+// description in doc/devel/netplay/protocol.
+
+void
+Melee_RemoteChange_ship (MELEE_STATE *pMS, NetConnection *conn, COUNT side,
+ FleetShipIndex index, MeleeShip ship)
+{
+ MeleeSetup *setup = pMS->meleeSetup;
+
+ MeleeShip sentShip = MeleeSetup_getSentShip (setup, side, index);
+ MeleeShip currentShip;
+
+ if (sentShip == MELEE_UNSET)
+ {
+ // State 1
+
+ // Change the ship locally.
+ Melee_Change_ship (pMS, side, index, ship);
+
+ // Notify the remote side.
+ Netplay_NotifyAll_setShip (pMS, side, index);
+
+ // A packet has now been received and sent. End of turn.
+ return;
+ }
+
+ // A packet has been sent and received. End of turn.
+ MeleeSetup_setSentShip (setup, side, index, MELEE_UNSET);
+
+ if (ship != sentShip)
+ {
+ // Rule 2c or 3d. The value which we sent is different from the value
+ // which the opponent sent. We need a tie-breaker to determine which
+ // value prevails.
+ if (NetConnection_getPlayerNr (conn) != side)
+ {
+ // Rule 2c+ or 3d+
+ // We win the tie-breaker. The value which we sent prevails.
+ }
+ else
+ {
+ // Rule 2c- or 3d-.
+ // We lose the tie-breaker. We adopt the remote value.
+ Melee_Change_ship (pMS, side, index, ship);
+ return;
+ }
+ }
+ /*
+ else
+ {
+ // Rule 2b or 3c. The value which we sent is the value which
+ // the opponent sent. This confirms the value.
+ }
+ */
+
+ // Rule 2b, 2c+, 3c, or 3d+. The value which we sent is confirmed.
+
+ currentShip = MeleeSetup_getShip (setup, side, index);
+ if (currentShip != sentShip)
+ {
+ // Rule 3c or 3d+. We had a local change which was yet
+ // unreported.
+
+ // Notify the remote side and keep track of what we sent.
+ Netplay_NotifyAll_setShip (pMS, side, index);
+ MeleeSetup_setSentShip (setup, side, index, ship);
+ }
+}
+
+void
+Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn,
+ COUNT side, const char *newName)
+{
+ MeleeSetup *setup = pMS->meleeSetup;
+
+ const char *sentName = MeleeSetup_getSentTeamName (setup, side);
+ const char *currentName;
+
+ if (sentName == NULL)
+ {
+ // State 1
+
+ // Change the team name locally.
+ Melee_Change_teamName (pMS, side, newName);
+
+ // Notify the remote side.
+ Netplay_NotifyAll_setTeamName (pMS, side);
+
+ // A packet has now been received and sent. End of turn.
+ // The sent team name is still unset, so we don't have to reset it.
+ return;
+ }
+
+ if (strcmp (newName, sentName) == 0)
+ {
+ // Rule 2c or 3d. The value which we sent is different from the value
+ // which the opponent sent. We need a tie-breaker to determine which
+ // value prevails.
+ if (NetConnection_getPlayerNr (conn) != side)
+ {
+ // Rule 2c+ or 3d+
+ // We win the tie-breaker. The value which we sent prevails.
+ }
+ else
+ {
+ // Rule 2c- or 3d-.
+ // We lose the tie-breaker. We adopt the remote value.
+ Melee_Change_teamName (pMS, side, newName);
+ MeleeSetup_setSentTeamName (setup, side, NULL);
+ return;
+ }
+ }
+ /*
+ else
+ {
+ // Rule 2b or 3c. The value which we sent is the value which
+ // the opponent sent. This confirms the value.
+ }
+ */
+
+ // Rule 2b, 2c+, 3c, or 3d+. The value which we sent is confirmed.
+
+ currentName = MeleeSetup_getTeamName (setup, side);
+ if (strcmp (currentName, sentName) != 0)
+ {
+ // Rule 3c or 3d+. We had a local change which was yet
+ // unreported.
+
+ // A packet has been sent and received, which ends the turn.
+ // We don't bother clearing the sent team name, as we're going
+ // to send a new packet immediately.
+
+ // Notify the remote side and keep track of what we sent.
+ Netplay_NotifyAll_setTeamName (pMS, side);
+
+ // Update the last sent message.
+ MeleeSetup_setSentTeamName (setup, side, newName);
+ }
+ else
+ {
+ // A packet has been sent and received. End of turn.
+ MeleeSetup_setSentTeamName (setup, side, NULL);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#endif /* NETPLAY */
+
diff --git a/src/uqm/supermelee/melee.h b/src/uqm/supermelee/melee.h
new file mode 100644
index 0000000..e6026a3
--- /dev/null
+++ b/src/uqm/supermelee/melee.h
@@ -0,0 +1,144 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_SUPERMELEE_MELEE_H_
+#define UQM_SUPERMELEE_MELEE_H_
+
+#include "../init.h"
+#include "libs/gfxlib.h"
+#include "libs/mathlib.h"
+#include "libs/sndlib.h"
+#include "libs/timelib.h"
+#include "libs/reslib.h"
+#include "netplay/packet.h"
+ // for NetplayAbortReason and NetplayResetReason.
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct melee_state MELEE_STATE;
+
+#define NUM_MELEE_ROWS 2
+#define NUM_MELEE_COLUMNS 7
+//#define NUM_MELEE_COLUMNS 6
+#define MELEE_FLEET_SIZE (NUM_MELEE_ROWS * NUM_MELEE_COLUMNS)
+#define ICON_WIDTH 16
+#define ICON_HEIGHT 16
+
+extern FRAME PickMeleeFrame;
+
+#define PICK_BG_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x0F), 0x01)
+#define PICK_VALUE_COLOR BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2C)
+ // Used for the current fleet value in the next ship selection
+ // in SuperMelee.
+
+#define MAX_TEAM_CHARS 30
+#define NUM_PICK_COLS 5
+#define NUM_PICK_ROWS 5
+
+typedef BYTE MELEE_OPTIONS;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "loadmele.h"
+#include "meleesetup.h"
+#include "meleeship.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct melee_state
+{
+ BOOLEAN (*InputFunc) (struct melee_state *pInputState);
+
+ BOOLEAN Initialized;
+ BOOLEAN meleeStarted;
+ MELEE_OPTIONS MeleeOption;
+ COUNT side;
+ COUNT row;
+ COUNT col;
+ MeleeSetup *meleeSetup;
+ struct melee_load_state load;
+ MeleeShip currentShip;
+ // The ship currently displayed. Not really needed.
+ // Also the current ship position when selecting a ship.
+ COUNT CurIndex;
+#define MELEE_STATE_INDEX_DONE ((COUNT) -1)
+ // Current position in the team string when editing it.
+ // Set to MELEE_STATE_INDEX_DONE when done.
+ BOOLEAN buildPickConfirmed;
+ // Used by DoPickShip () to communicate to the calling
+ // function BuildPickShip() whether a ship has been selected
+ // to add to the fleet, or whether the operation has been
+ // cancelled. If a ship was selected, it is set in
+ // currentShip.
+ RandomContext *randomContext;
+ /* RNG state for all local random decisions, i.e. those
+ * decisions that are not shared among network parties. */
+ TimeCount LastInputTime;
+
+ MUSIC_REF hMusic;
+};
+
+extern void Melee (void);
+
+// Some prototypes for use by loadmele.c:
+BOOLEAN DoMelee (MELEE_STATE *pMS);
+void DrawMeleeIcon (COUNT which_icon);
+void GetShipBox (RECT *pRect, COUNT side, COUNT row, COUNT col);
+void RepairMeleeFrame (const RECT *pRect);
+void DrawMeleeShipStrings (MELEE_STATE *pMS, MeleeShip NewStarShip);
+extern FRAME MeleeFrame;
+void Melee_flashSelection (MELEE_STATE *pMS);
+
+COUNT GetShipValue (MeleeShip StarShip);
+
+void updateRandomSeed (MELEE_STATE *pMS, COUNT side, DWORD seed);
+void confirmationCancelled(MELEE_STATE *pMS, COUNT side);
+void connectedFeedback (NetConnection *conn);
+void abortFeedback (NetConnection *conn, NetplayAbortReason reason);
+void resetFeedback (NetConnection *conn, NetplayResetReason reason,
+ bool byRemote);
+void errorFeedback (NetConnection *conn);
+void closeFeedback (NetConnection *conn);
+
+bool Melee_LocalChange_ship (MELEE_STATE *pMS, COUNT side,
+ FleetShipIndex index, MeleeShip ship);
+bool Melee_LocalChange_teamName (MELEE_STATE *pMS, COUNT side,
+ const char *name);
+bool Melee_LocalChange_fleet (MELEE_STATE *pMS, size_t teamNr,
+ const MeleeShip *fleet);
+bool Melee_LocalChange_team (MELEE_STATE *pMS, size_t teamNr,
+ const MeleeTeam *team);
+
+void Melee_bootstrapSyncTeam (MELEE_STATE *pMS, size_t teamNr);
+
+void Melee_RemoteChange_ship (MELEE_STATE *pMS, NetConnection *conn,
+ COUNT side, FleetShipIndex index, MeleeShip ship);
+void Melee_RemoteChange_teamName (MELEE_STATE *pMS, NetConnection *conn,
+ COUNT side, const char *name);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_MELEE_H_ */
diff --git a/src/uqm/supermelee/meleesetup.c b/src/uqm/supermelee/meleesetup.c
new file mode 100644
index 0000000..a45f172
--- /dev/null
+++ b/src/uqm/supermelee/meleesetup.c
@@ -0,0 +1,440 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define MELEESETUP_INTERNAL
+#include "port.h"
+#include "meleesetup.h"
+
+#include "../master.h"
+#include "libs/log.h"
+
+
+///////////////////////////////////////////////////////////////////////////
+
+// Temporary
+const size_t MeleeTeam_serialSize = MELEE_FLEET_SIZE +
+ sizeof (((MeleeTeam*)0)->name);
+
+void
+MeleeTeam_init (MeleeTeam *team)
+{
+ FleetShipIndex slotI;
+
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ team->ships[slotI] = MELEE_NONE;
+
+ team->name[0] = '\0';
+}
+
+void
+MeleeTeam_uninit (MeleeTeam *team)
+{
+ (void) team;
+}
+
+MeleeTeam *
+MeleeTeam_new (void)
+{
+ MeleeTeam *result = HMalloc (sizeof (MeleeTeam));
+ MeleeTeam_init (result);
+ return result;
+}
+
+void
+MeleeTeam_delete (MeleeTeam *team)
+{
+ MeleeTeam_uninit (team);
+ HFree (team);
+}
+
+int
+MeleeTeam_serialize (const MeleeTeam *team, uio_Stream *stream)
+{
+ FleetShipIndex slotI;
+
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++) {
+ if (uio_putc ((int) team->ships[slotI], stream) == EOF)
+ return -1;
+ }
+ if (uio_fwrite ((const char *) team->name, sizeof team->name, 1,
+ stream) != 1)
+ return -1;
+
+ return 0;
+}
+
+int
+MeleeTeam_deserialize (MeleeTeam *team, uio_Stream *stream)
+{
+ FleetShipIndex slotI;
+
+ // Sanity check on the ships.
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ {
+ int ship = uio_getc (stream);
+ if (ship == EOF)
+ goto err;
+ team->ships[slotI] = (MeleeShip) ship;
+
+ if (team->ships[slotI] == MELEE_NONE)
+ continue;
+
+ if (team->ships[slotI] >= NUM_MELEE_SHIPS)
+ {
+ log_add (log_Warning, "Invalid ship type in loaded team (index "
+ "%d, ship type is %d, max valid is %d).",
+ slotI, team->ships[slotI], NUM_MELEE_SHIPS - 1);
+ team->ships[slotI] = MELEE_NONE;
+ }
+ }
+
+ if (uio_fread (team->name, sizeof team->name, 1, stream) != 1)
+ goto err;
+
+ team->name[MAX_TEAM_CHARS] = '\0';
+
+ return 0;
+
+err:
+ MeleeTeam_delete(team);
+ return -1;
+}
+
+// XXX: move this to elsewhere?
+COUNT
+MeleeTeam_getValue (const MeleeTeam *team)
+{
+ COUNT total = 0;
+ FleetShipIndex slotI;
+
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ {
+ MeleeShip ship = team->ships[slotI];
+ COUNT shipValue = GetShipValue (ship);
+ if (shipValue == (COUNT)~0)
+ {
+ // Invalid ship.
+ continue;
+ }
+ total += shipValue;
+ }
+
+ return total;
+}
+
+MeleeShip
+MeleeTeam_getShip (const MeleeTeam *team, FleetShipIndex slotNr)
+{
+ return team->ships[slotNr];
+}
+
+void
+MeleeTeam_setShip (MeleeTeam *team, FleetShipIndex slotNr, MeleeShip ship)
+{
+ team->ships[slotNr] = ship;
+}
+
+const MeleeShip *
+MeleeTeam_getFleet (const MeleeTeam *team)
+{
+ return team->ships;
+}
+
+const char *
+MeleeTeam_getTeamName (const MeleeTeam *team)
+{
+ return team->name;
+}
+
+// Returns true iff the state has actually changed.
+void
+MeleeTeam_setName (MeleeTeam *team, const char *name)
+{
+ strncpy (team->name, name, sizeof team->name - 1);
+ team->name[sizeof team->name - 1] = '\0';
+}
+
+void
+MeleeTeam_copy (MeleeTeam *copy, const MeleeTeam *original)
+{
+ *copy = *original;
+}
+
+#if 0
+bool
+MeleeTeam_isEqual (const MeleeTeam *team1, const MeleeTeam *team2)
+{
+ const MeleeShip *fleet1;
+ const MeleeShip *fleet2;
+ FleetShipIndex slotI;
+
+ if (strcmp (team1->name, team2->name) != 0)
+ return false;
+
+ fleet1 = team1->ships;
+ fleet2 = team2->ships;
+
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ {
+ if (fleet1[slotI] != fleet2[slotI])
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+
+#ifdef NETPLAY
+static void
+MeleeSetup_initSentTeam (MeleeSetup *setup, size_t teamNr)
+{
+ MeleeTeam *team = &setup->sentTeams[teamNr];
+ FleetShipIndex slotI;
+
+ for (slotI = 0; slotI < MELEE_FLEET_SIZE; slotI++)
+ MeleeTeam_setShip (team, slotI, MELEE_UNSET);
+
+ setup->haveSentTeamName[teamNr] = false;
+#ifdef DEBUG
+ // The actual team name should be irrelevant if haveSentTeamName is
+ // set to false. In a debug build, we set it to invalid, so that
+ // it is more likely that it will be noticed if it is ever used.
+ MeleeTeam_setName (team, "<INVALID>");
+#endif /* DEBUG */
+}
+#endif /* NETPLAY */
+
+MeleeSetup *
+MeleeSetup_new (void)
+{
+ size_t teamI;
+ MeleeSetup *result = HMalloc (sizeof (MeleeSetup));
+ if (result == NULL)
+ return NULL;
+
+ for (teamI = 0; teamI < NUM_SIDES; teamI++)
+ {
+ MeleeTeam_init (&result->teams[teamI]);
+ result->fleetValue[teamI] = 0;
+#ifdef NETPLAY
+ MeleeSetup_initSentTeam (result, teamI);
+#endif /* NETPLAY */
+ }
+ return result;
+}
+
+void
+MeleeSetup_delete (MeleeSetup *setup)
+{
+ HFree (setup);
+}
+
+#ifdef NETPLAY
+void
+MeleeSetup_resetSentTeams (MeleeSetup *setup)
+{
+ size_t teamI;
+
+ for (teamI = 0; teamI < NUM_SIDES; teamI++)
+ MeleeSetup_initSentTeam (setup, teamI);
+}
+#endif /* NETPLAY */
+
+// Returns true iff the state has actually changed.
+bool
+MeleeSetup_setShip (MeleeSetup *setup, size_t teamNr, FleetShipIndex slotNr,
+ MeleeShip ship)
+{
+ MeleeTeam *team = &setup->teams[teamNr];
+ MeleeShip oldShip = MeleeTeam_getShip (team, slotNr);
+
+ if (ship == oldShip)
+ return false;
+
+ if (oldShip != MELEE_NONE)
+ setup->fleetValue[teamNr] -= GetShipCostFromIndex (oldShip);
+
+ MeleeTeam_setShip (team, slotNr, ship);
+
+ if (ship != MELEE_NONE)
+ setup->fleetValue[teamNr] += GetShipCostFromIndex (ship);
+
+ return true;
+}
+
+MeleeShip
+MeleeSetup_getShip (const MeleeSetup *setup, size_t teamNr,
+ FleetShipIndex slotNr)
+{
+ return MeleeTeam_getShip (&setup->teams[teamNr], slotNr);
+}
+
+const MeleeShip *
+MeleeSetup_getFleet (const MeleeSetup *setup, size_t teamNr)
+{
+ return MeleeTeam_getFleet (&setup->teams[teamNr]);
+}
+
+// Returns true iff the state has actually changed.
+bool
+MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
+ const char *name)
+{
+ MeleeTeam *team = &setup->teams[teamNr];
+ const char *oldName = MeleeTeam_getTeamName (team);
+
+ if (strcmp (oldName, name) == 0)
+ return false;
+
+ MeleeTeam_setName (team, name);
+ return true;
+}
+
+// NB. This function returns a pointer to a static buffer, which is
+// overwritten by calls to MeleeSetup_setTeamName().
+const char *
+MeleeSetup_getTeamName (const MeleeSetup *setup, size_t teamNr)
+{
+ return MeleeTeam_getTeamName (&setup->teams[teamNr]);
+}
+
+COUNT
+MeleeSetup_getFleetValue (const MeleeSetup *setup, size_t teamNr)
+{
+ return setup->fleetValue[teamNr];
+}
+
+int
+MeleeSetup_deserializeTeam (MeleeSetup *setup, size_t teamNr,
+ uio_Stream *stream)
+{
+ MeleeTeam *team = &setup->teams[teamNr];
+ int ret = MeleeTeam_deserialize (team, stream);
+ if (ret == 0)
+ setup->fleetValue[teamNr] = MeleeTeam_getValue (team);
+ return ret;
+}
+
+int
+MeleeSetup_serializeTeam (const MeleeSetup *setup, size_t teamNr,
+ uio_Stream *stream)
+{
+ const MeleeTeam *team = &setup->teams[teamNr];
+ return MeleeTeam_serialize (team, stream);
+}
+
+#ifdef NETPLAY
+MeleeShip
+MeleeSetup_getSentShip (const MeleeSetup *setup, size_t teamNr,
+ FleetShipIndex slotNr)
+{
+ return MeleeTeam_getShip (&setup->sentTeams[teamNr], slotNr);
+}
+
+// Returns NULL if there is no team name set. This is not the same
+// as when an empty (zero-length) team name is set.
+// NB. This function returns a pointer to a static buffer, which is
+// overwritten by calls to MeleeSetup_setSentTeamName().
+const char *
+MeleeSetup_getSentTeamName (const MeleeSetup *setup, size_t teamNr)
+{
+ if (!setup->haveSentTeamName[teamNr])
+ return NULL;
+
+ return MeleeTeam_getTeamName (&setup->sentTeams[teamNr]);
+}
+
+// Returns true iff the state has actually changed.
+bool
+MeleeSetup_setSentShip (MeleeSetup *setup, size_t teamNr,
+ FleetShipIndex slotNr, MeleeShip ship)
+{
+ MeleeTeam *team = &setup->sentTeams[teamNr];
+ MeleeShip oldShip = MeleeTeam_getShip (team, slotNr);
+
+ if (ship == oldShip)
+ return false;
+
+ MeleeTeam_setShip (team, slotNr, ship);
+ return true;
+}
+
+// Returns true iff the state has actually changed.
+// 'name' can be NULL to indicate that no team name set. This is not the same
+// as when an empty (zero-length) team name is set.
+bool
+MeleeSetup_setSentTeamName (MeleeSetup *setup, size_t teamNr,
+ const char *name)
+{
+ bool haveSentName = setup->haveSentTeamName[teamNr];
+
+ if (name == NULL)
+ {
+ if (!haveSentName)
+ {
+ // Had not sent a team name, and still haven't.
+ return false;
+ }
+
+#ifdef DEBUG
+ {
+ // The actual team name should be irrelevant if haveSentTeamName
+ // is set to false. In a debug build, we set it to invalid, so
+ // that it is more likely that it will be noticed if it is ever
+ // used.
+ MeleeTeam *team = &setup->sentTeams[teamNr];
+ MeleeTeam_setName (team, "<INVALID>");
+ }
+#endif
+ }
+ else
+ {
+ MeleeTeam *team = &setup->sentTeams[teamNr];
+
+ if (haveSentName)
+ {
+ // Have sent a team name. Check whether it has actually changed.
+ const char *oldName = MeleeTeam_getTeamName (team);
+ if (strcmp (oldName, name) == 0)
+ return false; // Team name has not changed.
+ }
+
+ MeleeTeam_setName (team, name);
+ }
+
+ setup->haveSentTeamName[teamNr] = (name != NULL);
+
+ return true;
+}
+
+#if 0
+bool
+MeleeSetup_isTeamSent (MeleeSetup *setup, size_t teamNr)
+{
+ MeleeTeam *localTeam = &setup->teams[teamNr];
+ MeleeTeam *sentTeam = &setup->sentTeams[teamNr];
+
+ return MeleeTeam_isEqual (localTeam, sentTeam);
+}
+#endif
+
+#endif /* NETPLAY */
+
+///////////////////////////////////////////////////////////////////////////
+
+
diff --git a/src/uqm/supermelee/meleesetup.h b/src/uqm/supermelee/meleesetup.h
new file mode 100644
index 0000000..0097d92
--- /dev/null
+++ b/src/uqm/supermelee/meleesetup.h
@@ -0,0 +1,143 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MELEESETUP_H
+#define MELEESETUP_H
+
+typedef struct MeleeTeam MeleeTeam;
+typedef struct MeleeSetup MeleeSetup;
+
+#ifdef MELEESETUP_INTERNAL
+# define MELEETEAM_INTERNAL
+#endif /* MELEESETUP_INTERNAL */
+
+#include "libs/compiler.h"
+
+typedef COUNT FleetShipIndex;
+
+#include "libs/uio.h"
+#include "melee.h"
+#include "meleeship.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifdef MELEETEAM_INTERNAL
+struct MeleeTeam
+{
+ MeleeShip ships[MELEE_FLEET_SIZE];
+ char name[MAX_TEAM_CHARS + 1 + 24];
+ /* The +1 is for the terminating \0; the +24 is in case some
+ * default name in starcon.txt is unknowingly mangled. */
+ // XXX: SvdB: Why would it be mangled? Why don't we just reject
+ // it if it is? Is this so that we have some space
+ // for multibyte UTF-8 chars?
+};
+#endif /* MELEETEAM_INTERNAL */
+
+#ifdef MELEESETUP_INTERNAL
+struct MeleeSetup
+{
+ MeleeTeam teams[NUM_SIDES];
+ COUNT fleetValue[NUM_SIDES];
+#ifdef NETPLAY
+ MeleeTeam sentTeams[NUM_SIDES];
+ // The last sent (parts of) teams.
+ // Used in the Update protocol. See doc/devel/netplay/protocol
+ // XXX: this may actually be deallocated when the battle starts.
+ bool haveSentTeamName[NUM_SIDES];
+ // Whether we have sent a team name this 'turn'.
+ // Used in the Update protocol. See doc/devel/netplay/protocol
+ // (also for the term 'turn').
+#endif
+};
+
+#endif /* MELEESETUP_INTERNAL */
+
+extern const size_t MeleeTeam_serialSize;
+
+void MeleeTeam_init (MeleeTeam *team);
+void MeleeTeam_uninit (MeleeTeam *team);
+MeleeTeam *MeleeTeam_new (void);
+void MeleeTeam_delete (MeleeTeam *team);
+#ifdef NETPLAY
+void MeleeSetup_resetSentTeams (MeleeSetup *setup);
+#endif /* NETPLAY */
+int MeleeTeam_serialize (const MeleeTeam *team, uio_Stream *stream);
+int MeleeTeam_deserialize (MeleeTeam *team, uio_Stream *stream);
+COUNT MeleeTeam_getValue (const MeleeTeam *team);
+MeleeShip MeleeTeam_getShip (const MeleeTeam *team, FleetShipIndex slotNr);
+void MeleeTeam_setShip (MeleeTeam *team, FleetShipIndex slotNr,
+ MeleeShip ship);
+const MeleeShip *MeleeTeam_getFleet (const MeleeTeam *team);
+const char *MeleeTeam_getTeamName (const MeleeTeam *team);
+void MeleeTeam_setName (MeleeTeam *team, const char *name);
+void MeleeTeam_copy (MeleeTeam *copy, const MeleeTeam *original);
+#if 0
+bool MeleeTeam_isEqual (const MeleeTeam *team1, const MeleeTeam *team2);
+#endif
+
+#ifdef NETPLAY
+MeleeShip MeleeSetup_getSentShip (const MeleeSetup *setup, size_t teamNr,
+ FleetShipIndex slotNr);
+const char *MeleeSetup_getSentTeamName (const MeleeSetup *setup,
+ size_t teamNr);
+bool MeleeSetup_setSentShip (MeleeSetup *setup, size_t teamNr,
+ FleetShipIndex slotNr, MeleeShip ship);
+bool MeleeSetup_setSentTeamName (MeleeSetup *setup, size_t teamNr,
+ const char *name);
+#if 0
+bool MeleeSetup_isTeamSent (MeleeSetup *setup, size_t teamNr);
+#endif
+#endif /* NETPLAY */
+
+MeleeSetup *MeleeSetup_new (void);
+void MeleeSetup_delete (MeleeSetup *setup);
+
+bool MeleeSetup_setShip (MeleeSetup *setup, size_t teamNr,
+ FleetShipIndex slotNr, MeleeShip ship);
+MeleeShip MeleeSetup_getShip (const MeleeSetup *setup, size_t teamNr,
+ FleetShipIndex slotNr);
+bool MeleeSetup_setFleet (MeleeSetup *setup, size_t teamNr,
+ const MeleeShip *fleet);
+const MeleeShip *MeleeSetup_getFleet (const MeleeSetup *setup, size_t teamNr);
+bool MeleeSetup_setTeamName (MeleeSetup *setup, size_t teamNr,
+ const char *name);
+const char *MeleeSetup_getTeamName (const MeleeSetup *setup,
+ size_t teamNr);
+COUNT MeleeSetup_getFleetValue (const MeleeSetup *setup, size_t teamNr);
+int MeleeSetup_deserializeTeam (MeleeSetup *setup, size_t teamNr,
+ uio_Stream *stream);
+int MeleeSetup_serializeTeam (const MeleeSetup *setup, size_t teamNr,
+ uio_Stream *stream);
+
+
+void MeleeState_setShip (MELEE_STATE *pMS, size_t teamNr,
+ FleetShipIndex slotNr, MeleeShip ship);
+void MeleeState_setFleet (MELEE_STATE *pMS, size_t teamNr,
+ const MeleeShip *fleet);
+void MeleeState_setTeamName (MELEE_STATE *pMS, size_t teamNr,
+ const char *name);
+void MeleeState_setTeam (MELEE_STATE *pMS, size_t teamNr,
+ const MeleeTeam *team);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* MELEESETUP_H */
+
diff --git a/src/uqm/supermelee/meleeship.h b/src/uqm/supermelee/meleeship.h
new file mode 100644
index 0000000..e917b75
--- /dev/null
+++ b/src/uqm/supermelee/meleeship.h
@@ -0,0 +1,55 @@
+#ifndef MELEESHIP_H
+#define MELEESHIP_H
+
+#include "types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef enum MeleeShip {
+ MELEE_ANDROSYNTH,
+ MELEE_ARILOU,
+ MELEE_CHENJESU,
+ MELEE_CHMMR,
+ MELEE_DRUUGE,
+ MELEE_EARTHLING,
+ MELEE_ILWRATH,
+ MELEE_KOHR_AH,
+ MELEE_MELNORME,
+ MELEE_MMRNMHRM,
+ MELEE_MYCON,
+ MELEE_ORZ,
+ MELEE_PKUNK,
+ MELEE_SHOFIXTI,
+ MELEE_SLYLANDRO,
+ MELEE_SPATHI,
+ MELEE_SUPOX,
+ MELEE_SYREEN,
+ MELEE_THRADDASH,
+ MELEE_UMGAH,
+ MELEE_URQUAN,
+ MELEE_UTWIG,
+ MELEE_VUX,
+ MELEE_YEHAT,
+ MELEE_ZOQFOTPIK,
+
+ MELEE_UNSET = ((BYTE) ~0) - 1,
+ // Used with the Update protocol, to register in the sentTeam
+ MELEE_NONE = (BYTE) ~0
+ // Empty fleet position.
+} MeleeShip;
+#define NUM_MELEE_SHIPS (MELEE_ZOQFOTPIK + 1)
+
+static inline bool
+MeleeShip_valid (MeleeShip ship)
+{
+ return (ship < NUM_MELEE_SHIPS) || (ship == MELEE_NONE);
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* MELEESHIP_H */
+
diff --git a/src/uqm/supermelee/netplay/FILES b/src/uqm/supermelee/netplay/FILES
new file mode 100644
index 0000000..7b93fd1
--- /dev/null
+++ b/src/uqm/supermelee/netplay/FILES
@@ -0,0 +1,50 @@
+In netplay/:
+crc.{c,h} Generic CRC routines.
+checkbuf.{c,h} Buffer of checksums.
+checksum.{c,h} Routines for checksumming the game state.
+netconnection.{c,h} Definition of NetConnection, being the state of a
+ network connection, and operations on it.
+nc_connect.ci Part of netconnection.c that handles establishing
+ connections.
+netinput.{c,h} Definitions and operations for melee input commands
+ over a network connection.
+netmelee.{c,h} Keeps track of network connections used in the game,
+ with methods to control them all at once.
+ Functions that directly access the game data are kept
+ in the relevant files (melee.c, pickmele.c, battle.c).
+netmisc.{c,h} Miscelaneous functions that didn't fit in elsewhere.
+netoptions.{c,h} Description of a network connection to be established.
+netplay.h Some global netplay definitions.
+netrcv.{c,h} Processes incoming packets. Does know about the protocol
+ and will do consistency checking.
+ Does not directly manipulate the game state.
+netsend.{c,h} Enqueues all sorts of packets for sending.
+ Does not know about the protocol and hence will do
+ no checks for protocol sanity.
+netstate.{c,h} Definitions of the states of a network connection.
+notify.{c,h} Routines for notifying a remote side of local changes.
+ Knows about the protocal and has assert()s to
+ check for local consistency.
+packet.{c,h} Definition and creation of packets. Create functions
+ should only be called from netsend.c.
+packethandlers.{c,h} Routines for processing each type of incoming packet.
+packetq.{c,h} Manages the packet queue.
+packetsenders.{c,h} Creates and sends/queues packets.
+
+In netplay/proto/:
+npconfirm.{c,h} Functions for handing the 'confirmation' protocol.
+ready.{c,h} Functions for handling the 'ready' protocol.
+reset.{c,h} Functions for handling the 'reset' protocol.
+
+
+
+
+
+TODO:
+Division:
+- files that interface with sockets and knows nothing of the game
+ (these are in libs/network)
+- files that know of sockets and the game but don't directly interface
+ with either
+- files that interface with the game and know nothing of sockets
+
diff --git a/src/uqm/supermelee/netplay/Makeinfo b/src/uqm/supermelee/netplay/Makeinfo
new file mode 100644
index 0000000..ff31011
--- /dev/null
+++ b/src/uqm/supermelee/netplay/Makeinfo
@@ -0,0 +1,4 @@
+uqm_SUBDIRS="proto"
+uqm_CFILES="checkbuf.c checksum.c crc.c netconnection.c netinput.c netmelee.c netmisc.c netoptions.c netrcv.c netsend.c netstate.c notify.c notifyall.c packet.c packethandlers.c packetsenders.c packetq.c"
+uqm_HFILES="checkbuf.h checksum.h crc.h netconnection.h netinput.h netmelee.h netmisc.h netoptions.h netplay.h netrcv.h netsend.h netstate.h notifyall.h notify.h packet.h packethandlers.h packetq.h packetsenders.h"
+
diff --git a/src/uqm/supermelee/netplay/checkbuf.c b/src/uqm/supermelee/netplay/checkbuf.c
new file mode 100644
index 0000000..e9c5a32
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checkbuf.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+
+#include "netplay.h"
+#include "checkbuf.h"
+#include "libs/log.h"
+
+#include "../../battle.h"
+ // for battleFrameCount
+
+
+#include <errno.h>
+#include <stdlib.h>
+
+
+
+static inline BattleFrameCounter
+ChecksumBuffer_getCurrentFrameNr(void) {
+ return battleFrameCount;
+}
+
+void
+ChecksumBuffer_init(ChecksumBuffer *cb, size_t delay, size_t interval) {
+ // The input buffer lags BattleInput_inputDelay frames behind,
+ // but only every interval frames will there be a checksum to be
+ // checked.
+
+ // Checksums will be checked when 'frameNr % interval == 0'.
+ // (and frameNr is zero-based).
+ // The checksum of frame n will be processed in frame 'n + delay'.
+
+ // In the worst case, side 1 processes frames 'n' through 'n + delay - 1',
+ // then blocks in frame 'n + delay' (after sending a checksum, if that's
+ // pertinent for that frame).
+ // Then side 2 receives all this input and these checksums, and
+ // progresses to 'delay' frames after the last frame the received input
+ // originated from, and blocks in the frame after it (after sending a
+ // checksum, if that's pertinent for the frame).
+ // So it sent input and checksums for frames 'n' through
+ // 'n + delay + delay + 1'.
+ // The input and checksums for these '2*delay + 2' frames are still
+ // unhandled by side 1, so it needs buffer space for this.
+ // With checksums only sent every interval frames, the buffer space
+ // needed will be 'roundUp(2*delay + 2)' spaces.
+
+ size_t bufSize = ((2 * delay + 2) + (interval - 1)) / interval;
+
+ {
+#ifdef NETPLAY_DEBUG
+ size_t i;
+#endif
+
+ cb->checksums = malloc(bufSize * sizeof (ChecksumEntry));
+ cb->maxSize = bufSize;
+ cb->interval = interval;
+
+#ifdef NETPLAY_DEBUG
+ for (i = 0; i < bufSize; i++) {
+ cb->checksums[i].checksum = 0;
+ cb->checksums[i].frameNr = (BattleFrameCounter) -1;
+ }
+#endif
+ }
+}
+
+void
+ChecksumBuffer_uninit(ChecksumBuffer *cb) {
+ if (cb->checksums != NULL) {
+ free(cb->checksums);
+ cb->checksums = NULL;
+ }
+}
+
+// Returns the entry that would be used for the checksum for the specified
+// frame. Whether the entry is actually valid is not checked.
+static ChecksumEntry *
+ChecksumBuffer_getChecksumEntry(ChecksumBuffer *cb,
+ BattleFrameCounter frameNr) {
+ size_t index;
+ ChecksumEntry *entry;
+
+ assert(frameNr % cb->interval == 0);
+ // We only record checksums exactly every 'interval' frames.
+
+ index = (frameNr / cb->interval) % cb->maxSize;
+ entry = &cb->checksums[index];
+
+ return entry;
+}
+
+bool
+ChecksumBuffer_addChecksum(ChecksumBuffer *cb, BattleFrameCounter frameNr,
+ Checksum checksum) {
+ ChecksumEntry *entry;
+
+ assert(frameNr % cb->interval == 0);
+
+ entry = ChecksumBuffer_getChecksumEntry(cb, frameNr);
+
+#ifdef NETPLAY_DEBUG
+ entry->frameNr = frameNr;
+#endif
+ entry->checksum = checksum;
+ return true;
+}
+
+// Pre: frameNr is within the range of the checksums stored in cb.
+bool
+ChecksumBuffer_getChecksum(ChecksumBuffer *cb, BattleFrameCounter frameNr,
+ Checksum *result) {
+ ChecksumEntry *entry;
+
+ entry = ChecksumBuffer_getChecksumEntry(cb, frameNr);
+
+#ifdef NETPLAY_DEBUG
+ if (frameNr != entry->frameNr) {
+ log_add(log_Error, "Checksum buffer entry for requested frame %u "
+ "(still?) contains a checksum for frame %u.\n",
+ frameNr, entry->frameNr);
+ return false;
+ }
+#endif
+
+ *result = entry->checksum;
+ return true;
+}
+
diff --git a/src/uqm/supermelee/netplay/checkbuf.h b/src/uqm/supermelee/netplay/checkbuf.h
new file mode 100644
index 0000000..f609448
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checkbuf.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_CHECKBUF_H_
+#define UQM_SUPERMELEE_NETPLAY_CHECKBUF_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct ChecksumEntry ChecksumEntry;
+typedef struct ChecksumBuffer ChecksumBuffer;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "../../battle.h"
+ // for BattleFrameCounter
+#include "checksum.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+struct ChecksumEntry {
+#ifdef NETPLAY_DEBUG
+ BattleFrameCounter frameNr;
+ // The number of the frame this checksum originated from.
+ // If the checksumming code is working correctly, the checksum
+ // can only come from one frame, so this value is not needed
+ // for normal operation.
+ // Its only use is to detect some cases where checksumming code
+ // is *not* working correctly.
+#endif
+ Checksum checksum;
+};
+
+struct ChecksumBuffer {
+ ChecksumEntry *checksums;
+ // Cyclic buffer. if 'size' > 0, then 'first' is an index to
+ // the first used entry, 'size' is the number of used
+ // entries in the buffer, and (first + size) % maxSize is the
+ // index to just past the end of the buffer.
+ size_t maxSize;
+
+ size_t interval;
+};
+
+void ChecksumBuffer_init(ChecksumBuffer *cb, size_t delay, size_t interval);
+void ChecksumBuffer_uninit(ChecksumBuffer *cb);
+bool ChecksumBuffer_addChecksum(ChecksumBuffer *cb,
+ BattleFrameCounter frameNr, Checksum checksum);
+bool ChecksumBuffer_getChecksum(ChecksumBuffer *cb,
+ BattleFrameCounter frameNr, Checksum *result);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_CHECKBUF_H_ */
diff --git a/src/uqm/supermelee/netplay/checksum.c b/src/uqm/supermelee/netplay/checksum.c
new file mode 100644
index 0000000..5d687f0
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checksum.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef NETPLAY
+
+#include "checksum.h"
+#include "netoptions.h"
+
+#ifdef NETPLAY_CHECKSUM
+
+#include "checkbuf.h"
+#include "crc.h"
+ // for DUMP_CRC_OPS
+#include "netconnection.h"
+#include "netmelee.h"
+#include "libs/log.h"
+#include "libs/mathlib.h"
+
+ChecksumBuffer localChecksumBuffer;
+
+void
+crc_processEXTENT(crc_State *state, const EXTENT *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processEXTENT().");
+#endif
+ crc_processCOORD(state, val->width);
+ crc_processCOORD(state, val->height);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processEXTENT().");
+#endif
+}
+
+void
+crc_processVELOCITY_DESC(crc_State *state, const VELOCITY_DESC *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processVELOCITY_DESC().");
+#endif
+ crc_processCOUNT(state, val->TravelAngle);
+ crc_processEXTENT(state, &val->vector);
+ crc_processEXTENT(state, &val->fract);
+ crc_processEXTENT(state, &val->error);
+ crc_processEXTENT(state, &val->incr);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processVELOCITY_DESC().");
+#endif
+}
+
+void
+crc_processPOINT(crc_State *state, const POINT *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processPOINT().");
+#endif
+ crc_processCOORD(state, val->x);
+ crc_processCOORD(state, val->y);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processPOINT().");
+#endif
+}
+
+#if 0
+void
+crc_processSTAMP(crc_State *state, const STAMP *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processSTAMP().");
+#endif
+ crc_processPOINT(state, val->origin);
+ crc_processFRAME(state, val->frame);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processSTAMP().");
+#endif
+}
+
+void
+crc_processINTERSECT_CONTROL(crc_State *state, const INTERSECT_CONTROL *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processINTERSECT_CONTROL().");
+#endif
+ crc_processTIME_VALUE(state, val->last_time_val);
+ crc_processPOINT(state, &val->EndPoint);
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processINTERSECT_CONTROL().");
+#endif
+}
+#endif
+
+void
+crc_processSTATE(crc_State *state, const STATE *val) {
+ crc_processPOINT(state, &val->location);
+}
+
+void
+crc_processELEMENT(crc_State *state, const ELEMENT *val) {
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processELEMENT().");
+#endif
+ if (val->state_flags & BACKGROUND_OBJECT) {
+ // The element never influences the state of other elements,
+ // and is to be excluded from checksums.
+#ifdef DUMP_CRC_OPS
+ crc_log(" BACKGROUND_OBJECT element omited");
+#endif
+ } else {
+ crc_processELEMENT_FLAGS(state, val->state_flags);
+ crc_processCOUNT(state, val->life_span);
+ crc_processCOUNT(state, val->crew_level);
+ crc_processBYTE(state, val->mass_points);
+ crc_processBYTE(state, val->turn_wait);
+ crc_processBYTE(state, val->thrust_wait);
+ crc_processVELOCITY_DESC(state, &val->velocity);
+ crc_processSTATE(state, &val->current);
+ crc_processSTATE(state, &val->next);
+ }
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processELEMENT().");
+#endif
+}
+
+void
+crc_processDispQueue(crc_State *state) {
+ HELEMENT element;
+ HELEMENT nextElement;
+
+#ifdef DUMP_CRC_OPS
+ size_t i = 0;
+ crc_log("START crc_processDispQueue().");
+#endif
+ for (element = GetHeadElement(); element != 0; element = nextElement) {
+ ELEMENT *elementPtr;
+
+#ifdef DUMP_CRC_OPS
+ crc_log("===== disp_q[%d]:", i);
+#endif
+ LockElement(element, &elementPtr);
+
+ crc_processELEMENT(state, elementPtr);
+
+ nextElement = GetSuccElement(elementPtr);
+ UnlockElement(element);
+#ifdef DUMP_CRC_OPS
+ i++;
+#endif
+ }
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processDispQueue().");
+#endif
+}
+
+void
+crc_processRNG(crc_State *state) {
+ DWORD seed;
+
+#ifdef DUMP_CRC_OPS
+ crc_log("START crc_processRNG().");
+#endif
+
+ seed = TFB_SeedRandom(0);
+ // This modifies the seed too.
+ crc_processDWORD(state, seed);
+ TFB_SeedRandom(seed);
+ // Restore the old seed.
+
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processRNG().");
+#endif
+}
+
+void
+crc_processState(crc_State *state) {
+#ifdef DUMP_CRC_OPS
+ crc_log("--------------------\n"
+ "START crc_processState() (frame %u).", battleFrameCount);
+#endif
+
+ crc_processRNG(state);
+ crc_processDispQueue(state);
+
+#ifdef DUMP_CRC_OPS
+ crc_log("END crc_processState() (frame %u).",
+ battleFrameCount);
+#endif
+}
+
+void
+initChecksumBuffers(void) {
+ size_t player;
+
+ for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ ChecksumBuffer *cb;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ cb = NetConnection_getChecksumBuffer(conn);
+ ChecksumBuffer_init(cb, getBattleInputDelay(),
+ NETPLAY_CHECKSUM_INTERVAL);
+ }
+
+ ChecksumBuffer_init(&localChecksumBuffer, getBattleInputDelay(),
+ NETPLAY_CHECKSUM_INTERVAL);
+}
+
+void
+uninitChecksumBuffers(void)
+{
+ size_t player;
+
+ for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ ChecksumBuffer *cb;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ cb = NetConnection_getChecksumBuffer(conn);
+
+ ChecksumBuffer_uninit(cb);
+ }
+
+ ChecksumBuffer_uninit(&localChecksumBuffer);
+}
+
+void
+addLocalChecksum(BattleFrameCounter frameNr, Checksum checksum) {
+ assert(frameNr == battleFrameCount);
+
+ ChecksumBuffer_addChecksum(&localChecksumBuffer, frameNr, checksum);
+}
+
+void
+addRemoteChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum) {
+ ChecksumBuffer *cb;
+
+ assert(frameNr <= battleFrameCount + getBattleInputDelay() + 1);
+ assert(frameNr + getBattleInputDelay() >= battleFrameCount);
+
+ cb = NetConnection_getChecksumBuffer(conn);
+ ChecksumBuffer_addChecksum(cb, frameNr, checksum);
+}
+
+bool
+verifyChecksums(BattleFrameCounter frameNr) {
+ Checksum localChecksum;
+ size_t player;
+
+ if (!ChecksumBuffer_getChecksum(&localChecksumBuffer, frameNr,
+ &localChecksum)) {
+ // Right now, we require that a checksum is present.
+ // If/when we move to UDP, and packets may get lost, we may prefer
+ // not to do any checks in this case.
+ return false;
+ }
+
+ for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ ChecksumBuffer *cb;
+ Checksum remoteChecksum;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ cb = NetConnection_getChecksumBuffer(conn);
+
+ if (!ChecksumBuffer_getChecksum(cb, frameNr, &remoteChecksum))
+ return false;
+
+ if (localChecksum != remoteChecksum) {
+ log_add(log_Error, "Network connections have gone out of "
+ "sync.\n");
+ return false;
+ }
+ }
+ return true;
+}
+
+
+#endif /* NETPLAY_CHECKSUM */
+
+#endif /* NETPLAY */
+
diff --git a/src/uqm/supermelee/netplay/checksum.h b/src/uqm/supermelee/netplay/checksum.h
new file mode 100644
index 0000000..cfb48d6
--- /dev/null
+++ b/src/uqm/supermelee/netplay/checksum.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_CHECKSUM_H_
+#define UQM_SUPERMELEE_NETPLAY_CHECKSUM_H_
+
+
+#include "types.h"
+
+typedef uint32 Checksum;
+
+
+#include "netplay.h"
+#include "crc.h"
+
+#include "../../element.h"
+#include "libs/gfxlib.h"
+
+#include "netconnection.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+static inline void
+crc_processELEMENT_FLAGS(crc_State *state, ELEMENT_FLAGS val) {
+ crc_processUint16(state, (uint16) val);
+}
+
+static inline void
+crc_processCOUNT(crc_State *state, COUNT val) {
+ crc_processUint16(state, (uint16) val);
+}
+
+static inline void
+crc_processBYTE(crc_State *state, BYTE val) {
+ crc_processUint8(state, (uint8) val);
+}
+
+static inline void
+crc_processDWORD(crc_State *state, DWORD val) {
+ crc_processUint32(state, (uint32) val);
+}
+
+static inline void
+crc_processCOORD(crc_State *state, COORD val) {
+ crc_processUint16(state, (uint16) val);
+}
+
+#if 0
+static inline void
+crc_processTIME_VALUE(crc_State *state, const TIME_VALUE val) {
+ crc_processUint16(state, (uint16) val);
+}
+#endif
+
+void crc_processEXTENT(crc_State *state, const EXTENT *val);
+void crc_processVELOCITY_DESC(crc_State *state, const VELOCITY_DESC *val);
+void crc_processPOINT(crc_State *state, const POINT *val);
+#if 0
+void crc_processSTAMP(crc_State *state, const STAMP *val);
+void crc_processINTERSECT_CONTROL(crc_State *state,
+ const INTERSECT_CONTROL *val);
+#endif
+void crc_processSTATE(crc_State *state, const STATE *val);
+void crc_processELEMENT(crc_State *state, const ELEMENT *val);
+void crc_processDispQueue(crc_State *state);
+void crc_processRNG(crc_State *state);
+void crc_processState(crc_State *state);
+
+
+void initChecksumBuffers(void);
+void uninitChecksumBuffers(void);
+void addLocalChecksum(BattleFrameCounter frameNr, Checksum checksum);
+void addRemoteChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum);
+bool verifyChecksums(BattleFrameCounter frameNr);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_CHECKSUM_H_ */
diff --git a/src/uqm/supermelee/netplay/crc.c b/src/uqm/supermelee/netplay/crc.c
new file mode 100644
index 0000000..677b36f
--- /dev/null
+++ b/src/uqm/supermelee/netplay/crc.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netplay.h"
+ // For DUMP_CRC_OPS
+
+#include "crc.h"
+
+#ifdef DUMP_CRC_OPS
+# include "libs/log.h"
+#endif
+
+
+// CRC table for Polynomial 0x04c11db7 (0xedb88320 reversed)
+uint32 crcTable[256] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+void
+crc_init(crc_State *state) {
+ state->crc = 0xffffffff;
+}
+
+void
+crc_processBytes(crc_State *state, uint8 *buf, size_t bufLen) {
+ uint8 *end = buf + bufLen;
+ uint32 newCrc = state->crc;
+
+ while (buf < end)
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ *buf) & 0xff];
+
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processBytes(%08x, [%zu bytes]) --> %08x.",
+ state->crc, bufLen, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+void
+crc_processUint8(crc_State *state, uint8 val) {
+ uint32 newCrc = state->crc;
+
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ val) & 0xff];
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processUint8(%08x, %02x) --> %08x.",
+ state->crc, (int) val, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+void
+crc_processUint16(crc_State *state, uint16 val) {
+ uint32 newCrc = state->crc;
+
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val >> 8) ) & 0xff];
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processUint16(%08x, %04x) --> %08x.",
+ state->crc, (int) val, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+void
+crc_processUint32(crc_State *state, uint32 val) {
+ uint32 newCrc = state->crc;
+
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 8) & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 16) & 0xff)) & 0xff];
+ newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 24) )) & 0xff];
+
+#ifdef DUMP_CRC_OPS
+ crc_log("crc_processUint32(%08x, %08x) --> %08x.",
+ state->crc, (int) val, newCrc);
+#endif
+ state->crc = newCrc;
+}
+
+uint32
+crc_finish(const crc_State *state) {
+ return ~state->crc;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/crc.h b/src/uqm/supermelee/netplay/crc.h
new file mode 100644
index 0000000..1744d11
--- /dev/null
+++ b/src/uqm/supermelee/netplay/crc.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_CRC_H_
+#define UQM_SUPERMELEE_NETPLAY_CRC_H_
+
+typedef struct crc_State crc_State;
+
+#include "types.h"
+
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct crc_State {
+ uint32 crc;
+};
+
+void crc_init(crc_State *state);
+void crc_processBytes(crc_State *state, uint8 *buf, size_t bufLen);
+void crc_processUint8(crc_State *state, uint8 val);
+void crc_processUint16(crc_State *state, uint16 val);
+void crc_processUint32(crc_State *state, uint32 val);
+uint32 crc_finish(const crc_State *state);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef DUMP_CRC_OPS
+#include "netconnection.h"
+ // for netplayDebugFile
+//#define crc_log(...) log_add (logDebug, __VA_ARGS__)
+#define crc_log(...) if (netplayDebugFile != NULL) \
+ { \
+ uio_fprintf (netplayDebugFile, __VA_ARGS__); \
+ uio_putc ('\n', netplayDebugFile); \
+ } else \
+ (void) 0
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_CRC_H_ */
+
diff --git a/src/uqm/supermelee/netplay/nc_connect.ci b/src/uqm/supermelee/netplay/nc_connect.ci
new file mode 100644
index 0000000..6c67fed
--- /dev/null
+++ b/src/uqm/supermelee/netplay/nc_connect.ci
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// This file is part of netconnection.c, from where it is #included.
+
+static inline ConnectStateData *ConnectStateData_alloc(void);
+static inline ConnectStateData *ConnectStateData_new(void);
+static inline void ConnectStateData_free(ConnectStateData *connectStateData);
+static void ConnectStateData_delete(ConnectStateData *connectStateData);
+
+static int NetConnection_go(NetConnection *conn);
+static ListenState *NetConnection_serverGo(NetConnection *conn);
+static ConnectState *NetConnection_clientGo(NetConnection *conn);
+static void NetConnection_connected(NetConnection *conn);
+static void NetConnection_connectedServerCallback(ListenState *listenState,
+ NetDescriptor *listenNd, NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen);
+static void NetConnection_connectedClientCallback(ConnectState *connectState,
+ NetDescriptor *newNd, const struct sockaddr *addr, SOCKLEN_T addrLen);
+static void NetConnection_connectedServerErrorCallback(
+ ListenState *listenState, const ListenError *listenError);
+static void NetConnection_connectedClientErrorCallback(
+ ConnectState *connectState, const ConnectError *connectError);
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+static inline ConnectStateData *
+ConnectStateData_alloc(void) {
+ return (ConnectStateData *) malloc(sizeof (ConnectStateData));
+}
+
+static inline ConnectStateData *
+ConnectStateData_new(void) {
+ ConnectStateData *connectStateData = ConnectStateData_alloc();
+ connectStateData->releaseFunction =
+ (NetConnectionStateData_ReleaseFunction) ConnectStateData_delete;
+ return connectStateData;
+}
+
+static inline void
+ConnectStateData_free(ConnectStateData *connectStateData) {
+ free(connectStateData);
+}
+
+static void
+ConnectStateData_delete(ConnectStateData *connectStateData) {
+ if (connectStateData->isServer) {
+ ListenState *listenState = connectStateData->state.listenState;
+ ListenState_close(listenState);
+ } else {
+ ConnectState *connectState = connectStateData->state.connectState;
+ ConnectState_close(connectState);
+ }
+ ConnectStateData_free(connectStateData);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+static int
+NetConnection_go(NetConnection *conn) {
+ ConnectStateData *connectStateData;
+
+ if (NetConnection_isConnected(conn))
+ return 0;
+
+ if (conn->options->isServer) {
+ ListenState *listenState;
+ listenState = NetConnection_serverGo(conn);
+ if (listenState == NULL) {
+ // errno is set
+ return -1;
+ }
+ connectStateData = ConnectStateData_new();
+ connectStateData->state.listenState = listenState;
+ } else {
+ ConnectState *connectState;
+ connectState = NetConnection_clientGo(conn);
+ if (connectState == NULL) {
+ // errno is set
+ return -1;
+ }
+ connectStateData = ConnectStateData_new();
+ connectStateData->state.connectState = connectState;
+ }
+ connectStateData->isServer = conn->options->isServer;
+
+ NetConnection_setStateData(conn, (void *) connectStateData);
+ return 0;
+}
+
+static ListenState *
+NetConnection_serverGo(NetConnection *conn) {
+ ListenFlags listenFlags;
+ ListenState *result;
+
+ assert(conn->state == NetState_unconnected);
+ assert(conn->options->isServer);
+
+ NetConnection_setState(conn, NetState_connecting);
+
+ memset (&listenFlags, 0, sizeof listenFlags);
+ listenFlags.familyDemand =
+#if NETPLAY == NETPLAY_IPV4
+ PF_inet;
+#else
+ PF_unspec;
+#endif
+ listenFlags.familyPrefer = PF_unspec;
+ listenFlags.backlog = NETPLAY_LISTEN_BACKLOG;
+
+ result = listenPort(conn->options->port, IPProto_tcp, &listenFlags,
+ NetConnection_connectedServerCallback,
+ NetConnection_connectedServerErrorCallback, (void *) conn);
+
+ return result;
+}
+
+static ConnectState *
+NetConnection_clientGo(NetConnection *conn) {
+ ConnectFlags connectFlags;
+ ConnectState *result;
+
+ assert(conn->state == NetState_unconnected);
+ assert(!conn->options->isServer);
+
+ NetConnection_setState(conn, NetState_connecting);
+
+ memset (&connectFlags, 0, sizeof connectFlags);
+ connectFlags.familyDemand =
+#if NETPLAY == NETPLAY_IPV4
+ PF_inet;
+#else
+ PF_unspec;
+#endif
+ connectFlags.familyPrefer = PF_unspec;
+ connectFlags.timeout = NETPLAY_CONNECTTIMEOUT;
+ connectFlags.retryDelayMs = NETPLAY_RETRYDELAY;
+
+ result = connectHostByName(conn->options->host, conn->options->port,
+ IPProto_tcp, &connectFlags, NetConnection_connectedClientCallback,
+ NetConnection_connectedClientErrorCallback, (void *) conn);
+
+ return result;
+}
+
+// Called when an incoming connection has been established.
+// The caller gives up ownership of newNd
+static void
+NetConnection_connectedServerCallback(ListenState *listenState,
+ NetDescriptor *listenNd, NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen) {
+ NetConnection *conn =
+ (NetConnection *) ListenState_getExtra(listenState);
+
+ assert(conn->nd == NULL);
+
+ conn->nd = newNd;
+ // No incRef(); the caller gives up ownership.
+ NetDescriptor_setExtra(conn->nd, (void *) conn);
+
+ (void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd));
+ // Ignore errors; it's not a big deal. In debug mode, a message
+ // will already have been printed from the function itself.
+
+ conn->stateFlags.discriminant = true;
+
+ NetConnection_connected(conn);
+ (void) listenNd;
+ (void) addr;
+ (void) addrLen;
+}
+
+// Called when an outgoing connection has been established.
+// The caller gives up ownership of newNd
+static void
+NetConnection_connectedClientCallback(ConnectState *connectState,
+ NetDescriptor *newNd,
+ const struct sockaddr *addr, SOCKLEN_T addrLen) {
+ NetConnection *conn =
+ (NetConnection *) ConnectState_getExtra(connectState);
+
+ assert(conn->nd == NULL);
+
+ conn->nd = newNd;
+ // No incRef(); the caller gives up ownership.
+ NetDescriptor_setExtra(conn->nd, (void *) conn);
+
+ (void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd));
+ // Ignore errors; it's not a big deal. In debug mode, a message
+ // will already have been printed from the function itself.
+
+ conn->stateFlags.discriminant = false;
+
+ NetConnection_connected(conn);
+ (void) addr;
+ (void) addrLen;
+}
+
+// Called when a connection has been established.
+static void
+NetConnection_connected(NetConnection *conn) {
+ ConnectStateData *connectStateData;
+
+ conn->stateFlags.connected = true;
+ NetConnection_setState(conn, NetState_init);
+
+ connectStateData = (ConnectStateData *) NetConnection_getStateData(conn);
+ ConnectStateData_delete(connectStateData);
+ NetConnection_setStateData(conn, NULL);
+
+ NetDescriptor_setReadCallback(conn->nd, dataReadyCallback);
+ NetDescriptor_setCloseCallback(conn->nd, closeCallback);
+
+ (*conn->connectCallback)(conn);
+}
+
+static void
+NetConnection_connectedServerErrorCallback(ListenState *listenState,
+ const ListenError *listenError) {
+ NetConnection *conn =
+ (NetConnection *) ListenState_getExtra(listenState);
+ NetConnectionError error;
+
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = true;
+ ListenState_close(listenState);
+ NetConnection_setState(conn, NetState_unconnected);
+
+ {
+ ConnectStateData *connectStateData;
+ connectStateData =
+ (ConnectStateData *) NetConnection_getStateData(conn);
+ ConnectStateData_free(connectStateData);
+ NetConnection_setStateData(conn, NULL);
+ }
+
+ if (conn->errorCallback != NULL) {
+ error.state = NetState_connecting;
+ error.err = listenError->err;
+ error.extra.listenError = listenError;
+ //NetConnection_incRef(conn);
+ (*conn->errorCallback)(conn, &error);
+ //NetConnection_decRef(conn);
+ }
+
+ NetConnection_close(conn);
+}
+
+static void
+NetConnection_connectedClientErrorCallback(ConnectState *connectState,
+ const ConnectError *connectError) {
+ NetConnection *conn =
+ (NetConnection *) ConnectState_getExtra(connectState);
+ NetConnectionError error;
+
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = true;
+ ConnectState_close(connectState);
+ NetConnection_setState(conn, NetState_unconnected);
+
+ {
+ ConnectStateData *connectStateData;
+ connectStateData =
+ (ConnectStateData *) NetConnection_getStateData(conn);
+ ConnectStateData_free(connectStateData);
+ NetConnection_setStateData(conn, NULL);
+ }
+
+ if (conn->errorCallback != NULL) {
+ error.state = NetState_connecting;
+ error.err = connectError->err;
+ error.extra.connectError = connectError;
+ //NetConnection_incRef(conn);
+ (*conn->errorCallback)(conn, &error);
+ //NetConnection_decRef(conn);
+ }
+
+ NetConnection_close(conn);
+}
+
+
diff --git a/src/uqm/supermelee/netplay/netconnection.c b/src/uqm/supermelee/netplay/netconnection.c
new file mode 100644
index 0000000..48ab46b
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netconnection.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "netconnection.h"
+
+#include "netrcv.h"
+
+#if defined(DEBUG) || defined(NETPLAY_DEBUG)
+# include "libs/log.h"
+#endif
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+# include "options.h"
+ // for configDir
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+# include <errno.h>
+# include <time.h>
+#endif
+
+
+static void closeCallback(NetDescriptor *nd);
+static void NetConnection_doClose(NetConnection *conn);
+
+
+#include "nc_connect.ci"
+
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+uio_Stream *netplayDebugFile;
+#endif
+
+// Used as initial value for Agreement structures, by structure assignment.
+const Agreement Agreement_nothingAgreed;
+
+
+// The NetConnection keeps a pointer to the passed NetplayPeerOptions;
+// do not free it as long as the NetConnection exists.
+NetConnection *
+NetConnection_open(int player, const NetplayPeerOptions *options,
+ NetConnection_ConnectCallback connectCallback,
+ NetConnection_CloseCallback closeCallback,
+ NetConnection_ErrorCallback errorCallback,
+ NetConnection_DeleteCallback deleteCallback, void *extra) {
+ NetConnection *conn;
+
+ conn = malloc(sizeof (NetConnection));
+
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+ {
+ char dumpFileName[PATH_MAX];
+ time_t now;
+ struct tm *nowTm;
+ size_t strftimeResult;
+
+ now = time (NULL);
+ if (now == (time_t) -1) {
+ log_add (log_Fatal, "time() failed: %s.", strerror (errno));
+ abort ();
+ }
+
+ nowTm = localtime(&now);
+ // XXX: I would like to use localtime_r(), but it isn't very
+ // portable (yet), and adding a check for it to the build.sh script
+ // is not worth the effort for a debugging function right now.
+
+ strftimeResult = strftime (dumpFileName, sizeof dumpFileName,
+ "debug/netlog-%Y%m%d%H%M%S", nowTm);
+ if (strftimeResult == 0) {
+ log_add (log_Fatal, "strftime() failed: %s.", strerror (errno));
+ abort ();
+ }
+
+ // The user needs to create the debug/ dir manually. If there
+ // is no debug/ dir, no log will be created.
+ conn->debugFile = uio_fopen (configDir, dumpFileName, "wt");
+ if (conn->debugFile == NULL) {
+ log_add (log_Debug, "Not creating a netplay debug log for "
+ "player %d.", player);
+ } else {
+ log_add (log_Debug, "Creating netplay debug log '%s' for "
+ "player %d.", dumpFileName, player);
+ if (netplayDebugFile == NULL) {
+ // Debug info relating to no specific network connection
+ // is sent to the first opened one.
+ netplayDebugFile = conn->debugFile;
+ }
+ }
+ }
+#endif
+
+ conn->nd = NULL;
+ conn->player = player;
+ conn->state = NetState_unconnected;
+ conn->options = options;
+ conn->extra = extra;
+ PacketQueue_init(&conn->queue);
+
+ conn->connectCallback = connectCallback;
+ conn->closeCallback = closeCallback;
+ conn->errorCallback = errorCallback;
+ conn->deleteCallback = deleteCallback;
+ conn->readyCallback = NULL;
+ conn->readyCallbackArg = NULL;
+ conn->resetCallback = NULL;
+ conn->resetCallbackArg = NULL;
+
+ conn->readBuf = malloc(NETPLAY_READBUFSIZE);
+ conn->readEnd = conn->readBuf;
+
+ conn->stateData = NULL;
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = false;
+ conn->stateFlags.discriminant = false;
+ conn->stateFlags.handshake.localOk = false;
+ conn->stateFlags.handshake.remoteOk = false;
+ conn->stateFlags.handshake.canceling = false;
+ conn->stateFlags.ready.localReady = false;
+ conn->stateFlags.ready.remoteReady = false;
+ conn->stateFlags.reset.localReset = false;
+ conn->stateFlags.reset.remoteReset = false;
+ conn->stateFlags.agreement = Agreement_nothingAgreed;
+ conn->stateFlags.inputDelay = 0;
+#ifdef NETPLAY_CHECKSUM
+ conn->stateFlags.checksumInterval = NETPLAY_CHECKSUM_INTERVAL;
+#endif
+
+#ifdef NETPLAY_STATISTICS
+ {
+ size_t i;
+
+ conn->statistics.packetsReceived = 0;
+ conn->statistics.packetsSent = 0;
+ for (i = 0; i < PACKET_NUM; i++)
+ {
+ conn->statistics.packetTypeReceived[i] = 0;
+ conn->statistics.packetTypeSent[i] = 0;
+ }
+ }
+#endif
+
+ NetConnection_go(conn);
+
+ return conn;
+}
+
+static void
+NetConnection_doDeleteCallback(NetConnection *conn) {
+ if (conn->deleteCallback != NULL) {
+ //NetConnection_incRef(conn);
+ conn->deleteCallback(conn);
+ //NetConnection_decRef(conn);
+ }
+}
+
+static void
+NetConnection_delete(NetConnection *conn) {
+ NetConnection_doDeleteCallback(conn);
+ if (conn->stateData != NULL) {
+ NetConnectionStateData_release(conn->stateData);
+ conn->stateData = NULL;
+ }
+ free(conn->readBuf);
+ PacketQueue_uninit(&conn->queue);
+
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ if (netplayDebugFile == conn->debugFile) {
+ // There may be other network connections, with an open
+ // debug file, but we don't know about that.
+ // The debugging person just has to work around that.
+ netplayDebugFile = NULL;
+ }
+ uio_fclose(conn->debugFile);
+ }
+#endif
+
+ free(conn);
+}
+
+static void
+Netplay_doCloseCallback(NetConnection *conn) {
+ if (conn->closeCallback != NULL) {
+ //NetConnection_incRef(conn);
+ conn->closeCallback(conn);
+ //NetConnection_decRef(conn);
+ }
+}
+
+// Auxiliary function for closing, used by both closeCallback() and
+// NetConnection_close()
+static void
+NetConnection_doClose(NetConnection *conn) {
+ conn->stateFlags.connected = false;
+ conn->stateFlags.disconnected = true;
+
+ // First the callback, so that it can still use the information
+ // of what is the current state, and the stateData:
+ Netplay_doCloseCallback(conn);
+
+ NetConnection_setState(conn, NetState_unconnected);
+}
+
+// Called when the NetDescriptor is shut down.
+static void
+closeCallback(NetDescriptor *nd) {
+ NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
+ if (conn == NULL)
+ return;
+ conn->nd = NULL;
+ NetConnection_doClose(conn);
+}
+
+// Close and release a NetConnection.
+void
+NetConnection_close(NetConnection *conn) {
+ if (conn->nd != NULL) {
+ NetDescriptor_setCloseCallback(conn->nd, NULL);
+ // We're not interested in the close callback of the
+ // NetDescriptor anymore.
+ NetDescriptor_close(conn->nd);
+ // This would queue the close callback.
+ conn->nd = NULL;
+ }
+ if (!conn->stateFlags.disconnected)
+ NetConnection_doClose(conn);
+ NetConnection_delete(conn);
+}
+
+void
+NetConnection_doErrorCallback(NetConnection *nd, int err) {
+ NetConnectionError error;
+
+ if (nd->errorCallback != NULL) {
+ error.state = nd->state;
+ error.err = err;
+ }
+ (*nd->errorCallback)(nd, &error);
+}
+
+void
+NetConnection_setStateData(NetConnection *conn,
+ NetConnectionStateData *stateData) {
+ conn->stateData = stateData;
+}
+
+NetConnectionStateData *
+NetConnection_getStateData(const NetConnection *conn) {
+ return conn->stateData;
+}
+
+void
+NetConnection_setExtra(NetConnection *conn, void *extra) {
+ conn->extra = extra;
+}
+
+void *
+NetConnection_getExtra(const NetConnection *conn) {
+ return conn->extra;
+}
+
+void
+NetConnection_setReadyCallback(NetConnection *conn,
+ NetConnection_ReadyCallback callback, void *arg) {
+ conn->readyCallback = callback;
+ conn->readyCallbackArg = arg;
+}
+
+NetConnection_ReadyCallback
+NetConnection_getReadyCallback(const NetConnection *conn) {
+ return conn->readyCallback;
+}
+
+void *
+NetConnection_getReadyCallbackArg(const NetConnection *conn) {
+ return conn->readyCallbackArg;
+}
+
+void
+NetConnection_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *arg) {
+ conn->resetCallback = callback;
+ conn->resetCallbackArg = arg;
+}
+
+NetConnection_ResetCallback
+NetConnection_getResetCallback(const NetConnection *conn) {
+ return conn->resetCallback;
+}
+
+void *
+NetConnection_getResetCallbackArg(const NetConnection *conn) {
+ return conn->resetCallbackArg;
+}
+
+void
+NetConnection_setState(NetConnection *conn, NetState state) {
+#ifdef NETPLAY_DEBUG
+ log_add(log_Debug, "NETPLAY: [%d] +/- Connection state changed to: "
+ "%s.\n", conn->player, netStateData[state].name);
+#endif
+#ifdef DEBUG
+ if (state == conn->state) {
+ log_add(log_Warning, "NETPLAY: [%d] Connection state set to %s "
+ "while already in that state.\n",
+ conn->player, netStateData[state].name);
+ }
+#endif
+ conn->state = state;
+}
+
+NetState
+NetConnection_getState(const NetConnection *conn) {
+ return conn->state;
+}
+
+bool
+NetConnection_getDiscriminant(const NetConnection *conn) {
+ return conn->stateFlags.discriminant;
+}
+
+const NetplayPeerOptions *
+NetConnection_getPeerOptions(const NetConnection *conn) {
+ return conn->options;
+}
+
+bool
+NetConnection_isConnected(const NetConnection *conn) {
+ return conn->stateFlags.connected;
+}
+
+int
+NetConnection_getPlayerNr(const NetConnection *conn) {
+ return conn->player;
+}
+
+size_t
+NetConnection_getInputDelay(const NetConnection *conn) {
+ return conn->stateFlags.inputDelay;
+}
+
+#ifdef NETPLAY_CHECKSUM
+ChecksumBuffer *
+NetConnection_getChecksumBuffer(NetConnection *conn) {
+ return &conn->checksumBuffer;
+}
+
+size_t
+NetConnection_getChecksumInterval(const NetConnection *conn) {
+ return conn->stateFlags.checksumInterval;
+}
+#endif /* NETPLAY_CHECKSUM */
+
+#ifdef NETPLAY_STATISTICS
+NetStatistics *
+NetConnection_getStatistics(NetConnection *conn) {
+ return &conn->statistics;
+}
+#endif
+
diff --git a/src/uqm/supermelee/netplay/netconnection.h b/src/uqm/supermelee/netplay/netconnection.h
new file mode 100644
index 0000000..485d3c4
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netconnection.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETCONNECTION_H_
+#define UQM_SUPERMELEE_NETPLAY_NETCONNECTION_H_
+
+#include "netplay.h"
+ // for NETPLAY_STATISTICS
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct NetConnection NetConnection;
+typedef struct NetConnectionError NetConnectionError;
+typedef struct ConnectStateData ConnectStateData;
+#ifdef NETPLAY_STATISTICS
+typedef struct NetStatistics NetStatistics;
+#endif
+
+typedef void (*NetConnection_ConnectCallback)(NetConnection *nd);
+typedef void (*NetConnection_CloseCallback)(NetConnection *nd);
+typedef void (*NetConnection_ErrorCallback)(NetConnection *nd,
+ const NetConnectionError *error);
+typedef void (*NetConnection_DeleteCallback)(NetConnection *nd);
+
+typedef void (*NetConnection_ReadyCallback)(NetConnection *conn, void *arg);
+typedef void (*NetConnection_ResetCallback)(NetConnection *conn, void *arg);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "netstate.h"
+#include "netoptions.h"
+#ifdef NETPLAY_CHECKSUM
+# include "checkbuf.h"
+#endif
+#if defined(NETPLAY_STATISTICS) || defined(NETCONNECTION_INTERNAL)
+# include "packet.h"
+#endif
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+# include "libs/uio.h"
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct NetConnectionError {
+ NetState state;
+ int err;
+ union {
+ const struct ListenError *listenError;
+ const struct ConnectError *connectError;
+ } extra;
+};
+
+#ifdef NETPLAY_STATISTICS
+struct NetStatistics {
+ size_t packetsReceived;
+ size_t packetTypeReceived[PACKET_NUM];
+ size_t packetsSent;
+ size_t packetTypeSent[PACKET_NUM];
+};
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef NETCONNECTION_INTERNAL
+#include "libs/net.h"
+#include "packetq.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct {
+ // For actions that require agreement by both parties.
+ bool localOk : 1; /* Action confirmed by us */
+ bool remoteOk : 1; /* Action confirmed by the remote party */
+ bool canceling : 1; /* Awaiting cancel confirmation */
+} HandShakeFlags;
+
+typedef struct {
+ // For actions that do not require agreement, but for which it
+ // only is relevant that both sides are ready.
+ bool localReady : 1;
+ bool remoteReady : 1;
+} ReadyFlags;
+
+typedef struct {
+ bool localReset : 1;
+ bool remoteReset : 1;
+} ResetFlags;
+
+// Which parameters have we both sides of a connection reached agreement on?
+typedef struct {
+ bool randomSeed : 1;
+} Agreement;
+
+typedef struct {
+ bool connected;
+ /* This NetConnection is connected. */
+ bool disconnected;
+ /* This NetConnection has been disconnected. This implies
+ * !connected. It is only set if the NetConnection was once
+ * connected, but is no longer. */
+ bool discriminant;
+ /* If it is true here, it is false on the remote side
+ * of the same connection. It may be used to break ties.
+ * It is guaranteed not to change during a connection. Undefined
+ * while not connected. */
+ HandShakeFlags handshake;
+ ReadyFlags ready;
+ ResetFlags reset;
+ Agreement agreement;
+ size_t inputDelay;
+ /* Used during negotiation of the actual inputDelay. This
+ * field does NOT necessarilly contain the actual input delay,
+ * which is a property of the game, not of any specific
+ * connection. Use getBattleInputDelay() to get at it. */
+#ifdef NETPLAY_CHECKSUM
+ size_t checksumInterval;
+#endif
+} NetStateFlags;
+
+struct NetConnection {
+ NetDescriptor *nd;
+ int player;
+ // Number of the player for this connection, as it is
+ // known locally. For the other player, it may be
+ // differently.
+ NetState state;
+ NetStateFlags stateFlags;
+
+ NetConnection_ReadyCallback readyCallback;
+ // Called when both sides have indicated that they are ready.
+ // Set by Netplay_localReady().
+ void *readyCallbackArg;
+ // Extra argument for readyCallback().
+ // XXX: when is this cleaned up if a connection is broken?
+
+ NetConnection_ResetCallback resetCallback;
+ // Called when a reset has been signalled and confirmed.
+ // Set by Netplay_localReset().
+ void *resetCallbackArg;
+ // Extra argument for resetCallback().
+ // XXX: when is this cleaned up if a connection is broken?
+
+ const NetplayPeerOptions *options;
+ PacketQueue queue;
+#ifdef NETPLAY_STATISTICS
+ NetStatistics statistics;
+#endif
+#ifdef NETPLAY_CHECKSUM
+ ChecksumBuffer checksumBuffer;
+#endif
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+ uio_Stream *debugFile;
+#endif
+
+ NetConnection_ConnectCallback connectCallback;
+ NetConnection_CloseCallback closeCallback;
+ // Called when the NetConnection becomes disconnected.
+ NetConnection_ErrorCallback errorCallback;
+ NetConnection_DeleteCallback deleteCallback;
+ // Called when the NetConnection is destroyed.
+ uint8 *readBuf;
+ uint8 *readEnd;
+ NetConnectionStateData *stateData;
+ // State dependant information.
+ void *extra;
+};
+
+struct ConnectStateData {
+ NETCONNECTION_STATE_DATA_COMMON
+
+ bool isServer;
+ union {
+ struct ConnectState *connectState;
+ struct ListenState *listenState;
+ } state;
+};
+
+#endif /* NETCONNECTION_INTERNAL */
+
+
+NetConnection *NetConnection_open(int player,
+ const NetplayPeerOptions *options,
+ NetConnection_ConnectCallback connectCallback,
+ NetConnection_CloseCallback closeCallback,
+ NetConnection_ErrorCallback errorCallback,
+ NetConnection_DeleteCallback deleteCallback, void *extra);
+void NetConnection_close(NetConnection *conn);
+bool NetConnection_isConnected(const NetConnection *conn);
+
+void NetConnection_doErrorCallback(NetConnection *nd, int err);
+
+void NetConnection_setStateData(NetConnection *conn,
+ NetConnectionStateData *stateData);
+NetConnectionStateData *NetConnection_getStateData(const NetConnection *conn);
+void NetConnection_setExtra(NetConnection *conn, void *extra);
+void *NetConnection_getExtra(const NetConnection *conn);
+void NetConnection_setState(NetConnection *conn, NetState state);
+NetState NetConnection_getState(const NetConnection *conn);
+bool NetConnection_getDiscriminant(const NetConnection *conn);
+const NetplayPeerOptions *NetConnection_getPeerOptions(
+ const NetConnection *conn);
+int NetConnection_getPlayerNr(const NetConnection *conn);
+size_t NetConnection_getInputDelay(const NetConnection *conn);
+#ifdef NETPLAY_CHECKSUM
+ChecksumBuffer *NetConnection_getChecksumBuffer(NetConnection *conn);
+size_t NetConnection_getChecksumInterval(const NetConnection *conn);
+#endif
+#ifdef NETPLAY_STATISTICS
+NetStatistics *NetConnection_getStatistics(NetConnection *conn);
+#endif
+
+void NetConnection_setReadyCallback(NetConnection *conn,
+ NetConnection_ReadyCallback callback, void *arg);
+NetConnection_ReadyCallback NetConnection_getReadyCallback(
+ const NetConnection *conn);
+void *NetConnection_getReadyCallbackArg(const NetConnection *conn);
+
+void NetConnection_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *arg);
+NetConnection_ResetCallback NetConnection_getResetCallback(
+ const NetConnection *conn);
+void *NetConnection_getResetCallbackArg(const NetConnection *conn);
+
+
+#if defined(NETPLAY_DEBUG) && defined(NETPLAY_DEBUG_FILE)
+extern uio_Stream *netplayDebugFile;
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETCONNECTION_H_ */
+
+
diff --git a/src/uqm/supermelee/netplay/netinput.c b/src/uqm/supermelee/netplay/netinput.c
new file mode 100644
index 0000000..9823deb
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netinput.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+
+#include "netplay.h"
+#include "netinput.h"
+
+#include "../../intel.h"
+ // for NETWORK_CONTROL
+#include "../../setup.h"
+ // For PlayerControl
+#include "libs/log.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+
+static BattleInputBuffer battleInputBuffers[NUM_PLAYERS];
+static size_t BattleInput_inputDelay;
+
+// Call before initBattleInputBuffers()
+void
+setBattleInputDelay(size_t delay) {
+ BattleInput_inputDelay = delay;
+}
+
+size_t
+getBattleInputDelay(void) {
+ return BattleInput_inputDelay;
+}
+
+static void
+BattleInputBuffer_init(BattleInputBuffer *bib, size_t bufSize) {
+ bib->buf = malloc(bufSize * sizeof (BATTLE_INPUT_STATE));
+ bib->maxSize = bufSize;
+ bib->first = 0;
+ bib->size = 0;
+}
+
+static void
+BattleInputBuffer_uninit(BattleInputBuffer *bib) {
+ if (bib->buf != NULL) {
+ free(bib->buf);
+ bib->buf = NULL;
+ }
+ bib->maxSize = 0;
+ bib->first = 0;
+ bib->size = 0;
+}
+
+void
+initBattleInputBuffers(void) {
+ size_t player;
+ int bufSize = BattleInput_inputDelay * 2 + 2;
+
+ // The input of frame n will be processed in frame 'n + delay'.
+ //
+ // In the worst case, side 1 processes frames 'n' through 'n + delay - 1',
+ // then blocks in frame 'n + delay'.
+ // Then side 2 receives all this input, and progresses to 'delay' frames
+ // after the last frame the received input originated from, and blocks
+ // in the frame after it.
+ // So it sent input for frames 'n' through 'n + delay + delay + 1'.
+ // The input for these '2*delay + 2' frames are still
+ // unhandled by side 1, so it needs buffer space for this.
+ //
+ // Initially the buffer is filled with inputDelay zeroes,
+ // so that a party can process at least that much frames.
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleInputBuffer *bib = &battleInputBuffers[player];
+ BattleInputBuffer_init(bib, bufSize);
+
+ {
+ // Initially a party must be able to process at least inputDelay
+ // frames, so we fill the buffer with inputDelay zeros.
+ size_t i;
+ for (i = 0; i < BattleInput_inputDelay; i++)
+ BattleInputBuffer_push(bib, (BATTLE_INPUT_STATE) 0);
+ }
+ }
+}
+
+void
+uninitBattleInputBuffers(void)
+{
+ size_t player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleInputBuffer *bib;
+
+ bib = &battleInputBuffers[player];
+ BattleInputBuffer_uninit(bib);
+ }
+}
+
+// On error, returns false and sets errno.
+bool
+BattleInputBuffer_push(BattleInputBuffer *bib, BATTLE_INPUT_STATE input)
+{
+ size_t next;
+
+ if (bib->size == bib->maxSize) {
+ // No more space.
+ log_add(log_Error, "NETPLAY: battleInputBuffer full.\n");
+ errno = ENOBUFS;
+ return false;
+ }
+
+ next = (bib->first + bib->size) % bib->maxSize;
+ bib->buf[next] = input;
+ bib->size++;
+ return true;
+}
+
+// On error, returns false and sets errno, and *input remains unchanged.
+bool
+BattleInputBuffer_pop(BattleInputBuffer *bib, BATTLE_INPUT_STATE *input)
+{
+ if (bib->size == 0)
+ {
+ // Buffer is empty.
+ errno = EAGAIN;
+ return false;
+ }
+
+ *input = bib->buf[bib->first];
+ bib->first = (bib->first + 1) % bib->maxSize;
+ bib->size--;
+ return true;
+}
+
+BattleInputBuffer *
+getBattleInputBuffer(size_t player) {
+ return &battleInputBuffers[player];
+}
+
+
diff --git a/src/uqm/supermelee/netplay/netinput.h b/src/uqm/supermelee/netplay/netinput.h
new file mode 100644
index 0000000..2afdf02
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netinput.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETINPUT_H_
+#define UQM_SUPERMELEE_NETPLAY_NETINPUT_H_
+
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../init.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+ // for NUM_PLAYERS
+
+typedef struct BattleInputBuffer {
+ BATTLE_INPUT_STATE *buf;
+ // Cyclic buffer. if 'size' > 0, then 'first' is an index to
+ // the first used entry, 'size' is the number of used
+ // entries in the buffer, and (first + size) % maxSize is the
+ // index to just past the end of the buffer.
+ size_t maxSize;
+ size_t first;
+ size_t size;
+} BattleInputBuffer;
+
+void setBattleInputDelay(size_t delay);
+size_t getBattleInputDelay(void);
+void initBattleInputBuffers(void);
+void uninitBattleInputBuffers(void);
+bool BattleInputBuffer_push(BattleInputBuffer *bib,
+ BATTLE_INPUT_STATE input);
+bool BattleInputBuffer_pop(BattleInputBuffer *bib,
+ BATTLE_INPUT_STATE *input);
+
+BattleInputBuffer *getBattleInputBuffer(size_t player);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETINPUT_H_ */
diff --git a/src/uqm/supermelee/netplay/netmelee.c b/src/uqm/supermelee/netplay/netmelee.c
new file mode 100644
index 0000000..e9368fd
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmelee.c
@@ -0,0 +1,740 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+#include "netmelee.h"
+#include "libs/async.h"
+#include "libs/callback.h"
+#include "libs/log.h"
+#include "libs/net.h"
+#include "netinput.h"
+#include "netmisc.h"
+#include "netsend.h"
+#include "notify.h"
+#include "packetq.h"
+#include "proto/npconfirm.h"
+#include "proto/ready.h"
+#include "proto/reset.h"
+
+#include "../../battlecontrols.h"
+ // for NetworkInputContext
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../init.h"
+ // for NUM_PLAYERS
+#include "../../globdata.h"
+ // for GLOBAL
+
+#include <errno.h>
+#include <stdlib.h>
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+NetConnection *netConnections[NUM_PLAYERS];
+size_t numNetConnections;
+
+void
+addNetConnection(NetConnection *conn, int playerNr) {
+ netConnections[playerNr] = conn;
+ numNetConnections++;
+}
+
+void
+removeNetConnection(int playerNr) {
+ netConnections[playerNr] = NULL;
+ numNetConnections--;
+}
+
+size_t
+getNumNetConnections(void) {
+ return numNetConnections;
+}
+
+// If the callback function returns 'false', the function will immediately
+// return with 'false'. Otherwise it will return 'true' after calling
+// the callback function for each connected player.
+bool
+forEachConnectedPlayer(ForEachConnectionCallback callback, void *arg) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ if (!(*callback)(conn, arg))
+ return false;
+ }
+ return true;
+}
+
+void
+closeAllConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+
+ if (conn != NULL)
+ closePlayerNetworkConnection(player);
+ }
+}
+
+void
+closeDisconnectedConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+
+ if (conn != NULL && !NetConnection_isConnected(conn))
+ closePlayerNetworkConnection(player);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+
+struct melee_state *
+NetMelee_getMeleeState(NetConnection *conn) {
+ if (NetConnection_getState(conn) > NetState_connecting) {
+ BattleStateData *battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ return battleStateData->meleeState;
+ } else {
+ return (struct melee_state *) NetConnection_getExtra(conn);
+ }
+}
+
+struct battlestate_struct *
+NetMelee_getBattleState(NetConnection *conn) {
+ if (NetConnection_getState(conn) > NetState_connecting) {
+ BattleStateData *battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ return battleStateData->battleState;
+ } else {
+ return NULL;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static inline void
+netInputAux(uint32 timeoutMs) {
+ NetManager_process(&timeoutMs);
+ // This may cause more packets to be queued, hence the
+ // flushPacketQueues().
+ Async_process();
+ flushPacketQueues();
+ // During the flush, a disconnect may be noticed, which triggers
+ // another callback. It must be handled immediately, before
+ // another flushPacketQueue() can occur, which would not know
+ // that the socket is no longer valid.
+ // TODO: modify the close handling so this order isn't
+ // necessary.
+ Callback_process();
+}
+
+// Check the network connections for input.
+void
+netInput(void) {
+ netInputAux(0);
+}
+
+void
+netInputBlocking(uint32 timeoutMs) {
+ uint32 nextAsyncMs;
+
+ nextAsyncMs = Async_timeBeforeNextMs();
+ if (nextAsyncMs < timeoutMs)
+ timeoutMs = nextAsyncMs;
+
+ netInputAux(timeoutMs);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+// Send along all pending network packets.
+void
+flushPacketQueues(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn;
+ int flushStatus;
+
+ conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ flushStatus = flushPacketQueue(conn);
+ if (flushStatus == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
+ closePlayerNetworkConnection(player);
+ }
+}
+
+void
+confirmConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_confirm(conn);
+ }
+}
+
+void
+cancelConfirmations(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_cancelConfirmation(conn);
+ }
+}
+
+void
+connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_localReady(conn, callback, arg, true);
+ }
+}
+
+bool
+allConnected(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ return false;
+ }
+ return true;
+}
+
+void
+initBattleStateDataConnections(void) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleStateData *battleStateData;
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ battleStateData->endFrameCount = 0;
+ }
+}
+
+void
+setBattleStateConnections(struct battlestate_struct *bs) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ BattleStateData *battleStateData;
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData(conn);
+ battleStateData->battleState = bs;
+ }
+}
+
+BATTLE_INPUT_STATE
+networkBattleInput(NetworkInputContext *context, STARSHIP *StarShipPtr) {
+ BattleInputBuffer *bib = getBattleInputBuffer(context->playerNr);
+ BATTLE_INPUT_STATE result;
+
+ for (;;) {
+ bool ok;
+
+#if 0
+ // This is a useful debugging trick. By enabling this #if
+ // block, this side will always lag the maximum number of frames
+ // behind the other side. When the remote side stops on some event
+ // (a breakpoint or so), this side will stop too, waiting for input
+ // in the loop below, but it won't have processed the frame that
+ // triggered the event yet. If you then jump over this 'if'
+ // statement here, you can walk through the decisive frames
+ // manually. Works best with no input delay.
+ if (bib->size <= getBattleInputDelay() + 1) {
+ ok = false;
+ } else
+#endif
+ ok = BattleInputBuffer_pop(bib, &result);
+ // Get the input from the front of the
+ // buffer.
+ if (ok)
+ break;
+
+ {
+ NetConnection *conn = netConnections[context->playerNr];
+
+ // First try whether there is incoming data, without blocking.
+ // If there isn't any, only then give a warning, and then
+ // block after all.
+ netInput();
+ if (!NetConnection_isConnected(conn))
+ {
+ // Connection aborted.
+ GLOBAL(CurrentActivity) |= CHECK_ABORT;
+ return (BATTLE_INPUT_STATE) 0;
+ }
+
+ if (GLOBAL(CurrentActivity) & CHECK_ABORT)
+ return (BATTLE_INPUT_STATE) 0;
+
+#if 0
+ log_add(log_Warning, "NETPLAY: [%d] stalling for "
+ "network input. Increase the input delay if this "
+ "happens a lot.\n", context->playerNr);
+#endif
+#define MAX_BLOCK_TIME 500
+ netInputBlocking(MAX_BLOCK_TIME);
+ if (!NetConnection_isConnected(conn))
+ {
+ // Connection aborted.
+ GLOBAL(CurrentActivity) |= CHECK_ABORT;
+ return (BATTLE_INPUT_STATE) 0;
+ }
+ }
+ }
+
+ (void) StarShipPtr;
+ return result;
+}
+
+static void
+deleteConnectionCallback(NetConnection *conn) {
+ removeNetConnection(NetConnection_getPlayerNr(conn));
+}
+
+NetConnection *
+openPlayerNetworkConnection(COUNT player, void *extra) {
+ NetConnection *conn;
+
+ assert(netConnections[player] == NULL);
+
+ conn = NetConnection_open(player,
+ &netplayOptions.peer[player], NetMelee_connectCallback,
+ NetMelee_closeCallback, NetMelee_errorCallback,
+ deleteConnectionCallback, extra);
+
+ addNetConnection(conn, player);
+ return conn;
+}
+
+void
+closePlayerNetworkConnection(COUNT player) {
+ assert(netConnections[player] != NULL);
+
+ NetConnection_close(netConnections[player]);
+}
+
+bool
+setupInputDelay(size_t localInputDelay) {
+ COUNT player;
+ bool haveNetworkPlayer = false;
+ // We have at least one network controlled player.
+ size_t inputDelay = 0;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ haveNetworkPlayer = true;
+ if (NetConnection_getInputDelay(conn) > inputDelay)
+ inputDelay = NetConnection_getInputDelay(conn);
+ }
+
+ if (haveNetworkPlayer && inputDelay < localInputDelay)
+ inputDelay = localInputDelay;
+
+ setBattleInputDelay(inputDelay);
+ return true;
+}
+
+static bool
+setStateConnection(NetConnection *conn, void *arg) {
+ const NetState *state = (NetState *) arg;
+ NetConnection_setState(conn, *state);
+ return true;
+}
+
+bool
+setStateConnections(NetState state) {
+ return forEachConnectedPlayer(setStateConnection, &state);
+}
+
+static bool
+sendAbortConnection(NetConnection *conn, void *arg) {
+ const NetplayAbortReason *reason = (NetplayAbortReason *) arg;
+ sendAbort(conn, *reason);
+ return true;
+}
+
+bool
+sendAbortConnections(NetplayAbortReason reason) {
+ return forEachConnectedPlayer(sendAbortConnection, &reason);
+}
+
+static bool
+resetConnection(NetConnection *conn, void *arg) {
+ const NetplayResetReason *reason = (NetplayResetReason *) arg;
+ Netplay_localReset(conn, *reason);
+ return true;
+}
+
+bool
+resetConnections(NetplayResetReason reason) {
+ return forEachConnectedPlayer(resetConnection, &reason);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef struct {
+ NetConnection_ReadyCallback readyCallback;
+ void *readyCallbackArg;
+ bool notifyRemote;
+} LocalReadyConnectionArg;
+
+static bool
+localReadyConnection(NetConnection *conn, void *arg) {
+ LocalReadyConnectionArg *readyArg = (LocalReadyConnectionArg *) arg;
+ Netplay_localReady(conn, readyArg->readyCallback,
+ readyArg->readyCallbackArg, readyArg->notifyRemote);
+ return true;
+}
+
+bool
+localReadyConnections(NetConnection_ReadyCallback readyCallback,
+ void *readyArg, bool notifyRemote) {
+ LocalReadyConnectionArg arg;
+ arg.readyCallback = readyCallback;
+ arg.readyCallbackArg = readyArg;
+ arg.notifyRemote = notifyRemote;
+
+ return forEachConnectedPlayer(localReadyConnection, &arg);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define NETWORK_POLL_DELAY (ONE_SECOND / 24)
+
+typedef struct NegotiateReadyState NegotiateReadyState;
+struct NegotiateReadyState {
+ // Common fields of INPUT_STATE_DESC, from which this structure
+ // "inherits".
+ BOOLEAN(*InputFunc)(void *pInputState);
+
+ NetConnection *conn;
+ NetState nextState;
+ bool done;
+};
+
+static BOOLEAN
+negotiateReadyInputFunc(NegotiateReadyState *state) {
+ netInputBlocking(NETWORK_POLL_DELAY);
+ // The timing out is necessary so that immediate key presses get
+ // handled while we wait. If we could do without the timeout,
+ // we wouldn't even need negotiateReadyInputFunc() and the
+ // DoInput() call.
+
+ // No need to call flushPacketQueues(); nothing needs to be sent
+ // right now.
+
+ if (!NetConnection_isConnected(state->conn))
+ return FALSE;
+
+ return !state->done;
+}
+
+// Called when both sides are ready
+static void
+negotiateReadyBothReadyCallback(NetConnection *conn, void *arg) {
+ NegotiateReadyState *state =(NegotiateReadyState *) arg;
+
+ NetConnection_setState(conn, state->nextState);
+ // This has to be done immediately, as more packets in the
+ // receive queue may be handled by the netInput() call that
+ // triggered this callback.
+ // This is the reason for the nextState argument to
+ // negotiateReady(); setting the state after the call to
+ // negotiateReady() would be too late.
+ state->done = true;
+}
+
+bool
+negotiateReady(NetConnection *conn, bool notifyRemote, NetState nextState) {
+ NegotiateReadyState state;
+ state.InputFunc = (BOOLEAN(*)(void *)) negotiateReadyInputFunc;
+ state.conn = conn;
+ state.nextState = nextState;
+ state.done = false;
+
+ Netplay_localReady(conn, negotiateReadyBothReadyCallback,
+ (void *) &state, notifyRemote);
+ flushPacketQueue(conn);
+ if (!state.done)
+ DoInput(&state, FALSE);
+
+ return NetConnection_isConnected(conn);
+}
+
+// Wait for all connections to get ready.
+// XXX: Right now all connections are handled one by one. Handling them all
+// at once would be faster but would require more work, which is
+// not worth it as the time is minimal and this function is not
+// time critical.
+bool
+negotiateReadyConnections(bool notifyRemote, NetState nextState) {
+ COUNT player;
+ size_t numDisconnected = 0;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn)) {
+ numDisconnected++;
+ continue;
+ }
+
+ negotiateReady(conn, notifyRemote, nextState);
+ }
+
+ return numDisconnected == 0;
+}
+
+typedef struct WaitReadyState WaitReadyState;
+struct WaitReadyState {
+ // Common fields of INPUT_STATE_DESC, from which this structure
+ // "inherits".
+ BOOLEAN (*InputFunc)(void *pInputState);
+
+ NetConnection *conn;
+ NetConnection_ReadyCallback readyCallback;
+ void *readyCallbackArg;
+ bool done;
+};
+
+static void
+waitReadyCallback(NetConnection *conn, void *arg) {
+ WaitReadyState *state =(WaitReadyState *) arg;
+ state->done = true;
+
+ // Call the original callback.
+ state->readyCallback(conn, state->readyCallbackArg);
+}
+
+static BOOLEAN
+waitReadyInputFunc(WaitReadyState *state) {
+ netInputBlocking(NETWORK_POLL_DELAY);
+ // The timing out is necessary so that immediate key presses get
+ // handled while we wait. If we could do without the timeout,
+ // we wouldn't even need negotiateReadyInputFunc() and the
+ // DoInput() call.
+
+ // No need to call flushPacketQueues(); nothing needs to be sent
+ // right now.
+
+ if (!NetConnection_isConnected(state->conn))
+ return FALSE;
+
+ return !state->done;
+}
+
+bool
+waitReady(NetConnection *conn) {
+ WaitReadyState state;
+ state.InputFunc =(BOOLEAN(*)(void *)) waitReadyInputFunc;
+ state.conn = conn;
+ state.readyCallback = NetConnection_getReadyCallback(conn);
+ state.readyCallbackArg = NetConnection_getReadyCallbackArg(conn);
+ state.done = false;
+
+ NetConnection_setReadyCallback(conn, waitReadyCallback, (void *) &state);
+
+ DoInput(&state, FALSE);
+
+ return NetConnection_isConnected(conn);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+typedef struct WaitResetState WaitResetState;
+struct WaitResetState {
+ // Common fields of INPUT_STATE_DESC, from which this structure
+ // "inherits".
+ BOOLEAN(*InputFunc)(void *pInputState);
+
+ NetConnection *conn;
+ NetState nextState;
+ bool done;
+};
+
+static BOOLEAN
+waitResetInputFunc(WaitResetState *state) {
+ netInputBlocking(NETWORK_POLL_DELAY);
+ // The timing out is necessary so that immediate key presses get
+ // handled while we wait. If we could do without the timeout,
+ // we wouldn't even need waitResetInputFunc() and the
+ // DoInput() call.
+
+ // No need to call flushPacketQueues(); nothing needs to be sent
+ // right now.
+
+ if (!NetConnection_isConnected(state->conn))
+ return FALSE;
+
+ return !state->done;
+}
+
+// Called when both sides are reset.
+static void
+waitResetBothResetCallback(NetConnection *conn, void *arg) {
+ WaitResetState *state = (WaitResetState *) arg;
+
+ if (state->nextState != (NetState) -1) {
+ NetConnection_setState(conn, state->nextState);
+ // This has to be done immediately, as more packets in the
+ // receive queue may be handled by the netInput() call that
+ // triggered this callback.
+ // This is the reason for the nextState argument to
+ // waitReset(); setting the state after the call to
+ // waitReset() would be too late.
+ }
+ state->done = true;
+}
+
+bool
+waitReset(NetConnection *conn, NetState nextState) {
+ WaitResetState state;
+ state.InputFunc = (BOOLEAN(*)(void *)) waitResetInputFunc;
+ state.conn = conn;
+ state.nextState = nextState;
+ state.done = false;
+
+ Netplay_setResetCallback(conn, waitResetBothResetCallback,
+ (void *) &state);
+ if (state.done)
+ goto out;
+
+
+ if (!Netplay_isLocalReset(conn)) {
+ Netplay_localReset(conn, ResetReason_manualReset);
+ flushPacketQueue(conn);
+ }
+
+ if (!state.done)
+ DoInput(&state, FALSE);
+
+out:
+ return NetConnection_isConnected(conn);
+}
+
+// Wait until we have received a reset packet from all connections. If we
+// ourselves have not sent a reset packet, one is sent, with reason
+// 'manualReset'.
+// XXX: Right now all connections are handled one by one. Handling them all
+// at once would be faster but would require more work, which is
+// not worth it as the time is minimal and this function is not
+// time critical.
+// Use '(NetState) -1' for nextState to keep the current state.
+bool
+waitResetConnections(NetState nextState) {
+ COUNT player;
+ size_t numDisconnected = 0;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn)) {
+ numDisconnected++;
+ continue;
+ }
+
+ waitReset(conn, nextState);
+ }
+
+ return numDisconnected == 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
diff --git a/src/uqm/supermelee/netplay/netmelee.h b/src/uqm/supermelee/netplay/netmelee.h
new file mode 100644
index 0000000..83f3562
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmelee.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(UQM_SUPERMELEE_NETPLAY_NETMELEE_H_) && defined(NETPLAY)
+#define UQM_SUPERMELEE_NETPLAY_NETMELEE_H_
+
+#include "netplay.h"
+#include "netinput.h"
+#include "netconnection.h"
+#include "packetsenders.h"
+
+#include "../../battlecontrols.h"
+ // for NetworkInputContext
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../races.h"
+ // for STARSHIP
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern struct NetConnection *netConnections[];
+
+
+void addNetConnection(NetConnection *conn, int playerNr);
+void removeNetConnection(int playerNr);
+void closeAllConnections(void);
+void closeDisconnectedConnections(void);
+size_t getNumNetConnections(void);
+typedef bool(*ForEachConnectionCallback)(NetConnection *conn, void *arg);
+bool forEachConnectedPlayer(ForEachConnectionCallback callback, void *arg);
+
+struct melee_state *NetMelee_getMeleeState(NetConnection *conn);
+struct battlestate_struct *NetMelee_getBattleState(NetConnection *conn);
+
+void netInput(void);
+void netInputBlocking(uint32 timeoutMs);
+void flushPacketQueues(void);
+
+void confirmConnections(void);
+void cancelConfirmations(void);
+void connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg);
+
+bool allConnected(void);
+
+void initBattleStateDataConnections(void);
+void setBattleStateConnections(struct battlestate_struct *bs);
+
+BATTLE_INPUT_STATE networkBattleInput(NetworkInputContext *context,
+ STARSHIP *StarShipPtr);
+
+NetConnection *openPlayerNetworkConnection(COUNT player, void *extra);
+void closePlayerNetworkConnection(COUNT player);
+
+bool setupInputDelay(size_t localInputDelay);
+bool setStateConnections(NetState state);
+bool sendAbortConnections(NetplayAbortReason reason);
+bool resetConnections(NetplayResetReason reason);
+bool localReadyConnections(NetConnection_ReadyCallback readyCallback,
+ void *arg, bool notifyRemote);
+
+bool negotiateReady(NetConnection *conn, bool notifyRemote,
+ NetState nextState);
+bool negotiateReadyConnections(bool notifyRemote, NetState nextState);
+bool waitReady(NetConnection *conn);
+
+bool waitReset(NetConnection *conn, NetState nextState);
+bool waitResetConnections(NetState nextState);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETMELEE_H_ */
diff --git a/src/uqm/supermelee/netplay/netmisc.c b/src/uqm/supermelee/netplay/netmisc.c
new file mode 100644
index 0000000..3ab2f72
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmisc.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netplay.h"
+#include "netmisc.h"
+
+#include "netmelee.h"
+#include "notifyall.h"
+#include "packetsenders.h"
+#include "proto/ready.h"
+
+#include "../melee.h"
+ // For feedback functions.
+
+#include <stdlib.h>
+
+
+static BattleStateData *BattleStateData_alloc(void);
+static void BattleStateData_free(BattleStateData *battleStateData);
+static inline BattleStateData *BattleStateData_new(
+ struct melee_state *meleeState,
+ struct battlestate_struct *battleState,
+ struct getmelee_struct *getMeleeState);
+static void BattleStateData_delete(BattleStateData *battleStateData);
+
+
+static BattleStateData *
+BattleStateData_alloc(void) {
+ return (BattleStateData *) malloc(sizeof (BattleStateData));
+}
+
+static void
+BattleStateData_free(BattleStateData *battleStateData) {
+ free(battleStateData);
+}
+
+static inline BattleStateData *
+BattleStateData_new(struct melee_state *meleeState,
+ struct battlestate_struct *battleState,
+ struct getmelee_struct *getMeleeState) {
+ BattleStateData *battleStateData = BattleStateData_alloc();
+ battleStateData->releaseFunction =
+ (NetConnectionStateData_ReleaseFunction) BattleStateData_delete;
+ battleStateData->meleeState = meleeState;
+ battleStateData->battleState = battleState;
+ battleStateData->getMeleeState = getMeleeState;
+ return battleStateData;
+}
+
+static void
+BattleStateData_delete(BattleStateData *battleStateData) {
+ BattleStateData_free(battleStateData);
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+
+
+static void NetMelee_enterState_inSetup(NetConnection *conn, void *arg);
+
+// Called when a connection has been established.
+void
+NetMelee_connectCallback(NetConnection *conn) {
+ BattleStateData *battleStateData;
+ struct melee_state *meleeState;
+
+ meleeState = (struct melee_state *) NetConnection_getExtra(conn);
+ battleStateData = BattleStateData_new(meleeState, NULL, NULL);
+ NetConnection_setStateData(conn, (void *) battleStateData);
+ NetConnection_setExtra(conn, NULL);
+
+ // We have sent no teams yet. Initialize the state accordingly.
+ MeleeSetup_resetSentTeams (meleeState->meleeSetup);
+
+ sendInit(conn);
+ Netplay_localReady (conn, NetMelee_enterState_inSetup, NULL, false);
+}
+
+// Called when a connection is closed.
+void
+NetMelee_closeCallback(NetConnection *conn) {
+ closeFeedback(conn);
+}
+
+// Called when a network error occurs during connect.
+void
+NetMelee_errorCallback(NetConnection *conn,
+ const NetConnectionError *error) {
+ errorFeedback(conn);
+ (void) error;
+}
+
+// Callback function for when both sides have finished initialisation after
+// initial connect.
+static void
+NetMelee_enterState_inSetup(NetConnection *conn, void *arg) {
+ BattleStateData *battleStateData;
+ struct melee_state *meleeState;
+ int player;
+
+ NetConnection_setState(conn, NetState_inSetup);
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ meleeState = battleStateData->meleeState;
+
+ player = NetConnection_getPlayerNr(conn);
+
+ connectedFeedback(conn);
+
+ // Send our team to the remote side.
+ // XXX This only works with 2 players atm.
+ assert (NUM_PLAYERS == 2);
+ Melee_bootstrapSyncTeam (meleeState, player);
+
+ flushPacketQueues();
+
+ (void) arg;
+}
+
diff --git a/src/uqm/supermelee/netplay/netmisc.h b/src/uqm/supermelee/netplay/netmisc.h
new file mode 100644
index 0000000..ea14921
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netmisc.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETMISC_H_
+#define UQM_SUPERMELEE_NETPLAY_NETMISC_H_
+
+typedef struct BattleStateData BattleStateData;
+
+#include "netconnection.h"
+#include "netstate.h"
+#include "types.h"
+
+#include "../../battle.h"
+ // for BattleFrameCounter, BATTLE_FRAME_RATE
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct BattleStateData {
+ NETCONNECTION_STATE_DATA_COMMON
+
+ struct melee_state *meleeState;
+ struct battlestate_struct *battleState;
+ struct getmelee_struct *getMeleeState;
+ BattleFrameCounter endFrameCount;
+};
+
+
+void NetMelee_connectCallback(NetConnection *conn);
+void NetMelee_closeCallback(NetConnection *conn);
+void NetMelee_errorCallback(NetConnection *conn,
+ const NetConnectionError *error);
+
+void NetMelee_reenterState_inSetup(NetConnection *conn);
+
+
+// Returns true iff the connection is in a state where the confirmation
+// handshake is meaningful. Right now this is only when we're in the
+// pre-game setup menu.
+static inline bool
+handshakeMeaningful(NetState state) {
+ return state == NetState_inSetup;
+}
+
+static inline bool
+readyFlagsMeaningful(NetState state) {
+ return state == NetState_init ||
+ state == NetState_preBattle ||
+ state == NetState_selectShip ||
+ state == NetState_interBattle ||
+ state == NetState_inBattle ||
+ state == NetState_endingBattle ||
+ state == NetState_endingBattle2;
+}
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETMISC_H_ */
diff --git a/src/uqm/supermelee/netplay/netoptions.c b/src/uqm/supermelee/netplay/netoptions.c
new file mode 100644
index 0000000..a74e1a3
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netoptions.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netoptions.h"
+
+NetplayOptions netplayOptions = {
+ /* .metaServer = */ "uqm.stack.nl",
+ /* .metaPort = */ "21836",
+ /* .peer = */ {
+ /* [0] Player 1 (bottom) */ {
+ /* .isServer = */ true,
+ /* .host = */ "localhost",
+ /* .port = */ "21837" /* 0x554d - "UM" */,
+ },
+ /* [1] Player 2 (top) */ {
+ /* .isServer = */ true,
+ /* .host = */ "localhost",
+ /* .port = */ "21837" /* 0x554d - "UM" */,
+ },
+ },
+ /* .inputDelay = */ 2,
+};
+
+
diff --git a/src/uqm/supermelee/netplay/netoptions.h b/src/uqm/supermelee/netplay/netoptions.h
new file mode 100644
index 0000000..77b1637
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netoptions.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETOPTIONS_H_
+#define UQM_SUPERMELEE_NETPLAY_NETOPTIONS_H_
+
+#include "types.h"
+
+#include <stddef.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define NETPLAY_NUM_PLAYERS 2
+ // Not using NUM_PLAYERS because that would mean we'd have
+ // to include init.h, and all that comes with it.
+ // XXX: Don't use a hardcoded limit.
+
+typedef struct {
+ bool isServer;
+ const char *host;
+ const char *port;
+ // May be given as a service name.
+} NetplayPeerOptions;
+
+typedef struct {
+ const char *metaServer;
+ const char *metaPort;
+ // May be given as a service name.
+ NetplayPeerOptions peer[NETPLAY_NUM_PLAYERS];
+ size_t inputDelay;
+} NetplayOptions;
+extern NetplayOptions netplayOptions;
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETOPTIONS_H_ */
diff --git a/src/uqm/supermelee/netplay/netplay.h b/src/uqm/supermelee/netplay/netplay.h
new file mode 100644
index 0000000..b78c69a
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netplay.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(UQM_SUPERMELEE_NETPLAY_NETPLAY_H_) && defined(NETPLAY)
+#define UQM_SUPERMELEE_NETPLAY_NETPLAY_H_
+
+// NETPLAY can either be unset (in which case we will never get here)
+// NETPLAY_FULL, or NETPLAY_IPV4 (disables IPv6)
+#define NETPLAY_IPV4 1
+#define NETPLAY_FULL 2
+
+#define NETPLAY_PROTOCOL_VERSION_MAJOR 0
+#define NETPLAY_PROTOCOL_VERSION_MINOR 4
+
+#define NETPLAY_MIN_UQM_VERSION_MAJOR 0
+#define NETPLAY_MIN_UQM_VERSION_MINOR 6
+#define NETPLAY_MIN_UQM_VERSION_PATCH 9
+
+#undef NETPLAY_DEBUG
+ /* Extra debugging for netplay */
+#undef NETPLAY_DEBUG_FILE
+ /* Dump extra debugging information to file.
+ * Implies NETPLAY_DEBUG.*/
+#define NETPLAY_STATISTICS
+ /* Keep some statistics */
+#define NETPLAY_CHECKSUM
+ /* Send/process checksums to verify that both sides of a network
+ * connection are still in sync.
+ * If not enabled, incoming checksum packets will be ignored.
+ * TODO: make compilation of crc.c and checksum.c conditional. */
+#define NETPLAY_CHECKSUM_INTERVAL 1
+ /* If NETPLAY_CHECKSUM is defined, this define determines
+ * every how many frames a checksum packet is sent. */
+
+#define NETPLAY_READBUFSIZE 2048
+#define NETPLAY_CONNECTTIMEOUT 2000
+ /* Time to wait for a connect() to succeed. In ms. */
+//#define NETPLAY_LISTENTIMEOUT 30000
+// /* Time to wait for a listen() to succeed. In ms. */
+#define NETPLAY_RETRYDELAY 2000
+ /* Time to wait after all addresses of a host have been tried
+ * before starting retrying them all. In ms. */
+#define NETPLAY_LISTEN_BACKLOG 2
+ /* Second argument to listen(). */
+
+
+#ifdef _MSC_VER
+# if _MSC_VER < 1300
+ /* NETPLAY_DEBUG_FILE requires the __VA_ARGS__ macro, which is
+ * not available on MSVC 6.0. */
+# undef NETPLAY_DEBUG_FILE
+# endif
+#endif
+
+#ifdef NETPLAY_DEBUG_FILE
+# define NETPLAY_DEBUG
+# define DUMP_CRC_OPS
+#endif
+
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETPLAY_H_ */
+
diff --git a/src/uqm/supermelee/netplay/netrcv.c b/src/uqm/supermelee/netplay/netrcv.c
new file mode 100644
index 0000000..b9ea5f7
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netrcv.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "port.h"
+
+#include "netconnection.h"
+#include "netrcv.h"
+#include "packet.h"
+
+#include "types.h"
+#include "libs/log.h"
+
+#include <errno.h>
+#include <string.h>
+
+
+// Try to get a single packet from a stream of data.
+// Returns 0 if the packet was successfully processed, or
+// -1 on an error, in which case the state is unchanged.
+static ssize_t
+dataReceivedSingle(NetConnection *conn, const uint8 *data,
+ size_t dataLen) {
+ uint32 packetLen;
+ PacketType type;
+ int result;
+
+ if (dataLen < sizeof (PacketHeader)) {
+ // Incomplete packet. We'll have to wait for the rest.
+ return 0;
+ }
+
+ packetLen = packetLength((const Packet *) data);
+ type = packetType((const Packet *) data);
+
+ if (!validPacketType(type)) {
+ log_add(log_Warning, "Packet with invalid type %d received.\n", type);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (packetLen < packetTypeData[type].len) {
+ // Bad len field of packet.
+ log_add(log_Warning, "Packet with bad length field received (type="
+ "%s, lenfield=%d.\n", packetTypeData[type].name,
+ packetLen);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (dataLen < packetLen) {
+ // Incomplete packet. We'll have to wait for the rest.
+ return 0;
+ }
+
+#ifdef NETPLAY_STATISTICS
+ NetConnection_getStatistics(conn)->packetsReceived++;
+ NetConnection_getStatistics(conn)->packetTypeReceived[type]++;
+#endif
+
+#ifdef NETPLAY_DEBUG
+ if (type != PACKET_BATTLEINPUT && type != PACKET_CHECKSUM) {
+ // Reporting BattleInput and Checksum would get so spammy that it
+ // would slow down the battle.
+ log_add(log_Debug, "NETPLAY: [%d] <== Received packet of type %s.\n",
+ NetConnection_getPlayerNr(conn), packetTypeData[type].name);
+ }
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ uio_fprintf(conn->debugFile,
+ "NETPLAY: [%d] <== Received packet of type %s.\n",
+ NetConnection_getPlayerNr(conn), packetTypeData[type].name);
+ }
+#endif /* NETPLAY_DEBUG_FILE */
+#endif /* NETPLAY_DEBUG */
+
+ result = packetTypeData[type].handler(conn, data);
+ if (result == -1) {
+ // An error occured. errno is set by the handler.
+ return -1;
+ }
+
+ return packetLen;
+}
+
+// Try to get all the packets from a stream of data.
+// Returns the number of bytes processed.
+static ssize_t
+dataReceivedMulti(NetConnection *conn, const uint8 *data, size_t len) {
+ size_t processed;
+
+ processed = 0;
+ while (len > 0) {
+ ssize_t packetLen = dataReceivedSingle(conn, data, len);
+ if (packetLen == -1) {
+ // Bad packet. Errno is set.
+ return -1;
+ }
+
+ if (packetLen == 0) {
+ // No packet was processed. This means that no complete
+ // packet arrived.
+ break;
+ }
+
+ processed += packetLen;
+ data += packetLen;
+ len -= packetLen;
+ }
+
+ return processed;
+}
+
+void
+dataReadyCallback(NetDescriptor *nd) {
+ NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
+ Socket *socket = NetDescriptor_getSocket(nd);
+
+ for (;;) {
+ ssize_t numRead;
+ ssize_t numProcessed;
+
+ numRead = Socket_recv(socket, conn->readEnd,
+ NETPLAY_READBUFSIZE - (conn->readEnd - conn->readBuf), 0);
+ if (numRead == 0) {
+ // Other side closed the connection.
+ NetDescriptor_close(nd);
+ return;
+ }
+
+ if (numRead == -1) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return; // No more data for now.
+ else if (errno == EINTR)
+ continue; // System call was interrupted. Retry.
+ else
+ {
+ int savedErrno = errno;
+ log_add(log_Error, "recv() failed: %s.\n",
+ strerror(errno));
+ NetConnection_doErrorCallback(conn, savedErrno);
+ NetDescriptor_close(nd);
+ return;
+ }
+ }
+
+ conn->readEnd += numRead;
+
+ numProcessed = dataReceivedMulti(conn, conn->readBuf,
+ conn->readEnd - conn->readBuf);
+ if (numProcessed == -1) {
+ // An error occured during processing.
+ // errno is set.
+ NetConnection_doErrorCallback(conn, errno);
+ NetDescriptor_close(nd);
+ return;
+ }
+ if (numProcessed == 0) {
+ // No packets could be processed. This means we need to receive
+ // more data first.
+ return;
+ }
+
+ // Some packets have been processed.
+ // We more any rest to the front of the buffer, to make room
+ // for more data.
+ // A cyclic buffer would obviate the need for this move,
+ // but it would complicate things a lot.
+ memmove(conn->readBuf, conn->readBuf + numProcessed,
+ (conn->readEnd - conn->readBuf) - numProcessed);
+ conn->readEnd -= numProcessed;
+ }
+}
+
+
+
diff --git a/src/uqm/supermelee/netplay/netrcv.h b/src/uqm/supermelee/netplay/netrcv.h
new file mode 100644
index 0000000..a7577d9
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netrcv.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETRCV_H_
+#define UQM_SUPERMELEE_NETPLAY_NETRCV_H_
+
+#include "libs/net.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void dataReadyCallback(NetDescriptor *nd);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETRCV_H_ */
diff --git a/src/uqm/supermelee/netplay/netsend.c b/src/uqm/supermelee/netplay/netsend.c
new file mode 100644
index 0000000..b9f371f
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netsend.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "port.h"
+
+#include "netsend.h"
+#include "netconnection.h"
+#include "packet.h"
+#include "libs/log.h"
+#include "libs/net.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+
+int
+sendPacket(NetConnection *conn, Packet *packet) {
+ ssize_t sendResult;
+ size_t len;
+ Socket *socket;
+
+ assert(NetConnection_isConnected(conn));
+
+#ifdef NETPLAY_DEBUG
+ //if (packetType(packet) != PACKET_BATTLEINPUT &&
+ // packetType(packet) != PACKET_CHECKSUM) {
+ // // Reporting BattleInput or Checksum would get so spammy that it
+ // // would slow down the battle.
+ // log_add(log_Debug, "NETPLAY: [%d] ==> Sending packet of type %s.\n",
+ // conn->player, packetTypeData[packetType(packet)].name);
+ //}
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ uio_fprintf(conn->debugFile,
+ "NETPLAY: [%d] ==> Sending packet of type %s.\n",
+ conn->player, packetTypeData[packetType(packet)].name);
+ }
+#endif /* NETPLAY_DEBUG_FILE */
+#endif /* NETPLAY_DEBUG */
+
+ socket = NetDescriptor_getSocket(conn->nd);
+
+ len = packetLength(packet);
+ while (len > 0) {
+ sendResult = Socket_send(socket, (void *) packet, len, 0);
+ if (sendResult >= 0) {
+ len -= sendResult;
+ continue;
+ }
+
+ switch (errno) {
+ case EINTR: // System call interrupted, retry;
+ continue;
+ case ECONNRESET: { // Connection reset by peer.
+ // keep errno
+ return -1;
+ }
+ default: {
+ // Should not happen.
+ int savedErrno = errno;
+ log_add(log_Error, "send() failed: %s.\n", strerror(errno));
+ errno = savedErrno;
+ return -1;
+ }
+ }
+ }
+
+#ifdef NETPLAY_STATISTICS
+ NetConnection_getStatistics(conn)->packetsSent++;
+ NetConnection_getStatistics(conn)->packetTypeSent[packetType(packet)]++;
+#endif
+
+ return 0;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/netsend.h b/src/uqm/supermelee/netplay/netsend.h
new file mode 100644
index 0000000..e005d03
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netsend.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETSEND_H_
+#define UQM_SUPERMELEE_NETPLAY_NETSEND_H_
+
+#include "packet.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+int sendPacket(NetConnection *conn, Packet *packet);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETSEND_H_ */
diff --git a/src/uqm/supermelee/netplay/netstate.c b/src/uqm/supermelee/netplay/netstate.c
new file mode 100644
index 0000000..4382b94
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netstate.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netplay.h"
+#include "netstate.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+
+#define DEFINE_NETSTATEDATA(stateName) \
+ { \
+ /* .name = */ #stateName, \
+ }
+NetStateData netStateData[] = {
+ DEFINE_NETSTATEDATA(unconnected),
+ DEFINE_NETSTATEDATA(connecting),
+ DEFINE_NETSTATEDATA(init),
+ DEFINE_NETSTATEDATA(inSetup),
+ DEFINE_NETSTATEDATA(preBattle),
+ DEFINE_NETSTATEDATA(interBattle),
+ DEFINE_NETSTATEDATA(selectShip),
+ DEFINE_NETSTATEDATA(inBattle),
+ DEFINE_NETSTATEDATA(endingBattle),
+ DEFINE_NETSTATEDATA(endingBattle2),
+};
+
+void
+NetConnectionStateData_release(NetConnectionStateData *stateData) {
+ assert(stateData->releaseFunction != NULL);
+ stateData->releaseFunction(stateData);
+}
+
diff --git a/src/uqm/supermelee/netplay/netstate.h b/src/uqm/supermelee/netplay/netstate.h
new file mode 100644
index 0000000..1d46d49
--- /dev/null
+++ b/src/uqm/supermelee/netplay/netstate.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NETSTATE_H_
+#define UQM_SUPERMELEE_NETPLAY_NETSTATE_H_
+
+#include "port.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct NetConnectionStateData NetConnectionStateData;
+
+// State of a NetConnection.
+typedef enum {
+ NetState_unconnected, /* No connection initiated */
+ NetState_connecting, /* Connection being setup */
+ NetState_init, /* Initialising the connection */
+ NetState_inSetup, /* In the network game setup */
+ NetState_preBattle, /* Pre-battle initialisations */
+ NetState_interBattle, /* Negotiations between battles. */
+ NetState_selectShip, /* Selecting a ship in battle */
+ NetState_inBattle, /* Battle has started */
+ NetState_endingBattle, /* Both sides are prepared to end */
+ NetState_endingBattle2, /* Waiting for the final synchronisation */
+} NetState;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#include "types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct {
+ const char *name;
+} NetStateData;
+extern NetStateData netStateData[];
+
+typedef void (*NetConnectionStateData_ReleaseFunction)(
+ NetConnectionStateData *stateData);
+
+#define NETCONNECTION_STATE_DATA_COMMON \
+ NetConnectionStateData_ReleaseFunction releaseFunction;
+
+struct
+NetConnectionStateData {
+ NETCONNECTION_STATE_DATA_COMMON
+};
+
+void NetConnectionStateData_release(NetConnectionStateData *stateData);
+
+static inline bool
+NetState_battleActive(NetState state) {
+ return state == NetState_inBattle || state == NetState_endingBattle ||
+ state == NetState_endingBattle2;
+}
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NETSTATE_H_ */
diff --git a/src/uqm/supermelee/netplay/notify.c b/src/uqm/supermelee/netplay/notify.c
new file mode 100644
index 0000000..8b35ead
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notify.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// This files contains functions that notify the other side of local
+// changes.
+
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "notify.h"
+
+#include "packetsenders.h"
+
+
+// Convert a local player number to a side indication relative to this
+// party.
+static inline NetplaySide
+netSide(NetConnection *conn, int side) {
+ if (side == conn->player)
+ return NetplaySide_remote;
+
+ return NetplaySide_local;
+}
+
+void
+Netplay_Notify_shipSelected(NetConnection *conn, FleetShipIndex index) {
+ assert(NetConnection_getState(conn) == NetState_selectShip);
+
+ sendSelectShip(conn, index);
+}
+
+void
+Netplay_Notify_battleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
+ assert(NetConnection_getState(conn) == NetState_inBattle ||
+ NetConnection_getState(conn) == NetState_endingBattle ||
+ NetConnection_getState(conn) == NetState_endingBattle2);
+
+ sendBattleInput(conn, input);
+}
+
+void
+Netplay_Notify_setTeamName(NetConnection *conn, int player,
+ const char *name, size_t len) {
+ assert(NetConnection_getState(conn) == NetState_inSetup);
+ assert(!conn->stateFlags.handshake.localOk);
+
+ sendTeamName(conn, netSide(conn, player), name, len);
+}
+
+// On initialisation, or load.
+void
+Netplay_Notify_setFleet(NetConnection *conn, int player,
+ const MeleeShip *fleet, size_t fleetSize) {
+ assert(NetConnection_getState(conn) == NetState_inSetup);
+ assert(!conn->stateFlags.handshake.localOk);
+
+ sendFleet(conn, netSide(conn, player), fleet, fleetSize);
+}
+
+void
+Netplay_Notify_setShip(NetConnection *conn, int player,
+ FleetShipIndex index, MeleeShip ship) {
+ assert(NetConnection_getState(conn) == NetState_inSetup);
+ assert(!conn->stateFlags.handshake.localOk);
+
+ sendFleetShip(conn, netSide(conn, player), index, ship);
+}
+
+void
+Netplay_Notify_seedRandom(NetConnection *conn, uint32 seed) {
+ assert(NetConnection_getState(conn) == NetState_preBattle);
+
+ sendSeedRandom(conn, seed);
+ conn->stateFlags.agreement.randomSeed = true;
+}
+
+void
+Netplay_Notify_inputDelay(NetConnection *conn, uint32 delay) {
+ assert(NetConnection_getState(conn) == NetState_preBattle);
+
+ sendInputDelay(conn, delay);
+}
+
+void
+Netplay_Notify_frameCount(NetConnection *conn,
+ BattleFrameCounter frameCount) {
+ assert(NetConnection_getState(conn) == NetState_endingBattle);
+
+ sendFrameCount(conn, frameCount);
+}
+
+#ifdef NETPLAY_CHECKSUM
+void
+Netplay_Notify_checksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum) {
+ assert(NetState_battleActive(NetConnection_getState(conn)));
+
+ sendChecksum(conn, frameNr, checksum);
+}
+#endif /* NETPLAY_CHECKSUM */
+
+
+
+
diff --git a/src/uqm/supermelee/netplay/notify.h b/src/uqm/supermelee/netplay/notify.h
new file mode 100644
index 0000000..60a127d
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notify.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_NOTIFY_H_
+#define UQM_SUPERMELEE_NETPLAY_NOTIFY_H_
+
+#include "netplay.h"
+ // for NETPLAY_CHECKSUM
+#include "netconnection.h"
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#ifdef NETPLAY_CHECKSUM
+# include "checksum.h"
+#endif
+#include "../meleeship.h"
+ // for MeleeShip
+#include "../meleesetup.h"
+ // for FleetShipIndex
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void Netplay_Notify_shipSelected(NetConnection *conn, FleetShipIndex index);
+void Netplay_Notify_battleInput(NetConnection *conn,
+ BATTLE_INPUT_STATE input);
+void Netplay_Notify_setTeamName(NetConnection *conn, int player,
+ const char *name, size_t len);
+void Netplay_Notify_setFleet(NetConnection *conn, int player,
+ const MeleeShip *fleet, size_t fleetSize);
+void Netplay_Notify_setShip(NetConnection *conn, int player,
+ FleetShipIndex index, MeleeShip ship);
+void Netplay_Notify_seedRandom(NetConnection *conn, uint32 seed);
+void Netplay_Notify_inputDelay(NetConnection *conn, uint32 delay);
+void Netplay_Notify_frameCount(NetConnection *conn,
+ BattleFrameCounter frameCount);
+#ifdef NETPLAY_CHECKSUM
+void Netplay_Notify_checksum(NetConnection *conn,
+ BattleFrameCounter frameCount, Checksum checksum);
+#endif
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_NOTIFY_H_ */
diff --git a/src/uqm/supermelee/netplay/notifyall.c b/src/uqm/supermelee/netplay/notifyall.c
new file mode 100644
index 0000000..2d0cc8a
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notifyall.c
@@ -0,0 +1,146 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "notifyall.h"
+
+#include "netmelee.h"
+#include "notify.h"
+
+// Notify the network connections of a team name change.
+void
+Netplay_NotifyAll_setTeamName (MELEE_STATE *pMS, size_t playerNr)
+{
+ const char *name;
+ size_t len;
+ size_t playerI;
+
+ name = MeleeSetup_getTeamName (pMS->meleeSetup, playerNr);
+ len = strlen (name);
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ if (NetConnection_getState (conn) != NetState_inSetup)
+ continue;
+
+ Netplay_Notify_setTeamName (conn, playerNr, name, len);
+ }
+}
+
+// Notify the network connections of the configuration of a fleet.
+void
+Netplay_NotifyAll_setFleet (MELEE_STATE *pMS, size_t playerNr)
+{
+ MeleeSetup *setup = pMS->meleeSetup;
+ const MeleeShip *ships = MeleeSetup_getFleet (setup, playerNr);
+ size_t playerI;
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++) {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ if (NetConnection_getState (conn) != NetState_inSetup)
+ continue;
+
+ Netplay_Notify_setFleet (conn, playerNr, ships, MELEE_FLEET_SIZE);
+ }
+}
+
+// Notify the network of a change in the configuration of a fleet.
+void
+Netplay_NotifyAll_setShip (MELEE_STATE *pMS, size_t playerNr, size_t index)
+{
+ MeleeSetup *setup = pMS->meleeSetup;
+ MeleeShip ship = MeleeSetup_getShip (setup, playerNr, index);
+
+ size_t playerI;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ if (NetConnection_getState (conn) != NetState_inSetup)
+ continue;
+
+ Netplay_Notify_setShip (conn, playerNr, index, ship);
+ }
+}
+
+static bool
+Netplay_NotifyAll_inputDelayCallback(NetConnection *conn, void *arg) {
+ const size_t *delay = (size_t *) arg;
+ Netplay_Notify_inputDelay(conn, *delay);
+ return true;
+}
+
+bool
+Netplay_NotifyAll_inputDelay(size_t delay) {
+ return forEachConnectedPlayer(Netplay_NotifyAll_inputDelayCallback,
+ &delay);
+}
+
+#ifdef NETPLAY_CHECKSUM
+void
+Netplay_NotifyAll_checksum(BattleFrameCounter frameNr, Checksum checksum) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_Notify_checksum(conn, frameNr, checksum);
+ }
+}
+#endif /* NETPLAY_CHECKSUM */
+
+void
+Netplay_NotifyAll_battleInput(BATTLE_INPUT_STATE input) {
+ COUNT player;
+
+ for (player = 0; player < NUM_PLAYERS; player++)
+ {
+ NetConnection *conn = netConnections[player];
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected(conn))
+ continue;
+
+ Netplay_Notify_battleInput(conn, input);
+ }
+}
+
diff --git a/src/uqm/supermelee/netplay/notifyall.h b/src/uqm/supermelee/netplay/notifyall.h
new file mode 100644
index 0000000..d20ca81
--- /dev/null
+++ b/src/uqm/supermelee/netplay/notifyall.h
@@ -0,0 +1,49 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef NOTIFYALL_H
+#define NOTIFYALL_H
+
+#include "../../battle.h"
+#include "../../battlecontrols.h"
+#include "../melee.h"
+#ifdef NETPLAY_CHECKSUM
+# include "checksum.h"
+#endif /* NETPLAY_CHECKSUM */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void Netplay_NotifyAll_setTeamName (MELEE_STATE *pMS, size_t playerNr);
+void Netplay_NotifyAll_setFleet (MELEE_STATE *pMS, size_t playerNr);
+void Netplay_NotifyAll_setShip (MELEE_STATE *pMS, size_t playerNr,
+ size_t index);
+
+bool Netplay_NotifyAll_inputDelay(size_t delay);
+#ifdef NETPLAY_CHECKSUM
+void Netplay_NotifyAll_checksum(BattleFrameCounter frameNr,
+ Checksum checksum);
+#endif /* NETPLAY_CHECKSUM */
+void Netplay_NotifyAll_battleInput(BATTLE_INPUT_STATE input);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* NOTIFYALL_H */
+
diff --git a/src/uqm/supermelee/netplay/packet.c b/src/uqm/supermelee/netplay/packet.c
new file mode 100644
index 0000000..442be17
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packet.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netplay.h"
+#include "packet.h"
+
+#include "uqmversion.h"
+
+#include "netrcv.h"
+#include "packethandlers.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define DEFINE_PACKETDATA(name) \
+ { \
+ /* .len = */ sizeof (Packet_##name), \
+ /* .handler = */ (PacketHandler) PacketHandler_##name, \
+ /* .name = */ #name, \
+ }
+PacketTypeData packetTypeData[PACKET_NUM] = {
+ DEFINE_PACKETDATA(Init),
+ DEFINE_PACKETDATA(Ping),
+ DEFINE_PACKETDATA(Ack),
+ DEFINE_PACKETDATA(Ready),
+ DEFINE_PACKETDATA(Fleet),
+ DEFINE_PACKETDATA(TeamName),
+ DEFINE_PACKETDATA(Handshake0),
+ DEFINE_PACKETDATA(Handshake1),
+ DEFINE_PACKETDATA(HandshakeCancel),
+ DEFINE_PACKETDATA(HandshakeCancelAck),
+ DEFINE_PACKETDATA(SeedRandom),
+ DEFINE_PACKETDATA(InputDelay),
+ DEFINE_PACKETDATA(SelectShip),
+ DEFINE_PACKETDATA(BattleInput),
+ DEFINE_PACKETDATA(FrameCount),
+ DEFINE_PACKETDATA(Checksum),
+ DEFINE_PACKETDATA(Abort),
+ DEFINE_PACKETDATA(Reset),
+};
+
+static inline void *
+Packet_alloc(size_t size) {
+ return malloc(size);
+}
+
+static Packet *
+Packet_create(PacketType type, size_t extraSize) {
+ Packet *result;
+ size_t len;
+
+ // Alignment requirement.
+ assert(extraSize % 4 == 0);
+
+ len = packetTypeData[type].len + extraSize;
+ result = Packet_alloc(len);
+ result->header.len = hton16((uint16) len);
+ result->header.type = hton16((uint16) type);
+ return result;
+}
+
+void
+Packet_delete(Packet *packet) {
+ free(packet);
+}
+
+Packet_Init *
+Packet_Init_create(void) {
+ Packet_Init *packet = (Packet_Init *) Packet_create(PACKET_INIT, 0);
+
+ packet->protoVersion.major = NETPLAY_PROTOCOL_VERSION_MAJOR;
+ packet->protoVersion.minor = NETPLAY_PROTOCOL_VERSION_MINOR;
+ packet->padding0 = 0;
+ packet->uqmVersion.major = UQM_MAJOR_VERSION;
+ packet->uqmVersion.minor = UQM_MINOR_VERSION;
+ packet->uqmVersion.patch = UQM_PATCH_VERSION;
+ packet->padding1 = 0;
+ return packet;
+}
+
+Packet_Ping *
+Packet_Ping_create(uint32 id) {
+ Packet_Ping *packet = (Packet_Ping *) Packet_create(PACKET_PING, 0);
+
+ packet->id = hton32(id);
+ return packet;
+}
+
+Packet_Ack *
+Packet_Ack_create(uint32 id) {
+ Packet_Ack *packet = (Packet_Ack *) Packet_create(PACKET_ACK, 0);
+
+ packet->id = hton32(id);
+ return packet;
+}
+
+Packet_Ready *
+Packet_Ready_create(void) {
+ Packet_Ready *packet = (Packet_Ready *) Packet_create(PACKET_READY, 0);
+
+ return packet;
+}
+
+// The fleet itself still needs to be filled by the caller.
+// This function takes care of the necessary padding; it is allocated,
+// and filled with zero chars.
+Packet_Fleet *
+Packet_Fleet_create(NetplaySide side, size_t numShips) {
+ Packet_Fleet *packet;
+ size_t fleetSize;
+ size_t extraSize;
+
+ fleetSize = numShips * sizeof (FleetEntry);
+ extraSize = (fleetSize + 3) & ~0x03;
+ packet = (Packet_Fleet *) Packet_create(PACKET_FLEET, extraSize);
+ packet->side = (uint8) side;
+ packet->padding = 0;
+ packet->numShips = hton16((uint16) numShips);
+ memset((char *) packet + sizeof (Packet_Fleet) + fleetSize,
+ '\0', extraSize - fleetSize);
+
+ return packet;
+}
+
+// 'size' is the number of bytes (not characters) in 'name', excluding
+// a possible terminating '\0'. A '\0' will be included in the packet though.
+// This function takes care of the required padding.
+Packet_TeamName *
+Packet_TeamName_create(NetplaySide side, const char *name, size_t size) {
+ Packet_TeamName *packet;
+ size_t extraSize;
+
+ extraSize = ((size + 1) + 3) & ~0x03;
+ // The +1 is for the '\0'.
+ packet = (Packet_TeamName *) Packet_create(PACKET_TEAMNAME, extraSize);
+ packet->side = (uint8) side;
+ packet->padding = 0;
+ memcpy(packet->name, name, size);
+ memset((char *) packet + sizeof (Packet_TeamName) + size, '\0',
+ extraSize - size);
+ // This takes care of the terminating '\0', as well as the
+ // padding.
+
+ return packet;
+}
+
+Packet_Handshake0 *
+Packet_Handshake0_create(void) {
+ Packet_Handshake0 *packet =
+ (Packet_Handshake0 *) Packet_create(PACKET_HANDSHAKE0, 0);
+ return packet;
+}
+
+Packet_Handshake1 *
+Packet_Handshake1_create(void) {
+ Packet_Handshake1 *packet =
+ (Packet_Handshake1 *) Packet_create(PACKET_HANDSHAKE1, 0);
+ return packet;
+}
+
+Packet_HandshakeCancel *
+Packet_HandshakeCancel_create(void) {
+ Packet_HandshakeCancel *packet =
+ (Packet_HandshakeCancel *) Packet_create(
+ PACKET_HANDSHAKECANCEL, 0);
+ return packet;
+}
+
+Packet_HandshakeCancelAck *
+Packet_HandshakeCancelAck_create(void) {
+ Packet_HandshakeCancelAck *packet =
+ (Packet_HandshakeCancelAck *) Packet_create(
+ PACKET_HANDSHAKECANCELACK, 0);
+ return packet;
+}
+
+Packet_SeedRandom *
+Packet_SeedRandom_create(uint32 seed) {
+ Packet_SeedRandom *packet =
+ (Packet_SeedRandom *) Packet_create(PACKET_SEEDRANDOM, 0);
+
+ packet->seed = hton32(seed);
+ return packet;
+}
+
+Packet_InputDelay *
+Packet_InputDelay_create(uint32 delay) {
+ Packet_InputDelay *packet =
+ (Packet_InputDelay *) Packet_create(PACKET_INPUTDELAY, 0);
+
+ packet->delay = hton32(delay);
+ return packet;
+}
+
+Packet_SelectShip *
+Packet_SelectShip_create(uint16 ship) {
+ Packet_SelectShip *packet =
+ (Packet_SelectShip *) Packet_create(PACKET_SELECTSHIP, 0);
+ packet->ship = hton16(ship);
+ packet->padding = 0;
+ return packet;
+}
+
+Packet_BattleInput *
+Packet_BattleInput_create(uint8 state) {
+ Packet_BattleInput *packet =
+ (Packet_BattleInput *) Packet_create(PACKET_BATTLEINPUT, 0);
+ packet->state = (uint8) state;
+ packet->padding0 = 0;
+ packet->padding1 = 0;
+ return packet;
+}
+
+Packet_FrameCount *
+Packet_FrameCount_create(uint32 frameCount) {
+ Packet_FrameCount *packet =
+ (Packet_FrameCount *) Packet_create(PACKET_FRAMECOUNT, 0);
+ packet->frameCount = hton32(frameCount);
+ return packet;
+}
+
+Packet_Checksum *
+Packet_Checksum_create(uint32 frameNr, uint32 checksum) {
+ Packet_Checksum *packet =
+ (Packet_Checksum *) Packet_create(PACKET_CHECKSUM, 0);
+ packet->frameNr = hton32(frameNr);
+ packet->checksum = hton32(checksum);
+ return packet;
+}
+
+Packet_Abort *
+Packet_Abort_create(uint16 reason) {
+ Packet_Abort *packet = (Packet_Abort *) Packet_create(PACKET_ABORT, 0);
+ packet->reason = hton16(reason);
+ return packet;
+}
+
+Packet_Reset *
+Packet_Reset_create(uint16 reason) {
+ Packet_Reset *packet = (Packet_Reset *) Packet_create(PACKET_RESET, 0);
+ packet->reason = hton16(reason);
+ return packet;
+}
+
+
+
diff --git a/src/uqm/supermelee/netplay/packet.h b/src/uqm/supermelee/netplay/packet.h
new file mode 100644
index 0000000..f8c9682
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packet.h
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKET_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKET_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct Packet Packet;
+
+typedef enum PacketType {
+ PACKET_INIT,
+ PACKET_PING,
+ PACKET_ACK,
+ PACKET_READY,
+ PACKET_FLEET,
+ PACKET_TEAMNAME,
+ PACKET_HANDSHAKE0,
+ PACKET_HANDSHAKE1,
+ PACKET_HANDSHAKECANCEL,
+ PACKET_HANDSHAKECANCELACK,
+ PACKET_SEEDRANDOM,
+ PACKET_INPUTDELAY,
+ PACKET_SELECTSHIP,
+ PACKET_BATTLEINPUT,
+ PACKET_FRAMECOUNT,
+ PACKET_CHECKSUM,
+ PACKET_ABORT,
+ PACKET_RESET,
+
+ PACKET_NUM, /* Number of packet types */
+} PacketType;
+
+// Sent before aborting the connection.
+typedef enum NetplayAbortReason {
+ AbortReason_unspecified,
+ AbortReason_versionMismatch,
+ AbortReason_invalidHash,
+ AbortReason_protocolError,
+ // Network is in an inconsistent state.
+} NetplayAbortReason;
+
+// Sent before resetting the connection. A game in progress is terminated.
+typedef enum NetplayResetReason {
+ ResetReason_unspecified,
+ ResetReason_syncLoss,
+ ResetReason_manualReset,
+} NetplayResetReason;
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifndef PACKET_H_STANDALONE
+#include "netconnection.h"
+
+#include "types.h"
+#include "libs/network/bytesex.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* NB: These handlers are expected not to modify the state if an
+ * error occurs.
+ * When a handler is called, it has already been validated that the
+ * a complete packet has arrived.
+ */
+typedef int (*PacketHandler)(NetConnection *conn, const void *packet);
+
+typedef struct {
+ size_t len; /* Minimal length of a packet of this type */
+ PacketHandler handler;
+ const char *name;
+} PacketTypeData;
+
+extern PacketTypeData packetTypeData[];
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+// When adding new packets, be sure to have all the fields properly aligned,
+// and that the size of a packet is a multiple of 4 bytes in length.
+// Fields should be no longer than 4 bytes in themselves, as larger
+// fields may require a larger alignment.
+
+typedef struct {
+ uint16 len;
+ uint16 type; /* Actually of type PacketType */
+} PacketHeader;
+
+// "Base class" for all packets.
+struct Packet {
+ PacketHeader header;
+};
+
+static inline size_t
+packetLength(const Packet *packet) {
+ return (size_t) ntoh16(packet->header.len);
+}
+
+static inline PacketType
+packetType(const Packet *packet) {
+ return (PacketType) (int) ntoh16(packet->header.type);
+}
+
+static inline bool
+validPacketType(PacketType type) {
+ return type < PACKET_NUM;
+}
+
+typedef struct {
+ PacketHeader header;
+ struct {
+ uint8 major;
+ uint8 minor;
+ } protoVersion; /* Protocol version */
+ uint16 padding0; /* Set to 0 */
+ struct {
+ uint8 major;
+ uint8 minor;
+ uint8 patch;
+ } uqmVersion; /* Protocol version */
+ uint8 padding1; /* Set to 0 */
+} Packet_Init;
+
+typedef struct {
+ PacketHeader header;
+ uint32 id;
+} Packet_Ping;
+
+// Acknowledgement of a Ping.
+typedef struct {
+ PacketHeader header;
+ uint32 id;
+} Packet_Ack;
+
+// Used to signal that a party is ready to continue.
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_Ready;
+
+typedef struct {
+ PacketHeader header;
+ uint32 seed;
+} Packet_SeedRandom;
+
+typedef struct {
+ PacketHeader header;
+ uint32 delay;
+} Packet_InputDelay;
+
+// This enum is used to indicate that a packet containing it relates to
+// either the local or the remote player, from the perspective of the
+// sender of the message;
+typedef enum {
+ NetplaySide_local,
+ NetplaySide_remote
+} NetplaySide;
+
+typedef struct {
+ uint8 index; /* Position in the fleet */
+ uint8 ship; /* Ship type index; actually MeleeShip */
+} FleetEntry;
+// Structure describing an update to a player's fleet.
+// TODO: use strings as ship identifiers, instead of numbers,
+// so that adding of new ships doesn't break this.
+typedef struct {
+ PacketHeader header;
+ uint8 side;
+ uint8 padding;
+ uint16 numShips;
+ FleetEntry ships[];
+ // Be sure to add padding to this structure to make it a multiple of
+ // 4 bytes in length.
+} Packet_Fleet;
+
+typedef struct {
+ PacketHeader header;
+ uint8 side;
+ uint8 padding;
+ uint8 name[];
+ // '\0' terminated.
+ // Be sure to add padding to this structure to make it a multiple of
+ // 4 bytes in length.
+} Packet_TeamName;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_Handshake0;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_Handshake1;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_HandshakeCancel;
+
+typedef struct {
+ PacketHeader header;
+ // No contents.
+} Packet_HandshakeCancelAck;
+
+typedef struct {
+ PacketHeader header;
+ uint16 ship;
+ // The value '(uint16) ~0' indicates random selection.
+ uint16 padding;
+} Packet_SelectShip;
+
+typedef struct {
+ PacketHeader header;
+ uint8 state; /* Actually BATTLE_INPUT_STATE */
+ uint8 padding0;
+ uint16 padding1;
+} Packet_BattleInput;
+
+typedef struct {
+ PacketHeader header;
+ uint32 frameCount; /* Actually BattleFrameCounter */
+} Packet_FrameCount;
+
+typedef struct {
+ PacketHeader header;
+ uint32 frameNr; /* Actually BattleFrameCounter */
+ uint32 checksum; /* Actually Checksum */
+} Packet_Checksum;
+
+typedef struct {
+ PacketHeader header;
+ uint16 reason; /* Actually NetplayAbortReason */
+ uint16 padding0;
+} Packet_Abort;
+
+typedef struct {
+ PacketHeader header;
+ uint16 reason; /* Actually NetplayResetReason */
+ uint16 padding0;
+} Packet_Reset;
+
+
+#ifndef PACKET_H_STANDALONE
+void Packet_delete(Packet *packet);
+Packet_Init *Packet_Init_create(void);
+Packet_Ping *Packet_Ping_create(uint32 id);
+Packet_Ack *Packet_Ack_create(uint32 id);
+Packet_Ready *Packet_Ready_create(void);
+Packet_Handshake0 *Packet_Handshake0_create(void);
+Packet_Handshake1 *Packet_Handshake1_create(void);
+Packet_HandshakeCancel *Packet_HandshakeCancel_create(void);
+Packet_HandshakeCancelAck *Packet_HandshakeCancelAck_create(void);
+Packet_SeedRandom *Packet_SeedRandom_create(uint32 seed);
+Packet_InputDelay *Packet_InputDelay_create(uint32 delay);
+Packet_Fleet *Packet_Fleet_create(NetplaySide side, size_t numShips);
+Packet_TeamName *Packet_TeamName_create(NetplaySide side, const char *name,
+ size_t size);
+Packet_SelectShip *Packet_SelectShip_create(uint16 ship);
+Packet_BattleInput *Packet_BattleInput_create(uint8 state);
+Packet_FrameCount *Packet_FrameCount_create(uint32 frameCount);
+Packet_Checksum *Packet_Checksum_create(uint32 frameNr, uint32 checksum);
+Packet_Abort *Packet_Abort_create(uint16 reason);
+Packet_Reset *Packet_Reset_create(uint16 reason);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PACKET_H_ */
+
diff --git a/src/uqm/supermelee/netplay/packethandlers.c b/src/uqm/supermelee/netplay/packethandlers.c
new file mode 100644
index 0000000..5d2d8f4
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packethandlers.c
@@ -0,0 +1,649 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define PORT_WANT_ERRNO
+#include "port.h"
+
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "packethandlers.h"
+
+#include "netinput.h"
+#include "netmisc.h"
+#include "packetsenders.h"
+#include "proto/npconfirm.h"
+#include "proto/ready.h"
+#include "proto/reset.h"
+#include "libs/log.h"
+
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../../init.h"
+ // for NUM_PLAYERS
+#include "../../globdata.h"
+ // for GLOBAL
+#include "../melee.h"
+ // for various update functions.
+#include "../meleeship.h"
+ // for MeleeShip
+#include "../pickmele.h"
+ // for various update functions.
+#include "libs/mathlib.h"
+ // for TFB_SeedRandom
+
+#include <errno.h>
+
+
+static bool
+testNetState(bool condition, PacketType type) {
+ if (!condition) {
+ log_add(log_Error, "Packet of type '%s' received from wrong "
+ "state.", packetTypeData[type].name);
+ errno = EBADMSG;
+ }
+ return condition;
+}
+
+static int
+versionCompare(int major1, int minor1, int patch1,
+ int major2, int minor2, int patch2) {
+ if (major1 < major2)
+ return -1;
+ if (major1 > major2)
+ return 1;
+
+ if (minor1 < minor2)
+ return -1;
+ if (minor1 > minor2)
+ return 1;
+
+ if (patch1 < patch2)
+ return -1;
+ if (patch1 > patch2)
+ return 1;
+
+ return 0;
+}
+
+int
+PacketHandler_Init(NetConnection *conn, const Packet_Init *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_init &&
+ !conn->stateFlags.ready.remoteReady, PACKET_INIT))
+ return -1; // errno is set
+
+ if (packet->protoVersion.major != NETPLAY_PROTOCOL_VERSION_MAJOR ||
+ packet->protoVersion.minor != NETPLAY_PROTOCOL_VERSION_MINOR) {
+ sendAbort (conn, AbortReason_versionMismatch);
+ abortFeedback(conn, AbortReason_versionMismatch);
+ log_add(log_Error, "Protocol version %d.%d not supported.",
+ packet->protoVersion.major, packet->protoVersion.minor);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ if (versionCompare(packet->uqmVersion.major, packet->uqmVersion.minor,
+ packet->uqmVersion.patch, NETPLAY_MIN_UQM_VERSION_MAJOR,
+ NETPLAY_MIN_UQM_VERSION_MINOR, NETPLAY_MIN_UQM_VERSION_PATCH)
+ < 0) {
+ sendAbort (conn, AbortReason_versionMismatch);
+ abortFeedback(conn, AbortReason_versionMismatch);
+ log_add(log_Error, "Remote side is running a version of UQM that "
+ "is too old (%d.%d.%d; %d.%d.%d is required).",
+ packet->uqmVersion.major, packet->uqmVersion.minor,
+ packet->uqmVersion.patch, NETPLAY_MIN_UQM_VERSION_MAJOR,
+ NETPLAY_MIN_UQM_VERSION_MINOR, NETPLAY_MIN_UQM_VERSION_PATCH);
+ errno = ENOSYS;
+ return -1;
+ }
+
+ Netplay_remoteReady(conn);
+
+ return 0;
+}
+
+int
+PacketHandler_Ping(NetConnection *conn, const Packet_Ping *packet) {
+ if (!testNetState(conn->state > NetState_init, PACKET_PING))
+ return -1; // errno is set
+
+ sendAck(conn, packet->id);
+ return 0;
+}
+
+int
+PacketHandler_Ack(NetConnection *conn, const Packet_Ack *packet) {
+ if (!testNetState(conn->state > NetState_init, PACKET_ACK))
+ return -1; // errno is set
+
+ (void) conn;
+ (void) packet;
+ return 0;
+}
+
+// Convert the side indication relative to a remote party to
+// a local player number.
+static inline int
+localSide(NetConnection *conn, NetplaySide side) {
+ if (side == NetplaySide_local) {
+ // "local" relative to the remote party.
+ return conn->player;
+ }
+
+ return 1 - conn->player;
+}
+
+int
+PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(readyFlagsMeaningful(conn->state) &&
+ !conn->stateFlags.ready.remoteReady, PACKET_READY))
+ return -1; // errno is set
+
+ Netplay_remoteReady(conn);
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_Fleet(NetConnection *conn, const Packet_Fleet *packet) {
+ uint16 numShips = ntoh16(packet->numShips);
+ size_t i;
+ size_t len;
+ int player;
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
+ return -1; // errno is set
+
+ player = localSide(conn, (NetplaySide) packet->side);
+
+ len = packetLength((const Packet *) packet);
+ if (sizeof packet + numShips * sizeof(packet->ships[0]) > len) {
+ // There is not enough room in the packet to contain all
+ // the ships it says it contains.
+ log_add(log_Warning, "Invalid fleet size. Specified size is %d, "
+ "actual size = %d",
+ numShips, (int) ((len - sizeof packet) / sizeof(packet->ships[0])));
+ errno = EBADMSG;
+ return -1;
+ }
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+
+ if (conn->stateFlags.handshake.localOk) {
+ Netplay_cancelConfirmation(conn);
+ confirmationCancelled(battleStateData->meleeState, conn->player);
+ }
+
+ for (i = 0; i < numShips; i++) {
+ MeleeShip ship = (MeleeShip) packet->ships[i].ship;
+ FleetShipIndex index = (FleetShipIndex) packet->ships[i].index;
+
+ if (!MeleeShip_valid(ship)) {
+ log_add (log_Warning, "Invalid ship type number %d (max = %d).\n",
+ ship, NUM_MELEE_SHIPS - 1);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (index >= MELEE_FLEET_SIZE)
+ {
+ log_add (log_Warning, "Invalid ship position number %d "
+ "(max = %d).\n", index, MELEE_FLEET_SIZE - 1);
+ errno = EBADMSG;
+ return -1;
+ }
+
+ Melee_RemoteChange_ship (battleStateData->meleeState, conn,
+ player, index, ship);
+ }
+
+ // Padding data may follow; it is ignored.
+ return 0;
+}
+
+int
+PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
+ size_t nameLen;
+ int side;
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+
+ if (conn->stateFlags.handshake.localOk) {
+ Netplay_cancelConfirmation(conn);
+ confirmationCancelled(battleStateData->meleeState, conn->player);
+ }
+
+ side = localSide(conn, (NetplaySide) packet->side);
+ nameLen = packetLength((const Packet *) packet)
+ - sizeof (Packet_TeamName) - 1;
+ // The -1 is for not counting the terminating '\0'.
+
+ {
+ char buf[MAX_TEAM_CHARS + 1];
+
+ if (nameLen > MAX_TEAM_CHARS)
+ nameLen = MAX_TEAM_CHARS;
+ memcpy (buf, (const char *) packet->name, nameLen);
+ buf[nameLen] = '\0';
+
+ Melee_RemoteChange_teamName(battleStateData->meleeState, conn,
+ side, buf);
+ }
+
+ // Padding data may follow; it is ignored.
+ return 0;
+}
+
+static void
+handshakeComplete(NetConnection *conn) {
+ assert(!conn->stateFlags.handshake.localOk);
+ assert(!conn->stateFlags.handshake.remoteOk);
+ assert(!conn->stateFlags.handshake.canceling);
+
+ assert(conn->state == NetState_inSetup);
+ NetConnection_setState(conn, NetState_preBattle);
+}
+
+int
+PacketHandler_Handshake0(NetConnection *conn,
+ const Packet_Handshake0 *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state)
+ && !conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKE0))
+ return -1; // errno is set
+
+ conn->stateFlags.handshake.remoteOk = true;
+ if (conn->stateFlags.handshake.localOk &&
+ !conn->stateFlags.handshake.canceling)
+ sendHandshake1(conn);
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_Handshake1(NetConnection *conn,
+ const Packet_Handshake1 *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state) &&
+ (conn->stateFlags.handshake.localOk ||
+ conn->stateFlags.handshake.canceling), PACKET_HANDSHAKE1))
+ return -1; // errno is set
+
+ if (conn->stateFlags.handshake.canceling) {
+ conn->stateFlags.handshake.remoteOk = true;
+ } else {
+ bool remoteWasOk = conn->stateFlags.handshake.remoteOk;
+
+ conn->stateFlags.handshake.localOk = false;
+ conn->stateFlags.handshake.remoteOk = false;
+
+ if (!remoteWasOk) {
+ // Received Handshake1 without prior Handshake0.
+ // A Handshake0 is implied, but we still need to confirm
+ // it with a Handshake1.
+ sendHandshake1(conn);
+ }
+
+ handshakeComplete(conn);
+ }
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_HandshakeCancel(NetConnection *conn,
+ const Packet_HandshakeCancel *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state)
+ && conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKECANCEL))
+ return -1; // errno is set
+
+ conn->stateFlags.handshake.remoteOk = false;
+ sendHandshakeCancelAck(conn);
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_HandshakeCancelAck(NetConnection *conn,
+ const Packet_HandshakeCancelAck *packet) {
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(handshakeMeaningful(conn->state)
+ && conn->stateFlags.handshake.canceling,
+ PACKET_HANDSHAKECANCELACK))
+ return -1; // errno is set
+
+ conn->stateFlags.handshake.canceling = false;
+ if (conn->stateFlags.handshake.localOk) {
+ if (conn->stateFlags.handshake.remoteOk) {
+ sendHandshake1(conn);
+ } else
+ sendHandshake0(conn);
+ }
+
+ (void) packet;
+ // Its contents is not interesting.
+
+ return 0;
+}
+
+int
+PacketHandler_SeedRandom(NetConnection *conn,
+ const Packet_SeedRandom *packet) {
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_preBattle &&
+ !conn->stateFlags.discriminant, PACKET_SEEDRANDOM))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ updateRandomSeed (battleStateData->meleeState, conn->player,
+ ntoh32(packet->seed));
+
+ conn->stateFlags.agreement.randomSeed = true;
+ return 0;
+}
+
+int
+PacketHandler_InputDelay(NetConnection *conn,
+ const Packet_InputDelay *packet) {
+ BattleStateData *battleStateData;
+ uint32 delay;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_preBattle, PACKET_INPUTDELAY))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ delay = ntoh32(packet->delay);
+ if (delay > BATTLE_FRAME_RATE) {
+ log_add(log_Error, "NETPLAY: [%d] Received absurdly large "
+ "input delay value (%d).", conn->player, delay);
+ return -1;
+ }
+ conn->stateFlags.inputDelay = delay;
+
+ return 0;
+}
+
+int
+PacketHandler_SelectShip(NetConnection *conn,
+ const Packet_SelectShip *packet) {
+ bool updateResult;
+ BattleStateData *battleStateData;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_selectShip, PACKET_SELECTSHIP))
+ return -1; // errno is set
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ updateResult = updateMeleeSelection(battleStateData->getMeleeState,
+ conn->player, ntoh16(packet->ship));
+ if (!updateResult)
+ {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+PacketHandler_BattleInput(NetConnection *conn,
+ const Packet_BattleInput *packet) {
+ BATTLE_INPUT_STATE input;
+ BattleInputBuffer *bib;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_inBattle ||
+ conn->state == NetState_endingBattle ||
+ conn->state == NetState_endingBattle2, PACKET_BATTLEINPUT))
+ return -1; // errno is set
+
+ input = (BATTLE_INPUT_STATE) packet->state;
+ bib = getBattleInputBuffer(conn->player);
+ if (!BattleInputBuffer_push(bib, input)) {
+ // errno is set
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+PacketHandler_FrameCount(NetConnection *conn,
+ const Packet_FrameCount *packet) {
+ BattleStateData *battleStateData;
+ BattleFrameCounter frameCount;
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(conn->state == NetState_endingBattle,
+ PACKET_FRAMECOUNT))
+ return -1; // errno is set
+
+ frameCount = (BattleFrameCounter) ntoh32(packet->frameCount);
+#ifdef NETPLAY_DEBUG
+ log_add(log_Debug, "NETPLAY: [%d] <== Received battleFrameCount %u.",
+ conn->player, (unsigned int) frameCount);
+#endif
+
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+ if (frameCount > battleStateData->endFrameCount)
+ battleStateData->endFrameCount = frameCount;
+ Netplay_remoteReady(conn);
+
+ return 0;
+}
+
+int
+PacketHandler_Checksum(NetConnection *conn, const Packet_Checksum *packet) {
+#ifdef NETPLAY_CHECKSUM
+ uint32 frameNr;
+ uint32 checksum;
+ size_t delay;
+ size_t interval;
+#endif
+
+ if (conn->stateFlags.reset.localReset)
+ return 0;
+ if (conn->stateFlags.reset.remoteReset) {
+ errno = EBADMSG;
+ return -1;
+ }
+
+ if (!testNetState(NetState_battleActive(conn->state), PACKET_CHECKSUM))
+ return -1; // errno is set
+
+#ifdef NETPLAY_CHECKSUM
+ frameNr = ntoh32(packet->frameNr);
+ checksum = ntoh32(packet->checksum);
+ interval = NetConnection_getChecksumInterval(conn);
+ delay = getBattleInputDelay();
+
+ if (frameNr % interval != 0) {
+ log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
+ "for frame %u, while we only expect checksums on frames "
+ "divisable by %u -- discarding.", conn->player,
+ (unsigned int) frameNr, (unsigned int) interval);
+ return 0;
+ // No need to close the connection; checksums are not
+ // essential.
+ }
+
+ // The checksum is sent at the beginning of a frame.
+ // If we're in frame n and have sent our input already,
+ // the remote side has got enough input to progress delay + 1 frames from
+ // frame n. The next frame is then n + delay + 1, for which we can
+ // receive a checksum.
+ if (frameNr > battleFrameCount + delay + 1) {
+ log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
+ "for a frame too far in the future (frame %u, current "
+ "is %u, input delay is %u) -- discarding.", conn->player,
+ (unsigned int) frameNr, (unsigned int) battleFrameCount, (unsigned int) delay);
+ return 0;
+ // No need to close the connection; checksums are not
+ // essential.
+ }
+
+ // We can progress delay more frames after the last frame for which we
+ // received input. If we call that frame n, we can complete frames
+ // n through n + delay - 1. While we are waiting for the next input,
+ // in frame n + delay, we will first receive the checksum that the
+ // remote side sent at the start of frame n + 1.
+ // In this situation frameNr is n + 1, and battleFrameCount is
+ // n + delay.
+ if (frameNr + delay < battleFrameCount) {
+ log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
+ "for a frame too far in the past (frame %u, current "
+ "is %u, input delay is %u) -- discarding.", conn->player,
+ (unsigned int) frameNr, (unsigned int) battleFrameCount, (unsigned int) delay);
+ return 0;
+ // No need to close the connection; checksums are not
+ // essential.
+ }
+
+ addRemoteChecksum(conn, frameNr, checksum);
+#endif
+
+#ifndef NETPLAY_CHECKSUM
+ (void) packet;
+#endif
+ return 0;
+}
+
+int
+PacketHandler_Abort(NetConnection *conn, const Packet_Abort *packet) {
+ abortFeedback(conn, packet->reason);
+
+ return -1;
+ // Close connection.
+}
+
+int
+PacketHandler_Reset(NetConnection *conn, const Packet_Reset *packet) {
+ NetplayResetReason reason;
+
+ if (!testNetState(!conn->stateFlags.reset.remoteReset, PACKET_RESET))
+ return -1; // errno is set
+
+ reason = ntoh16(packet->reason);
+
+ Netplay_remoteReset(conn, reason);
+ return 0;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/packethandlers.h b/src/uqm/supermelee/netplay/packethandlers.h
new file mode 100644
index 0000000..7bd686e
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packethandlers.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKETHANDLERS_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKETHANDLERS_H_
+
+#include "packet.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define DECLARE_PACKETHANDLER(type) \
+ int PacketHandler_##type(NetConnection *conn, \
+ const Packet_##type *packet)
+
+DECLARE_PACKETHANDLER(Init);
+DECLARE_PACKETHANDLER(Ping);
+DECLARE_PACKETHANDLER(Ack);
+DECLARE_PACKETHANDLER(Ready);
+DECLARE_PACKETHANDLER(Fleet);
+DECLARE_PACKETHANDLER(TeamName);
+DECLARE_PACKETHANDLER(Handshake0);
+DECLARE_PACKETHANDLER(Handshake1);
+DECLARE_PACKETHANDLER(HandshakeCancel);
+DECLARE_PACKETHANDLER(HandshakeCancelAck);
+DECLARE_PACKETHANDLER(SeedRandom);
+DECLARE_PACKETHANDLER(InputDelay);
+DECLARE_PACKETHANDLER(SelectShip);
+DECLARE_PACKETHANDLER(BattleInput);
+DECLARE_PACKETHANDLER(FrameCount);
+DECLARE_PACKETHANDLER(Checksum);
+DECLARE_PACKETHANDLER(Abort);
+DECLARE_PACKETHANDLER(Reset);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PACKETHANDLERS_H_ */
diff --git a/src/uqm/supermelee/netplay/packetq.c b/src/uqm/supermelee/netplay/packetq.c
new file mode 100644
index 0000000..ee8ec01
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetq.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NETCONNECTION_INTERNAL
+#include "netplay.h"
+#include "netconnection.h"
+#include "packetq.h"
+#include "netsend.h"
+#include "packetsenders.h"
+#ifdef NETPLAY_DEBUG
+# include "libs/log.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+static inline PacketQueueLink *
+PacketQueueLink_alloc(void) {
+ // XXX: perhaps keep a pool of links?
+ return malloc(sizeof (PacketQueueLink));
+}
+
+static inline void
+PacketQueueLink_delete(PacketQueueLink *link) {
+ free(link);
+}
+
+// 'maxSize' should at least be 1
+void
+PacketQueue_init(PacketQueue *queue) {
+ queue->size = 0;
+ queue->first = NULL;
+ queue->end = &queue->first;
+}
+
+static void
+PacketQueue_deleteLinks(PacketQueueLink *link) {
+ while (link != NULL) {
+ PacketQueueLink *next = link->next;
+ Packet_delete(link->packet);
+ PacketQueueLink_delete(link);
+ link = next;
+ }
+}
+
+void
+PacketQueue_uninit(PacketQueue *queue) {
+ PacketQueue_deleteLinks(queue->first);
+}
+
+void
+queuePacket(NetConnection *conn, Packet *packet) {
+ PacketQueue *queue;
+ PacketQueueLink *link;
+
+ assert(NetConnection_isConnected(conn));
+
+ queue = &conn->queue;
+
+ link = PacketQueueLink_alloc();
+ link->packet = packet;
+ link->next = NULL;
+ *queue->end = link;
+ queue->end = &link->next;
+
+ queue->size++;
+ // XXX: perhaps check that this queue isn't getting too large?
+
+#ifdef NETPLAY_DEBUG
+ if (packetType(packet) != PACKET_BATTLEINPUT &&
+ packetType(packet) != PACKET_CHECKSUM) {
+ // Reporting BattleInput or Checksum would get so spammy that it
+ // would slow down the battle.
+ log_add(log_Debug, "NETPLAY: [%d] ==> Queueing packet of type %s.\n",
+ NetConnection_getPlayerNr(conn),
+ packetTypeData[packetType(packet)].name);
+ }
+#ifdef NETPLAY_DEBUG_FILE
+ if (conn->debugFile != NULL) {
+ uio_fprintf(conn->debugFile,
+ "NETPLAY: [%d] ==> Queueing packet of type %s.\n",
+ NetConnection_getPlayerNr(conn),
+ packetTypeData[packetType(packet)].name);
+ }
+#endif /* NETPLAY_DEBUG_FILE */
+#endif /* NETPLAY_DEBUG */
+}
+
+// If an error occurs during sending, we leave the unsent packets in
+// the queue, and let the caller decide what to do with them.
+// This function may return -1 with errno EAGAIN or EWOULDBLOCK
+// if we're waiting for the other party to act first.
+static int
+flushPacketQueueLinks(NetConnection *conn, PacketQueueLink **first) {
+ PacketQueueLink *link;
+ PacketQueueLink *next;
+ PacketQueue *queue = &conn->queue;
+
+ for (link = *first; link != NULL; link = next) {
+ if (sendPacket(conn, link->packet) == -1) {
+ // Errno is set.
+ *first = link;
+ return -1;
+ }
+
+ next = link->next;
+ Packet_delete(link->packet);
+ PacketQueueLink_delete(link);
+ queue->size--;
+ }
+
+ *first = link;
+ return 0;
+}
+
+int
+flushPacketQueue(NetConnection *conn) {
+ int flushResult;
+ PacketQueue *queue = &conn->queue;
+
+ assert(NetConnection_isConnected(conn));
+
+ flushResult = flushPacketQueueLinks(conn, &queue->first);
+ if (queue->first == NULL)
+ queue->end = &queue->first;
+ if (flushResult == -1) {
+ // errno is set
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/src/uqm/supermelee/netplay/packetq.h b/src/uqm/supermelee/netplay/packetq.h
new file mode 100644
index 0000000..71f2347
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetq.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKETQ_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKETQ_H_
+
+typedef struct PacketQueue PacketQueue;
+
+#include "packet.h"
+#include "types.h"
+
+#include <sys/types.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct PacketQueueLink PacketQueueLink;
+struct PacketQueueLink {
+ PacketQueueLink *next;
+ Packet *packet;
+};
+
+struct PacketQueue {
+ size_t size;
+ PacketQueueLink *first;
+ PacketQueueLink **end;
+
+ // first points to the first entry in the queue
+ // end points to the location where the next message should be inserted.
+};
+
+void PacketQueue_init(PacketQueue *queue);
+void PacketQueue_uninit(PacketQueue *queue);
+void queuePacket(NetConnection *conn, Packet *packet);
+int flushPacketQueue(NetConnection *conn);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
diff --git a/src/uqm/supermelee/netplay/packetsenders.c b/src/uqm/supermelee/netplay/packetsenders.c
new file mode 100644
index 0000000..fb9f232
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetsenders.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "netplay.h"
+#include "packetsenders.h"
+
+#include "packet.h"
+#include "packetq.h"
+#include "netsend.h"
+
+
+void
+sendInit(NetConnection *conn) {
+ Packet_Init *packet;
+
+ packet = Packet_Init_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendPing(NetConnection *conn, uint32 id) {
+ Packet_Ping *packet;
+
+ packet = Packet_Ping_create(id);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendAck(NetConnection *conn, uint32 id) {
+ Packet_Ack *packet;
+
+ packet = Packet_Ack_create(id);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendReady(NetConnection *conn) {
+ Packet_Ready *packet;
+
+ packet = Packet_Ready_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshake0(NetConnection *conn) {
+ Packet_Handshake0 *packet;
+
+ packet = Packet_Handshake0_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshake1(NetConnection *conn) {
+ Packet_Handshake1 *packet;
+
+ packet = Packet_Handshake1_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshakeCancel(NetConnection *conn) {
+ Packet_HandshakeCancel *packet;
+
+ packet = Packet_HandshakeCancel_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendHandshakeCancelAck(NetConnection *conn) {
+ Packet_HandshakeCancelAck *packet;
+
+ packet = Packet_HandshakeCancelAck_create();
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendTeamName(NetConnection *conn, NetplaySide side, const char *name,
+ size_t len) {
+ Packet_TeamName *packet;
+
+ packet = Packet_TeamName_create(side, name, len);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendFleet(NetConnection *conn, NetplaySide side, const MeleeShip *ships,
+ size_t shipCount) {
+ size_t i;
+ Packet_Fleet *packet;
+
+ packet = Packet_Fleet_create(side, shipCount);
+
+ for (i = 0; i < shipCount; i++) {
+ packet->ships[i].index = (uint8) i;
+ packet->ships[i].ship = (uint8) ships[i];
+ }
+
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendFleetShip(NetConnection *conn, NetplaySide side,
+ FleetShipIndex shipIndex, MeleeShip ship) {
+ Packet_Fleet *packet;
+
+ packet = Packet_Fleet_create(side, 1);
+
+ packet->ships[0].index = (uint8) shipIndex;
+ packet->ships[0].ship = (uint8) ship;
+
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendSeedRandom(NetConnection *conn, uint32 seed) {
+ Packet_SeedRandom *packet;
+
+ packet = Packet_SeedRandom_create(seed);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendInputDelay(NetConnection *conn, uint32 delay) {
+ Packet_InputDelay *packet;
+
+ packet = Packet_InputDelay_create(delay);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendSelectShip(NetConnection *conn, FleetShipIndex index) {
+ Packet_SelectShip *packet;
+
+ packet = Packet_SelectShip_create((uint16) index);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
+ Packet_BattleInput *packet;
+
+ packet = Packet_BattleInput_create((uint8) input);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount) {
+ Packet_FrameCount *packet;
+
+ packet = Packet_FrameCount_create((uint32) frameCount);
+ queuePacket(conn, (Packet *) packet);
+}
+
+#ifdef NETPLAY_CHECKSUM
+void
+sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum) {
+ Packet_Checksum *packet;
+
+ packet = Packet_Checksum_create((uint32) frameNr, (uint32) checksum);
+ queuePacket(conn, (Packet *) packet);
+}
+#endif
+
+void
+sendAbort(NetConnection *conn, NetplayAbortReason reason) {
+ Packet_Abort *packet;
+
+ packet = Packet_Abort_create((uint16) reason);
+ queuePacket(conn, (Packet *) packet);
+}
+
+void
+sendReset(NetConnection *conn, NetplayResetReason reason) {
+ Packet_Reset *packet;
+
+ packet = Packet_Reset_create((uint16) reason);
+ queuePacket(conn, (Packet *) packet);
+}
+
+
+
diff --git a/src/uqm/supermelee/netplay/packetsenders.h b/src/uqm/supermelee/netplay/packetsenders.h
new file mode 100644
index 0000000..de0bc6d
--- /dev/null
+++ b/src/uqm/supermelee/netplay/packetsenders.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PACKETSENDERS_H_
+#define UQM_SUPERMELEE_NETPLAY_PACKETSENDERS_H_
+
+#include "types.h"
+
+#include "netconnection.h"
+#include "packet.h"
+
+#include "../../controls.h"
+ // for BATTLE_INPUT_STATE
+#include "../meleeship.h"
+ // for MeleeShip
+#include "../meleesetup.h"
+ // for FleetShipIndex
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void sendInit(NetConnection *conn);
+void sendPing(NetConnection *conn, uint32 id);
+void sendAck(NetConnection *conn, uint32 id);
+void sendReady(NetConnection *conn);
+void sendHandshake0(NetConnection *conn);
+void sendHandshake1(NetConnection *conn);
+void sendHandshakeCancel(NetConnection *conn);
+void sendHandshakeCancelAck(NetConnection *conn);
+void sendTeamName(NetConnection *conn, NetplaySide side,
+ const char *name, size_t len);
+void sendFleet(NetConnection *conn, NetplaySide side,
+ const MeleeShip *ships, size_t numShips);
+void sendFleetShip(NetConnection *conn, NetplaySide player,
+ FleetShipIndex shipIndex, MeleeShip ship);
+void sendSeedRandom(NetConnection *conn, uint32 seed);
+void sendInputDelay(NetConnection *conn, uint32 delay);
+void sendSelectShip(NetConnection *conn, FleetShipIndex index);
+void sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input);
+void sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount);
+void sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
+ Checksum checksum);
+void sendAbort(NetConnection *conn, NetplayAbortReason reason);
+void sendReset(NetConnection *conn, NetplayResetReason reason);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PACKETSENDERS_H_ */
diff --git a/src/uqm/supermelee/netplay/proto/Makeinfo b/src/uqm/supermelee/netplay/proto/Makeinfo
new file mode 100644
index 0000000..1d9739c
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="npconfirm.c ready.c reset.c"
+uqm_HFILES="npconfirm.h ready.h reset.h"
diff --git a/src/uqm/supermelee/netplay/proto/npconfirm.c b/src/uqm/supermelee/netplay/proto/npconfirm.c
new file mode 100644
index 0000000..6929219
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/npconfirm.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NETCONNECTION_INTERNAL
+#include "../netplay.h"
+#include "npconfirm.h"
+
+#include "types.h"
+#include "../netmisc.h"
+#include "../packetsenders.h"
+
+#include <assert.h>
+#include <errno.h>
+
+int
+Netplay_confirm(NetConnection *conn) {
+ assert(handshakeMeaningful(NetConnection_getState(conn)));
+
+ if (conn->stateFlags.handshake.localOk) {
+ // Already confirmed
+ errno = EINVAL;
+ return -1;
+ }
+
+ conn->stateFlags.handshake.localOk = true;
+
+ if (conn->stateFlags.handshake.canceling) {
+ // If a previous confirmation was cancelled, but the cancel
+ // is not acknowledged yet, we don't have to send anything yet.
+ // The handshake0 packet will be sent when the acknowledgement
+ // arrives.
+ } else if (conn->stateFlags.handshake.remoteOk) {
+ // A Handshake0 is implied by the following Handshake1.
+ sendHandshake1(conn);
+ } else {
+ sendHandshake0(conn);
+ }
+
+ return 0;
+}
+
+int
+Netplay_cancelConfirmation(NetConnection *conn) {
+ assert(handshakeMeaningful(NetConnection_getState(conn)));
+
+ if (!conn->stateFlags.handshake.localOk) {
+ // Not confirmed, or already canceling.
+ errno = EINVAL;
+ return -1;
+ }
+
+ conn->stateFlags.handshake.localOk = false;
+ if (conn->stateFlags.handshake.canceling) {
+ // If previous cancellation is still waiting to be acknowledged,
+ // the confirmation we are cancelling here, has not actually been
+ // sent yet. By setting the localOk flag to false, it is
+ // cancelled, without the need for any packets to be sent.
+ } else {
+ conn->stateFlags.handshake.canceling = true;
+ sendHandshakeCancel(conn);
+ }
+
+ return 0;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/proto/npconfirm.h b/src/uqm/supermelee/netplay/proto/npconfirm.h
new file mode 100644
index 0000000..1ae58f5
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/npconfirm.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PROTO_NPCONFIRM_H_
+#define UQM_SUPERMELEE_NETPLAY_PROTO_NPCONFIRM_H_
+
+#include "../netplay.h"
+#include "../netconnection.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+int Netplay_confirm(NetConnection *conn);
+int Netplay_cancelConfirmation(NetConnection *conn);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PROTO_NPCONFIRM_H_ */
diff --git a/src/uqm/supermelee/netplay/proto/ready.c b/src/uqm/supermelee/netplay/proto/ready.c
new file mode 100644
index 0000000..e9f8c58
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/ready.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define NETCONNECTION_INTERNAL
+#include "../netplay.h"
+#include "ready.h"
+
+#include "types.h"
+#include "../netmisc.h"
+#include "../packetsenders.h"
+
+#include <assert.h>
+
+static void
+Netplay_bothReady(NetConnection *conn) {
+ NetConnection_ReadyCallback callback;
+ void *readyArg;
+
+ assert(conn->readyCallback != NULL);
+
+ callback = conn->readyCallback;
+ readyArg = conn->readyCallbackArg;
+
+ NetConnection_setReadyCallback(conn, NULL, NULL);
+ // Clear the readyCallback field before performing the callback,
+ // so that it can be set again from inside the callback
+ // function.
+
+ callback(conn, readyArg);
+}
+
+// If notifyRemote is set, a 'Ready' message will be sent to the other side.
+// returns true iff both sides are ready.
+// Inside the callback function, ready flags may be set for a possible
+// next Ready communication.
+bool
+Netplay_localReady(NetConnection *conn, NetConnection_ReadyCallback callback,
+ void *readyArg, bool notifyRemote) {
+ assert(readyFlagsMeaningful(NetConnection_getState(conn)));
+ assert(!conn->stateFlags.ready.localReady);
+ assert(callback != NULL);
+
+ NetConnection_setReadyCallback(conn, callback, readyArg);
+
+ if (notifyRemote)
+ sendReady(conn);
+ if (!conn->stateFlags.ready.remoteReady) {
+ conn->stateFlags.ready.localReady = true;
+ return false;
+ }
+
+ // Reset ready flags:
+ conn->stateFlags.ready.remoteReady = false;
+
+ // Trigger the callback.
+ Netplay_bothReady(conn);
+ return true;
+}
+
+// returns true iff both sides are ready.
+bool
+Netplay_remoteReady(NetConnection *conn) {
+ assert(readyFlagsMeaningful(NetConnection_getState(conn)));
+ // This is supposed to be already verified by the calling
+ // function.
+ assert(!conn->stateFlags.ready.remoteReady);
+
+ if (!conn->stateFlags.ready.localReady) {
+ conn->stateFlags.ready.remoteReady = true;
+ return false;
+ }
+
+ // Reset ready flags:
+ conn->stateFlags.ready.localReady = false;
+
+ // Trigger the callback.
+ Netplay_bothReady(conn);
+ return true;
+}
+
+bool
+Netplay_isLocalReady(const NetConnection *conn) {
+ return conn->stateFlags.ready.localReady;
+}
+
+bool
+Netplay_isRemoteReady(const NetConnection *conn) {
+ return conn->stateFlags.ready.remoteReady;
+}
+
+
diff --git a/src/uqm/supermelee/netplay/proto/ready.h b/src/uqm/supermelee/netplay/proto/ready.h
new file mode 100644
index 0000000..3521557
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/ready.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PROTO_READY_H_
+#define UQM_SUPERMELEE_NETPLAY_PROTO_READY_H_
+
+#include "../netconnection.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool Netplay_localReady(NetConnection *conn,
+ NetConnection_ReadyCallback callback, void *arg, bool notifyRemote);
+bool Netplay_remoteReady(NetConnection *conn);
+bool Netplay_isLocalReady(const NetConnection *conn);
+bool Netplay_isRemoteReady(const NetConnection *conn);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PROTO_READY_H_ */
diff --git a/src/uqm/supermelee/netplay/proto/reset.c b/src/uqm/supermelee/netplay/proto/reset.c
new file mode 100644
index 0000000..82483b1
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/reset.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+// See doc/devel/netplay/protocol
+
+#define NETCONNECTION_INTERNAL
+#include "../netplay.h"
+#include "reset.h"
+
+#include "types.h"
+#include "../packetsenders.h"
+#include "../../melee.h"
+ // For resetFeedback.
+
+#include <assert.h>
+
+// Reset packets are sent to indicate that a game is to be reset.
+// i.e. the game is to return to the SuperMelee fleet setup menu.
+// The reset will occur when a reset packet has both been sent and
+// received. When a reset packet is received and the local side had not
+// sent a reset packet itself, the local side will confirm the reset.
+// When both sides initiate a reset simultaneously, the reset packets
+// of each side will act as a confirmation for the other side.
+//
+// When a reset packet has been sent, no further gameplay packets should be
+// sent until the game has been reset. Non-gameplay packets such as 'ping'
+// are allowed.
+// When a reset packet has been received, all further incoming gameplay
+// packets are ignored until the game has been reset.
+//
+// conn->stateFlags.reset.localReset is set when a reset packet is sent.
+// conn->stateFlags.reset.remoteReset is set when a reset packet is
+// received.
+//
+// When either localReset or remoteReset gets set and the other flag isn't
+// set, Netplay_connectionReset() gets called.
+//
+// As soon as the following three conditions are met, the reset callback is
+// called and the localReset and remoteReset flags are cleared.
+// - conn->stateFlags.reset.localReset is set
+// - conn->stateFlags.reset.remoteReset is set
+// - the reset callback is non-NULL.
+//
+// Elsewhere in the UQM source:
+// When the local side causes a reset, it calls Netplay_localReset().
+// When a remote reset packet is received, Netplay_remoteReset() is called
+// (which will sent a reset packet back as confirmation, as required).
+// At the end of melee, the reset callback is set (for each connection),
+// and the game will wait until the reset callback for each connection has
+// been called (when the forementioned conditions have become true)
+// (or until the connection is terminated).
+
+
+// This function is called when one side initiates a reset.
+static void
+Netplay_connectionReset(NetConnection *conn, NetplayResetReason reason,
+ bool byRemote) {
+ switch (NetConnection_getState(conn)) {
+ case NetState_unconnected:
+ case NetState_connecting:
+ case NetState_init:
+ case NetState_inSetup:
+ break;
+ case NetState_preBattle:
+ case NetState_interBattle:
+ case NetState_selectShip:
+ case NetState_inBattle:
+ case NetState_endingBattle:
+ case NetState_endingBattle2:
+ resetFeedback(conn, reason, byRemote);
+ break;
+ }
+}
+
+static void
+Netplay_doConnectionResetCallback(NetConnection *conn) {
+ NetConnection_ResetCallback callback;
+ void *resetArg;
+
+ callback = conn->resetCallback;
+ resetArg = conn->resetCallbackArg;
+
+ NetConnection_setResetCallback(conn, NULL, NULL);
+ // Clear the resetCallback field before performing the callback,
+ // so that it can be set again from inside the callback
+ // function.
+ callback(conn, resetArg);
+}
+
+static void
+Netplay_resetConditionTriggered(NetConnection *conn) {
+ if (conn->resetCallback == NULL)
+ return;
+
+ if (!conn->stateFlags.reset.localReset ||
+ !conn->stateFlags.reset.remoteReset)
+ return;
+
+ conn->stateFlags.reset.localReset = false;
+ conn->stateFlags.reset.remoteReset = false;
+
+ Netplay_doConnectionResetCallback(conn);
+}
+
+void
+Netplay_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *resetArg) {
+ NetConnection_setResetCallback(conn, callback, resetArg);
+
+ Netplay_resetConditionTriggered(conn);
+}
+
+void
+Netplay_localReset(NetConnection *conn, NetplayResetReason reason) {
+ assert(!conn->stateFlags.reset.localReset);
+
+ conn->stateFlags.reset.localReset = true;
+ if (conn->stateFlags.reset.remoteReset) {
+ // Both sides have initiated/confirmed the reset.
+ Netplay_resetConditionTriggered(conn);
+ } else {
+ sendReset(conn, reason);
+ Netplay_connectionReset(conn, reason, false);
+ }
+}
+
+void
+Netplay_remoteReset(NetConnection *conn, NetplayResetReason reason) {
+ assert(!conn->stateFlags.reset.remoteReset);
+ // Should already be checked when the packet arrives.
+
+ conn->stateFlags.reset.remoteReset = true;
+ if (!conn->stateFlags.reset.localReset) {
+ sendReset(conn, reason);
+ conn->stateFlags.reset.localReset = true;
+ Netplay_connectionReset(conn, reason, true);
+ }
+
+ Netplay_resetConditionTriggered(conn);
+}
+
+bool
+Netplay_isLocalReset(const NetConnection *conn) {
+ return conn->stateFlags.reset.localReset;
+}
+
+bool
+Netplay_isRemoteReset(const NetConnection *conn) {
+ return conn->stateFlags.reset.remoteReset;
+}
+
diff --git a/src/uqm/supermelee/netplay/proto/reset.h b/src/uqm/supermelee/netplay/proto/reset.h
new file mode 100644
index 0000000..e16b1d1
--- /dev/null
+++ b/src/uqm/supermelee/netplay/proto/reset.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2006 Serge van den Boom <svdb@stack.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_NETPLAY_PROTO_RESET_H_
+#define UQM_SUPERMELEE_NETPLAY_PROTO_RESET_H_
+
+#include "../netconnection.h"
+#include "../packet.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void Netplay_setResetCallback(NetConnection *conn,
+ NetConnection_ResetCallback callback, void *resetArg);
+void Netplay_localReset(NetConnection *conn, NetplayResetReason reason);
+void Netplay_remoteReset(NetConnection *conn, NetplayResetReason reason);
+bool Netplay_isLocalReset(const NetConnection *conn);
+bool Netplay_isRemoteReset(const NetConnection *conn);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_SUPERMELEE_NETPLAY_PROTO_RESET_H_ */
+
diff --git a/src/uqm/supermelee/pickmele.c b/src/uqm/supermelee/pickmele.c
new file mode 100644
index 0000000..0ce6489
--- /dev/null
+++ b/src/uqm/supermelee/pickmele.c
@@ -0,0 +1,948 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define PICKMELE_INTERNAL
+#include "pickmele.h"
+
+#include "../battlecontrols.h"
+#include "../battle.h"
+#include "../build.h"
+#include "../controls.h"
+#include "../flash.h"
+#include "../igfxres.h"
+#include "../intel.h"
+#include "../master.h"
+#include "../nameref.h"
+#include "melee.h"
+#ifdef NETPLAY
+# include "netplay/netmelee.h"
+# include "netplay/netmisc.h"
+# include "netplay/notify.h"
+#endif
+#include "../races.h"
+#include "../setup.h"
+#include "../sounds.h"
+#include "libs/async.h"
+#include "libs/log.h"
+#include "libs/mathlib.h"
+
+
+#define NUM_PICKMELEE_ROWS 2
+#define NUM_PICKMELEE_COLUMNS 7
+
+#define PICK_X_OFFS 57
+#define PICK_Y_OFFS 24
+#define PICK_SIDE_OFFS 100
+
+#define NAME_AREA_HEIGHT 7
+#define MELEE_WIDTH 149
+#define MELEE_HEIGHT (48 + NAME_AREA_HEIGHT)
+
+#define PICKSHIP_TEAM_NAME_TEXT_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x1F), 0x09)
+#define PICKSHIP_TEAM_START_VALUE_COLOR \
+ BUILD_COLOR (MAKE_RGB15 (0x04, 0x05, 0x1F), 0x4B)
+
+
+#ifdef NETPLAY
+static void reportShipSelected (GETMELEE_STATE *gms, COUNT index);
+#endif
+
+
+FRAME PickMeleeFrame;
+
+
+static FleetShipIndex
+PickMelee_GetShipIndex (BYTE row, BYTE col)
+{
+ return row * NUM_PICKMELEE_COLUMNS + col;
+}
+
+static BYTE
+PickMelee_GetShipRow (FleetShipIndex index)
+{
+ return index / NUM_PICKMELEE_COLUMNS;
+}
+
+static BYTE
+PickMelee_GetShipColumn (int index)
+{
+ return index % NUM_PICKMELEE_COLUMNS;
+}
+
+// Returns the <index>th ship in the queue, or 0 if it is not available.
+static HSTARSHIP
+MeleeShipByQueueIndex (const QUEUE *queue, COUNT index)
+{
+ HSTARSHIP hShip;
+ HSTARSHIP hNextShip;
+
+ for (hShip = GetHeadLink (queue); hShip != 0; hShip = hNextShip)
+ {
+ STARSHIP *StarShipPtr = LockStarShip (queue, hShip);
+ if (StarShipPtr->index == index)
+ {
+ hNextShip = hShip;
+ if (StarShipPtr->SpeciesID == NO_ID)
+ hShip = 0;
+ UnlockStarShip (queue, hNextShip);
+ break;
+ }
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockStarShip (queue, hShip);
+ }
+
+ return hShip;
+}
+
+// Returns the <index>th available ship in the queue.
+static HSTARSHIP
+MeleeShipByUsedIndex (const QUEUE *queue, COUNT index)
+{
+ HSTARSHIP hShip;
+ HSTARSHIP hNextShip;
+
+ for (hShip = GetHeadLink (queue); hShip != 0; hShip = hNextShip)
+ {
+ STARSHIP *StarShipPtr = LockStarShip (queue, hShip);
+ if ((StarShipPtr->SpeciesID != NO_ID) && index-- == 0)
+ {
+ UnlockStarShip (queue, hShip);
+ break;
+ }
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockStarShip (queue, hShip);
+ }
+
+ return hShip;
+}
+
+#if 0
+static COUNT
+queueIndexFromShip (HSTARSHIP hShip)
+{
+ COUNT result;
+ STARSHIP *StarShipPtr = LockStarShip (queue, hShip);
+ result = StarShipPtr->index;
+ UnlockStarShip (queue, hShip);
+}
+#endif
+
+// Pre: called does not hold the graphics lock
+static void
+PickMelee_ChangedSelection (GETMELEE_STATE *gms, COUNT playerI)
+{
+ RECT r;
+ r.corner.x = PICK_X_OFFS + ((ICON_WIDTH + 2) * gms->player[playerI].col);
+ r.corner.y = PICK_Y_OFFS + ((ICON_HEIGHT + 2) * gms->player[playerI].row)
+ + ((1 - playerI) * PICK_SIDE_OFFS);
+ r.extent.width = (ICON_WIDTH + 2);
+ r.extent.height = (ICON_HEIGHT + 2);
+ Flash_setRect (gms->player[playerI].flashContext, &r);
+}
+
+// Only returns false when there is no ship for the choice.
+bool
+setShipSelected(GETMELEE_STATE *gms, COUNT playerI, COUNT choice,
+ bool reportNetwork)
+{
+ HSTARSHIP ship;
+
+ assert (!gms->player[playerI].done);
+
+ if (choice == (COUNT) ~0)
+ {
+ // Random ship selection.
+ ship = MeleeShipByUsedIndex (&race_q[playerI],
+ gms->player[playerI].randomIndex);
+ }
+ else
+ {
+ // Explicit ship selection.
+ ship = MeleeShipByQueueIndex (&race_q[playerI], choice);
+ }
+
+ if (ship == 0)
+ return false;
+
+ gms->player[playerI].choice = choice;
+ gms->player[playerI].hBattleShip = ship;
+ PlayMenuSound (MENU_SOUND_SUCCESS);
+#ifdef NETPLAY
+ if (reportNetwork)
+ reportShipSelected (gms, choice);
+#else
+ (void) reportNetwork;
+#endif
+ gms->player[playerI].done = true;
+ return true;
+}
+
+// Returns FALSE if aborted.
+static BOOLEAN
+SelectShip_processInput (GETMELEE_STATE *gms, COUNT playerI,
+ BATTLE_INPUT_STATE inputState)
+{
+ if (inputState & BATTLE_WEAPON)
+ {
+ if (gms->player[playerI].col == NUM_PICKMELEE_COLUMNS &&
+ gms->player[playerI].row == 0)
+ {
+ // Random ship
+ (void) setShipSelected (gms, playerI, (COUNT) ~0, TRUE);
+ }
+ else if (gms->player[playerI].col == NUM_PICKMELEE_COLUMNS &&
+ gms->player[playerI].row == 1)
+ {
+ // Selected exit
+ if (ConfirmExit ())
+ return FALSE;
+ }
+ else
+ {
+ // Selection is on a ship slot.
+ COUNT slotNr = PickMelee_GetShipIndex (gms->player[playerI].row,
+ gms->player[playerI].col);
+ (void) setShipSelected (gms, playerI, slotNr, TRUE);
+ // If the choice is not valid, setShipSelected()
+ // will not set .done.
+ }
+ }
+ else
+ {
+ // Process motion commands.
+ COUNT new_row, new_col;
+
+ new_row = gms->player[playerI].row;
+ new_col = gms->player[playerI].col;
+ if (inputState & BATTLE_LEFT)
+ {
+ if (new_col-- == 0)
+ new_col = NUM_PICKMELEE_COLUMNS;
+ }
+ else if (inputState & BATTLE_RIGHT)
+ {
+ if (new_col++ == NUM_PICKMELEE_COLUMNS)
+ new_col = 0;
+ }
+ if (inputState & BATTLE_THRUST)
+ {
+ if (new_row-- == 0)
+ new_row = NUM_PICKMELEE_ROWS - 1;
+ }
+ else if (inputState & BATTLE_DOWN)
+ {
+ if (++new_row == NUM_PICKMELEE_ROWS)
+ new_row = 0;
+ }
+
+ if (new_row != gms->player[playerI].row ||
+ new_col != gms->player[playerI].col)
+ {
+ gms->player[playerI].row = new_row;
+ gms->player[playerI].col = new_col;
+
+ PlayMenuSound (MENU_SOUND_MOVE);
+ PickMelee_ChangedSelection (gms, playerI);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+selectShipHuman (HumanInputContext *context, GETMELEE_STATE *gms)
+{
+ BATTLE_INPUT_STATE inputState =
+ PulsedInputToBattleInput (context->playerNr);
+
+ return SelectShip_processInput (gms, context->playerNr, inputState);
+}
+
+BOOLEAN
+selectShipComputer (ComputerInputContext *context, GETMELEE_STATE *gms)
+{
+#define COMPUTER_SELECTION_DELAY (ONE_SECOND >> 1)
+ TimeCount now = GetTimeCounter ();
+ if (now < gms->player[context->playerNr].timeIn +
+ COMPUTER_SELECTION_DELAY)
+ return TRUE;
+
+ return SelectShip_processInput (gms, context->playerNr, BATTLE_WEAPON);
+ // Simulate selection of the random choice button.
+}
+
+#ifdef NETPLAY
+BOOLEAN
+selectShipNetwork (NetworkInputContext *context, GETMELEE_STATE *gms)
+{
+ flushPacketQueues ();
+ // Sets gms->player[context->playerNr].remoteSelected if input
+ // is received.
+ if (gms->player[context->playerNr].remoteSelected)
+ gms->player[context->playerNr].done = TRUE;
+
+ return TRUE;
+}
+#endif
+
+// Select a new ship from the fleet for battle.
+// Returns 'TRUE' if no choice has been made yet; this function is to be
+// called again later.
+// Returns 'FALSE' if a choice has been made. gms->hStarShip is set
+// to the chosen (or randomly selected) ship, or to 0 if 'exit' has
+// been chosen.
+/* TODO: Include player timeouts */
+static BOOLEAN
+DoGetMelee (GETMELEE_STATE *gms)
+{
+ BOOLEAN done;
+ COUNT playerI;
+
+ SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
+
+ if (!gms->Initialized)
+ {
+ gms->Initialized = TRUE;
+ return TRUE;
+ }
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gms->player[playerI].selecting)
+ continue;
+
+ if (!gms->player[playerI].done)
+ Flash_process (gms->player[playerI].flashContext);
+ }
+
+ SleepThread (ONE_SECOND / 120);
+
+#ifdef NETPLAY
+ netInput ();
+
+ if (!allConnected ())
+ goto aborted;
+#endif
+
+ if (GLOBAL (CurrentActivity) & CHECK_ABORT)
+ goto aborted;
+
+ done = TRUE;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gms->player[playerI].selecting)
+ continue;
+
+ if (!gms->player[playerI].done) {
+ if (!PlayerInput[playerI]->handlers->selectShip (
+ PlayerInput[playerI], gms))
+ goto aborted;
+
+ if (gms->player[playerI].done)
+ {
+ Flash_terminate (gms->player[playerI].flashContext);
+ gms->player[playerI].flashContext = NULL;
+ }
+ else
+ done = FALSE;
+ }
+ }
+
+#ifdef NETPLAY
+ flushPacketQueues ();
+#endif
+ return !done;
+
+aborted:
+#ifdef NETPLAY
+ flushPacketQueues ();
+#endif
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gms->player[playerI].selecting)
+ continue;
+
+ gms->player[playerI].choice = 0;
+ gms->player[playerI].hBattleShip = 0;
+ }
+ GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
+ return FALSE;
+}
+
+static COUNT
+GetRaceQueueValue (const QUEUE *queue) {
+ COUNT result;
+ HSTARSHIP hBattleShip, hNextShip;
+
+ result = 0;
+ for (hBattleShip = GetHeadLink (queue);
+ hBattleShip != 0; hBattleShip = hNextShip)
+ {
+ STARSHIP *StarShipPtr = LockStarShip (queue, hBattleShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+
+ if (StarShipPtr->SpeciesID == NO_ID)
+ continue; // Not active any more.
+
+ result += StarShipPtr->ship_cost;
+
+ UnlockStarShip (queue, hBattleShip);
+ }
+
+ return result;
+}
+
+// Cross out the icon for the dead ship.
+// 'frame' is the PickMeleeFrame for the player.
+// 'shipI' is the index in the ship list.
+// Pre: caller holds the graphics lock.
+static void
+CrossOutShip (FRAME frame, COUNT shipNr)
+{
+ CONTEXT OldContext;
+ STAMP s;
+ BYTE row = PickMelee_GetShipRow (shipNr);
+ BYTE col = PickMelee_GetShipColumn (shipNr);
+
+ OldContext = SetContext (OffScreenContext);
+
+ SetContextFGFrame (frame);
+
+ s.origin.x = 3 + ((ICON_WIDTH + 2) * col);
+ s.origin.y = 9 + ((ICON_HEIGHT + 2) * row);
+ s.frame = SetAbsFrameIndex (StatusFrame, 3);
+ // Cross for through the ship image.
+ DrawStamp (&s);
+
+ SetContext (OldContext);
+}
+
+// Draw the value of the fleet in the top right of the PickMeleeFrame.
+// Pre: caller holds the graphics lock.
+static void
+UpdatePickMeleeFleetValue (FRAME frame, COUNT which_player)
+{
+ CONTEXT OldContext;
+ COUNT value;
+ RECT r;
+ TEXT t;
+ UNICODE buf[40];
+
+ value = GetRaceQueueValue (&race_q[which_player]);
+
+ OldContext = SetContext (OffScreenContext);
+ SetContextFGFrame (frame);
+
+ // Erase the old value text.
+ GetFrameRect (frame, &r);
+ r.extent.width -= 4;
+ t.baseline.x = r.extent.width;
+ r.corner.x = r.extent.width - (6 * 3);
+ r.corner.y = 2;
+ r.extent.width = (6 * 3);
+ r.extent.height = 7 - 2;
+ SetContextForeGroundColor (PICK_BG_COLOR);
+ DrawFilledRectangle (&r);
+
+ // Draw the new value text.
+ sprintf (buf, "%d", value);
+ t.baseline.y = 7;
+ t.align = ALIGN_RIGHT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (PICK_VALUE_COLOR);
+ font_DrawText (&t);
+
+ SetContext (OldContext);
+}
+
+// Create a frame for each player to display their current fleet in,
+// to be used when selecting the next ship to fight with.
+void
+BuildPickMeleeFrame (void)
+{
+ STAMP s;
+ CONTEXT OldContext = SetContext (OffScreenContext);
+
+ if (PickMeleeFrame)
+ DestroyDrawable (ReleaseDrawable (PickMeleeFrame));
+
+ PickMeleeFrame = CaptureDrawable (CreateDrawable (
+ WANT_PIXMAP, MELEE_WIDTH, MELEE_HEIGHT, 2));
+ s.origin.x = 0;
+ s.origin.y = 0;
+
+ s.frame = CaptureDrawable (LoadGraphic (MELEE_PICK_MASK_PMAP_ANIM));
+ SetContextFGFrame (PickMeleeFrame);
+ DrawStamp (&s);
+
+ s.frame = IncFrameIndex (s.frame);
+ SetContextFGFrame (IncFrameIndex (PickMeleeFrame));
+ DrawStamp (&s);
+
+ DestroyDrawable (ReleaseDrawable (s.frame));
+
+ SetContext (OldContext);
+}
+
+// Put the ship icons in the PickMeleeFrame, and create a queue
+// for each player.
+// XXX TODO: split off creating the queue into a separate function.
+void
+FillPickMeleeFrame (MeleeSetup *setup)
+{
+ COUNT i;
+ CONTEXT OldContext;
+
+ OldContext = SetContext (OffScreenContext);
+
+ for (i = 0; i < NUM_SIDES; ++i)
+ {
+ COUNT side;
+ COUNT sideI;
+ RECT r;
+ TEXT t;
+ STAMP s;
+ UNICODE buf[30];
+ FleetShipIndex index;
+
+ sideI = GetPlayerOrder (i);
+ side = !sideI;
+
+ s.frame = SetAbsFrameIndex (PickMeleeFrame, side);
+ SetContextFGFrame (s.frame);
+
+ GetFrameRect (s.frame, &r);
+ t.baseline.x = r.extent.width >> 1;
+ t.baseline.y = r.extent.height - NAME_AREA_HEIGHT + 4;
+
+ r.corner.x += 2;
+ r.corner.y += 2;
+ r.extent.width -= (2 * 2) + (ICON_WIDTH + 2) + 1;
+ r.extent.height -= (2 * 2) + NAME_AREA_HEIGHT;
+ SetContextForeGroundColor (PICK_BG_COLOR);
+ DrawFilledRectangle (&r);
+
+ r.corner.x += 2;
+ r.extent.width += (ICON_WIDTH + 2) - (2 * 2);
+ r.corner.y += r.extent.height;
+ r.extent.height = NAME_AREA_HEIGHT;
+ DrawFilledRectangle (&r);
+
+ // Team name at the bottom of the frame:
+ t.align = ALIGN_CENTER;
+ t.pStr = MeleeSetup_getTeamName (setup, sideI);
+ t.CharCount = (COUNT) ~0;
+ SetContextFont (TinyFont);
+ SetContextForeGroundColor (PICKSHIP_TEAM_NAME_TEXT_COLOR);
+ font_DrawText (&t);
+
+ // Total team value of the starting team:
+ sprintf (buf, "%u", MeleeSetup_getFleetValue (setup, sideI));
+ t.baseline.x = 4;
+ t.baseline.y = 7;
+ t.align = ALIGN_LEFT;
+ t.pStr = buf;
+ t.CharCount = (COUNT)~0;
+ SetContextForeGroundColor (PICKSHIP_TEAM_START_VALUE_COLOR);
+ font_DrawText (&t);
+
+ assert (CountLinks (&race_q[side]) == 0);
+
+ for (index = 0; index < MELEE_FLEET_SIZE; index++)
+ {
+ MeleeShip StarShip;
+
+ StarShip = MeleeSetup_getShip (setup, sideI, index);
+ if (StarShip == MELEE_NONE)
+ continue;
+
+ {
+ BYTE row, col;
+ BYTE ship_cost;
+ HMASTERSHIP hMasterShip;
+ HSTARSHIP hBuiltShip;
+ MASTER_SHIP_INFO *MasterPtr;
+ STARSHIP *BuiltShipPtr;
+ BYTE captains_name_index;
+
+ hMasterShip = GetStarShipFromIndex (&master_q, StarShip);
+ MasterPtr = LockMasterShip (&master_q, hMasterShip);
+
+ captains_name_index = NameCaptain (&race_q[side],
+ MasterPtr->SpeciesID);
+ hBuiltShip = Build (&race_q[side], MasterPtr->SpeciesID);
+
+ // Draw the icon.
+ row = PickMelee_GetShipRow (index);
+ col = PickMelee_GetShipColumn (index);
+ s.origin.x = 4 + ((ICON_WIDTH + 2) * col);
+ s.origin.y = 10 + ((ICON_HEIGHT + 2) * row);
+ s.frame = MasterPtr->ShipInfo.icons;
+ DrawStamp (&s);
+
+ ship_cost = MasterPtr->ShipInfo.ship_cost;
+ UnlockMasterShip (&master_q, hMasterShip);
+
+ BuiltShipPtr = LockStarShip (&race_q[side], hBuiltShip);
+ BuiltShipPtr->index = index;
+ BuiltShipPtr->ship_cost = ship_cost;
+ BuiltShipPtr->playerNr = side;
+ BuiltShipPtr->captains_name_index = captains_name_index;
+ // The next ones are not used in Melee
+ BuiltShipPtr->crew_level = 0;
+ BuiltShipPtr->max_crew = 0;
+ BuiltShipPtr->race_strings = 0;
+ BuiltShipPtr->icons = 0;
+ BuiltShipPtr->RaceDescPtr = 0;
+ UnlockStarShip (&race_q[side], hBuiltShip);
+ }
+ }
+ }
+
+ SetContext (OldContext);
+}
+
+void
+DestroyPickMeleeFrame (void)
+{
+ DestroyDrawable (ReleaseDrawable (PickMeleeFrame));
+ PickMeleeFrame = 0;
+}
+
+// Pre: caller holds the graphics lock.
+static void
+DrawPickMeleeFrame (COUNT which_player)
+{
+ CONTEXT oldContext;
+ STAMP s;
+
+ oldContext = SetContext (SpaceContext);
+ s.frame = SetAbsFrameIndex (PickMeleeFrame, which_player);
+ s.origin.x = PICK_X_OFFS - 3;
+ s.origin.y = PICK_Y_OFFS - 9 + ((1 - which_player) * PICK_SIDE_OFFS);
+ DrawStamp (&s);
+ // Draw the selection box to screen.
+
+ SetContext (oldContext);
+}
+
+// Pre: caller holds the graphics lock.
+void
+MeleeGameOver (void)
+{
+ COUNT playerI;
+ DWORD TimeOut;
+ BOOLEAN PressState, ButtonState;
+
+ // Show the battle result.
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ DrawPickMeleeFrame (playerI);
+
+
+#ifdef NETPLAY
+ negotiateReadyConnections(true, NetState_inSetup);
+#endif
+
+ TimeOut = GetTimeCounter () + (ONE_SECOND * 4);
+
+ PressState = PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL];
+ do
+ {
+ UpdateInputState ();
+ ButtonState = PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL];
+ if (PressState)
+ {
+ PressState = ButtonState;
+ ButtonState = FALSE;
+ }
+
+ Async_process ();
+ TaskSwitch ();
+ } while (!(GLOBAL (CurrentActivity) & CHECK_ABORT) && (!ButtonState
+ && (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL)
+ || GetTimeCounter () < TimeOut)));
+
+}
+
+void
+MeleeShipDeath (STARSHIP *ship)
+{
+ FRAME frame;
+
+ // Deactivate fleet position.
+ ship->SpeciesID = NO_ID;
+
+ frame = SetAbsFrameIndex (PickMeleeFrame, ship->playerNr);
+ CrossOutShip (frame, ship->index);
+ UpdatePickMeleeFleetValue (frame, ship->playerNr);
+}
+
+// Post: the NetState for all players is NetState_interBattle
+static BOOLEAN
+GetMeleeStarShips (COUNT playerMask, HSTARSHIP *ships)
+{
+ COUNT playerI;
+ BOOLEAN ok;
+ GETMELEE_STATE gmstate;
+ TimeCount now;
+ COUNT i;
+
+#ifdef NETPLAY
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn;
+
+ if ((playerMask & (1 << playerI)) == 0)
+ continue;
+
+ // XXX: This does not have to be done per connection.
+ conn = netConnections[playerI];
+ if (conn != NULL) {
+ BattleStateData *battleStateData;
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData (conn);
+ battleStateData->getMeleeState = &gmstate;
+ }
+ }
+#endif
+
+ ok = true;
+
+ now = GetTimeCounter ();
+ gmstate.InputFunc = DoGetMelee;
+ gmstate.Initialized = FALSE;
+ for (i = 0; i < NUM_PLAYERS; ++i)
+ {
+ // We have to use TFB_Random() results in specific order
+ playerI = GetPlayerOrder (i);
+ gmstate.player[playerI].selecting =
+ (playerMask & (1 << playerI)) != 0;
+ gmstate.player[playerI].ships_left = battle_counter[playerI];
+
+ // We determine in advance which ship would be chosen if the player
+ // wants a random ship, to keep it simple to keep network parties
+ // synchronised.
+ gmstate.player[playerI].randomIndex =
+ (COUNT)TFB_Random () % gmstate.player[playerI].ships_left;
+ gmstate.player[playerI].done = FALSE;
+
+ if (!gmstate.player[playerI].selecting)
+ continue;
+
+ gmstate.player[playerI].timeIn = now;
+ gmstate.player[playerI].row = 0;
+ gmstate.player[playerI].col = NUM_PICKMELEE_COLUMNS;
+#ifdef NETPLAY
+ gmstate.player[playerI].remoteSelected = FALSE;
+#endif
+
+ gmstate.player[playerI].flashContext =
+ Flash_createHighlight (ScreenContext, NULL);
+ Flash_setMergeFactors (gmstate.player[playerI].flashContext,
+ 2, 3, 2);
+ Flash_setFrameTime (gmstate.player[playerI].flashContext,
+ ONE_SECOND / 16);
+#ifdef NETPLAY
+ if (PlayerControl[playerI] & NETWORK_CONTROL)
+ Flash_setSpeed (gmstate.player[playerI].flashContext,
+ ONE_SECOND / 2, 0, ONE_SECOND / 2, 0);
+ else
+#endif
+ {
+ Flash_setSpeed (gmstate.player[playerI].flashContext,
+ 0, ONE_SECOND / 16, 0, ONE_SECOND / 16);
+ }
+ PickMelee_ChangedSelection (&gmstate, playerI);
+ Flash_start (gmstate.player[playerI].flashContext);
+ }
+
+#ifdef NETPLAY
+ {
+ // NB. gmstate.player[].randomIndex and gmstate.player[].done must
+ // be initialised before negotiateReadyConnections is completed, to
+ // ensure that they are initialised when the SelectShip packet
+ // arrives.
+ bool allOk = negotiateReadyConnections (true, NetState_selectShip);
+ if (!allOk)
+ {
+ // Some network connection has been reset.
+ ok = false;
+ }
+ }
+#endif
+ SetDefaultMenuRepeatDelay ();
+
+ SetContext (OffScreenContext);
+
+
+ DoInput (&gmstate, FALSE);
+ WaitForSoundEnd (0);
+
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ if (!gmstate.player[playerI].selecting)
+ continue;
+
+ if (gmstate.player[playerI].done)
+ {
+ // Flash rectangle is already terminated.
+ ships[playerI] = gmstate.player[playerI].hBattleShip;
+ }
+ else
+ {
+ Flash_terminate (gmstate.player[playerI].flashContext);
+ gmstate.player[playerI].flashContext = NULL;
+ ok = false;
+ }
+ }
+
+#ifdef NETPLAY
+ if (ok)
+ {
+ if (!negotiateReadyConnections (true, NetState_interBattle))
+ ok = false;
+ }
+ else
+ setStateConnections (NetState_interBattle);
+#endif
+
+ if (!ok)
+ {
+ // Aborting.
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ }
+
+#ifdef NETPLAY
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn;
+
+ if ((playerMask & (1 << playerI)) == 0)
+ continue;
+
+ // XXX: This does not have to be done per connection.
+ conn = netConnections[playerI];
+ if (conn != NULL && NetConnection_isConnected (conn))
+ {
+ BattleStateData *battleStateData;
+ battleStateData =
+ (BattleStateData *) NetConnection_getStateData (conn);
+ battleStateData->getMeleeState = NULL;
+ }
+ }
+#endif
+
+ return ok;
+}
+
+BOOLEAN
+GetInitialMeleeStarShips (HSTARSHIP *result)
+{
+ COUNT playerI;
+ COUNT playerMask;
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ FRAME frame;
+ frame = SetAbsFrameIndex (PickMeleeFrame, playerI);
+ UpdatePickMeleeFleetValue (frame, playerI);
+ DrawPickMeleeFrame (playerI);
+ }
+
+ // Fade in
+ SleepThreadUntil (FadeScreen (FadeAllToColor, ONE_SECOND / 2)
+ + ONE_SECOND / 60);
+ FlushColorXForms ();
+
+ playerMask = 0;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ playerMask |= (1 << playerI);
+
+ return GetMeleeStarShips (playerMask, result);
+}
+
+// Get the next ship to use in SuperMelee.
+BOOLEAN
+GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result)
+{
+ COUNT playerMask;
+ HSTARSHIP ships[NUM_PLAYERS];
+ BOOLEAN ok;
+
+ DrawPickMeleeFrame (which_player);
+
+ playerMask = 1 << which_player;
+ ok = GetMeleeStarShips (playerMask, ships);
+ if (ok)
+ *result = ships[which_player];
+
+ return ok;
+}
+
+#ifdef NETPLAY
+// Called when a ship selection has arrived from a remote player.
+bool
+updateMeleeSelection (GETMELEE_STATE *gms, COUNT playerI, COUNT ship)
+{
+ if (gms == NULL || !gms->player[playerI].selecting ||
+ gms->player[playerI].done)
+ {
+ // This happens when we get an update message from a connection
+ // for who we are not selecting a ship.
+ log_add (log_Warning, "Unexpected ship selection packet "
+ "received.\n");
+ return false;
+ }
+
+ if (!setShipSelected (gms, playerI, ship, false))
+ {
+ log_add (log_Warning, "Invalid ship selection received from remote "
+ "party.\n");
+ return false;
+ }
+
+ gms->player[playerI].remoteSelected = TRUE;
+ return true;
+}
+
+static void
+reportShipSelected (GETMELEE_STATE *gms, COUNT index)
+{
+ size_t playerI;
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ {
+ NetConnection *conn = netConnections[playerI];
+
+ if (conn == NULL)
+ continue;
+
+ if (!NetConnection_isConnected (conn))
+ continue;
+
+ Netplay_Notify_shipSelected (conn, index);
+ }
+ (void) gms;
+}
+#endif
+
diff --git a/src/uqm/supermelee/pickmele.h b/src/uqm/supermelee/pickmele.h
new file mode 100644
index 0000000..3588063
--- /dev/null
+++ b/src/uqm/supermelee/pickmele.h
@@ -0,0 +1,102 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef UQM_SUPERMELEE_PICKMELE_H_
+#define UQM_SUPERMELEE_PICKMELE_H_
+
+typedef struct getmelee_struct GETMELEE_STATE;
+
+#include "../races.h"
+#include "../battlecontrols.h"
+#include "meleesetup.h"
+#include "libs/compiler.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void MeleeShipDeath (STARSHIP *);
+void BuildPickMeleeFrame (void);
+void DestroyPickMeleeFrame (void);
+void FillPickMeleeFrame (MeleeSetup *setup);
+void MeleeGameOver (void);
+BOOLEAN GetInitialMeleeStarShips (HSTARSHIP *result);
+BOOLEAN GetNextMeleeStarShip (COUNT which_player, HSTARSHIP *result);
+
+bool updateMeleeSelection (GETMELEE_STATE *gms, COUNT player, COUNT ship);
+
+BOOLEAN selectShipHuman (HumanInputContext *context, GETMELEE_STATE *gms);
+BOOLEAN selectShipComputer (ComputerInputContext *context,
+ GETMELEE_STATE *gms);
+#ifdef NETPLAY
+BOOLEAN selectShipNetwork (NetworkInputContext *context, GETMELEE_STATE *gms);
+#endif /* NETPLAY */
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef PICKMELE_INTERNAL
+
+#include "../flash.h"
+#include "libs/timelib.h"
+#include "../init.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct getmelee_struct {
+ BOOLEAN (*InputFunc) (struct getmelee_struct *pInputState);
+
+ BOOLEAN Initialized;
+
+ struct {
+ TimeCount timeIn;
+ HSTARSHIP hBattleShip;
+ // Chosen ship.
+ COUNT choice;
+ // Index of chosen ship, or (COUNT) ~0 for random choice.
+
+ COUNT row;
+ COUNT col;
+ COUNT ships_left;
+ // Number of ships still available.
+ COUNT randomIndex;
+ // Pre-generated random number.
+ BOOLEAN selecting;
+ // Is this player selecting a ship?
+ BOOLEAN done;
+ // Has a selection been made for this player?
+ FlashContext *flashContext;
+ // Context for controlling the flash rectangle.
+#ifdef NETPLAY
+ BOOLEAN remoteSelected;
+#endif
+ } player[NUM_PLAYERS];
+};
+
+bool setShipSelected(GETMELEE_STATE *gms, COUNT playerI, COUNT choice,
+ bool reportNetwork);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* PICKMELE_INTERNAL */
+
+#endif /* UQM_SUPERMELEE_PICKMELE_H_ */
+
diff --git a/src/uqm/tactrans.c b/src/uqm/tactrans.c
new file mode 100644
index 0000000..4e2b896
--- /dev/null
+++ b/src/uqm/tactrans.c
@@ -0,0 +1,1032 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "tactrans.h"
+
+#include "battlecontrols.h"
+#include "build.h"
+#include "collide.h"
+#include "globdata.h"
+#include "element.h"
+#include "ship.h"
+#include "status.h"
+#include "battle.h"
+#include "init.h"
+#include "supermelee/pickmele.h"
+#ifdef NETPLAY
+# include "supermelee/netplay/netmelee.h"
+# include "supermelee/netplay/netmisc.h"
+# include "supermelee/netplay/notify.h"
+# include "supermelee/netplay/proto/ready.h"
+# include "supermelee/netplay/packet.h"
+# include "supermelee/netplay/packetq.h"
+#endif
+#include "races.h"
+#include "encount.h"
+#include "settings.h"
+#include "sounds.h"
+#include "libs/mathlib.h"
+
+
+static void cleanup_dead_ship (ELEMENT *ElementPtr);
+
+static BOOLEAN dittyIsPlaying;
+static STARSHIP *winnerStarShip;
+ // Indicates which ship is the winner of the current battle.
+ // The winner will be last to pick the next ship.
+
+
+BOOLEAN
+OpponentAlive (STARSHIP *TestStarShipPtr)
+{
+ HELEMENT hElement, hSuccElement;
+
+ for (hElement = GetHeadElement (); hElement; hElement = hSuccElement)
+ {
+ ELEMENT *ElementPtr;
+ STARSHIP *StarShipPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hSuccElement = GetSuccElement (ElementPtr);
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ UnlockElement (hElement);
+
+ if (StarShipPtr && StarShipPtr != TestStarShipPtr
+ && StarShipPtr->RaceDescPtr->ship_info.crew_level == 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+PlayDitty (STARSHIP *ship)
+{
+ PlayMusic (ship->RaceDescPtr->ship_data.victory_ditty, FALSE, 3);
+ dittyIsPlaying = TRUE;
+}
+
+void
+StopDitty (void)
+{
+ if (dittyIsPlaying)
+ StopMusic ();
+ dittyIsPlaying = FALSE;
+}
+
+static BOOLEAN
+DittyPlaying (void)
+{
+ if (!dittyIsPlaying)
+ return FALSE;
+
+ dittyIsPlaying = PLRPlaying ((MUSIC_REF)~0);
+ return dittyIsPlaying;
+}
+
+void
+ResetWinnerStarShip (void)
+{
+ winnerStarShip = NULL;
+}
+
+#ifdef NETPLAY
+static void
+readyToEnd2Callback (NetConnection *conn, void *arg)
+{
+ NetConnection_setState (conn, NetState_endingBattle2);
+ (void) arg;
+}
+
+static void
+readyToEndCallback (NetConnection *conn, void *arg)
+{
+ // This callback function gets called from inside the function that
+ // updates the frame counter, but this is not a problem as the
+ // ending frame count will at least be 1 greater than the current
+ // frame count.
+
+ BattleStateData *battleStateData;
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+
+#ifdef NETPLAY_DEBUG
+ fprintf (stderr, "Both sides are ready to end the battle; starting "
+ "end-of-battle synchronisation.\n");
+#endif
+ NetConnection_setState (conn, NetState_endingBattle);
+ if (battleFrameCount + 1 > battleStateData->endFrameCount)
+ battleStateData->endFrameCount = battleFrameCount + 1;
+ Netplay_Notify_frameCount (conn, battleFrameCount + 1);
+ // The +1 is to ensure that after the remote side receives the
+ // frame count it will still receive one more frame data packet,
+ // so it will know in advance when the last frame data packet
+ // will come so it won't block. It also ensures that the
+ // local frame counter won't go past the sent number, which
+ // could happen when the function triggering the call to this
+ // function is the frame update function which might update
+ // the frame counter one more time.
+ flushPacketQueue (conn);
+#ifdef NETPLAY_DEBUG
+ fprintf (stderr, "NETPLAY: [%d] ==> Sent battleFrameCount %d.\n",
+ NetConnection_getPlayerNr(conn), battleFrameCount + 1);
+#endif
+ Netplay_localReady(conn, readyToEnd2Callback, NULL, false);
+ (void) arg;
+}
+
+/*
+ * When one player's ship dies, there's a delay before the next ship
+ * can be chosen. This time depends on the time the ditty is playing
+ * and may differ for each side.
+ * To synchronise the time, the following protocol is followed:
+ * 1. (NetState_inBattle) The Ready protocol is used to let either
+ * party know when they're ready to stop the battle.
+ * 2. (NetState_endingBattle) Each party sends the frame number of when
+ * it wants to end the battle, and continues until that point, where
+ * it waits until it has received the frame number of the other party.
+ * 3. After a player has both sent and received a frame count, the
+ * simulation continues for each party, until the maximum of both
+ * frame counts has been achieved.
+ * 4. The Ready protocol is used to let each side signal that it has
+ * reached the target frame count.
+ * 5. The battle ends.
+ */
+static bool
+readyForBattleEndPlayer (NetConnection *conn)
+{
+ BattleStateData *battleStateData;
+ battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
+
+ if (NetConnection_getState (conn) == NetState_interBattle ||
+ NetConnection_getState (conn) == NetState_inSetup)
+ {
+ // This connection is already ready. The entire synchronisation
+ // protocol has already been done for this connection.
+ return true;
+ }
+
+ if (NetConnection_getState (conn) == NetState_inBattle)
+ {
+ if (Netplay_isLocalReady(conn))
+ {
+ // We've already sent notice that we are ready, but we're
+ // still waiting for the other side to say it's ready too.
+ return false;
+ }
+
+ // We haven't yet told the other side we're ready. We do so now.
+ Netplay_localReady (conn, readyToEndCallback, NULL, true);
+ // This may set the state to endingBattle.
+
+ if (NetConnection_getState (conn) == NetState_inBattle)
+ return false;
+ }
+
+ assert (NetConnection_getState (conn) == NetState_endingBattle ||
+ NetConnection_getState (conn) == NetState_endingBattle2);
+
+ // Keep the simulation going as long as the target frame count
+ // hasn't been reached yet. Note that if the connection state is
+ // NetState_endingBattle, then we haven't yet received the
+ // remote frame count, so the target frame count may still rise.
+ if (battleFrameCount < battleStateData->endFrameCount)
+ return false;
+
+ if (NetConnection_getState (conn) == NetState_endingBattle)
+ {
+ // We have reached the target frame count, but we don't know
+ // the remote target frame count yet. So we wait until it has
+ // come in.
+ waitReady (conn);
+ // TODO: check whether all connections are still connected.
+ assert (NetConnection_getState (conn) == NetState_endingBattle2);
+
+ // Continue the simulation if the battleFrameCount has gone up.
+ if (battleFrameCount < battleStateData->endFrameCount)
+ return false;
+ }
+
+ // We are ready and wait for the other party to become ready too.
+ negotiateReady (conn, true, NetState_interBattle);
+
+ return true;
+}
+#endif
+
+bool
+battleEndReadyHuman (HumanInputContext *context)
+{
+ (void) context;
+ return true;
+}
+
+bool
+battleEndReadyComputer (ComputerInputContext *context)
+{
+ (void) context;
+ return true;
+}
+
+#ifdef NETPLAY
+bool
+battleEndReadyNetwork (NetworkInputContext *context)
+{
+ return readyForBattleEndPlayer (netConnections[context->playerNr]);
+}
+#endif
+
+// Returns true iff this side is ready to end the battle.
+static inline bool
+readyForBattleEnd (void)
+{
+#ifndef NETPLAY
+#if DEMO_MODE
+ // In Demo mode, the saved journal should be replayed with frame
+ // accuracy. PLRPlaying () isn't consistent enough.
+ return true;
+#else /* !DEMO_MODE */
+ return !DittyPlaying ();
+#endif /* !DEMO_MODE */
+#else /* defined (NETPLAY) */
+ int playerI;
+
+ if (DittyPlaying ())
+ return false;
+
+ for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
+ if (!PlayerInput[playerI]->handlers->battleEndReady (
+ PlayerInput[playerI]))
+ return false;
+
+ return true;
+#endif /* defined (NETPLAY) */
+}
+
+static void
+preprocess_dead_ship (ELEMENT *DeadShipPtr)
+{
+ ProcessSound ((SOUND)~0, NULL);
+ (void)DeadShipPtr; // unused argument
+}
+
+void
+cleanup_dead_ship (ELEMENT *DeadShipPtr)
+{
+ STARSHIP *DeadStarShipPtr;
+
+ ProcessSound ((SOUND)~0, NULL);
+
+ GetElementStarShip (DeadShipPtr, &DeadStarShipPtr);
+ {
+ // Ship explosion has finished, or ship has just warped out
+ // if DeadStarShipPtr->crew_level != 0
+ BOOLEAN MusicStarted;
+ HELEMENT hElement, hSuccElement;
+
+ /* Record crew left after the battle */
+ DeadStarShipPtr->crew_level =
+ DeadStarShipPtr->RaceDescPtr->ship_info.crew_level;
+
+ MusicStarted = FALSE;
+
+ for (hElement = GetHeadElement (); hElement; hElement = hSuccElement)
+ {
+ ELEMENT *ElementPtr;
+ STARSHIP *StarShipPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hSuccElement = GetSuccElement (ElementPtr);
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ // Get the STARSHIP that this ELEMENT belongs to.
+
+ if (StarShipPtr == DeadStarShipPtr)
+ {
+ // This element belongs to the dead ship; it may be the
+ // ship's own element.
+ SetElementStarShip (ElementPtr, 0);
+
+ if (!(ElementPtr->state_flags & CREW_OBJECT)
+ || ElementPtr->preprocess_func != crew_preprocess)
+ {
+ // Set the element up for deletion.
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex],
+ NO_PRIM);
+ ElementPtr->life_span = 0;
+ ElementPtr->state_flags =
+ NONSOLID | DISAPPEARING | FINITE_LIFE;
+ ElementPtr->preprocess_func = 0;
+ ElementPtr->postprocess_func = 0;
+ ElementPtr->death_func = 0;
+ ElementPtr->collision_func = 0;
+ }
+ }
+
+ if (StarShipPtr
+ && (StarShipPtr->cur_status_flags & PLAY_VICTORY_DITTY))
+ {
+ // StarShipPtr points to the remaining ship.
+ MusicStarted = TRUE;
+ PlayDitty (StarShipPtr);
+ StarShipPtr->cur_status_flags &= ~PLAY_VICTORY_DITTY;
+ }
+
+ UnlockElement (hElement);
+ }
+
+#define MIN_DITTY_FRAME_COUNT ((ONE_SECOND * 3) / BATTLE_FRAME_RATE)
+ // The ship will be "alive" for at least 2 more frames to make sure
+ // the elements it owns (set up for deletion above) expire first.
+ // Ditty does NOT play in the following circumstances:
+ // * The winning ship dies before the loser finishes exploding
+ // * At the moment the losing ship dies, the winner has started
+ // the warp out sequence
+ DeadShipPtr->life_span = MusicStarted ? MIN_DITTY_FRAME_COUNT : 1;
+ if (DeadStarShipPtr == winnerStarShip)
+ { // This ship died but won the battle. We need to keep it alive
+ // longer than the dead opponent ship so that the winning player
+ // picks last.
+ DeadShipPtr->life_span = MIN_DITTY_FRAME_COUNT + 1;
+ }
+ DeadShipPtr->death_func = new_ship;
+ DeadShipPtr->preprocess_func = preprocess_dead_ship;
+ DeadShipPtr->state_flags &= ~DISAPPEARING;
+ // XXX: this increment was originally done by another piece of code
+ // just below this one. I am almost sure it is not needed, but it
+ // keeps the original framecount.
+ ++DeadShipPtr->life_span;
+ SetElementStarShip (DeadShipPtr, DeadStarShipPtr);
+ }
+}
+
+static void
+setMinShipLifeSpan (ELEMENT *ship, COUNT life_span)
+{
+ if (ship->death_func == new_ship)
+ { // The ship has finished exploding or warping out, and now
+ // we can work with the remaining element
+ assert (ship->state_flags & FINITE_LIFE);
+ assert (!(ship->state_flags & DISAPPEARING));
+ if (ship->life_span < life_span)
+ ship->life_span = life_span;
+ }
+}
+
+static void
+setMinStarShipLifeSpan (STARSHIP *starShip, COUNT life_span)
+{
+ ELEMENT *ship;
+
+ LockElement (starShip->hShip, &ship);
+ setMinShipLifeSpan (ship, life_span);
+ UnlockElement (starShip->hShip);
+}
+
+static void
+checkOtherShipLifeSpan (ELEMENT *deadShip)
+{
+ STARSHIP *deadStarShip;
+
+ GetElementStarShip (deadShip, &deadStarShip);
+
+ if (winnerStarShip != NULL && deadStarShip != winnerStarShip
+ && winnerStarShip->RaceDescPtr->ship_info.crew_level == 0)
+ { // The opponent ship also died but won anyway (e.g. Glory device)
+ // We need to keep the opponent ship alive longer so that the
+ // winning player picks last.
+ setMinStarShipLifeSpan (winnerStarShip, deadShip->life_span + 1);
+ }
+ else if (winnerStarShip == NULL)
+ { // Both died at the same time, or the loser has already expired
+ HELEMENT hElement, hNextElement;
+
+ // Find the other dead ship(s) and keep them alive for at least as
+ // long as this ship.
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *element;
+ STARSHIP *starShip;
+
+ LockElement (hElement, &element);
+ hNextElement = GetSuccElement (element);
+ GetElementStarShip (element, &starShip);
+
+ if (starShip != NULL && element != deadShip
+ && starShip->RaceDescPtr->ship_info.crew_level == 0)
+ { // This is another dead ship
+ setMinShipLifeSpan (element, deadShip->life_span);
+ }
+
+ UnlockElement (hElement);
+ }
+ }
+}
+
+// This function is called when dead ship element's life_span reaches 0
+void
+new_ship (ELEMENT *DeadShipPtr)
+{
+ STARSHIP *DeadStarShipPtr;
+
+ GetElementStarShip (DeadShipPtr, &DeadStarShipPtr);
+
+ if (!readyForBattleEnd ())
+ {
+ DeadShipPtr->state_flags &= ~DISAPPEARING;
+ ++DeadShipPtr->life_span;
+
+ // Keep the winner alive longer, or in a simultaneous destruction
+ // tie, keep the other dead ship alive so that readyForBattleEnd()
+ // is called for only one ship at a time.
+ // When a ship has been destroyed, each side of a network
+ // connection waits until the other side is ready.
+ // When two ships die at the same time, this is handled for one
+ // ship after the other.
+ checkOtherShipLifeSpan (DeadShipPtr);
+ return;
+ }
+
+ // Once a ship is being picked, we do not care about the winner anymore
+ winnerStarShip = NULL;
+
+ {
+ BOOLEAN RestartMusic;
+
+ StopDitty ();
+ StopMusic ();
+ StopSound ();
+
+ SetElementStarShip (DeadShipPtr, 0);
+ RestartMusic = OpponentAlive (DeadStarShipPtr);
+
+ free_ship (DeadStarShipPtr->RaceDescPtr, TRUE, TRUE);
+ DeadStarShipPtr->RaceDescPtr = 0;
+
+ // Graphics are batched while the draw queue is processed,
+ // but we are going to draw the ship selection box now
+ UnbatchGraphics ();
+
+#ifdef NETPLAY
+ initBattleStateDataConnections ();
+ {
+ bool allOk =
+ negotiateReadyConnections (true, NetState_interBattle);
+ // We are already in NetState_interBattle, but all
+ // sides just need to pass this checkpoint before
+ // going on.
+ if (!allOk)
+ {
+ // Some network connection has been reset.
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ BatchGraphics ();
+ return;
+ }
+ }
+#endif /* NETPLAY */
+
+ if (!FleetIsInfinite (DeadStarShipPtr->playerNr))
+ { // This may be a dead ship (crew_level == 0) or a warped out ship
+ UpdateShipFragCrew (DeadStarShipPtr);
+ // Deactivate the ship (cannot be selected)
+ DeadStarShipPtr->SpeciesID = NO_ID;
+ }
+
+ if (GetNextStarShip (DeadStarShipPtr, DeadStarShipPtr->playerNr))
+ {
+#ifdef NETPLAY
+ {
+ bool allOk =
+ negotiateReadyConnections (true, NetState_inBattle);
+ if (!allOk)
+ {
+ // Some network connection has been reset.
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ BatchGraphics ();
+ return;
+ }
+ }
+#endif
+ if (RestartMusic)
+ BattleSong (TRUE);
+ }
+ else if (battle_counter[0] == 0 || battle_counter[1] == 0)
+ {
+ // One player is out of ships. The battle is over.
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+ }
+#ifdef NETPLAY
+ else
+ {
+ // Battle has been aborted.
+ GLOBAL (CurrentActivity) |= CHECK_ABORT;
+ }
+#endif
+ BatchGraphics ();
+ }
+}
+
+static void
+explosion_preprocess (ELEMENT *ShipPtr)
+{
+ BYTE i;
+
+ i = (NUM_EXPLOSION_FRAMES * 3) - ShipPtr->life_span;
+ switch (i)
+ {
+ case 25:
+ ShipPtr->preprocess_func = NULL;
+ case 0:
+ case 1:
+ case 2:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24:
+ i = 1;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 18:
+ case 19:
+ i = 2;
+ break;
+ case 15:
+ SetPrimType (&DisplayArray[ShipPtr->PrimIndex], NO_PRIM);
+ ShipPtr->state_flags |= CHANGING;
+ default:
+ i = 3;
+ break;
+ }
+
+ do
+ {
+ HELEMENT hElement;
+
+ hElement = AllocElement ();
+ if (hElement)
+ {
+ COUNT angle, dist;
+ DWORD rand_val;
+ ELEMENT *ElementPtr;
+ extern FRAME explosion[];
+
+ PutElement (hElement);
+ LockElement (hElement, &ElementPtr);
+ ElementPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ ElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ ElementPtr->life_span = 9;
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMP_PRIM);
+ ElementPtr->current.image.farray = explosion;
+ ElementPtr->current.image.frame = explosion[0];
+ rand_val = TFB_Random ();
+ angle = LOBYTE (HIWORD (rand_val));
+ dist = DISPLAY_TO_WORLD (LOBYTE (LOWORD (rand_val)) % 8);
+ if (HIBYTE (LOWORD (rand_val)) < 256 * 1 / 3)
+ dist += DISPLAY_TO_WORLD (8);
+ ElementPtr->current.location.x =
+ ShipPtr->current.location.x + COSINE (angle, dist);
+ ElementPtr->current.location.y =
+ ShipPtr->current.location.y + SINE (angle, dist);
+ ElementPtr->preprocess_func = animation_preprocess;
+ rand_val = TFB_Random ();
+ angle = LOBYTE (LOWORD (rand_val));
+ dist = WORLD_TO_VELOCITY (
+ DISPLAY_TO_WORLD (HIBYTE (LOWORD (rand_val)) % 5));
+ SetVelocityComponents (&ElementPtr->velocity,
+ COSINE (angle, dist), SINE (angle, dist));
+ UnlockElement (hElement);
+ }
+ } while (--i);
+}
+
+void
+StopAllBattleMusic (void)
+{
+ StopDitty ();
+ StopMusic ();
+}
+
+STARSHIP *
+FindAliveStarShip (ELEMENT *deadShip)
+{
+ STARSHIP *aliveShip = NULL;
+ HELEMENT hElement, hNextElement;
+
+ // Find the remaining ship, if any, and see if it is still alive.
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ if ((ElementPtr->state_flags & PLAYER_SHIP)
+ && ElementPtr != deadShip
+ /* and not running away */
+ && ElementPtr->mass_points <= MAX_SHIP_MASS + 1)
+ {
+ GetElementStarShip (ElementPtr, &aliveShip);
+ assert (aliveShip != NULL);
+ if (aliveShip->RaceDescPtr->ship_info.crew_level == 0
+ /* reincarnating Pkunk is not actually dead */
+ && ElementPtr->mass_points != MAX_SHIP_MASS + 1)
+ {
+ aliveShip = NULL;
+ }
+
+ UnlockElement (hElement);
+ break;
+ }
+ hNextElement = GetSuccElement (ElementPtr);
+ UnlockElement (hElement);
+ }
+
+ return aliveShip;
+}
+
+STARSHIP *
+GetWinnerStarShip (void)
+{
+ return winnerStarShip;
+}
+
+void
+SetWinnerStarShip (STARSHIP *winner)
+{
+ if (winner == NULL)
+ return; // nothing to do
+
+ winner->cur_status_flags |= PLAY_VICTORY_DITTY;
+
+ // The winner is set once per battle. If both ships die, this function is
+ // called twice, once for each ship. We need to preserve the winner
+ // determined on the first call.
+ if (winnerStarShip == NULL)
+ winnerStarShip = winner;
+}
+
+void
+RecordShipDeath (ELEMENT *deadShip)
+{
+ STARSHIP *deadStarShip;
+
+ GetElementStarShip (deadShip, &deadStarShip);
+ assert (deadStarShip != NULL);
+
+ if (deadShip->mass_points <= MAX_SHIP_MASS)
+ { // Not running away.
+ // When a ship tries to run away, it is (dis)counted in DoRunAway(),
+ // so when it dies while running away, we will not count it again
+ assert (deadStarShip->playerNr >= 0);
+ battle_counter[deadStarShip->playerNr]--;
+ }
+
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ MeleeShipDeath (deadStarShip);
+}
+
+void
+StartShipExplosion (ELEMENT *ShipPtr, bool playSound)
+{
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ ZeroVelocityComponents (&ShipPtr->velocity);
+
+ DeltaEnergy (ShipPtr,
+ -(SIZE)StarShipPtr->RaceDescPtr->ship_info.energy_level);
+
+ ShipPtr->life_span = NUM_EXPLOSION_FRAMES * 3;
+ ShipPtr->state_flags &= ~DISAPPEARING;
+ ShipPtr->state_flags |= FINITE_LIFE | NONSOLID;
+ ShipPtr->preprocess_func = explosion_preprocess;
+ ShipPtr->postprocess_func = PostProcessStatus;
+ ShipPtr->death_func = cleanup_dead_ship;
+ ShipPtr->hTarget = 0;
+
+ if (playSound)
+ {
+ PlaySound (SetAbsSoundIndex (GameSounds, SHIP_EXPLODES),
+ CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1);
+ }
+}
+
+void
+ship_death (ELEMENT *ShipPtr)
+{
+ STARSHIP *StarShipPtr;
+ STARSHIP *winner;
+
+ GetElementStarShip (ShipPtr, &StarShipPtr);
+
+ StopAllBattleMusic ();
+
+ // If the winning ship dies before the ditty starts, do not play it.
+ // e.g. a ship can die after the opponent begins exploding but
+ // before the explosion is over.
+ StarShipPtr->cur_status_flags &= ~PLAY_VICTORY_DITTY;
+
+ StartShipExplosion (ShipPtr, true);
+
+ winner = FindAliveStarShip (ShipPtr);
+ SetWinnerStarShip (winner);
+ RecordShipDeath (ShipPtr);
+}
+
+#define START_ION_COLOR BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A)
+
+// Called from the death_func of an element for an ion trail pixel, or a
+// ship shadow (when warping in/out).
+static void
+cycle_ion_trail (ELEMENT *ElementPtr)
+{
+ static const Color colorTab[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x15, 0x00), 0x7a),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x11, 0x00), 0x7b),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0E, 0x00), 0x7c),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x00), 0x7d),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x07, 0x00), 0x7e),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x03, 0x00), 0x7f),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x2a),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2b),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2c),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2d),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2e),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0B, 0x00, 0x00), 0x2f),
+ };
+ const size_t colorTabCount = sizeof colorTab / sizeof colorTab[0];
+
+ assert (!(ElementPtr->state_flags & PLAYER_SHIP));
+
+ ElementPtr->colorCycleIndex++;
+ if (ElementPtr->colorCycleIndex != colorTabCount)
+ {
+ ElementPtr->life_span = ElementPtr->thrust_wait;
+ // Reset the life span.
+
+ SetPrimColor (&DisplayArray[ElementPtr->PrimIndex],
+ colorTab[ElementPtr->colorCycleIndex]);
+
+ ElementPtr->state_flags &= ~DISAPPEARING;
+ ElementPtr->state_flags |= CHANGING;
+ } // else, the element disappears.
+}
+
+void
+spawn_ion_trail (ELEMENT *ElementPtr)
+{
+ HELEMENT hIonElement;
+
+ assert (ElementPtr->state_flags & PLAYER_SHIP);
+
+ hIonElement = AllocElement ();
+ if (hIonElement)
+ {
+#define ION_LIFE 1
+ COUNT angle;
+ RECT r;
+ ELEMENT *IonElementPtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing) + HALF_CIRCLE;
+ GetFrameRect (StarShipPtr->RaceDescPtr->ship_data.ship[0], &r);
+ r.extent.height = DISPLAY_TO_WORLD (r.extent.height + r.corner.y);
+
+ InsertElement (hIonElement, GetHeadElement ());
+ LockElement (hIonElement, &IonElementPtr);
+ IonElementPtr->playerNr = NEUTRAL_PLAYER_NUM;
+ IonElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ IonElementPtr->thrust_wait = ION_LIFE;
+ IonElementPtr->life_span = IonElementPtr->thrust_wait;
+ // When the element "dies", in the death_func
+ // 'cycle_ion_trail', it is given new life a number of
+ // times, by setting life_span to thrust_wait.
+ SetPrimType (&DisplayArray[IonElementPtr->PrimIndex], POINT_PRIM);
+ SetPrimColor (&DisplayArray[IonElementPtr->PrimIndex],
+ START_ION_COLOR);
+ IonElementPtr->colorCycleIndex = 0;
+ IonElementPtr->current.image.frame =
+ DecFrameIndex (stars_in_space);
+ IonElementPtr->current.image.farray = &stars_in_space;
+ IonElementPtr->current.location = ElementPtr->current.location;
+ IonElementPtr->current.location.x +=
+ (COORD)COSINE (angle, r.extent.height);
+ IonElementPtr->current.location.y +=
+ (COORD)SINE (angle, r.extent.height);
+ IonElementPtr->death_func = cycle_ion_trail;
+
+ SetElementStarShip (IonElementPtr, StarShipPtr);
+
+ {
+ /* normally done during preprocess, but because
+ * object is being inserted at head rather than
+ * appended after tail it may never get preprocessed.
+ */
+ IonElementPtr->next = IonElementPtr->current;
+ --IonElementPtr->life_span;
+ IonElementPtr->state_flags |= PRE_PROCESS;
+ }
+
+ UnlockElement (hIonElement);
+ }
+}
+
+// Preprocess function for spawning a ship into or out of battle.
+// Used when a new ship warps in, or a ship escapes by warping out, but not
+// when a Pkunk ship is reborn.
+void
+ship_transition (ELEMENT *ElementPtr)
+{
+ if (ElementPtr->state_flags & PLAYER_SHIP)
+ {
+ if (ElementPtr->state_flags & APPEARING)
+ {
+ ElementPtr->life_span = HYPERJUMP_LIFE;
+ ElementPtr->preprocess_func = ship_transition;
+ ElementPtr->postprocess_func = NULL;
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM);
+ ElementPtr->state_flags |= NONSOLID | FINITE_LIFE | CHANGING;
+ }
+ else if (ElementPtr->life_span < HYPERJUMP_LIFE)
+ {
+ if (ElementPtr->life_span == NORMAL_LIFE
+ && ElementPtr->crew_level)
+ {
+ ElementPtr->current.image.frame =
+ ElementPtr->next.image.frame =
+ SetEquFrameIndex (
+ ElementPtr->current.image.farray[0],
+ ElementPtr->current.image.frame);
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMP_PRIM);
+ InitIntersectStartPoint (ElementPtr);
+ InitIntersectEndPoint (ElementPtr);
+ InitIntersectFrame (ElementPtr);
+ ZeroVelocityComponents (&ElementPtr->velocity);
+ ElementPtr->state_flags &= ~(NONSOLID | FINITE_LIFE);
+ ElementPtr->state_flags |= CHANGING;
+
+ ElementPtr->preprocess_func = ship_preprocess;
+ ElementPtr->postprocess_func = ship_postprocess;
+ }
+
+ return;
+ }
+ }
+
+ {
+ HELEMENT hShipImage;
+ ELEMENT *ShipImagePtr;
+ STARSHIP *StarShipPtr;
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ LockElement (StarShipPtr->hShip, &ShipImagePtr);
+
+ if (!(ShipImagePtr->state_flags & NONSOLID))
+ {
+ ElementPtr->preprocess_func = NULL;
+ }
+ else if ((hShipImage = AllocElement ()))
+ {
+#define TRANSITION_SPEED DISPLAY_TO_WORLD (40)
+#define TRANSITION_LIFE 1
+ COUNT angle;
+
+ PutElement (hShipImage);
+
+ angle = FACING_TO_ANGLE (StarShipPtr->ShipFacing);
+
+ LockElement (hShipImage, &ShipImagePtr);
+ ShipImagePtr->playerNr = NEUTRAL_PLAYER_NUM;
+ ShipImagePtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ ShipImagePtr->thrust_wait = TRANSITION_LIFE;
+ ShipImagePtr->life_span = ShipImagePtr->thrust_wait;
+ // When the element "dies", in the death_func
+ // 'cycle_ion_trail', it is given new life a number of
+ // times, by setting life_span to thrust_wait.
+ SetPrimType (&DisplayArray[ShipImagePtr->PrimIndex],
+ STAMPFILL_PRIM);
+ SetPrimColor (&DisplayArray[ShipImagePtr->PrimIndex],
+ START_ION_COLOR);
+ ShipImagePtr->colorCycleIndex = 0;
+ ShipImagePtr->current.image = ElementPtr->current.image;
+ ShipImagePtr->current.location = ElementPtr->current.location;
+ if (!(ElementPtr->state_flags & PLAYER_SHIP))
+ {
+ ShipImagePtr->current.location.x +=
+ COSINE (angle, TRANSITION_SPEED);
+ ShipImagePtr->current.location.y +=
+ SINE (angle, TRANSITION_SPEED);
+ ElementPtr->preprocess_func = NULL;
+ }
+ else if (ElementPtr->crew_level)
+ {
+ ShipImagePtr->current.location.x -=
+ COSINE (angle, TRANSITION_SPEED)
+ * (ElementPtr->life_span - 1);
+ ShipImagePtr->current.location.y -=
+ SINE (angle, TRANSITION_SPEED)
+ * (ElementPtr->life_span - 1);
+
+ ShipImagePtr->current.location.x =
+ WRAP_X (ShipImagePtr->current.location.x);
+ ShipImagePtr->current.location.y =
+ WRAP_Y (ShipImagePtr->current.location.y);
+ }
+ ShipImagePtr->preprocess_func = ship_transition;
+ ShipImagePtr->death_func = cycle_ion_trail;
+ SetElementStarShip (ShipImagePtr, StarShipPtr);
+
+ UnlockElement (hShipImage);
+ }
+
+ UnlockElement (StarShipPtr->hShip);
+ }
+}
+
+void
+flee_preprocess (ELEMENT *ElementPtr)
+{
+ STARSHIP *StarShipPtr;
+
+ if (--ElementPtr->turn_wait == 0)
+ {
+ static const Color colorTab[] =
+ {
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0A, 0x00, 0x00), 0x2E),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0E, 0x00, 0x00), 0x2D),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x29),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x04, 0x04), 0x28),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x0A), 0x27),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0F, 0x0F), 0x26),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x13, 0x13), 0x25),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x19, 0x19), 0x24),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x13, 0x13), 0x25),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0F, 0x0F), 0x26),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x0A, 0x0A), 0x27),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x04, 0x04), 0x28),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1F, 0x00, 0x00), 0x29),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x1B, 0x00, 0x00), 0x2A),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x17, 0x00, 0x00), 0x2B),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x13, 0x00, 0x00), 0x2C),
+ BUILD_COLOR (MAKE_RGB15_INIT (0x0E, 0x00, 0x00), 0x2D),
+ };
+ const size_t colorTabCount = sizeof colorTab / sizeof colorTab[0];
+
+ ElementPtr->colorCycleIndex++;
+ if (ElementPtr->colorCycleIndex == colorTabCount)
+ ElementPtr->colorCycleIndex = 0;
+
+ SetPrimColor (&DisplayArray[ElementPtr->PrimIndex],
+ colorTab[ElementPtr->colorCycleIndex]);
+
+ if (ElementPtr->colorCycleIndex == 0)
+ --ElementPtr->thrust_wait;
+
+ ElementPtr->turn_wait = ElementPtr->thrust_wait;
+ if (ElementPtr->turn_wait)
+ {
+ ElementPtr->turn_wait = ((ElementPtr->turn_wait - 1) >> 1) + 1;
+ }
+ else if (ElementPtr->colorCycleIndex != (colorTabCount / 2))
+ {
+ ElementPtr->turn_wait = 1;
+ }
+ else
+ {
+ ElementPtr->death_func = cleanup_dead_ship;
+ ElementPtr->crew_level = 0;
+
+ ElementPtr->life_span = HYPERJUMP_LIFE + 1;
+ ElementPtr->preprocess_func = ship_transition;
+ ElementPtr->postprocess_func = NULL;
+ SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM);
+ ElementPtr->state_flags |= NONSOLID | FINITE_LIFE | CHANGING;
+ }
+ }
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ StarShipPtr->cur_status_flags &=
+ ~(LEFT | RIGHT | THRUST | WEAPON | SPECIAL);
+ // Ignore control input when fleeing.
+ PreProcessStatus (ElementPtr);
+}
diff --git a/src/uqm/tactrans.h b/src/uqm/tactrans.h
new file mode 100644
index 0000000..c0f8479
--- /dev/null
+++ b/src/uqm/tactrans.h
@@ -0,0 +1,59 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_TACTRANS_H_
+#define UQM_TACTRANS_H_
+
+#include "libs/compiler.h"
+#include "races.h"
+#include "element.h"
+#include "battlecontrols.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+bool battleEndReadyHuman (HumanInputContext *context);
+bool battleEndReadyComputer (ComputerInputContext *context);
+#ifdef NETPLAY
+bool battleEndReadyNetwork (NetworkInputContext *context);
+#endif
+
+extern void ship_transition (ELEMENT *ElementPtr);
+extern BOOLEAN OpponentAlive (STARSHIP *TestStarShipPtr);
+extern void new_ship (ELEMENT *ElementPtr);
+extern void ship_death (ELEMENT *ShipPtr);
+extern void spawn_ion_trail (ELEMENT *ElementPtr);
+extern void flee_preprocess (ELEMENT *ElementPtr);
+
+extern void StopDitty (void);
+extern void ResetWinnerStarShip (void);
+extern void StopAllBattleMusic (void);
+extern STARSHIP* FindAliveStarShip (ELEMENT *deadShip);
+extern STARSHIP* GetWinnerStarShip (void);
+extern void SetWinnerStarShip (STARSHIP *winner);
+extern void RecordShipDeath (ELEMENT *deadShip);
+extern void StartShipExplosion (ELEMENT *ShipPtr, bool playSound);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_TACTRANS_H_ */
+
+
diff --git a/src/uqm/trans.c b/src/uqm/trans.c
new file mode 100644
index 0000000..a6eb6d5
--- /dev/null
+++ b/src/uqm/trans.c
@@ -0,0 +1,154 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "units.h"
+#include "libs/compiler.h"
+
+
+SIZE sinetab[] =
+{
+ -FLT_ADJUST (1.000000),
+ -FLT_ADJUST (0.995185),
+ -FLT_ADJUST (0.980785),
+ -FLT_ADJUST (0.956940),
+ -FLT_ADJUST (0.923880),
+ -FLT_ADJUST (0.881921),
+ -FLT_ADJUST (0.831470),
+ -FLT_ADJUST (0.773010),
+ -FLT_ADJUST (0.707107),
+ -FLT_ADJUST (0.634393),
+ -FLT_ADJUST (0.555570),
+ -FLT_ADJUST (0.471397),
+ -FLT_ADJUST (0.382683),
+ -FLT_ADJUST (0.290285),
+ -FLT_ADJUST (0.195090),
+ -FLT_ADJUST (0.098017),
+ FLT_ADJUST (0.000000),
+ FLT_ADJUST (0.098017),
+ FLT_ADJUST (0.195090),
+ FLT_ADJUST (0.290285),
+ FLT_ADJUST (0.382683),
+ FLT_ADJUST (0.471397),
+ FLT_ADJUST (0.555570),
+ FLT_ADJUST (0.634393),
+ FLT_ADJUST (0.707107),
+ FLT_ADJUST (0.773010),
+ FLT_ADJUST (0.831470),
+ FLT_ADJUST (0.881921),
+ FLT_ADJUST (0.923880),
+ FLT_ADJUST (0.956940),
+ FLT_ADJUST (0.980785),
+ FLT_ADJUST (0.995185),
+ FLT_ADJUST (1.000000),
+ FLT_ADJUST (0.995185),
+ FLT_ADJUST (0.980785),
+ FLT_ADJUST (0.956940),
+ FLT_ADJUST (0.923880),
+ FLT_ADJUST (0.881921),
+ FLT_ADJUST (0.831470),
+ FLT_ADJUST (0.773010),
+ FLT_ADJUST (0.707107),
+ FLT_ADJUST (0.634393),
+ FLT_ADJUST (0.555570),
+ FLT_ADJUST (0.471397),
+ FLT_ADJUST (0.382683),
+ FLT_ADJUST (0.290285),
+ FLT_ADJUST (0.195090),
+ FLT_ADJUST (0.098017),
+ FLT_ADJUST (0.000000),
+ -FLT_ADJUST (0.098017),
+ -FLT_ADJUST (0.195090),
+ -FLT_ADJUST (0.290285),
+ -FLT_ADJUST (0.382683),
+ -FLT_ADJUST (0.471397),
+ -FLT_ADJUST (0.555570),
+ -FLT_ADJUST (0.634393),
+ -FLT_ADJUST (0.707107),
+ -FLT_ADJUST (0.773010),
+ -FLT_ADJUST (0.831470),
+ -FLT_ADJUST (0.881921),
+ -FLT_ADJUST (0.923880),
+ -FLT_ADJUST (0.956940),
+ -FLT_ADJUST (0.980785),
+ -FLT_ADJUST (0.995185),
+};
+
+COUNT
+ARCTAN (SIZE delta_x, SIZE delta_y)
+{
+ SIZE v1, v2;
+ static COUNT atantab[] =
+ {
+ 0,
+ 0,
+ 1,
+ 1,
+ 1,
+ 2,
+ 2,
+ 2,
+ 2,
+ 3,
+ 3,
+ 3,
+ 4,
+ 4,
+ 4,
+ 4,
+ 5,
+ 5,
+ 5,
+ 5,
+ 6,
+ 6,
+ 6,
+ 6,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7,
+ 7,
+ 8,
+ 8,
+ 8,
+ };
+
+ v1 = delta_x;
+ v2 = delta_y;
+ if (v1 == 0 && v2 == 0)
+ return (FULL_CIRCLE);
+
+ if (v1 < 0)
+ v1 = -v1;
+ if (v2 < 0)
+ v2 = -v2;
+ if (v1 > v2)
+ v1 = QUADRANT
+ - atantab[(((DWORD)v2 << (CIRCLE_SHIFT - 1)) + (v1 >> 1)) / v1];
+ else
+ v1 = atantab[(((DWORD)v1 << (CIRCLE_SHIFT - 1)) + (v2 >> 1)) / v2];
+
+ if (delta_x < 0)
+ v1 = FULL_CIRCLE - v1;
+ if (delta_y > 0)
+ v1 = HALF_CIRCLE - v1;
+
+ return (NORMALIZE_ANGLE (v1));
+}
+
diff --git a/src/uqm/units.h b/src/uqm/units.h
new file mode 100644
index 0000000..93d903c
--- /dev/null
+++ b/src/uqm/units.h
@@ -0,0 +1,227 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_UNITS_H_
+#define UQM_UNITS_H_
+
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern int ScreenWidth;
+extern int ScreenHeight;
+
+#define SCREEN_WIDTH ScreenWidth
+#define SCREEN_HEIGHT ScreenHeight
+#define SAFE_X 0
+ /* Left and right screen margin to be left unused */
+#define SAFE_Y 0
+ /* Top and bottom screen margin to be left unused */
+#define SIS_ORG_X (7 + SAFE_X)
+#define SIS_ORG_Y (10 + SAFE_Y)
+#define STATUS_WIDTH 64
+ /* Width of the status "window" (the right part of the screen) */
+#define STATUS_HEIGHT (SCREEN_HEIGHT - (SAFE_Y * 2))
+ /* Height of the status "window" (the right part of the screen) */
+#define SPACE_WIDTH (SCREEN_WIDTH - STATUS_WIDTH - (SAFE_X * 2))
+ /* Width of the space "window" (the left part of the screen) */
+#define SPACE_HEIGHT (SCREEN_HEIGHT - (SAFE_Y * 2))
+ /* Height of the space "window" (the left part of the screen) */
+#define SIS_SCREEN_WIDTH (SPACE_WIDTH - 14)
+ /* Width of the usable part of the space "window" */
+#define SIS_SCREEN_HEIGHT (SPACE_HEIGHT - 13)
+ /* Height of the usable part of the space "window" */
+#define RADAR_X (4 + (SCREEN_WIDTH - STATUS_WIDTH - SAFE_X))
+#define RADAR_WIDTH (STATUS_WIDTH - 8)
+#define RADAR_HEIGHT 53
+#define RADAR_Y (SIS_ORG_Y + SIS_SCREEN_HEIGHT - RADAR_HEIGHT)
+
+#define SIS_TITLE_BOX_WIDTH 57
+#define SIS_TITLE_WIDTH (SIS_TITLE_BOX_WIDTH - 2)
+#define SIS_TITLE_HEIGHT 8
+#define SIS_SPACER_BOX_WIDTH 12
+#define SIS_MESSAGE_BOX_WIDTH (SIS_SCREEN_WIDTH - SIS_TITLE_BOX_WIDTH \
+ - SIS_SPACER_BOX_WIDTH)
+#define SIS_MESSAGE_WIDTH (SIS_MESSAGE_BOX_WIDTH - 2)
+#define SIS_MESSAGE_HEIGHT SIS_TITLE_HEIGHT
+
+#define STATUS_MESSAGE_WIDTH (STATUS_WIDTH - 4)
+#define STATUS_MESSAGE_HEIGHT 7
+
+#define SHIP_NAME_WIDTH (STATUS_WIDTH - 4)
+#define SHIP_NAME_HEIGHT 7
+
+#define MAX_REDUCTION 3
+#define MAX_VIS_REDUCTION 2
+#define REDUCTION_SHIFT 1
+#define NUM_VIEWS (MAX_VIS_REDUCTION + 1)
+
+#define ZOOM_SHIFT 8
+#define MAX_ZOOM_OUT (1 << (ZOOM_SHIFT + MAX_REDUCTION - 1))
+
+#define ONE_SHIFT 2
+#define BACKGROUND_SHIFT 3
+#define SCALED_ONE (1 << ONE_SHIFT)
+#define DISPLAY_TO_WORLD(x) ((x)<<ONE_SHIFT)
+#define WORLD_TO_DISPLAY(x) ((x)>>ONE_SHIFT)
+#define DISPLAY_ALIGN(x) ((COORD)(x)&~(SCALED_ONE-1))
+#define DISPLAY_ALIGN_X(x) ((COORD)((COUNT)(x)%LOG_SPACE_WIDTH)&~(SCALED_ONE-1))
+#define DISPLAY_ALIGN_Y(y) ((COORD)((COUNT)(y)%LOG_SPACE_HEIGHT)&~(SCALED_ONE-1))
+
+#define LOG_SPACE_WIDTH \
+ (DISPLAY_TO_WORLD (SPACE_WIDTH) << MAX_REDUCTION)
+#define LOG_SPACE_HEIGHT \
+ (DISPLAY_TO_WORLD (SPACE_HEIGHT) << MAX_REDUCTION)
+#define TRANSITION_WIDTH \
+ (DISPLAY_TO_WORLD (SPACE_WIDTH) << MAX_VIS_REDUCTION)
+#define TRANSITION_HEIGHT \
+ (DISPLAY_TO_WORLD (SPACE_HEIGHT) << MAX_VIS_REDUCTION)
+
+#define MAX_X_UNIVERSE 9999
+#define MAX_Y_UNIVERSE 9999
+// Due to the added rounding error correction, the maximum logical X and Y
+// in Hyperspace cannot go past 999.94999, otherwise the values will be
+// rounded up to 1000.0. We do not want that so we subtract half a unit.
+#define MAX_X_LOGICAL \
+ (UNIVERSE_TO_LOGX (MAX_X_UNIVERSE + 1) - (UNIVERSE_TO_LOGX (1) >> 1) \
+ - 1L)
+// The Y axis is inverted with respect to the screen Y axis.
+// (MAX_Y_UNIVERSE - 1) is really 1 for our purposes.
+#define MAX_Y_LOGICAL \
+ (UNIVERSE_TO_LOGY (-1) - (UNIVERSE_TO_LOGY (MAX_Y_UNIVERSE - 1) >> 1) \
+ - 1L)
+
+#define SPHERE_RADIUS_INCREMENT 11
+
+#define MAX_FLEET_STRENGTH (254 * SPHERE_RADIUS_INCREMENT)
+
+// XXX: These corrected for the weird screen aspect ratio on DOS
+// In part because of them, hyperflight is slower vertically
+#define UNIT_SCREEN_WIDTH 63
+#define UNIT_SCREEN_HEIGHT 50
+
+// Bug #945: Simplified, these set the speed of SIS in Hyperspace and
+// Quasispace. The ratio between UNIVERSE_UNITS_ and LOG_UNITS_ is
+// what sets the speed, and it should be 1:16 to match the original.
+// The unit factors are reduced to keep the translation math within
+// 32 bits. The original math is unnecessarily complex and depends
+// on the screen resolution when it should not.
+// Using the new math will break old savegames.
+#ifdef NORMALIZED_HYPERSPACE_SPEED
+#define LOG_UNITS_X ((SDWORD)(UNIVERSE_UNITS_X * 16))
+#define LOG_UNITS_Y ((SDWORD)(UNIVERSE_UNITS_Y * 16))
+#define UNIVERSE_UNITS_X (((MAX_X_UNIVERSE + 1) >> 4))
+#define UNIVERSE_UNITS_Y (((MAX_Y_UNIVERSE + 1) >> 4))
+#else
+// Original (and now broken) Hyperspace speed factors
+#define SECTOR_WIDTH 195
+#define SECTOR_HEIGHT 25
+
+#define LOG_UNITS_X ((SDWORD)(LOG_SPACE_WIDTH >> 4) * SECTOR_WIDTH)
+#define LOG_UNITS_Y ((SDWORD)(LOG_SPACE_HEIGHT >> 4) * SECTOR_HEIGHT)
+#define UNIVERSE_UNITS_X (((MAX_X_UNIVERSE + 1) >> 4) * 10)
+#define UNIVERSE_UNITS_Y (((MAX_Y_UNIVERSE + 1) >> 4))
+#endif
+
+#define ROUNDING_ERROR(div) ((div) >> 1)
+
+static inline COORD
+logxToUniverse (SDWORD lx)
+{
+ return (COORD) ((lx * UNIVERSE_UNITS_X + ROUNDING_ERROR(LOG_UNITS_X))
+ / LOG_UNITS_X);
+}
+#define LOGX_TO_UNIVERSE(lx) \
+ logxToUniverse (lx)
+static inline COORD
+logyToUniverse (SDWORD ly)
+{
+ return (COORD) (MAX_Y_UNIVERSE -
+ ((ly * UNIVERSE_UNITS_Y + ROUNDING_ERROR(LOG_UNITS_Y))
+ / LOG_UNITS_Y));
+}
+#define LOGY_TO_UNIVERSE(ly) \
+ logyToUniverse (ly)
+static inline SDWORD
+universeToLogx (COORD ux)
+{
+ return (ux * LOG_UNITS_X + ROUNDING_ERROR(UNIVERSE_UNITS_X))
+ / UNIVERSE_UNITS_X;
+}
+#define UNIVERSE_TO_LOGX(ux) \
+ universeToLogx (ux)
+static inline SDWORD
+universeToLogy (COORD uy)
+{
+ return ((MAX_Y_UNIVERSE - uy) * LOG_UNITS_Y
+ + ROUNDING_ERROR(UNIVERSE_UNITS_Y))
+ / UNIVERSE_UNITS_Y;
+}
+#define UNIVERSE_TO_LOGY(uy) \
+ universeToLogy (uy)
+
+#define CIRCLE_SHIFT 6
+#define FULL_CIRCLE (1 << CIRCLE_SHIFT)
+#define OCTANT_SHIFT (CIRCLE_SHIFT - 3) /* (1 << 3) == 8 */
+#define HALF_CIRCLE (FULL_CIRCLE >> 1)
+#define QUADRANT (FULL_CIRCLE >> 2)
+#define OCTANT (FULL_CIRCLE >> 3)
+
+#define FACING_SHIFT 4
+
+#define ANGLE_TO_FACING(a) (((a)+(1<<(CIRCLE_SHIFT-FACING_SHIFT-1))) \
+ >>(CIRCLE_SHIFT-FACING_SHIFT))
+#define FACING_TO_ANGLE(f) ((f)<<(CIRCLE_SHIFT-FACING_SHIFT))
+
+#define NORMALIZE_ANGLE(a) ((COUNT)((a)&(FULL_CIRCLE-1)))
+#define NORMALIZE_FACING(f) ((COUNT)((f)&((1 << FACING_SHIFT)-1)))
+
+#define DEGREES_TO_ANGLE(d) NORMALIZE_ANGLE((((d) % 360) * FULL_CIRCLE \
+ + HALF_CIRCLE) / 360)
+#define ANGLE_TO_DEGREES(d) (NORMALIZE_ANGLE(d) * 360 / FULL_CIRCLE)
+
+#define SIN_SHIFT 14
+#define SIN_SCALE (1 << SIN_SHIFT)
+#define INT_ADJUST(x) ((x)<<SIN_SHIFT)
+#define FLT_ADJUST(x) (SIZE)((x)*SIN_SCALE)
+#define UNADJUST(x) (SIZE)((x)>>SIN_SHIFT)
+#define ROUND(x,y) ((x)+((x)>=0?((y)>>1):-((y)>>1)))
+
+extern SIZE sinetab[];
+#define SINVAL(a) sinetab[NORMALIZE_ANGLE(a)]
+#define COSVAL(a) SINVAL((a)+QUADRANT)
+#define SINE(a,m) ((SIZE)((((long)SINVAL(a))*(long)(m))>>SIN_SHIFT))
+#define COSINE(a,m) SINE((a)+QUADRANT,m)
+extern COUNT ARCTAN (SIZE delta_x, SIZE delta_y);
+
+#define WRAP_VAL(v,w) ((COUNT)((v)<0?((v)+(w)):((v)>=(w)?((v)-(w)):(v))))
+#define WRAP_X(x) WRAP_VAL(x,LOG_SPACE_WIDTH)
+#define WRAP_Y(y) WRAP_VAL(y,LOG_SPACE_HEIGHT)
+#define WRAP_DELTA_X(dx) ((dx)<0 ? \
+ ((-(dx)<=LOG_SPACE_WIDTH>>1)?(dx):(LOG_SPACE_WIDTH+(dx))) : \
+ (((dx)<=LOG_SPACE_WIDTH>>1)?(dx):((dx)-LOG_SPACE_WIDTH)))
+#define WRAP_DELTA_Y(dy) ((dy)<0 ? \
+ ((-(dy)<=LOG_SPACE_HEIGHT>>1)?(dy):(LOG_SPACE_HEIGHT+(dy))) : \
+ (((dy)<=LOG_SPACE_HEIGHT>>1)?(dy):((dy)-LOG_SPACE_HEIGHT)))
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_UNITS_H_ */
diff --git a/src/uqm/uqmdebug.c b/src/uqm/uqmdebug.c
new file mode 100644
index 0000000..4113a5d
--- /dev/null
+++ b/src/uqm/uqmdebug.c
@@ -0,0 +1,1926 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#if defined(DEBUG) || defined(USE_DEBUG_KEY)
+
+#include "uqmdebug.h"
+
+#include "build.h"
+#include "colors.h"
+#include "controls.h"
+#include "clock.h"
+#include "starmap.h"
+#include "element.h"
+#include "sis.h"
+#include "status.h"
+#include "gamestr.h"
+#include "gameev.h"
+#include "gendef.h"
+#include "globdata.h"
+#include "planets/lifeform.h"
+#include "planets/scan.h"
+#include "races.h"
+#include "setup.h"
+#include "state.h"
+#include "libs/mathlib.h"
+
+#include <stdio.h>
+#include <errno.h>
+
+
+static void dumpEventCallback (const EVENT *eventPtr, void *arg);
+
+static void starRecurse (STAR_DESC *star, void *arg);
+static void planetRecurse (STAR_DESC *star, SOLARSYS_STATE *system,
+ PLANET_DESC *planet, void *arg);
+static void moonRecurse (STAR_DESC *star, SOLARSYS_STATE *system,
+ PLANET_DESC *planet, PLANET_DESC *moon, void *arg);
+
+static void dumpSystemCallback (const STAR_DESC *star,
+ const SOLARSYS_STATE *system, void *arg);
+static void dumpPlanetCallback (const PLANET_DESC *planet, void *arg);
+static void dumpMoonCallback (const PLANET_DESC *moon, void *arg);
+static void dumpWorld (FILE *out, const PLANET_DESC *world);
+
+typedef struct TallyResourcesArg TallyResourcesArg;
+static void tallySystemPreCallback (const STAR_DESC *star, const
+ SOLARSYS_STATE *system, void *arg);
+static void tallySystemPostCallback (const STAR_DESC *star, const
+ SOLARSYS_STATE *system, void *arg);
+static void tallyPlanetCallback (const PLANET_DESC *planet, void *arg);
+static void tallyMoonCallback (const PLANET_DESC *moon, void *arg);
+static void tallyResourcesWorld (TallyResourcesArg *arg,
+ const PLANET_DESC *world);
+
+static void dumpPlanetTypeCallback (int index, const PlanetFrame *planet,
+ void *arg);
+
+
+BOOLEAN instantMove = FALSE;
+BOOLEAN disableInteractivity = FALSE;
+void (* volatile debugHook) (void) = NULL;
+
+
+// Must be called on the Starcon2Main thread.
+// This function is called synchronously wrt the game logic thread.
+void
+debugKeyPressedSynchronous (void)
+{
+ // State modifying:
+ equipShip ();
+ giveDevices ();
+
+ // Give the player the ships you can't ally with under normal
+ // conditions.
+ clearEscorts ();
+ AddEscortShips (ARILOU_SHIP, 1);
+ AddEscortShips (PKUNK_SHIP, 1);
+ AddEscortShips (VUX_SHIP, 1);
+ AddEscortShips (YEHAT_SHIP, 1);
+ AddEscortShips (MELNORME_SHIP, 1);
+ AddEscortShips (DRUUGE_SHIP, 1);
+ AddEscortShips (ILWRATH_SHIP, 1);
+ AddEscortShips (MYCON_SHIP, 1);
+ AddEscortShips (SLYLANDRO_SHIP, 1);
+ AddEscortShips (UMGAH_SHIP, 1);
+ AddEscortShips (URQUAN_SHIP, 1);
+ AddEscortShips (BLACK_URQUAN_SHIP, 1);
+
+ resetCrewBattle ();
+ resetEnergyBattle ();
+ instantMove = !instantMove;
+ showSpheres ();
+ activateAllShips ();
+// forwardToNextEvent (TRUE);
+// SET_GAME_STATE (MELNORME_CREDIT1, 100);
+// GLOBAL_SIS (ResUnits) = 100000;
+
+ // Informational:
+// dumpEvents (stderr);
+
+ // Graphical and textual:
+// debugContexts();
+}
+
+// Can be called on any thread, but usually on main()
+// This function is called asynchronously wrt the game logic thread,
+// which means locking applies. Use carefully.
+// TODO: Once game logic thread is purged of graphics and clock locks,
+// this function may not call graphics and game clock functions at all.
+void
+debugKeyPressed (void)
+{
+ // Tests
+// Scale_PerfTest ();
+
+ // Informational:
+// dumpStrings (stdout);
+// dumpPlanetTypes(stderr);
+// debugHook = dumpUniverseToFile;
+ // This will cause dumpUniverseToFile to be called from the
+ // Starcon2Main loop. Calling it from here would give threading
+ // problems.
+// debugHook = tallyResourcesToFile;
+ // This will cause tallyResourcesToFile to be called from the
+ // Starcon2Main loop. Calling it from here would give threading
+ // problems.
+
+ // Interactive:
+// uio_debugInteractive(stdin, stdout, stderr);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+// Fast forwards to the next event.
+// If skipHEE is set, HYPERSPACE_ENCOUNTER_EVENTs are skipped.
+// Must be called from the Starcon2Main thread.
+// TODO: LockGameClock may be removed since it is only
+// supposed to be called synchronously wrt the game logic thread.
+void
+forwardToNextEvent (BOOLEAN skipHEE)
+{
+ HEVENT hEvent;
+ EVENT *EventPtr;
+ COUNT year, month, day;
+ // time of next event
+ BOOLEAN done;
+
+ if (!GameClockRunning ())
+ return;
+
+ LockGameClock ();
+
+ done = !skipHEE;
+ do {
+ hEvent = GetHeadEvent ();
+ if (hEvent == 0)
+ return;
+ LockEvent (hEvent, &EventPtr);
+ if (EventPtr->func_index != HYPERSPACE_ENCOUNTER_EVENT)
+ done = TRUE;
+ year = EventPtr->year_index;
+ month = EventPtr->month_index;
+ day = EventPtr->day_index;
+ UnlockEvent (hEvent);
+
+ for (;;) {
+ if (GLOBAL (GameClock.year_index) > year ||
+ (GLOBAL (GameClock.year_index) == year &&
+ (GLOBAL (GameClock.month_index) > month ||
+ (GLOBAL (GameClock.month_index) == month &&
+ GLOBAL (GameClock.day_index) >= day))))
+ break;
+
+ MoveGameClockDays (1);
+ }
+ } while (!done);
+
+ UnlockGameClock ();
+}
+
+const char *
+eventName (BYTE func_index)
+{
+ switch (func_index) {
+ case ARILOU_ENTRANCE_EVENT:
+ return "ARILOU_ENTRANCE_EVENT";
+ case ARILOU_EXIT_EVENT:
+ return "ARILOU_EXIT_EVENT";
+ case HYPERSPACE_ENCOUNTER_EVENT:
+ return "HYPERSPACE_ENCOUNTER_EVENT";
+ case KOHR_AH_VICTORIOUS_EVENT:
+ return "KOHR_AH_VICTORIOUS_EVENT";
+ case ADVANCE_PKUNK_MISSION:
+ return "ADVANCE_PKUNK_MISSION";
+ case ADVANCE_THRADD_MISSION:
+ return "ADVANCE_THRADD_MISSION";
+ case ZOQFOT_DISTRESS_EVENT:
+ return "ZOQFOT_DISTRESS";
+ case ZOQFOT_DEATH_EVENT:
+ return "ZOQFOT_DEATH_EVENT";
+ case SHOFIXTI_RETURN_EVENT:
+ return "SHOFIXTI_RETURN_EVENT";
+ case ADVANCE_UTWIG_SUPOX_MISSION:
+ return "ADVANCE_UTWIG_SUPOX_MISSION";
+ case KOHR_AH_GENOCIDE_EVENT:
+ return "KOHR_AH_GENOCIDE_EVENT";
+ case SPATHI_SHIELD_EVENT:
+ return "SPATHI_SHIELD_EVENT";
+ case ADVANCE_ILWRATH_MISSION:
+ return "ADVANCE_ILWRATH_MISSION";
+ case ADVANCE_MYCON_MISSION:
+ return "ADVANCE_MYCON_MISSION";
+ case ARILOU_UMGAH_CHECK:
+ return "ARILOU_UMGAH_CHECK";
+ case YEHAT_REBEL_EVENT:
+ return "YEHAT_REBEL_EVENT";
+ case SLYLANDRO_RAMP_UP:
+ return "SLYLANDRO_RAMP_UP";
+ case SLYLANDRO_RAMP_DOWN:
+ return "SLYLANDRO_RAMP_DOWN";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+static void
+dumpEventCallback (const EVENT *eventPtr, void *arg)
+{
+ FILE *out = (FILE *) arg;
+ dumpEvent (out, eventPtr);
+}
+
+void
+dumpEvent (FILE *out, const EVENT *eventPtr)
+{
+ fprintf (out, "%4u/%02u/%02u: %s\n",
+ eventPtr->year_index,
+ eventPtr->month_index,
+ eventPtr->day_index,
+ eventName (eventPtr->func_index));
+}
+
+void
+dumpEvents (FILE *out)
+{
+ LockGameClock ();
+ ForAllEvents (dumpEventCallback, out);
+ UnlockGameClock ();
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+// NB: Ship maximum speed and turning rate aren't updated in
+// HyperSpace/QuasiSpace or in melee.
+void
+equipShip (void)
+{
+ int i;
+
+ // Don't do anything unless in the full game.
+ if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
+ return;
+
+ // Thrusters:
+ for (i = 0; i < NUM_DRIVE_SLOTS; i++)
+ GLOBAL_SIS (DriveSlots[i]) = FUSION_THRUSTER;
+
+ // Turning jets:
+ for (i = 0; i < NUM_JET_SLOTS; i++)
+ GLOBAL_SIS (JetSlots[i]) = TURNING_JETS;
+
+ // Shields:
+ SET_GAME_STATE (LANDER_SHIELDS,
+ (1 << EARTHQUAKE_DISASTER) |
+ (1 << BIOLOGICAL_DISASTER) |
+ (1 << LIGHTNING_DISASTER) |
+ (1 << LAVASPOT_DISASTER));
+ // Lander upgrades:
+ SET_GAME_STATE (IMPROVED_LANDER_SPEED, 1);
+ SET_GAME_STATE (IMPROVED_LANDER_CARGO, 1);
+ SET_GAME_STATE (IMPROVED_LANDER_SHOT, 1);
+
+ // Modules:
+ if (GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
+ {
+ // The Precursor bomb has not been installed.
+ // This is the original TFB testing layout.
+ i = 0;
+ GLOBAL_SIS (ModuleSlots[i++]) = HIGHEFF_FUELSYS;
+ GLOBAL_SIS (ModuleSlots[i++]) = HIGHEFF_FUELSYS;
+ GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
+ GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
+ GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
+ GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
+ GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
+ GLOBAL_SIS (ModuleSlots[i++]) = STORAGE_BAY;
+ GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
+ GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
+ GLOBAL_SIS (ModuleSlots[i++]) = DYNAMO_UNIT;
+ GLOBAL_SIS (ModuleSlots[i++]) = TRACKING_SYSTEM;
+ GLOBAL_SIS (ModuleSlots[i++]) = TRACKING_SYSTEM;
+ GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
+ GLOBAL_SIS (ModuleSlots[i++]) = CANNON_WEAPON;
+ GLOBAL_SIS (ModuleSlots[i++]) = CANNON_WEAPON;
+
+ // Landers:
+ GLOBAL_SIS (NumLanders) = MAX_LANDERS;
+ }
+ else
+ {
+ // The Precursor bomb has been installed.
+ i = NUM_BOMB_MODULES;
+ GLOBAL_SIS (ModuleSlots[i++]) = HIGHEFF_FUELSYS;
+ GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
+ GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
+ GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
+ GLOBAL_SIS (ModuleSlots[i++]) = CANNON_WEAPON;
+ GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
+ }
+
+ assert (i <= NUM_MODULE_SLOTS);
+
+ // Fill the fuel and crew compartments to the maximum.
+ GLOBAL_SIS (FuelOnBoard) = FUEL_RESERVE;
+ GLOBAL_SIS (CrewEnlisted) = 0;
+ for (i = 0; i < NUM_MODULE_SLOTS; i++)
+ {
+ switch (GLOBAL_SIS (ModuleSlots[i])) {
+ case CREW_POD:
+ GLOBAL_SIS (CrewEnlisted) += CREW_POD_CAPACITY;
+ break;
+ case FUEL_TANK:
+ GLOBAL_SIS (FuelOnBoard) += FUEL_TANK_CAPACITY;
+ break;
+ case HIGHEFF_FUELSYS:
+ GLOBAL_SIS (FuelOnBoard) += HEFUEL_TANK_CAPACITY;
+ break;
+ }
+ }
+
+ // Update the maximum speed and turning rate when in interplanetary.
+ if (pSolarSysState != NULL)
+ {
+ // Thrusters:
+ pSolarSysState->max_ship_speed = 5 * IP_SHIP_THRUST_INCREMENT;
+ for (i = 0; i < NUM_DRIVE_SLOTS; i++)
+ if (GLOBAL_SIS (DriveSlots[i] == FUSION_THRUSTER))
+ pSolarSysState->max_ship_speed += IP_SHIP_THRUST_INCREMENT;
+
+ // Turning jets:
+ pSolarSysState->turn_wait = IP_SHIP_TURN_WAIT;
+ for (i = 0; i < NUM_JET_SLOTS; i++)
+ if (GLOBAL_SIS (JetSlots[i]) == TURNING_JETS)
+ pSolarSysState->turn_wait -= IP_SHIP_TURN_DECREMENT;
+ }
+
+ // Make sure everything is redrawn:
+ if (inHQSpace () ||
+ LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
+ {
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+giveDevices (void) {
+ SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 1);
+ SET_GAME_STATE (ARTIFACT_2_ON_SHIP, 1);
+ SET_GAME_STATE (ARTIFACT_3_ON_SHIP, 1);
+ SET_GAME_STATE (SUN_DEVICE_ON_SHIP, 1);
+ SET_GAME_STATE (UTWIG_BOMB_ON_SHIP, 1);
+ SET_GAME_STATE (ULTRON_CONDITION, 1);
+ //SET_GAME_STATE (ULTRON_CONDITION, 2);
+ //SET_GAME_STATE (ULTRON_CONDITION, 3);
+ //SET_GAME_STATE (ULTRON_CONDITION, 4);
+ SET_GAME_STATE (MAIDENS_ON_SHIP, 1);
+ SET_GAME_STATE (TALKING_PET_ON_SHIP, 1);
+ SET_GAME_STATE (AQUA_HELIX_ON_SHIP, 1);
+ SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 1);
+ SET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP, 1);
+ SET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP, 1);
+ SET_GAME_STATE (EGG_CASE0_ON_SHIP, 1);
+ SET_GAME_STATE (EGG_CASE1_ON_SHIP, 1);
+ SET_GAME_STATE (EGG_CASE2_ON_SHIP, 1);
+ SET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP, 1);
+ SET_GAME_STATE (VUX_BEAST_ON_SHIP, 1);
+ SET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP, 1);
+ SET_GAME_STATE (PORTAL_KEY_ON_SHIP, 1);
+ SET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP, 1);
+ SET_GAME_STATE (MOONBASE_ON_SHIP, 1);
+
+ // Not strictly a device (although it originally was one).
+ SET_GAME_STATE (DESTRUCT_CODE_ON_SHIP, 1);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+clearEscorts (void)
+{
+ HSHIPFRAG hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
+ hStarShip; hStarShip = hNextShip)
+ {
+ SHIP_FRAGMENT *StarShipPtr;
+
+ StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ hNextShip = _GetSuccLink (StarShipPtr);
+ UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
+
+ RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
+ FreeShipFrag (&GLOBAL (built_ship_q), hStarShip);
+ }
+
+ DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+#if 0
+// Not needed anymore, but could be useful in the future.
+
+// Find the HELEMENT belonging to the Flagship.
+static HELEMENT
+findFlagshipElement (void)
+{
+ HELEMENT hElement, hNextElement;
+ ELEMENT *ElementPtr;
+
+ // Find the ship element.
+ for (hElement = GetTailElement (); hElement != 0;
+ hElement = hNextElement)
+ {
+ LockElement (hElement, &ElementPtr);
+
+ if ((ElementPtr->state_flags & PLAYER_SHIP) != 0)
+ {
+ UnlockElement (hElement);
+ return hElement;
+ }
+
+ hNextElement = GetPredElement (ElementPtr);
+ UnlockElement (hElement);
+ }
+ return 0;
+}
+#endif
+
+// Move the Flagship to the destination of the autopilot.
+// Should only be called from HyperSpace/QuasiSpace.
+// It can be called from debugHook directly after entering HS/QS though.
+void
+doInstantMove (void)
+{
+ // Move to the new location:
+ if ((GLOBAL (autopilot)).x == ~0 || (GLOBAL (autopilot)).y == ~0)
+ {
+ // If no destination has been selected, use the current location
+ // as the destination.
+ (GLOBAL (autopilot)).x = LOGX_TO_UNIVERSE(GLOBAL_SIS (log_x));
+ (GLOBAL (autopilot)).y = LOGY_TO_UNIVERSE(GLOBAL_SIS (log_y));
+ }
+ else
+ {
+ // A new destination has been selected.
+ GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX((GLOBAL (autopilot)).x);
+ GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY((GLOBAL (autopilot)).y);
+ }
+
+ // Check for a solar systems at the destination.
+ if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
+ {
+ // If there's a solar system at the destination, enter it.
+ CurStarDescPtr = FindStar (0, &(GLOBAL (autopilot)), 0, 0);
+ if (CurStarDescPtr)
+ {
+ // Leave HyperSpace/QuasiSpace if we're there:
+ SET_GAME_STATE (USED_BROADCASTER, 0);
+ GLOBAL (CurrentActivity) &= ~IN_BATTLE;
+
+ // Enter IP:
+ GLOBAL (ShipFacing) = 0;
+ GLOBAL (ip_planet) = 0;
+ GLOBAL (in_orbit) = 0;
+ // This causes the ship position in IP to be reset.
+ GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
+ }
+ }
+
+ // Turn off the autopilot:
+ (GLOBAL (autopilot)).x = ~0;
+ (GLOBAL (autopilot)).y = ~0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+showSpheres (void)
+{
+ HFLEETINFO hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip != NULL; hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if ((FleetPtr->actual_strength != INFINITE_RADIUS) &&
+ (FleetPtr->known_strength != FleetPtr->actual_strength))
+ {
+ FleetPtr->known_strength = FleetPtr->actual_strength;
+ FleetPtr->known_loc = FleetPtr->loc;
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+activateAllShips (void)
+{
+ HFLEETINFO hStarShip, hNextShip;
+
+ for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
+ hStarShip != NULL; hStarShip = hNextShip)
+ {
+ FLEET_INFO *FleetPtr;
+
+ FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ hNextShip = _GetSuccLink (FleetPtr);
+
+ if (FleetPtr->icons != NULL)
+ // Skip the Ur-Quan probe.
+ {
+ FleetPtr->allied_state = GOOD_GUY;
+ }
+
+ UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+forAllStars (void (*callback) (STAR_DESC *, void *), void *arg)
+{
+ int i;
+ extern STAR_DESC starmap_array[];
+
+ for (i = 0; i < NUM_SOLAR_SYSTEMS; i++)
+ callback (&starmap_array[i], arg);
+}
+
+void
+forAllPlanets (STAR_DESC *star, SOLARSYS_STATE *system, void (*callback) (
+ STAR_DESC *, SOLARSYS_STATE *, PLANET_DESC *, void *), void *arg)
+{
+ COUNT i;
+
+ assert(CurStarDescPtr == star);
+ assert(pSolarSysState == system);
+
+ for (i = 0; i < system->SunDesc[0].NumPlanets; i++)
+ callback (star, system, &system->PlanetDesc[i], arg);
+}
+
+void
+forAllMoons (STAR_DESC *star, SOLARSYS_STATE *system, PLANET_DESC *planet,
+ void (*callback) (STAR_DESC *, SOLARSYS_STATE *, PLANET_DESC *,
+ PLANET_DESC *, void *), void *arg)
+{
+ COUNT i;
+
+ assert(pSolarSysState == system);
+
+ for (i = 0; i < planet->NumPlanets; i++)
+ callback (star, system, planet, &system->MoonDesc[i], arg);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+// Must be called from the Starcon2Main thread.
+// TODO: LockGameClock may be removed
+void
+UniverseRecurse (UniverseRecurseArg *universeRecurseArg)
+{
+ ACTIVITY savedActivity;
+
+ if (universeRecurseArg->systemFuncPre == NULL
+ && universeRecurseArg->systemFuncPost == NULL
+ && universeRecurseArg->planetFuncPre == NULL
+ && universeRecurseArg->planetFuncPost == NULL
+ && universeRecurseArg->moonFunc == NULL)
+ return;
+
+ LockGameClock ();
+ //TFB_DEBUG_HALT = 1;
+ savedActivity = GLOBAL (CurrentActivity);
+ disableInteractivity = TRUE;
+
+ forAllStars (starRecurse, (void *) universeRecurseArg);
+
+ disableInteractivity = FALSE;
+ GLOBAL (CurrentActivity) = savedActivity;
+ UnlockGameClock ();
+}
+
+static void
+starRecurse (STAR_DESC *star, void *arg)
+{
+ UniverseRecurseArg *universeRecurseArg = (UniverseRecurseArg *) arg;
+
+ SOLARSYS_STATE SolarSysState;
+ SOLARSYS_STATE *oldPSolarSysState = pSolarSysState;
+ STAR_DESC *oldStarDescPtr = CurStarDescPtr;
+ CurStarDescPtr = star;
+
+ RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (star));
+
+ memset (&SolarSysState, 0, sizeof (SolarSysState));
+ SolarSysState.SunDesc[0].pPrevDesc = 0;
+ SolarSysState.SunDesc[0].rand_seed = RandomContext_Random (SysGenRNG);
+ SolarSysState.SunDesc[0].data_index = STAR_TYPE (star->Type);
+ SolarSysState.SunDesc[0].location.x = 0;
+ SolarSysState.SunDesc[0].location.y = 0;
+ //SolarSysState.SunDesc[0].radius = MIN_ZOOM_RADIUS;
+ SolarSysState.genFuncs = getGenerateFunctions (star->Index);
+
+ pSolarSysState = &SolarSysState;
+ (*SolarSysState.genFuncs->generatePlanets) (&SolarSysState);
+
+ if (universeRecurseArg->systemFuncPre != NULL)
+ {
+ (*universeRecurseArg->systemFuncPre) (
+ star, &SolarSysState, universeRecurseArg->arg);
+ }
+
+ if (universeRecurseArg->planetFuncPre != NULL
+ || universeRecurseArg->planetFuncPost != NULL
+ || universeRecurseArg->moonFunc != NULL)
+ {
+ forAllPlanets (star, &SolarSysState, planetRecurse,
+ (void *) universeRecurseArg);
+ }
+
+ if (universeRecurseArg->systemFuncPost != NULL)
+ {
+ (*universeRecurseArg->systemFuncPost) (
+ star, &SolarSysState, universeRecurseArg->arg);
+ }
+
+ pSolarSysState = oldPSolarSysState;
+ CurStarDescPtr = oldStarDescPtr;
+}
+
+static void
+planetRecurse (STAR_DESC *star, SOLARSYS_STATE *system, PLANET_DESC *planet,
+ void *arg)
+{
+ UniverseRecurseArg *universeRecurseArg = (UniverseRecurseArg *) arg;
+
+ assert(CurStarDescPtr == star);
+ assert(pSolarSysState == system);
+
+ planet->pPrevDesc = &system->SunDesc[0];
+
+ if (universeRecurseArg->planetFuncPre != NULL)
+ {
+ system->pOrbitalDesc = planet;
+ DoPlanetaryAnalysis (&system->SysInfo, planet);
+ // When GenerateDefaultFunctions is used as genFuncs,
+ // generateOrbital will also call DoPlanetaryAnalysis,
+ // but with other GenerateFunctions this is not guaranteed.
+ (*system->genFuncs->generateOrbital) (system, planet);
+ (*universeRecurseArg->planetFuncPre) (
+ planet, universeRecurseArg->arg);
+ }
+
+ if (universeRecurseArg->moonFunc != NULL)
+ {
+ RandomContext_SeedRandom (SysGenRNG, planet->rand_seed);
+
+ (*system->genFuncs->generateMoons) (system, planet);
+
+ forAllMoons (star, system, planet, moonRecurse,
+ (void *) universeRecurseArg);
+ }
+
+ if (universeRecurseArg->planetFuncPost != NULL)
+ {
+ system->pOrbitalDesc = planet;
+ DoPlanetaryAnalysis (&system->SysInfo, planet);
+ // When GenerateDefaultFunctions is used as genFuncs,
+ // generateOrbital will also call DoPlanetaryAnalysis,
+ // but with other GenerateFunctions this is not guaranteed.
+ (*system->genFuncs->generateOrbital) (system, planet);
+ (*universeRecurseArg->planetFuncPost) (
+ planet, universeRecurseArg->arg);
+ }
+}
+
+static void
+moonRecurse (STAR_DESC *star, SOLARSYS_STATE *system, PLANET_DESC *planet,
+ PLANET_DESC *moon, void *arg)
+{
+ UniverseRecurseArg *universeRecurseArg = (UniverseRecurseArg *) arg;
+
+ assert(CurStarDescPtr == star);
+ assert(pSolarSysState == system);
+
+ moon->pPrevDesc = planet;
+
+ if (universeRecurseArg->moonFunc != NULL)
+ {
+ system->pOrbitalDesc = moon;
+ if (moon->data_index != HIERARCHY_STARBASE && moon->data_index != SA_MATRA)
+ {
+ DoPlanetaryAnalysis (&system->SysInfo, moon);
+ // When GenerateDefaultFunctions is used as genFuncs,
+ // generateOrbital will also call DoPlanetaryAnalysis,
+ // but with other GenerateFunctions this is not guaranteed.
+ }
+ (*system->genFuncs->generateOrbital) (system, moon);
+ (*universeRecurseArg->moonFunc) (
+ moon, universeRecurseArg->arg);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+typedef struct
+{
+ FILE *out;
+} DumpUniverseArg;
+
+// Must be called from the Starcon2Main thread.
+void
+dumpUniverse (FILE *out)
+{
+ DumpUniverseArg dumpUniverseArg;
+ UniverseRecurseArg universeRecurseArg;
+
+ dumpUniverseArg.out = out;
+
+ universeRecurseArg.systemFuncPre = dumpSystemCallback;
+ universeRecurseArg.systemFuncPost = NULL;
+ universeRecurseArg.planetFuncPre = dumpPlanetCallback;
+ universeRecurseArg.planetFuncPost = NULL;
+ universeRecurseArg.moonFunc = dumpMoonCallback;
+ universeRecurseArg.arg = (void *) &dumpUniverseArg;
+
+ UniverseRecurse (&universeRecurseArg);
+}
+
+// Must be called from the Starcon2Main thread.
+void
+dumpUniverseToFile (void)
+{
+ FILE *out;
+
+# define UNIVERSE_DUMP_FILE "PlanetInfo"
+ out = fopen(UNIVERSE_DUMP_FILE, "w");
+ if (out == NULL)
+ {
+ fprintf(stderr, "Error: Could not open file '%s' for "
+ "writing: %s\n", UNIVERSE_DUMP_FILE, strerror(errno));
+ return;
+ }
+
+ dumpUniverse (out);
+
+ fclose(out);
+
+ fprintf(stdout, "*** Star dump complete. The game may be in an "
+ "undefined state.\n");
+ // Data generation may have changed the game state,
+ // in particular for special planet generation.
+}
+
+static void
+dumpSystemCallback (const STAR_DESC *star, const SOLARSYS_STATE *system,
+ void *arg)
+{
+ FILE *out = ((DumpUniverseArg *) arg)->out;
+ dumpSystem (out, star, system);
+}
+
+void
+dumpSystem (FILE *out, const STAR_DESC *star, const SOLARSYS_STATE *system)
+{
+ UNICODE name[256];
+ UNICODE buf[40];
+
+ GetClusterName (star, name);
+ snprintf (buf, sizeof buf, "%s %s",
+ bodyColorString (STAR_COLOR(star->Type)),
+ starTypeString (STAR_TYPE(star->Type)));
+ fprintf (out, "%-22s (%3d.%1d, %3d.%1d) %-19s %s\n",
+ name,
+ star->star_pt.x / 10, star->star_pt.x % 10,
+ star->star_pt.y / 10, star->star_pt.y % 10,
+ buf,
+ starPresenceString (star->Index));
+
+ (void) system; /* satisfy compiler */
+}
+
+const char *
+bodyColorString (BYTE col)
+{
+ switch (col) {
+ case BLUE_BODY:
+ return "blue";
+ case GREEN_BODY:
+ return "green";
+ case ORANGE_BODY:
+ return "orange";
+ case RED_BODY:
+ return "red";
+ case WHITE_BODY:
+ return "white";
+ case YELLOW_BODY:
+ return "yellow";
+ case CYAN_BODY:
+ return "cyan";
+ case PURPLE_BODY:
+ return "purple";
+ case VIOLET_BODY:
+ return "violet";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+const char *
+starTypeString (BYTE type)
+{
+ switch (type) {
+ case DWARF_STAR:
+ return "dwarf";
+ case GIANT_STAR:
+ return "giant";
+ case SUPER_GIANT_STAR:
+ return "super giant";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+const char *
+starPresenceString (BYTE index)
+{
+ switch (index) {
+ case 0:
+ // nothing
+ return "";
+ case SOL_DEFINED:
+ return "Sol";
+ case SHOFIXTI_DEFINED:
+ return "Shofixti male";
+ case MAIDENS_DEFINED:
+ return "Shofixti maidens";
+ case START_COLONY_DEFINED:
+ return "Starting colony";
+ case SPATHI_DEFINED:
+ return "Spathi home";
+ case ZOQFOT_DEFINED:
+ return "Zoq-Fot-Pik home";
+ case MELNORME0_DEFINED:
+ return "Melnorme trader #0";
+ case MELNORME1_DEFINED:
+ return "Melnorme trader #1";
+ case MELNORME2_DEFINED:
+ return "Melnorme trader #2";
+ case MELNORME3_DEFINED:
+ return "Melnorme trader #3";
+ case MELNORME4_DEFINED:
+ return "Melnorme trader #4";
+ case MELNORME5_DEFINED:
+ return "Melnorme trader #5";
+ case MELNORME6_DEFINED:
+ return "Melnorme trader #6";
+ case MELNORME7_DEFINED:
+ return "Melnorme trader #7";
+ case MELNORME8_DEFINED:
+ return "Melnorme trader #8";
+ case TALKING_PET_DEFINED:
+ return "Talking Pet";
+ case CHMMR_DEFINED:
+ return "Chmmr home";
+ case SYREEN_DEFINED:
+ return "Syreen home";
+ case BURVIXESE_DEFINED:
+ return "Burvixese ruins";
+ case SLYLANDRO_DEFINED:
+ return "Slylandro home";
+ case DRUUGE_DEFINED:
+ return "Druuge home";
+ case BOMB_DEFINED:
+ return "Precursor bomb";
+ case AQUA_HELIX_DEFINED:
+ return "Aqua Helix";
+ case SUN_DEVICE_DEFINED:
+ return "Sun Device";
+ case TAALO_PROTECTOR_DEFINED:
+ return "Taalo Shield";
+ case SHIP_VAULT_DEFINED:
+ return "Syreen ship vault";
+ case URQUAN_WRECK_DEFINED:
+ return "Ur-Quan ship wreck";
+ case VUX_BEAST_DEFINED:
+ return "Zex' beauty";
+ case SAMATRA_DEFINED:
+ return "Sa-Matra";
+ case ZOQ_SCOUT_DEFINED:
+ return "Zoq-Fot-Pik scout";
+ case MYCON_DEFINED:
+ return "Mycon home";
+ case EGG_CASE0_DEFINED:
+ return "Mycon egg shell #0";
+ case EGG_CASE1_DEFINED:
+ return "Mycon egg shell #1";
+ case EGG_CASE2_DEFINED:
+ return "Mycon egg shell #2";
+ case PKUNK_DEFINED:
+ return "Pkunk home";
+ case UTWIG_DEFINED:
+ return "Utwig home";
+ case SUPOX_DEFINED:
+ return "Supox home";
+ case YEHAT_DEFINED:
+ return "Yehat home";
+ case VUX_DEFINED:
+ return "Vux home";
+ case ORZ_DEFINED:
+ return "Orz home";
+ case THRADD_DEFINED:
+ return "Thraddash home";
+ case RAINBOW_DEFINED:
+ return "Rainbow world";
+ case ILWRATH_DEFINED:
+ return "Ilwrath home";
+ case ANDROSYNTH_DEFINED:
+ return "Androsynth ruins";
+ case MYCON_TRAP_DEFINED:
+ return "Mycon trap";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+static void
+dumpPlanetCallback (const PLANET_DESC *planet, void *arg)
+{
+ FILE *out = ((DumpUniverseArg *) arg)->out;
+ dumpPlanet (out, planet);
+}
+
+void
+dumpPlanet (FILE *out, const PLANET_DESC *planet)
+{
+ (*pSolarSysState->genFuncs->generateName) (pSolarSysState, planet);
+ fprintf (out, "- %-37s %s\n", GLOBAL_SIS (PlanetName),
+ planetTypeString (planet->data_index & ~PLANET_SHIELDED));
+ dumpWorld (out, planet);
+}
+
+static void
+dumpMoonCallback (const PLANET_DESC *moon, void *arg)
+{
+ FILE *out = ((DumpUniverseArg *) arg)->out;
+ dumpMoon (out, moon);
+}
+
+void
+dumpMoon (FILE *out, const PLANET_DESC *moon)
+{
+ const char *typeStr;
+
+ if (moon->data_index == HIERARCHY_STARBASE)
+ {
+ typeStr = "StarBase";
+ }
+ else if (moon->data_index == SA_MATRA)
+ {
+ typeStr = "Sa-Matra";
+ }
+ else
+ {
+ typeStr = planetTypeString (moon->data_index & ~PLANET_SHIELDED);
+ }
+ fprintf (out, " - Moon %-30c %s\n",
+ 'a' + (moon - &pSolarSysState->MoonDesc[0]), typeStr);
+
+ dumpWorld (out, moon);
+}
+
+static void
+dumpWorld (FILE *out, const PLANET_DESC *world)
+{
+ PLANET_INFO *info;
+
+ if (world->data_index == HIERARCHY_STARBASE) {
+ return;
+ }
+
+ if (world->data_index == SA_MATRA) {
+ return;
+ }
+
+ info = &pSolarSysState->SysInfo.PlanetInfo;
+ fprintf(out, " AxialTilt: %d\n", info->AxialTilt);
+ fprintf(out, " Tectonics: %d\n", info->Tectonics);
+ fprintf(out, " Weather: %d\n", info->Weather);
+ fprintf(out, " Density: %d\n", info->PlanetDensity);
+ fprintf(out, " Radius: %d\n", info->PlanetRadius);
+ fprintf(out, " Gravity: %d\n", info->SurfaceGravity);
+ fprintf(out, " Temp: %d\n", info->SurfaceTemperature);
+ fprintf(out, " Day: %d\n", info->RotationPeriod);
+ fprintf(out, " Atmosphere: %d\n", info->AtmoDensity);
+ fprintf(out, " LifeChance: %d\n", info->LifeChance);
+ fprintf(out, " DistToSun: %d\n", info->PlanetToSunDist);
+
+ if (world->data_index & PLANET_SHIELDED) {
+ // Slave-shielded planet
+ return;
+ }
+
+ fprintf (out, " Bio: %4d Min: %4d\n",
+ calculateBioValue (pSolarSysState, world),
+ calculateMineralValue (pSolarSysState, world));
+}
+
+COUNT
+calculateBioValue (const SOLARSYS_STATE *system, const PLANET_DESC *world)
+{
+ COUNT result;
+ COUNT numBio;
+ COUNT i;
+
+ assert (system->pOrbitalDesc == world);
+
+ numBio = callGenerateForScanType (system, world, GENERATE_ALL,
+ BIOLOGICAL_SCAN, NULL);
+
+ result = 0;
+ for (i = 0; i < numBio; i++)
+ {
+ NODE_INFO info;
+ callGenerateForScanType (system, world, i, BIOLOGICAL_SCAN, &info);
+ result += BIO_CREDIT_VALUE *
+ LONIBBLE (CreatureData[info.type].ValueAndHitPoints);
+ }
+ return result;
+}
+
+void
+generateBioIndex(const SOLARSYS_STATE *system, const PLANET_DESC *world,
+ COUNT bio[])
+{
+ COUNT numBio;
+ COUNT i;
+
+ assert (system->pOrbitalDesc == world);
+
+ numBio = callGenerateForScanType (system, world, GENERATE_ALL,
+ BIOLOGICAL_SCAN, NULL);
+
+ for (i = 0; i < NUM_CREATURE_TYPES + NUM_SPECIAL_CREATURE_TYPES; i++)
+ bio[i] = 0;
+
+ for (i = 0; i < numBio; i++)
+ {
+ NODE_INFO info;
+ callGenerateForScanType (system, world, i, BIOLOGICAL_SCAN, &info);
+ bio[info.type]++;
+ }
+}
+
+COUNT
+calculateMineralValue (const SOLARSYS_STATE *system, const PLANET_DESC *world)
+{
+ COUNT result;
+ COUNT numDeposits;
+ COUNT i;
+
+ assert (system->pOrbitalDesc == world);
+
+ numDeposits = callGenerateForScanType (system, world, GENERATE_ALL,
+ MINERAL_SCAN, NULL);
+
+ result = 0;
+ for (i = 0; i < numDeposits; i++)
+ {
+ NODE_INFO info;
+ callGenerateForScanType (system, world, i, MINERAL_SCAN, &info);
+ result += HIBYTE (info.density) *
+ GLOBAL (ElementWorth[ElementCategory (info.type)]);
+ }
+ return result;
+}
+
+void
+generateMineralIndex(const SOLARSYS_STATE *system, const PLANET_DESC *world,
+ COUNT minerals[])
+{
+ COUNT numDeposits;
+ COUNT i;
+
+ assert (system->pOrbitalDesc == world);
+
+ numDeposits = callGenerateForScanType (system, world, GENERATE_ALL,
+ MINERAL_SCAN, NULL);
+
+ for (i = 0; i < NUM_ELEMENT_CATEGORIES; i++)
+ minerals[i] = 0;
+
+ for (i = 0; i < numDeposits; i++)
+ {
+ NODE_INFO info;
+ callGenerateForScanType (system, world, i, MINERAL_SCAN, &info);
+ minerals[ElementCategory (info.type)] += HIBYTE (info.density);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+struct TallyResourcesArg
+{
+ FILE *out;
+ COUNT mineralCount;
+ COUNT bioCount;
+};
+
+// Must be called from the Starcon2Main thread.
+void
+tallyResources (FILE *out)
+{
+ TallyResourcesArg tallyResourcesArg;
+ UniverseRecurseArg universeRecurseArg;
+
+ tallyResourcesArg.out = out;
+
+ universeRecurseArg.systemFuncPre = tallySystemPreCallback;
+ universeRecurseArg.systemFuncPost = tallySystemPostCallback;
+ universeRecurseArg.planetFuncPre = tallyPlanetCallback;
+ universeRecurseArg.planetFuncPost = NULL;
+ universeRecurseArg.moonFunc = tallyMoonCallback;
+ universeRecurseArg.arg = (void *) &tallyResourcesArg;
+
+ UniverseRecurse (&universeRecurseArg);
+}
+
+// Must be called from the Starcon2Main thread.
+void
+tallyResourcesToFile (void)
+{
+ FILE *out;
+
+# define RESOURCE_TALLY_FILE "ResourceTally"
+ out = fopen(RESOURCE_TALLY_FILE, "w");
+ if (out == NULL)
+ {
+ fprintf(stderr, "Error: Could not open file '%s' for "
+ "writing: %s\n", RESOURCE_TALLY_FILE, strerror(errno));
+ return;
+ }
+
+ tallyResources (out);
+
+ fclose(out);
+
+ fprintf(stdout, "*** Resource tally complete. The game may be in an "
+ "undefined state.\n");
+ // Data generation may have changed the game state,
+ // in particular for special planet generation.
+}
+
+static void
+tallySystemPreCallback (const STAR_DESC *star, const SOLARSYS_STATE *system,
+ void *arg)
+{
+ TallyResourcesArg *tallyResourcesArg = (TallyResourcesArg *) arg;
+ tallyResourcesArg->mineralCount = 0;
+ tallyResourcesArg->bioCount = 0;
+
+ (void) star; /* satisfy compiler */
+ (void) system; /* satisfy compiler */
+}
+
+static void
+tallySystemPostCallback (const STAR_DESC *star, const SOLARSYS_STATE *system,
+ void *arg)
+{
+ UNICODE name[256];
+ TallyResourcesArg *tallyResourcesArg = (TallyResourcesArg *) arg;
+ FILE *out = tallyResourcesArg->out;
+
+ GetClusterName (star, name);
+ fprintf (out, "%s\t%d\t%d\n", name, tallyResourcesArg->mineralCount,
+ tallyResourcesArg->bioCount);
+
+ (void) star; /* satisfy compiler */
+ (void) system; /* satisfy compiler */
+}
+
+static void
+tallyPlanetCallback (const PLANET_DESC *planet, void *arg)
+{
+ tallyResourcesWorld ((TallyResourcesArg *) arg, planet);
+}
+
+static void
+tallyMoonCallback (const PLANET_DESC *moon, void *arg)
+{
+ tallyResourcesWorld ((TallyResourcesArg *) arg, moon);
+}
+
+static void
+tallyResourcesWorld (TallyResourcesArg *arg, const PLANET_DESC *world)
+{
+ if (world->data_index == HIERARCHY_STARBASE) {
+ return;
+ }
+
+ if (world->data_index == SA_MATRA) {
+ return;
+ }
+
+ arg->bioCount += calculateBioValue (pSolarSysState, world),
+ arg->mineralCount += calculateMineralValue (pSolarSysState, world);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+forAllPlanetTypes (void (*callback) (int, const PlanetFrame *, void *),
+ void *arg)
+{
+ int i;
+ extern const PlanetFrame planet_array[];
+
+ for (i = 0; i < NUMBER_OF_PLANET_TYPES; i++)
+ callback (i, &planet_array[i], arg);
+}
+
+typedef struct
+{
+ FILE *out;
+} DumpPlanetTypesArg;
+
+void
+dumpPlanetTypes (FILE *out)
+{
+ DumpPlanetTypesArg dumpPlanetTypesArg;
+ dumpPlanetTypesArg.out = out;
+
+ forAllPlanetTypes (dumpPlanetTypeCallback, (void *) &dumpPlanetTypesArg);
+}
+
+static void
+dumpPlanetTypeCallback (int index, const PlanetFrame *planetType, void *arg)
+{
+ DumpPlanetTypesArg *dumpPlanetTypesArg = (DumpPlanetTypesArg *) arg;
+
+ dumpPlanetType(dumpPlanetTypesArg->out, index, planetType);
+}
+
+void
+dumpPlanetType (FILE *out, int index, const PlanetFrame *planetType)
+{
+ int i;
+ fprintf (out,
+ "%s\n"
+ "\tType: %s\n"
+ "\tColor: %s\n"
+ "\tSurface generation algoritm: %s\n"
+ "\tTectonics: %s\n"
+ "\tAtmosphere: %s\n"
+ "\tDensity: %s\n"
+ "\tElements:\n",
+ planetTypeString (index),
+ worldSizeString (PLANSIZE (planetType->Type)),
+ bodyColorString (PLANCOLOR (planetType->Type)),
+ worldGenAlgoString (PLANALGO (planetType->Type)),
+ tectonicsString (planetType->BaseTectonics),
+ atmosphereString (HINIBBLE (planetType->AtmoAndDensity)),
+ densityString (LONIBBLE (planetType->AtmoAndDensity))
+ );
+ for (i = 0; i < NUM_USEFUL_ELEMENTS; i++)
+ {
+ const ELEMENT_ENTRY *entry;
+ entry = &planetType->UsefulElements[i];
+ if (entry->Density == 0)
+ continue;
+ fprintf(out, "\t\t0 to %d %s-quality (+%d) deposits of %s (%s)\n",
+ DEPOSIT_QUANTITY (entry->Density),
+ depositQualityString (DEPOSIT_QUALITY (entry->Density)),
+ DEPOSIT_QUALITY (entry->Density) * 5,
+ GAME_STRING (ELEMENTS_STRING_BASE + entry->ElementType),
+ GAME_STRING (CARGO_STRING_BASE + 2 + ElementCategory (
+ entry->ElementType))
+ );
+ }
+ fprintf (out, "\n");
+}
+
+const char *
+planetTypeString (int typeIndex)
+{
+ static UNICODE typeStr[40];
+
+ if (typeIndex >= FIRST_GAS_GIANT)
+ {
+ // "Gas Giant"
+ snprintf(typeStr, sizeof typeStr, "%s",
+ GAME_STRING (SCAN_STRING_BASE + 4 + 51));
+ }
+ else
+ {
+ // "<type> World" (eg. "Water World")
+ snprintf(typeStr, sizeof typeStr, "%s %s",
+ GAME_STRING (SCAN_STRING_BASE + 4 + typeIndex),
+ GAME_STRING (SCAN_STRING_BASE + 4 + 50));
+ }
+ return typeStr;
+}
+
+// size is what you get from PLANSIZE (planetFrame.Type)
+const char *
+worldSizeString (BYTE size)
+{
+ switch (size)
+ {
+ case SMALL_ROCKY_WORLD:
+ return "small rocky world";
+ case LARGE_ROCKY_WORLD:
+ return "large rocky world";
+ case GAS_GIANT:
+ return "gas giant";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+// algo is what you get from PLANALGO (planetFrame.Type)
+const char *
+worldGenAlgoString (BYTE algo)
+{
+ switch (algo)
+ {
+ case TOPO_ALGO:
+ return "TOPO_ALGO";
+ case CRATERED_ALGO:
+ return "CRATERED_ALGO";
+ case GAS_GIANT_ALGO:
+ return "GAS_GIANT_ALGO";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+// tectonics is what you get from planetFrame.BaseTechtonics
+// not reentrant
+const char *
+tectonicsString (BYTE tectonics)
+{
+ static char buf[sizeof "-127"];
+ switch (tectonics)
+ {
+ case NO_TECTONICS:
+ return "none";
+ case LOW_TECTONICS:
+ return "low";
+ case MED_TECTONICS:
+ return "medium";
+ case HIGH_TECTONICS:
+ return "high";
+ case SUPER_TECTONICS:
+ return "super";
+ default:
+ snprintf (buf, sizeof buf, "%d", tectonics);
+ return buf;
+ }
+}
+
+// atmosphere is what you get from HINIBBLE (planetFrame.AtmoAndDensity)
+const char *
+atmosphereString (BYTE atmosphere)
+{
+ switch (atmosphere)
+ {
+ case LIGHT:
+ return "thin";
+ case MEDIUM:
+ return "normal";
+ case HEAVY:
+ return "thick";
+ default:
+ return "super thick";
+ }
+}
+
+// density is what you get from LONIBBLE (planetFrame.AtmoAndDensity)
+const char *
+densityString (BYTE density)
+{
+ switch (density)
+ {
+ case GAS_DENSITY:
+ return "gaseous";
+ case LIGHT_DENSITY:
+ return "light";
+ case LOW_DENSITY:
+ return "low";
+ case NORMAL_DENSITY:
+ return "normal";
+ case HIGH_DENSITY:
+ return "high";
+ case SUPER_DENSITY:
+ return "super high";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+// quality is what you get from DEPOSIT_QUALITY (elementEntry.Density)
+const char *
+depositQualityString (BYTE quality)
+{
+ switch (quality)
+ {
+ case LIGHT:
+ return "low";
+ case MEDIUM:
+ return "medium";
+ case HEAVY:
+ return "high";
+ default:
+ // Should not happen
+ return "???";
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+// playerNr should be 0 or 1
+STARSHIP*
+findPlayerShip (SIZE playerNr)
+{
+ HELEMENT hElement, hNextElement;
+
+ for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
+ {
+ ELEMENT *ElementPtr;
+
+ LockElement (hElement, &ElementPtr);
+ hNextElement = GetSuccElement (ElementPtr);
+
+ if ((ElementPtr->state_flags & PLAYER_SHIP) &&
+ ElementPtr->playerNr == playerNr)
+ {
+ STARSHIP *StarShipPtr;
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ UnlockElement (hElement);
+ return StarShipPtr;
+ }
+
+ UnlockElement (hElement);
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+void
+resetCrewBattle (void)
+{
+ STARSHIP *StarShipPtr;
+ COUNT delta;
+ CONTEXT OldContext;
+
+ if (!(GLOBAL (CurrentActivity) & IN_BATTLE) ||
+ (inHQSpace ()))
+ return;
+
+ StarShipPtr = findPlayerShip (RPG_PLAYER_NUM);
+ if (StarShipPtr == NULL || StarShipPtr->RaceDescPtr == NULL)
+ return;
+
+ delta = StarShipPtr->RaceDescPtr->ship_info.max_crew -
+ StarShipPtr->RaceDescPtr->ship_info.crew_level;
+
+ OldContext = SetContext (StatusContext);
+ DeltaCrew (StarShipPtr->hShip, delta);
+ SetContext (OldContext);
+}
+
+void
+resetEnergyBattle (void)
+{
+ STARSHIP *StarShipPtr;
+ COUNT delta;
+ CONTEXT OldContext;
+
+ if (!(GLOBAL (CurrentActivity) & IN_BATTLE) ||
+ (inHQSpace ()))
+ return;
+
+ StarShipPtr = findPlayerShip (RPG_PLAYER_NUM);
+ if (StarShipPtr == NULL || StarShipPtr->RaceDescPtr == NULL)
+ return;
+
+ delta = StarShipPtr->RaceDescPtr->ship_info.max_energy -
+ StarShipPtr->RaceDescPtr->ship_info.energy_level;
+
+ OldContext = SetContext (StatusContext);
+ DeltaEnergy (StarShipPtr->hShip, delta);
+ SetContext (OldContext);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+// This function should help in making sure that gamestr.h matches
+// gamestrings.txt.
+void
+dumpStrings (FILE *out)
+{
+#define STRINGIZE(a) #a
+#define MAKE_STRING_CATEGORY(prefix) \
+ { \
+ /* .name = */ STRINGIZE(prefix ## _BASE), \
+ /* .base = */ prefix ## _BASE, \
+ /* .count = */ prefix ## _COUNT \
+ }
+ struct {
+ const char *name;
+ size_t base;
+ size_t count;
+ } categories[] = {
+ { "0", 0, 0 },
+ MAKE_STRING_CATEGORY(STAR_STRING),
+ MAKE_STRING_CATEGORY(DEVICE_STRING),
+ MAKE_STRING_CATEGORY(CARGO_STRING),
+ MAKE_STRING_CATEGORY(ELEMENTS_STRING),
+ MAKE_STRING_CATEGORY(SCAN_STRING),
+ MAKE_STRING_CATEGORY(STAR_NUMBER),
+ MAKE_STRING_CATEGORY(PLANET_NUMBER),
+ MAKE_STRING_CATEGORY(MONTHS_STRING),
+ MAKE_STRING_CATEGORY(FEEDBACK_STRING),
+ MAKE_STRING_CATEGORY(STARBASE_STRING),
+ MAKE_STRING_CATEGORY(ENCOUNTER_STRING),
+ MAKE_STRING_CATEGORY(NAVIGATION_STRING),
+ MAKE_STRING_CATEGORY(NAMING_STRING),
+ MAKE_STRING_CATEGORY(MELEE_STRING),
+ MAKE_STRING_CATEGORY(SAVEGAME_STRING),
+ MAKE_STRING_CATEGORY(OPTION_STRING),
+ MAKE_STRING_CATEGORY(QUITMENU_STRING),
+ MAKE_STRING_CATEGORY(STATUS_STRING),
+ MAKE_STRING_CATEGORY(FLAGSHIP_STRING),
+ MAKE_STRING_CATEGORY(ORBITSCAN_STRING),
+ MAKE_STRING_CATEGORY(MAINMENU_STRING),
+ MAKE_STRING_CATEGORY(NETMELEE_STRING),
+ { "GAMESTR_COUNT", GAMESTR_COUNT, (size_t) -1 }
+ };
+ size_t numCategories = sizeof categories / sizeof categories[0];
+ size_t numStrings = GetStringTableCount (GameStrings);
+ size_t stringI;
+ size_t categoryI = 0;
+
+ // Start with a sanity check to see if gamestr.h has been changed but
+ // not this file.
+ for (categoryI = 0; categoryI < numCategories - 1; categoryI++) {
+ if (categories[categoryI].base + categories[categoryI].count !=
+ categories[categoryI + 1].base) {
+ fprintf(stderr, "Error: String category list in dumpStrings() is "
+ "not up to date.\n");
+ return;
+ }
+ }
+
+ if (GAMESTR_COUNT != numStrings) {
+ fprintf(stderr, "Warning: GAMESTR_COUNT is %d, but GameStrings "
+ "contains %d strings.\n", GAMESTR_COUNT, numStrings);
+ }
+
+ categoryI = 0;
+ for (stringI = 0; stringI < numStrings; stringI++) {
+ while (categoryI < numCategories &&
+ stringI >= categories[categoryI + 1].base)
+ categoryI++;
+ fprintf(out, "[ %s + %d ] %s\n", categories[categoryI].name,
+ stringI - categories[categoryI].base, GAME_STRING(stringI));
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+
+static Color
+hsvaToRgba (double hue, double sat, double val, BYTE alpha)
+{
+ unsigned int hi = (int) (hue / 60.0);
+ double f = (hue / 60.0) - ((int) (hue / 60.0));
+ double p = val * (1.0 - sat);
+ double q = val * (1.0 - f * sat);
+ double t = val * (1.0 - (1.0 - f * sat));
+
+ // Convert p, q, t, and v from [0..1] to [0..255]
+ BYTE pb = (BYTE) (p * 255.0 + 0.5);
+ BYTE qb = (BYTE) (q * 255.0 + 0.5);
+ BYTE tb = (BYTE) (t * 255.0 + 0.5);
+ BYTE vb = (BYTE) (val * 255.0 + 0.5);
+
+ assert (hue >= 0.0 && hue < 360.0);
+ assert (sat >= 0 && sat <= 1.0);
+ assert (val >= 0 && val <= 1.0);
+ /*fprintf(stderr, "hsva = (%.1f, %.2f, %.2f, %.2d)\n",
+ hue, sat, val, alpha);*/
+
+ assert (hi < 6);
+ switch (hi) {
+ case 0: return BUILD_COLOR_RGBA (vb, tb, pb, alpha);
+ case 1: return BUILD_COLOR_RGBA (qb, vb, pb, alpha);
+ case 2: return BUILD_COLOR_RGBA (pb, vb, tb, alpha);
+ case 3: return BUILD_COLOR_RGBA (pb, qb, vb, alpha);
+ case 4: return BUILD_COLOR_RGBA (tb, pb, vb, alpha);
+ case 5: return BUILD_COLOR_RGBA (vb, pb, qb, alpha);
+ }
+
+ // Should not happen.
+ return BUILD_COLOR_RGBA (0, 0, 0, alpha);
+}
+
+// Returns true iff this context has a visible FRAME.
+static bool
+isContextVisible (CONTEXT context)
+{
+ FRAME contextFrame;
+
+ // Save the original context.
+ CONTEXT oldContext = SetContext (context);
+
+ // Get the frame of the specified context.
+ contextFrame = GetContextFGFrame ();
+
+ // Restore the original context.
+ SetContext (oldContext);
+
+ return contextFrame == Screen;
+}
+
+static size_t
+countVisibleContexts (void)
+{
+ size_t contextCount;
+ CONTEXT context;
+
+ contextCount = 0;
+ for (context = GetFirstContext (); context != NULL;
+ context = GetNextContext (context))
+ {
+ if (!isContextVisible (context))
+ continue;
+
+ contextCount++;
+ }
+
+ return contextCount;
+}
+
+static void
+drawContext (CONTEXT context, double hue /* no pun intended */)
+{
+ FRAME drawFrame;
+ CONTEXT oldContext;
+ FONT oldFont;
+ DrawMode oldMode;
+ Color oldFgCol;
+ Color rectCol;
+ Color lineCol;
+ Color textCol;
+ bool haveClippingRect;
+ RECT rect;
+ LINE line;
+ TEXT text;
+ POINT p1, p2, p3, p4;
+
+ drawFrame = GetContextFGFrame ();
+ rectCol = hsvaToRgba (hue, 1.0, 0.5, 100);
+ lineCol = hsvaToRgba (hue, 1.0, 1.0, 90);
+ textCol = lineCol;
+
+ // Save the original context.
+ oldContext = SetContext (context);
+
+ // Get the clipping rectangle of the specified context.
+ haveClippingRect = GetContextClipRect (&rect);
+
+ // Switch back the old context; we're going to draw in it.
+ (void) SetContext (oldContext);
+
+ p1 = rect.corner;
+ p2.x = rect.corner.x + rect.extent.width - 1;
+ p2.y = rect.corner.y;
+ p3.x = rect.corner.x;
+ p3.y = rect.corner.y + rect.extent.height - 1;
+ p4.x = rect.corner.x + rect.extent.width - 1;
+ p4.y = rect.corner.y + rect.extent.height - 1;
+
+ oldFgCol = SetContextForeGroundColor (rectCol);
+ DrawFilledRectangle (&rect);
+
+ SetContextForeGroundColor (lineCol);
+ line.first = p1; line.second = p2; DrawLine (&line);
+ line.first = p2; line.second = p4; DrawLine (&line);
+ line.first = p1; line.second = p3; DrawLine (&line);
+ line.first = p3; line.second = p4; DrawLine (&line);
+ line.first = p1; line.second = p4; DrawLine (&line);
+ line.first = p2; line.second = p3; DrawLine (&line);
+ // Gimme C'99! So I can do:
+ // DrawLine ((LINE) { .first = p1, .second = p2 })
+
+ oldFont = SetContextFont (TinyFont);
+ SetContextForeGroundColor (textCol);
+ // Text prim does not yet support alpha via Color.a
+ oldMode = SetContextDrawMode (MAKE_DRAW_MODE (DRAW_ALPHA, textCol.a));
+ text.baseline.x = (p1.x + (p2.x + 1)) / 2;
+ text.baseline.y = p1.y + 8;
+ text.pStr = GetContextName (context);
+ text.align = ALIGN_CENTER;
+ text.CharCount = (COUNT) ~0;
+ font_DrawText (&text);
+ (void) SetContextDrawMode (oldMode);
+
+ (void) SetContextForeGroundColor (oldFgCol);
+ (void) SetContextFont (oldFont);
+}
+
+static void
+describeContext (FILE *out, const CONTEXT context) {
+ RECT rect;
+ CONTEXT oldContext = SetContext (context);
+
+ GetContextClipRect (&rect);
+ fprintf(out, "Context '%s':\n"
+ "\tClipRect = (%d, %d)-(%d, %d) (%d x %d)\n",
+ GetContextName (context),
+ rect.corner.x, rect.corner.y,
+ rect.corner.x + rect.extent.width,
+ rect.corner.y + rect.extent.height,
+ rect.extent.width, rect.extent.height);
+
+ SetContext (oldContext);
+}
+
+
+typedef struct wait_state
+{
+ // standard state required by DoInput
+ BOOLEAN (*InputFunc) (struct wait_state *self);
+} WAIT_STATE;
+
+
+// Maybe move to elsewhere, where it can be reused?
+static BOOLEAN
+waitForKey (struct wait_state *self) {
+ if (PulsedInputState.menu[KEY_MENU_SELECT] ||
+ PulsedInputState.menu[KEY_MENU_CANCEL])
+ return FALSE;
+
+ SleepThread (ONE_SECOND / 20);
+
+ (void) self;
+ return TRUE;
+}
+
+// Maybe move to elsewhere, where it can be reused?
+static FRAME
+getScreen (void)
+{
+ CONTEXT oldContext = SetContext (ScreenContext);
+ FRAME savedFrame;
+ RECT screenRect;
+
+ screenRect.corner.x = 0;
+ screenRect.corner.y = 0;
+ screenRect.extent.width = ScreenWidth;
+ screenRect.extent.height = ScreenHeight;
+ savedFrame = CaptureDrawable (LoadDisplayPixmap (&screenRect, (FRAME) 0));
+
+ (void) SetContext (oldContext);
+ return savedFrame;
+}
+
+static void
+putScreen (FRAME savedFrame) {
+ STAMP stamp;
+
+ CONTEXT oldContext = SetContext (ScreenContext);
+
+ stamp.origin.x = 0;
+ stamp.origin.y = 0;
+ stamp.frame = savedFrame;
+ DrawStamp (&stamp);
+
+ (void) SetContext (oldContext);
+}
+
+// Show the contexts on the screen.
+// Must be called from the main thread.
+void
+debugContexts (void)
+{
+ static volatile bool inDebugContexts = false;
+ // Prevent this function from being called from within itself.
+
+ CONTEXT orgContext;
+ CONTEXT debugDrawContext;
+ // We're going to use this context to draw in.
+ FRAME debugDrawFrame;
+ double hueIncrement;
+ size_t visibleContextI;
+ CONTEXT context;
+ size_t contextCount;
+ FRAME savedScreen;
+
+ // Prevent this function from being called from within itself.
+ if (inDebugContexts)
+ return;
+ inDebugContexts = true;
+
+ contextCount = countVisibleContexts ();
+ if (contextCount == 0)
+ {
+ goto out;
+ }
+
+ savedScreen = getScreen ();
+ FlushGraphics ();
+ // Make sure that the screen has actually been captured,
+ // before we use the frame.
+
+ // Create a new frame to draw on.
+ debugDrawContext = CreateContext ("debugDrawContext");
+ // New work frame is a copy of the original.
+ debugDrawFrame = CaptureDrawable (CloneFrame (savedScreen));
+ orgContext = SetContext (debugDrawContext);
+ SetContextFGFrame (debugDrawFrame);
+
+ hueIncrement = 360.0 / contextCount;
+
+ visibleContextI = 0;
+ for (context = GetFirstContext (); context != NULL;
+ context = GetNextContext (context))
+ {
+ if (context == debugDrawContext) {
+ // Skip our own context.
+ continue;
+ }
+
+ if (isContextVisible (context))
+ {
+ // Only draw the visible contexts.
+ drawContext (context, visibleContextI * hueIncrement);
+ visibleContextI++;
+ }
+
+ describeContext (stderr, context);
+ }
+
+ // Blit the final debugging frame to the screen.
+ putScreen (debugDrawFrame);
+
+ // Wait for a key:
+ {
+ WAIT_STATE state;
+ state.InputFunc = waitForKey;
+ DoInput(&state, TRUE);
+ }
+
+ SetContext (orgContext);
+
+ // Destroy the debugging frame and context.
+ DestroyContext (debugDrawContext);
+ // This does nothing with the drawable set with
+ // SetContextFGFrame().
+ DestroyDrawable (ReleaseDrawable (debugDrawFrame));
+
+ putScreen (savedScreen);
+
+ DestroyDrawable (ReleaseDrawable (savedScreen));
+
+out:
+ inDebugContexts = false;
+}
+
+#endif /* DEBUG */
+
diff --git a/src/uqm/uqmdebug.h b/src/uqm/uqmdebug.h
new file mode 100644
index 0000000..423841e
--- /dev/null
+++ b/src/uqm/uqmdebug.h
@@ -0,0 +1,200 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#if !defined(UQMDEBUG_H) && (defined(DEBUG) || defined(USE_DEBUG_KEY))
+#define UQMDEBUG_H
+
+#include "clock.h"
+#include "planets/planets.h"
+#include "races.h"
+#include "libs/compiler.h"
+
+#include <stdio.h>
+
+
+// If set to true, interactive routines that are called (indirectly) in debug
+// functions are a no-op.
+extern BOOLEAN disableInteractivity;
+
+// If a function is assigned to this, it will be called from the
+// Starcon2Main thread, in the main game loop.
+extern void (* volatile debugHook) (void);
+
+// Called on the main() thread when the debug key (symbol 'Debug' in the
+// keys.cfg) is pressed
+void debugKeyPressed (void);
+// Called on the Starcon2Main() thread when the debug key (symbol 'Debug'
+// in the keys.cfg) is pressed.
+void debugKeyPressedSynchronous (void);
+
+// Forward time to the next event. If skipHEE is set, the event named
+// HYPERSPACE_ENCOUNTER_EVENT, which normally occurs every game day,
+// is skipped. Must be called on the Starcon2Main thread.
+void forwardToNextEvent (BOOLEAN skipHEE);
+// Generate a list of all events in the event queue.
+// Must be called on the Starcon2Main thread.
+void dumpEvents (FILE *out);
+// Describe one event.
+void dumpEvent (FILE *out, const EVENT *eventPtr);
+// Get the name of one event.
+const char *eventName (BYTE func_index);
+
+// Give the flagship a decent equipment for debugging.
+void equipShip (void);
+// Give the player all devices.
+void giveDevices (void);
+
+// Remove all escort ships.
+void clearEscorts (void);
+
+// Show all active spheres of influence.
+void showSpheres (void);
+
+// Make the ships of all races available for building at the shipyard.
+void activateAllShips (void);
+
+// Move the Flagship to the destination of the autopilot.
+// Should only be called from HS/QS.
+// It can be called from debugHook directly after entering HS/QS though.
+void doInstantMove (void);
+
+
+// Call a function for all stars.
+void forAllStars (void (*callback) (STAR_DESC *, void *), void *arg);
+// Call a function for all planets in a star system.
+void forAllPlanets (STAR_DESC *star, SOLARSYS_STATE *system,
+ void (*callback) (STAR_DESC *, SOLARSYS_STATE *, PLANET_DESC *,
+ void *), void *arg);
+// Call a function for all moons of a planet.
+void forAllMoons (STAR_DESC *star, SOLARSYS_STATE *system, PLANET_DESC *planet,
+ void (*callback) (STAR_DESC *, SOLARSYS_STATE *, PLANET_DESC *,
+ PLANET_DESC *, void *), void *arg);
+
+// Argument to UniverseRecurse()
+typedef struct
+{
+ void (*systemFuncPre) (const STAR_DESC *star,
+ const SOLARSYS_STATE *system, void *arg);
+ // Called for each system prior to recursing to its planets.
+ void (*systemFuncPost) (const STAR_DESC *star,
+ const SOLARSYS_STATE *system, void *arg);
+ // Called for each system after recursing to its planets.
+ void (*planetFuncPre) (const PLANET_DESC *planet, void *arg);
+ // Called for each planet prior to recursing to its moons.
+ void (*planetFuncPost) (const PLANET_DESC *planet, void *arg);
+ // Called for each planet after recursing to its moons.
+ void (*moonFunc) (const PLANET_DESC *moon, void *arg);
+ // Called for each moon.
+ void *arg;
+ // User data.
+} UniverseRecurseArg;
+// Recurse through all systems, planets, and moons in the universe.
+// Must be called on the Starcon2Main thread.
+void UniverseRecurse (UniverseRecurseArg *universeRecurseArg);
+
+// Describe the entire universe. Must be called on the Starcon2Main thread.
+void dumpUniverse (FILE *out);
+// Describe the entire universe, output to a file "./PlanetInfo".
+// Must be called on the Starcon2Main thread.
+void dumpUniverseToFile (void);
+// Describe one star system.
+void dumpSystem (FILE *out, const STAR_DESC *star,
+ const SOLARSYS_STATE *system);
+// Get a star color as a string.
+const char *bodyColorString (BYTE col);
+// Get a star type as a string.
+const char *starTypeString (BYTE type);
+// Get a string describing special presence in the star system.
+const char *starPresenceString (BYTE index);
+// Get a list describing all planets in a star.
+void dumpPlanets (FILE *out, const STAR_DESC *star);
+// Describe one planet.
+void dumpPlanet(FILE *out, const PLANET_DESC *planet);
+// Describe one moon.
+void dumpMoon (FILE *out, const PLANET_DESC *moon);
+// Calculate the total value of all minerals on a world.
+COUNT calculateMineralValue (const SOLARSYS_STATE *system,
+ const PLANET_DESC *world);
+// Determine how much of each mineral type is present on a world
+void generateMineralIndex(const SOLARSYS_STATE *system,
+ const PLANET_DESC *world, COUNT minerals[]);
+// Calculate the total value of all bio on a world.
+COUNT calculateBioValue (const SOLARSYS_STATE *system,
+ const PLANET_DESC *world);
+// Determine how much of each mineral type is present on a world
+void generateBioIndex(const SOLARSYS_STATE *system,
+ const PLANET_DESC *world, COUNT bio[]);
+
+// Tally the resources for each star system.
+// Must be called on the Starcon2Main thread.
+void tallyResources (FILE *out);
+// Tally the resources for each star system, output to a file
+// "./ResourceTally". Must be called on the Starcon2Main thread.
+void tallyResourcesToFile (void);
+
+
+// Call a function for all planet types.
+void forAllPlanetTypes (void (*callBack) (int, const PlanetFrame *,
+ void *), void *arg);
+// Describe one planet type.
+void dumpPlanetType(FILE *out, int index, const PlanetFrame *planetFrame);
+// Generate a list of all planet types.
+void dumpPlanetTypes(FILE *out);
+// Get a string describing a planet type.
+const char *planetTypeString (int typeIndex);
+// Get a string describing the size of a type of planet.
+const char *worldSizeString (BYTE size);
+// Get a string describing a planet type map generation algoritm.
+const char *worldGenAlgoString (BYTE algo);
+// Get a string describing the severity of a tectonics on a type of planet.
+const char *tectonicsString (BYTE tectonics);
+// Get a string describing the atmospheric pressure on a type of planet.
+const char *atmosphereString (BYTE atmosphere);
+// Get a string describing the density of a type of planet.
+const char *densityString (BYTE density);
+
+// Get a string describing the quality of a deposit.
+const char *depositQualityString (BYTE quality);
+
+
+// Find a player ship. Setting playerNr to non-0 is only meaningful in battle.
+STARSHIP* findPlayerShip (SIZE playerNr);
+
+// Resets the crew of the first player (the bottom one) to its maximum.
+void resetCrewBattle(void);
+
+// Resets the energy of the first player (the bottom one) to its maximum.
+void resetEnergyBattle(void);
+
+
+// Move instantly across hyperspace/quasispace.
+extern BOOLEAN instantMove;
+
+
+// Dump all game strings.
+void dumpStrings(FILE *out);
+
+
+// Graphically and textually show all the contexts.
+// Must be called on the Starcon2Main thread.
+void debugContexts (void);
+
+
+// To add some day:
+// - a function to fast forward the game clock to a specifiable time.
+
+#endif /* _DEBUG_H */
+
diff --git a/src/uqm/util.c b/src/uqm/util.c
new file mode 100644
index 0000000..aee73a6
--- /dev/null
+++ b/src/uqm/util.c
@@ -0,0 +1,312 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "controls.h"
+#include "util.h"
+#include "setup.h"
+#include "units.h"
+#include "settings.h"
+#include "libs/inplib.h"
+#include "libs/sound/trackplayer.h"
+#include "libs/mathlib.h"
+#include "libs/log.h"
+
+
+void
+DrawStarConBox (RECT *pRect, SIZE BorderWidth, Color TopLeftColor,
+ Color BottomRightColor, BOOLEAN FillInterior, Color InteriorColor)
+{
+ RECT locRect;
+
+ if (BorderWidth == 0)
+ BorderWidth = 2;
+ else
+ {
+ SetContextForeGroundColor (TopLeftColor);
+ locRect.corner = pRect->corner;
+ locRect.extent.width = pRect->extent.width;
+ locRect.extent.height = 1;
+ DrawFilledRectangle (&locRect);
+ if (BorderWidth == 2)
+ {
+ ++locRect.corner.x;
+ ++locRect.corner.y;
+ locRect.extent.width -= 2;
+ DrawFilledRectangle (&locRect);
+ }
+
+ locRect.corner = pRect->corner;
+ locRect.extent.width = 1;
+ locRect.extent.height = pRect->extent.height;
+ DrawFilledRectangle (&locRect);
+ if (BorderWidth == 2)
+ {
+ ++locRect.corner.x;
+ ++locRect.corner.y;
+ locRect.extent.height -= 2;
+ DrawFilledRectangle (&locRect);
+ }
+
+ SetContextForeGroundColor (BottomRightColor);
+ locRect.corner.x = pRect->corner.x + pRect->extent.width - 1;
+ locRect.corner.y = pRect->corner.y + 1;
+ locRect.extent.height = pRect->extent.height - 1;
+ DrawFilledRectangle (&locRect);
+ if (BorderWidth == 2)
+ {
+ --locRect.corner.x;
+ ++locRect.corner.y;
+ locRect.extent.height -= 2;
+ DrawFilledRectangle (&locRect);
+ }
+
+ locRect.corner.x = pRect->corner.x;
+ locRect.extent.width = pRect->extent.width;
+ locRect.corner.y = pRect->corner.y + pRect->extent.height - 1;
+ locRect.extent.height = 1;
+ DrawFilledRectangle (&locRect);
+ if (BorderWidth == 2)
+ {
+ ++locRect.corner.x;
+ --locRect.corner.y;
+ locRect.extent.width -= 2;
+ DrawFilledRectangle (&locRect);
+ }
+ }
+
+ if (FillInterior)
+ {
+ SetContextForeGroundColor (InteriorColor);
+ locRect.corner.x = pRect->corner.x + BorderWidth;
+ locRect.corner.y = pRect->corner.y + BorderWidth;
+ locRect.extent.width = pRect->extent.width - (BorderWidth << 1);
+ locRect.extent.height = pRect->extent.height - (BorderWidth << 1);
+ DrawFilledRectangle (&locRect);
+ }
+}
+
+DWORD
+SeedRandomNumbers (void)
+{
+ DWORD cur_time;
+
+ cur_time = GetTimeCounter ();
+ TFB_SeedRandom (cur_time);
+
+ return (cur_time);
+}
+
+STAMP
+SaveContextFrame (const RECT *saveRect)
+{
+ STAMP s;
+
+ if (saveRect)
+ { // a portion of the context
+ s.origin = saveRect->corner;
+ }
+ else
+ { // the entire context
+ s.origin.x = 0;
+ s.origin.y = 0;
+ }
+
+ s.frame = CaptureDrawable (CopyContextRect (saveRect));
+
+ return s;
+}
+
+BOOLEAN
+PauseGame (void)
+{
+ RECT r;
+ STAMP s;
+ CONTEXT OldContext;
+ STAMP saveStamp;
+ RECT ctxRect;
+ POINT oldOrigin;
+ RECT OldRect;
+
+ if (ActivityFrame == 0
+ || (GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_PAUSE))
+ || (LastActivity & (CHECK_LOAD | CHECK_RESTART)))
+ return (FALSE);
+
+ GLOBAL (CurrentActivity) |= CHECK_PAUSE;
+
+ if (PlayingTrack ())
+ PauseTrack ();
+
+ OldContext = SetContext (ScreenContext);
+ oldOrigin = SetContextOrigin (MAKE_POINT (0, 0));
+ GetContextClipRect (&OldRect);
+ SetContextClipRect (NULL);
+
+ GetContextClipRect (&ctxRect);
+ GetFrameRect (ActivityFrame, &r);
+ r.corner.x = (ctxRect.extent.width - r.extent.width) >> 1;
+ r.corner.y = (ctxRect.extent.height - r.extent.height) >> 1;
+ saveStamp = SaveContextFrame (&r);
+
+ // TODO: This should draw a localizable text message instead
+ s.origin = r.corner;
+ s.frame = ActivityFrame;
+ SetSystemRect (&r);
+ DrawStamp (&s);
+
+ FlushGraphics ();
+
+ while (ImmediateInputState.menu[KEY_PAUSE] && GamePaused)
+ {
+ BeginInputFrame ();
+ TaskSwitch ();
+ }
+
+ while (!ImmediateInputState.menu[KEY_PAUSE] && GamePaused)
+ {
+ BeginInputFrame ();
+ TaskSwitch ();
+ }
+
+ while (ImmediateInputState.menu[KEY_PAUSE] && GamePaused)
+ {
+ BeginInputFrame ();
+ TaskSwitch ();
+ }
+
+ GamePaused = FALSE;
+
+ DrawStamp (&saveStamp);
+ DestroyDrawable (ReleaseDrawable (saveStamp.frame));
+ ClearSystemRect ();
+
+ SetContextClipRect (&OldRect);
+ SetContextOrigin (oldOrigin);
+ SetContext (OldContext);
+
+ WaitForNoInput (ONE_SECOND / 4, TRUE);
+
+ if (PlayingTrack ())
+ ResumeTrack ();
+
+
+ TaskSwitch ();
+ GLOBAL (CurrentActivity) &= ~CHECK_PAUSE;
+ return (TRUE);
+}
+
+// Waits for a button to be pressed
+// Returns TRUE if the wait succeeded (found input)
+// FALSE if timed out or game aborted
+BOOLEAN
+WaitForAnyButtonUntil (BOOLEAN newButton, TimeCount timeOut,
+ BOOLEAN resetInput)
+{
+ BOOLEAN buttonPressed;
+
+ if (newButton && !WaitForNoInputUntil (timeOut, FALSE))
+ return FALSE;
+
+ buttonPressed = AnyButtonPress (TRUE);
+ while (!buttonPressed
+ && (timeOut == WAIT_INFINITE || GetTimeCounter () < timeOut)
+ && !(GLOBAL (CurrentActivity) & CHECK_ABORT)
+ && !QuitPosted)
+ {
+ SleepThread (ONE_SECOND / 40);
+ buttonPressed = AnyButtonPress (TRUE);
+ }
+
+ if (resetInput)
+ FlushInput ();
+
+ return buttonPressed;
+}
+
+BOOLEAN
+WaitForAnyButton (BOOLEAN newButton, TimePeriod duration, BOOLEAN resetInput)
+{
+ TimeCount timeOut = duration;
+ if (duration != WAIT_INFINITE)
+ timeOut += GetTimeCounter ();
+ return WaitForAnyButtonUntil (newButton, timeOut, resetInput);
+}
+
+// Returns TRUE if the wait succeeded (found no input)
+// FALSE if timed out or game aborted
+BOOLEAN
+WaitForNoInputUntil (TimeCount timeOut, BOOLEAN resetInput)
+{
+ BOOLEAN buttonPressed;
+
+ buttonPressed = AnyButtonPress (TRUE);
+ while (buttonPressed
+ && (timeOut == WAIT_INFINITE || GetTimeCounter () < timeOut)
+ && !(GLOBAL (CurrentActivity) & CHECK_ABORT)
+ && !QuitPosted)
+ {
+ SleepThread (ONE_SECOND / 40);
+ buttonPressed = AnyButtonPress (TRUE);
+ }
+
+ if (resetInput)
+ FlushInput ();
+
+ return !buttonPressed;
+}
+
+BOOLEAN
+WaitForNoInput (TimePeriod duration, BOOLEAN resetInput)
+{
+ TimeCount timeOut = duration;
+ if (duration != WAIT_INFINITE)
+ timeOut += GetTimeCounter ();
+ return WaitForNoInputUntil (timeOut, resetInput);
+}
+
+// Stops game clock and music thread and minimizes interrupts/cycles
+// based on value of global GameActive variable
+// See similar sleep state for main thread in uqm.c:main()
+void
+SleepGame (void)
+{
+ if (QuitPosted)
+ return; // Do not sleep the game when already asked to quit
+
+ log_add (log_Debug, "Game is going to sleep");
+
+ if (PlayingTrack ())
+ PauseTrack ();
+ PauseMusic ();
+
+
+ while (!GameActive && !QuitPosted)
+ SleepThread (ONE_SECOND / 2);
+
+ log_add (log_Debug, "Game is waking up");
+
+ WaitForNoInput (ONE_SECOND / 10, TRUE);
+
+ ResumeMusic ();
+
+ if (PlayingTrack ())
+ ResumeTrack ();
+
+
+ TaskSwitch ();
+}
diff --git a/src/uqm/util.h b/src/uqm/util.h
new file mode 100644
index 0000000..0949809
--- /dev/null
+++ b/src/uqm/util.h
@@ -0,0 +1,39 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_UTIL_H_
+#define UQM_UTIL_H_
+
+#include "libs/compiler.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern void DrawStarConBox (RECT *pRect, SIZE BorderWidth,
+ Color TopLeftColor, Color BottomRightColor, BOOLEAN FillInterior,
+ Color InteriorColor);
+extern DWORD SeedRandomNumbers (void);
+
+// saveRect can be NULL to save the entire context frame
+extern STAMP SaveContextFrame (const RECT *saveRect);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_UTIL_H_ */
diff --git a/src/uqm/velocity.c b/src/uqm/velocity.c
new file mode 100644
index 0000000..5e39f02
--- /dev/null
+++ b/src/uqm/velocity.c
@@ -0,0 +1,153 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "velocity.h"
+
+#include "units.h"
+#include "libs/compiler.h"
+
+
+#define VELOCITY_REMAINDER(v) ((v) & (VELOCITY_SCALE - 1))
+
+void
+GetCurrentVelocityComponents (VELOCITY_DESC *velocityptr, SIZE *pdx, SIZE *pdy)
+{
+ *pdx = WORLD_TO_VELOCITY (velocityptr->vector.width)
+ + (velocityptr->fract.width - (SIZE)HIBYTE (velocityptr->incr.width));
+ *pdy = WORLD_TO_VELOCITY (velocityptr->vector.height)
+ + (velocityptr->fract.height - (SIZE)HIBYTE (velocityptr->incr.height));
+}
+
+void
+GetNextVelocityComponents (VELOCITY_DESC *velocityptr, SIZE *pdx, SIZE *pdy,
+ COUNT num_frames)
+{
+ COUNT e;
+
+ e = (COUNT)((COUNT)velocityptr->error.width +
+ ((COUNT)velocityptr->fract.width * num_frames));
+ *pdx = (velocityptr->vector.width * num_frames)
+ + ((SIZE)((SBYTE)LOBYTE (velocityptr->incr.width))
+ * (e >> VELOCITY_SHIFT));
+ velocityptr->error.width = VELOCITY_REMAINDER (e);
+
+ e = (COUNT)((COUNT)velocityptr->error.height +
+ ((COUNT)velocityptr->fract.height * num_frames));
+ *pdy = (velocityptr->vector.height * num_frames)
+ + ((SIZE)((SBYTE)LOBYTE (velocityptr->incr.height))
+ * (e >> VELOCITY_SHIFT));
+ velocityptr->error.height = VELOCITY_REMAINDER (e);
+}
+
+void
+SetVelocityVector (VELOCITY_DESC *velocityptr, SIZE magnitude, COUNT facing)
+{
+ COUNT angle;
+ SIZE dx, dy;
+
+ angle = velocityptr->TravelAngle =
+ FACING_TO_ANGLE (NORMALIZE_FACING (facing));
+ magnitude = WORLD_TO_VELOCITY (magnitude);
+ dx = COSINE (angle, magnitude);
+ dy = SINE (angle, magnitude);
+ if (dx >= 0)
+ {
+ velocityptr->vector.width = VELOCITY_TO_WORLD (dx);
+ velocityptr->incr.width = MAKE_WORD ((BYTE)1, (BYTE)0);
+ }
+ else
+ {
+ dx = -dx;
+ velocityptr->vector.width = -VELOCITY_TO_WORLD (dx);
+ velocityptr->incr.width =
+ MAKE_WORD ((BYTE)0xFF, (BYTE)(VELOCITY_REMAINDER (dx) << 1));
+ }
+ if (dy >= 0)
+ {
+ velocityptr->vector.height = VELOCITY_TO_WORLD (dy);
+ velocityptr->incr.height = MAKE_WORD ((BYTE)1, (BYTE)0);
+ }
+ else
+ {
+ dy = -dy;
+ velocityptr->vector.height = -VELOCITY_TO_WORLD (dy);
+ velocityptr->incr.height =
+ MAKE_WORD ((BYTE)0xFF, (BYTE)(VELOCITY_REMAINDER (dy) << 1));
+ }
+
+ velocityptr->fract.width = VELOCITY_REMAINDER (dx);
+ velocityptr->fract.height = VELOCITY_REMAINDER (dy);
+ velocityptr->error.width = velocityptr->error.height = 0;
+}
+
+void
+SetVelocityComponents (VELOCITY_DESC *velocityptr, SIZE dx, SIZE dy)
+{
+ COUNT angle;
+
+ if ((angle = ARCTAN (dx, dy)) == FULL_CIRCLE)
+ {
+ ZeroVelocityComponents (velocityptr);
+ }
+ else
+ {
+ if (dx >= 0)
+ {
+ velocityptr->vector.width = VELOCITY_TO_WORLD (dx);
+ velocityptr->incr.width = MAKE_WORD ((BYTE)1, (BYTE)0);
+ }
+ else
+ {
+ dx = -dx;
+ velocityptr->vector.width = -VELOCITY_TO_WORLD (dx);
+ velocityptr->incr.width =
+ MAKE_WORD ((BYTE)0xFF, (BYTE)(VELOCITY_REMAINDER (dx) << 1));
+ }
+ if (dy >= 0)
+ {
+ velocityptr->vector.height = VELOCITY_TO_WORLD (dy);
+ velocityptr->incr.height = MAKE_WORD ((BYTE)1, (BYTE)0);
+ }
+ else
+ {
+ dy = -dy;
+ velocityptr->vector.height = -VELOCITY_TO_WORLD (dy);
+ velocityptr->incr.height =
+ MAKE_WORD ((BYTE)0xFF, (BYTE)(VELOCITY_REMAINDER (dy) << 1));
+ }
+
+ velocityptr->fract.width = VELOCITY_REMAINDER (dx);
+ velocityptr->fract.height = VELOCITY_REMAINDER (dy);
+ velocityptr->error.width = velocityptr->error.height = 0;
+ }
+
+ velocityptr->TravelAngle = angle;
+}
+
+void
+DeltaVelocityComponents (VELOCITY_DESC *velocityptr, SIZE dx, SIZE dy)
+{
+
+ dx += WORLD_TO_VELOCITY (velocityptr->vector.width)
+ + (velocityptr->fract.width - (SIZE)HIBYTE (velocityptr->incr.width));
+ dy += WORLD_TO_VELOCITY (velocityptr->vector.height)
+ + (velocityptr->fract.height - (SIZE)HIBYTE (velocityptr->incr.height));
+
+ SetVelocityComponents (velocityptr, dx, dy);
+}
+
diff --git a/src/uqm/velocity.h b/src/uqm/velocity.h
new file mode 100644
index 0000000..968e1f7
--- /dev/null
+++ b/src/uqm/velocity.h
@@ -0,0 +1,76 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_VELOCITY_H_
+#define UQM_VELOCITY_H_
+
+#include <string.h> /* for memset */
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct velocity_desc
+{
+ COUNT TravelAngle;
+ EXTENT vector;
+ EXTENT fract;
+ EXTENT error;
+ EXTENT incr;
+} VELOCITY_DESC;
+
+#define ZeroVelocityComponents(pv) memset(pv,0,sizeof (*(pv)))
+#define GetVelocityTravelAngle(pv) (pv)->TravelAngle
+
+extern void GetCurrentVelocityComponents (VELOCITY_DESC *velocityptr,
+ SIZE *pdx, SIZE *pdy);
+extern void GetNextVelocityComponents (VELOCITY_DESC *velocityptr,
+ SIZE *pdx, SIZE *pdy, COUNT num_frames);
+extern void SetVelocityVector (VELOCITY_DESC *velocityptr, SIZE magnitude,
+ COUNT facing);
+extern void SetVelocityComponents (VELOCITY_DESC *velocityptr, SIZE dx,
+ SIZE dy);
+extern void DeltaVelocityComponents (VELOCITY_DESC *velocityptr, SIZE dx,
+ SIZE dy);
+
+static inline bool
+IsVelocityZero (VELOCITY_DESC *vptr)
+{
+ return vptr->vector.width == 0 && vptr->vector.height == 0 &&
+ vptr->incr.width == 0 && vptr->incr.height == 0 &&
+ vptr->fract.width == 0 && vptr->fract.height == 0;
+}
+
+static inline DWORD
+VelocitySquared (SIZE dx, SIZE dy)
+{
+ return (DWORD)((long)dx * dx + (long)dy * dy);
+}
+
+#define VELOCITY_SHIFT 5
+#define VELOCITY_SCALE (1<<VELOCITY_SHIFT)
+
+#define VELOCITY_TO_WORLD(v) ((v)>>VELOCITY_SHIFT)
+#define WORLD_TO_VELOCITY(l) ((l)<<VELOCITY_SHIFT)
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_VELOCITY_H_ */
diff --git a/src/uqm/weapon.c b/src/uqm/weapon.c
new file mode 100644
index 0000000..3f88f98
--- /dev/null
+++ b/src/uqm/weapon.c
@@ -0,0 +1,414 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "weapon.h"
+
+#include "colors.h"
+#include "globdata.h"
+#include "status.h"
+#include "init.h"
+#include "races.h"
+#include "ship.h"
+#include "setup.h"
+#include "sounds.h"
+#include "units.h"
+#include "libs/mathlib.h"
+
+#include <stdio.h>
+
+// A wrapper function for weapon_collision that discards the return value.
+// This makes its signature match ElementCollisionFunc.
+static void
+weapon_collision_cb (ELEMENT *WeaponElementPtr, POINT *pWPt,
+ ELEMENT *HitElementPtr, POINT *pHPt)
+{
+ weapon_collision (WeaponElementPtr, pWPt, HitElementPtr, pHPt);
+}
+
+
+HELEMENT
+initialize_laser (LASER_BLOCK *pLaserBlock)
+{
+ HELEMENT hLaserElement;
+
+ hLaserElement = AllocElement ();
+ if (hLaserElement)
+ {
+#define LASER_LIFE 1
+ ELEMENT *LaserElementPtr;
+
+ LockElement (hLaserElement, &LaserElementPtr);
+ LaserElementPtr->playerNr = pLaserBlock->sender;
+ LaserElementPtr->hit_points = 1;
+ LaserElementPtr->mass_points = 1;
+ LaserElementPtr->state_flags = APPEARING | FINITE_LIFE
+ | pLaserBlock->flags;
+ LaserElementPtr->life_span = LASER_LIFE;
+ LaserElementPtr->collision_func = weapon_collision_cb;
+ LaserElementPtr->blast_offset = 1;
+
+ LaserElementPtr->current.location.x = pLaserBlock->cx
+ + COSINE (FACING_TO_ANGLE (pLaserBlock->face),
+ DISPLAY_TO_WORLD (pLaserBlock->pixoffs));
+ LaserElementPtr->current.location.y = pLaserBlock->cy
+ + SINE (FACING_TO_ANGLE (pLaserBlock->face),
+ DISPLAY_TO_WORLD (pLaserBlock->pixoffs));
+ SetPrimType (&DisplayArray[LaserElementPtr->PrimIndex], LINE_PRIM);
+ SetPrimColor (&DisplayArray[LaserElementPtr->PrimIndex],
+ pLaserBlock->color);
+ LaserElementPtr->current.image.frame = DecFrameIndex (stars_in_space);
+ LaserElementPtr->current.image.farray = &stars_in_space;
+ SetVelocityComponents (&LaserElementPtr->velocity,
+ WORLD_TO_VELOCITY ((pLaserBlock->cx + pLaserBlock->ex)
+ - LaserElementPtr->current.location.x),
+ WORLD_TO_VELOCITY ((pLaserBlock->cy + pLaserBlock->ey)
+ - LaserElementPtr->current.location.y));
+ UnlockElement (hLaserElement);
+ }
+
+ return (hLaserElement);
+}
+
+HELEMENT
+initialize_missile (MISSILE_BLOCK *pMissileBlock)
+{
+ HELEMENT hMissileElement;
+
+ hMissileElement = AllocElement ();
+ if (hMissileElement)
+ {
+ SIZE delta_x, delta_y;
+ COUNT angle;
+ ELEMENT *MissileElementPtr;
+
+ LockElement (hMissileElement, &MissileElementPtr);
+ MissileElementPtr->hit_points = (BYTE)pMissileBlock->hit_points;
+ MissileElementPtr->mass_points = (BYTE)pMissileBlock->damage;
+ MissileElementPtr->playerNr = pMissileBlock->sender;
+ MissileElementPtr->state_flags = APPEARING | FINITE_LIFE
+ | pMissileBlock->flags;
+ MissileElementPtr->life_span = pMissileBlock->life;
+ SetPrimType (&DisplayArray[MissileElementPtr->PrimIndex], STAMP_PRIM);
+ MissileElementPtr->current.image.farray = pMissileBlock->farray;
+ MissileElementPtr->current.image.frame =
+ SetAbsFrameIndex (pMissileBlock->farray[0],
+ pMissileBlock->index);
+ MissileElementPtr->preprocess_func = pMissileBlock->preprocess_func;
+ MissileElementPtr->collision_func = weapon_collision_cb;
+ MissileElementPtr->blast_offset = (BYTE)pMissileBlock->blast_offs;
+
+ angle = FACING_TO_ANGLE (pMissileBlock->face);
+ MissileElementPtr->current.location.x = pMissileBlock->cx
+ + COSINE (angle, DISPLAY_TO_WORLD (pMissileBlock->pixoffs));
+ MissileElementPtr->current.location.y = pMissileBlock->cy
+ + SINE (angle, DISPLAY_TO_WORLD (pMissileBlock->pixoffs));
+
+ delta_x = COSINE (angle, WORLD_TO_VELOCITY (pMissileBlock->speed));
+ delta_y = SINE (angle, WORLD_TO_VELOCITY (pMissileBlock->speed));
+ SetVelocityComponents (&MissileElementPtr->velocity,
+ delta_x, delta_y);
+
+ MissileElementPtr->current.location.x -= VELOCITY_TO_WORLD (delta_x);
+ MissileElementPtr->current.location.y -= VELOCITY_TO_WORLD (delta_y);
+ UnlockElement (hMissileElement);
+ }
+
+ return (hMissileElement);
+}
+
+HELEMENT
+weapon_collision (ELEMENT *WeaponElementPtr, POINT *pWPt,
+ ELEMENT *HitElementPtr, POINT *pHPt)
+{
+ SIZE damage;
+ HELEMENT hBlastElement;
+
+ if (WeaponElementPtr->state_flags & COLLISION) /* if already did effect */
+ return ((HELEMENT)0);
+
+ damage = (SIZE)WeaponElementPtr->mass_points;
+ if (damage
+ && ((HitElementPtr->state_flags & FINITE_LIFE)
+ || HitElementPtr->life_span == NORMAL_LIFE))
+#ifdef NEVER
+ &&
+ /* lasers from the same ship can't hit each other */
+ (GetPrimType (&DisplayArray[HitElementPtr->PrimIndex]) != LINE_PRIM
+ || GetPrimType (&DisplayArray[WeaponElementPtr->PrimIndex]) != LINE_PRIM
+ || !elementsOfSamePlayer (HitElementPtr, WeaponElementPtr)))
+#endif /* NEVER */
+ {
+ do_damage (HitElementPtr, damage);
+ if (HitElementPtr->hit_points)
+ WeaponElementPtr->state_flags |= COLLISION;
+ }
+
+ if (!(HitElementPtr->state_flags & FINITE_LIFE)
+ || (!(/* WeaponElementPtr->state_flags
+ & */ HitElementPtr->state_flags & COLLISION)
+ && WeaponElementPtr->hit_points <= HitElementPtr->mass_points))
+ {
+ if (damage)
+ {
+ damage = TARGET_DAMAGED_FOR_1_PT + (damage >> 1);
+ if (damage > TARGET_DAMAGED_FOR_6_PLUS_PT)
+ damage = TARGET_DAMAGED_FOR_6_PLUS_PT;
+ ProcessSound (SetAbsSoundIndex (GameSounds, damage),
+ HitElementPtr);
+ }
+
+ if (GetPrimType (&DisplayArray[WeaponElementPtr->PrimIndex])
+ != LINE_PRIM)
+ WeaponElementPtr->state_flags |= DISAPPEARING;
+
+ WeaponElementPtr->hit_points = 0;
+ WeaponElementPtr->life_span = 0;
+ WeaponElementPtr->state_flags |= COLLISION | NONSOLID;
+
+ hBlastElement = AllocElement ();
+ if (hBlastElement)
+ {
+ COUNT blast_index;
+ SIZE blast_offs;
+ COUNT angle, num_blast_frames;
+ ELEMENT *BlastElementPtr;
+ extern FRAME blast[];
+
+ PutElement (hBlastElement);
+ LockElement (hBlastElement, &BlastElementPtr);
+ BlastElementPtr->playerNr = WeaponElementPtr->playerNr;
+ BlastElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
+ SetPrimType (&DisplayArray[BlastElementPtr->PrimIndex], STAMP_PRIM);
+
+ BlastElementPtr->current.location.x = DISPLAY_TO_WORLD (pWPt->x);
+ BlastElementPtr->current.location.y = DISPLAY_TO_WORLD (pWPt->y);
+
+ angle = GetVelocityTravelAngle (&WeaponElementPtr->velocity);
+ if ((blast_offs = WeaponElementPtr->blast_offset) > 0)
+ {
+ BlastElementPtr->current.location.x +=
+ COSINE (angle, DISPLAY_TO_WORLD (blast_offs));
+ BlastElementPtr->current.location.y +=
+ SINE (angle, DISPLAY_TO_WORLD (blast_offs));
+ }
+
+ blast_index =
+ NORMALIZE_FACING (ANGLE_TO_FACING (angle + HALF_CIRCLE));
+ blast_index = ((blast_index >> 2) << 1) +
+ (blast_index & 0x3 ? 1 : 0);
+
+ num_blast_frames =
+ GetFrameCount (WeaponElementPtr->next.image.frame);
+ if (num_blast_frames <= ANGLE_TO_FACING (FULL_CIRCLE))
+ {
+ BlastElementPtr->life_span = 2;
+ BlastElementPtr->current.image.farray = blast;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (blast[0], blast_index);
+ }
+ else
+ {
+ BlastElementPtr->life_span = num_blast_frames
+ - ANGLE_TO_FACING (FULL_CIRCLE);
+ BlastElementPtr->turn_wait = BlastElementPtr->next_turn = 0;
+ BlastElementPtr->preprocess_func = animation_preprocess;
+ BlastElementPtr->current.image.farray =
+ WeaponElementPtr->next.image.farray;
+ BlastElementPtr->current.image.frame =
+ SetAbsFrameIndex (
+ BlastElementPtr->current.image.farray[0],
+ ANGLE_TO_FACING (FULL_CIRCLE));
+ }
+
+ UnlockElement (hBlastElement);
+
+ return (hBlastElement);
+ }
+ }
+
+ (void) pHPt; /* Satisfying compiler (unused parameter) */
+ return ((HELEMENT)0);
+}
+
+FRAME
+ModifySilhouette (ELEMENT *ElementPtr, STAMP *modify_stamp,
+ BYTE modify_flags)
+{
+ FRAME f;
+ RECT r, or;
+ INTERSECT_CONTROL ShipIntersect, ObjectIntersect;
+ STARSHIP *StarShipPtr;
+ CONTEXT OldContext;
+
+ f = 0;
+ ObjectIntersect.IntersectStamp = *modify_stamp;
+ GetFrameRect (ObjectIntersect.IntersectStamp.frame, &or);
+
+ GetElementStarShip (ElementPtr, &StarShipPtr);
+ if (modify_flags & MODIFY_IMAGE)
+ {
+ ShipIntersect.IntersectStamp.frame = SetAbsFrameIndex (
+ StarShipPtr->RaceDescPtr->ship_info.icons, 1);
+ if (ShipIntersect.IntersectStamp.frame == 0)
+ return (0);
+
+ GetFrameRect (ShipIntersect.IntersectStamp.frame, &r);
+
+ ShipIntersect.IntersectStamp.origin.x = 0;
+ ShipIntersect.IntersectStamp.origin.y = 0;
+ ShipIntersect.EndPoint = ShipIntersect.IntersectStamp.origin;
+ do
+ {
+ ObjectIntersect.IntersectStamp.origin.x = ((COUNT)TFB_Random ()
+ % (r.extent.width - or.extent.width))
+ + ((or.extent.width - r.extent.width) >> 1);
+ ObjectIntersect.IntersectStamp.origin.y = ((COUNT)TFB_Random ()
+ % (r.extent.height - or.extent.height))
+ + ((or.extent.height - r.extent.height) >> 1);
+ ObjectIntersect.EndPoint = ObjectIntersect.IntersectStamp.origin;
+ } while (!DrawablesIntersect (&ObjectIntersect,
+ &ShipIntersect, MAX_TIME_VALUE));
+
+ ObjectIntersect.IntersectStamp.origin.x += STATUS_WIDTH >> 1;
+ ObjectIntersect.IntersectStamp.origin.y += 31;
+ }
+
+ ObjectIntersect.IntersectStamp.origin.y +=
+ status_y_offsets[ElementPtr->playerNr];
+
+ if (modify_flags & MODIFY_SWAP)
+ {
+ or.corner.x += ObjectIntersect.IntersectStamp.origin.x;
+ or.corner.y += ObjectIntersect.IntersectStamp.origin.y;
+ InitShipStatus (&StarShipPtr->RaceDescPtr->ship_info,
+ StarShipPtr, &or);
+ }
+ else
+ {
+ OldContext = SetContext (StatusContext);
+ DrawStamp (&ObjectIntersect.IntersectStamp);
+ SetContext (OldContext);
+ }
+
+ return (f);
+}
+
+// Find the closest possible target ship, to be set in Tracker->hTarget.
+// *pfacing will be turned one angle unit into the direction towards the
+// target.
+// The return value will be the actual number of angle units to turn, or
+// -1 if no target was found.
+// Cloaked ships won't be detected, except when the APPEARING flag is
+// set for the Tracker.
+SIZE
+TrackShip (ELEMENT *Tracker, COUNT *pfacing)
+{
+ SIZE best_delta_facing, best_delta;
+ HELEMENT hShip, hNextShip;
+ ELEMENT *Trackee;
+
+ best_delta = 0;
+ best_delta_facing = -1;
+
+ hShip = Tracker->hTarget;
+ if (hShip)
+ {
+ LockElement (hShip, &Trackee);
+ Tracker->hTarget = hNextShip = 0;
+
+ goto CheckTracking;
+ }
+
+ for (hShip = GetHeadElement (); hShip != 0; hShip = hNextShip)
+ {
+ LockElement (hShip, &Trackee);
+ hNextShip = GetSuccElement (Trackee);
+ if ((Trackee->state_flags & PLAYER_SHIP)
+ && !elementsOfSamePlayer (Trackee, Tracker)
+ && (!OBJECT_CLOAKED (Trackee)
+ || ((Tracker->state_flags & PLAYER_SHIP)
+ && (Tracker->state_flags & APPEARING))
+ ))
+ {
+ STARSHIP *StarShipPtr;
+
+CheckTracking:
+ GetElementStarShip (Trackee, &StarShipPtr);
+ if (Trackee->life_span
+ && StarShipPtr->RaceDescPtr->ship_info.crew_level)
+ {
+ SIZE delta_x, delta_y, delta_facing;
+
+ if (Tracker->state_flags & PRE_PROCESS)
+ {
+ delta_x = Trackee->next.location.x
+ - Tracker->next.location.x;
+ delta_y = Trackee->next.location.y
+ - Tracker->next.location.y;
+ }
+ else
+ {
+ delta_x = Trackee->current.location.x
+ - Tracker->current.location.x;
+ delta_y = Trackee->current.location.y
+ - Tracker->current.location.y;
+ }
+
+ delta_x = WRAP_DELTA_X (delta_x);
+ delta_y = WRAP_DELTA_Y (delta_y);
+ delta_facing = NORMALIZE_FACING (
+ ANGLE_TO_FACING (ARCTAN (delta_x, delta_y)) - *pfacing
+ );
+
+ if (delta_x < 0)
+ delta_x = -delta_x;
+ if (delta_y < 0)
+ delta_y = -delta_y;
+ delta_x += delta_y;
+ // 'delta_x + delta_y' is used as an approximation
+ // of the actual distance 'sqrt(sqr(delta_x) +
+ // sqr(delta_y))'.
+
+ if (best_delta == 0 || delta_x < best_delta)
+ {
+ best_delta = delta_x;
+ best_delta_facing = delta_facing;
+ Tracker->hTarget = hShip;
+ }
+ }
+ }
+ UnlockElement (hShip);
+ }
+
+ if (best_delta_facing > 0)
+ {
+ COUNT facing;
+
+ facing = *pfacing;
+ if (best_delta_facing == ANGLE_TO_FACING (HALF_CIRCLE))
+ facing += (((BYTE)TFB_Random () & 1) << 1) - 1;
+ else if (best_delta_facing < ANGLE_TO_FACING (HALF_CIRCLE))
+ ++facing;
+ else
+ --facing;
+ *pfacing = NORMALIZE_FACING (facing);
+ }
+
+ return (best_delta_facing);
+}
+
diff --git a/src/uqm/weapon.h b/src/uqm/weapon.h
new file mode 100644
index 0000000..128d71c
--- /dev/null
+++ b/src/uqm/weapon.h
@@ -0,0 +1,68 @@
+//Copyright Paul Reiche, Fred Ford. 1992-2002
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQM_WEAPON_H_
+#define UQM_WEAPON_H_
+
+#include "element.h"
+#include "libs/gfxlib.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+typedef struct
+{
+ COORD cx, cy, ex, ey;
+ ELEMENT_FLAGS flags;
+ SIZE sender; // player number
+ SIZE pixoffs;
+ COUNT face;
+ Color color;
+} LASER_BLOCK;
+
+typedef struct
+{
+ COORD cx, cy;
+ ELEMENT_FLAGS flags;
+ SIZE sender; // player number
+ SIZE pixoffs, speed, hit_points, damage;
+ COUNT face, index, life;
+ FRAME *farray;
+ void (*preprocess_func) (ELEMENT *ElementPtr);
+ SIZE blast_offs;
+} MISSILE_BLOCK;
+
+extern HELEMENT initialize_laser (LASER_BLOCK *pLaserBlock);
+extern HELEMENT initialize_missile (MISSILE_BLOCK *pMissileBlock);
+extern HELEMENT weapon_collision (ELEMENT *ElementPtr0, POINT *pPt0,
+ ELEMENT *ElementPtr1, POINT *pPt1);
+extern SIZE TrackShip (ELEMENT *Tracker, COUNT *pfacing);
+extern void Untarget (ELEMENT *ElementPtr);
+
+#define MODIFY_IMAGE (1 << 0)
+#define MODIFY_SWAP (1 << 1)
+
+extern FRAME ModifySilhouette (ELEMENT *ElementPtr, STAMP *modify_stamp,
+ BYTE modify_flags);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UQM_WEAPON_H_ */
diff --git a/src/uqmversion.h b/src/uqmversion.h
new file mode 100644
index 0000000..11b7156
--- /dev/null
+++ b/src/uqmversion.h
@@ -0,0 +1,36 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UQMVERSION_H
+#define UQMVERSION_H
+
+#define UQM_MAJOR_VERSION 0
+#define UQM_MAJOR_VERSION_S "0"
+#define UQM_MINOR_VERSION 8
+#define UQM_MINOR_VERSION_S "8"
+#define UQM_PATCH_VERSION 0
+#define UQM_PATCH_VERSION_S "0"
+#define UQM_EXTRA_VERSION ""
+/* The final version is interpreted as:
+ * printf ("%d.%d.%d%s", UQM_MAJOR_VERSION, UQM_MINOR_VERSION,
+ * UQM_PATCH_VERSION, UQM_EXTRA_VERSION);
+ */
+
+#define UQM_STRING_VERSION \
+ UQM_MAJOR_VERSION_S "." UQM_MINOR_VERSION_S "." UQM_PATCH_VERSION_S \
+ UQM_EXTRA_VERSION
+
+#endif
diff --git a/subst b/subst
new file mode 100755
index 0000000..16a5209
--- /dev/null
+++ b/subst
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Substitute a string in many files. By Serge van den Boom, 20020826
+
+# We need bash functionality. Executing bash with the shebang won't be
+# portable as the location of bash differs.
+if [ -z "$BASH_VERSION" ]; then
+ exec bash "$0" "$@"
+fi
+
+if [ $# -eq 0 ]; then
+ {
+ echo "subst: substitute a string in a lot of files."
+ echo "By Serge van den Boom, 20020826"
+ echo "Usage: subst <pattern> <file> [...]"
+ echo -n "'pattern' should be a pattern in the form used by sed, "
+ echo " like 's/old/new/g'."
+ } 1>&2
+ exit 1
+fi
+
+PAT="$1"
+SEP="${PAT:1:1}"
+GREPPAT="${PAT:2}"
+eval GREPPAT='${GREPPAT%%'$SEP'*}'
+shift
+while [ "$#" -gt "0" ]; do
+ FILE="$1"
+ echo -n "$FILE: "
+ grep -q "$GREPPAT" < "$FILE"
+ EXITVAL=$?
+ case $EXITVAL in
+ 0) # Match found
+ ;;
+ 1) # No match found
+ echo "Nothing to do."
+ shift
+ continue
+ ;;
+ *)
+ echo "ERROR"
+ echo "Error: grep returned exit value ${EXITVAL}. Aborted." 1>&2
+ exit 1
+ esac
+ TEMPFILE="${FILE}.patchtree.$$.tmp"
+ mv -- "$FILE" "$TEMPFILE"
+ sed -e "$PAT" < "$TEMPFILE" > "$FILE"
+ rm -- "$TEMPFILE"
+ echo "Done."
+ shift
+done
+
diff --git a/uqm-indent b/uqm-indent
new file mode 100755
index 0000000..4d441b9
--- /dev/null
+++ b/uqm-indent
@@ -0,0 +1,2 @@
+#!/bin/bash
+indent -bad -bap -bl -bli0 -bls -cbi0 -cdw -ci8 -cli4 -i4 -hnl -l76 -nbc -nbfda -nce -nfca -nlp -nprs -nsob -pcs -psl -saf -sai -saw -sbi0 -ts4 -ut "$@"
diff --git a/uqm.lsm b/uqm.lsm
new file mode 100644
index 0000000..6836af1
--- /dev/null
+++ b/uqm.lsm
@@ -0,0 +1,24 @@
+Begin3
+Title: The Ur-Quan Masters
+Version: 0.7.0
+Entered-date: 2011-07-04
+Description: Port of the classic game 'Star Control II'.
+Keywords: Ur-Quan Masters, Star Control, game, space adventure,
+ Super Melee
+Author: Toys for Bob <alexness@toysforbob.com>
+ The Ur-Quan Masters developers <sc2-devel@sourceforge.net>
+Maintained-by: Serge van den Boom <svdb@stack.nl>
+ Mika Kolehmainen <mimakole@cc.jyu.fi>
+ Michael Chapman Martin <mcmartin@gmail.com>
+ Alex Volkov <codepro@usa.net>
+Primary-site: http://sourceforge.net/projects/sc2/files/
+ 1251331 uqm-0.7.0-installer.exe
+ 11538533 uqm-0.7.0-content.uqm
+ 18980671 uqm-0.7.0-3domusic.uqm
+ 115143439 uqm-0.7.0-voice.uqm
+ 14716453 uqm-0.7.0-macosx.dmg
+ 1562065 uqm-0.7.0-source.tgz
+Platforms: Windows, Linux, FreeBSD, OpenBSD, MacOS X, BeOS
+Copying-policy: GPL for the C code, CC-by-nc-sa 2.5 or custom for the graphics and audio content, CC-by 2.0 for the documentation
+End
+